Files
go-sip/internal/rpc/dispatcher_upload_integration_test.go
T

129 lines
5.8 KiB
Go

package rpc
import (
"context"
"crypto/sha256"
"encoding/hex"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
agentv1 "git.ipao.vip/rogee/go-sip/gen/agent/v1"
"git.ipao.vip/rogee/go-sip/internal/agent"
ossclient "git.ipao.vip/rogee/go-sip/internal/oss"
"git.ipao.vip/rogee/go-sip/internal/store"
"google.golang.org/protobuf/proto"
)
func TestAlibabaOSSDispatcherUploadDurableIntegration(t *testing.T) {
if os.Getenv("AGENT_CALL_OSS_INTEGRATION") != "1" {
t.Skip("set AGENT_CALL_OSS_INTEGRATION=1 to use the authorized Alibaba OSS test bucket")
}
client, err := ossclient.NewClient(ossclient.Config{
Endpoint: os.Getenv("DISPATCHER_OSS_ENDPOINT"),
Region: os.Getenv("DISPATCHER_OSS_REGION"),
Bucket: os.Getenv("DISPATCHER_OSS_BUCKET"),
AccessKeyID: os.Getenv("DISPATCHER_OSS_ACCESS_KEY_ID"),
AccessKeySecret: os.Getenv("DISPATCHER_OSS_ACCESS_KEY_SECRET"),
KeyPrefix: "agent-call/rpc-integration-tests",
GrantTTL: 15 * time.Minute,
MaxAssetBytes: 1 << 20,
})
if err != nil {
t.Fatal(err)
}
path := filepath.Join(t.TempDir(), "recording.wav")
payload := []byte(strings.Repeat("dispatcher-upload-integration\n", 1024))
if sourcePath := os.Getenv("OSS_INTEGRATION_RECORDING_PATH"); sourcePath != "" {
var readErr error
payload, readErr = os.ReadFile(sourcePath)
if readErr != nil {
t.Fatal(readErr)
}
if len(payload) == 0 {
t.Fatal("OSS_INTEGRATION_RECORDING_PATH is empty")
}
}
if err := os.WriteFile(path, payload, 0o600); err != nil {
t.Fatal(err)
}
digest := sha256.Sum256(payload)
checksum := hex.EncodeToString(digest[:])
binding := &agentv1.ExecutionBinding{TenantId: "tenant-integration", TenantKey: "tenant-integration", ExecutionId: "exec-oss-integration", TaskId: "task-oss-integration", TaskItemId: "item-oss-integration", TaskRevision: 1, CallId: "call-oss-integration", AttemptId: "attempt-oss-integration"}
asset := &agentv1.AssetDescriptor{Kind: agentv1.AssetKind_ASSET_KIND_RECORDING, AssetId: "recording-integration", CallId: binding.CallId, ExecutionId: binding.ExecutionId, Format: "wav", SizeBytes: int64(len(payload)), ChecksumSha256: checksum, Channels: 1, SampleRateHz: 16000, DurationMs: 1000}
requestMeta := &agentv1.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "request-oss-integration", TraceId: "trace-oss-integration", OperationId: "operation-oss-integration", IdempotencyKey: "idempotency-oss-integration", AgentId: "agent-integration", CellId: "cell-integration"}
st, err := store.Open(filepath.Join(t.TempDir(), "dispatcher.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
clock := time.Now()
server, err := NewDispatcherUploadServer(st, client, func() time.Time { return clock }, false)
if err != nil {
t.Fatal(err)
}
uploadID := "upload-oss-integration"
grantResponse, err := server.RequestUpload(context.Background(), &agentv1.RequestUploadRequest{Meta: requestMeta, Binding: binding, Asset: asset, UploadId: uploadID})
if err != nil {
t.Fatal(err)
}
if grantResponse.Receipt.Result != agentv1.ResultCode_RESULT_CODE_ACCEPTED || grantResponse.Grant == nil {
t.Fatalf("grant rejected: %+v", grantResponse.Receipt)
}
clock = clock.Add(16 * time.Minute)
retryMeta := proto.Clone(requestMeta).(*agentv1.RequestMeta)
retryMeta.RequestId = "request-oss-integration-retry"
retryMeta.TraceId = "trace-oss-integration-retry"
retryMeta.OperationId = "operation-oss-integration-retry"
retryMeta.IdempotencyKey = "idempotency-oss-integration-retry"
reissuedResponse, err := server.RequestUpload(context.Background(), &agentv1.RequestUploadRequest{Meta: retryMeta, Binding: binding, Asset: asset, UploadId: uploadID})
if err != nil {
t.Fatal(err)
}
if reissuedResponse.Grant.ExpiresAtUnixMs <= grantResponse.Grant.ExpiresAtUnixMs {
t.Fatal("explicit post-expiry request did not receive a new grant")
}
grantResponse = reissuedResponse
parsed, err := url.Parse(grantResponse.Grant.TargetUrl)
if err != nil {
t.Fatal(err)
}
uploader := agent.UploadClient{Now: time.Now, AllowedHosts: map[string]struct{}{strings.ToLower(parsed.Host): {}}}
result, err := uploader.UploadFile(context.Background(), grantResponse.Grant, path)
if err != nil {
t.Fatal(err)
}
completeResponse, err := server.CompleteUpload(context.Background(), &agentv1.CompleteUploadRequest{Meta: requestMeta, Binding: binding, Asset: asset, UploadId: uploadID, UploadedSizeBytes: result.SizeBytes, UploadedChecksumSha256: result.SHA256})
if err != nil {
t.Fatal(err)
}
if completeResponse.Receipt.Result != agentv1.ResultCode_RESULT_CODE_ACCEPTED || completeResponse.OssId == "" {
t.Fatalf("completion rejected: %+v", completeResponse.Receipt)
}
var outboxCount int
if err := st.DB().QueryRow(`SELECT COUNT(*) FROM outbox WHERE event_id = ?`, "recording-ready-"+uploadID).Scan(&outboxCount); err != nil {
t.Fatal(err)
}
if outboxCount != 1 {
t.Fatalf("recording.ready outbox rows = %d, want 1", outboxCount)
}
requestAgain, err := server.RequestUpload(context.Background(), &agentv1.RequestUploadRequest{Meta: requestMeta, Binding: binding, Asset: asset, UploadId: uploadID})
if err != nil {
t.Fatal(err)
}
if requestAgain.Grant.ObjectKey != grantResponse.Grant.ObjectKey || requestAgain.Grant.ExpiresAtUnixMs != grantResponse.Grant.ExpiresAtUnixMs {
t.Fatal("duplicate request changed a completed upload grant")
}
completeAgain, err := server.CompleteUpload(context.Background(), &agentv1.CompleteUploadRequest{Meta: requestMeta, Binding: binding, Asset: asset, UploadId: uploadID, UploadedSizeBytes: result.SizeBytes, UploadedChecksumSha256: result.SHA256})
if err != nil {
t.Fatal(err)
}
if completeAgain.OssId != completeResponse.OssId || completeAgain.State != agentv1.UploadState_UPLOAD_STATE_COMPLETED {
t.Fatalf("duplicate completion changed result: %+v", completeAgain)
}
}