129 lines
5.8 KiB
Go
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)
|
|
}
|
|
}
|