85 lines
3.2 KiB
Go
85 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
|
|
"git.ipao.vip/rogee/go-sip/internal/agent"
|
|
"git.ipao.vip/rogee/go-sip/internal/config"
|
|
)
|
|
|
|
type uploadRPCStub struct {
|
|
grant *agentpb.UploadGrant
|
|
requests, notifications int
|
|
pending bool
|
|
}
|
|
|
|
func (s *uploadRPCStub) RequestUpload(_ context.Context, r *agentpb.RequestUploadRequest) (*agentpb.RequestUploadResponse, error) {
|
|
s.requests++
|
|
s.grant.UploadId = r.UploadId
|
|
return &agentpb.RequestUploadResponse{Grant: s.grant, Receipt: &agentpb.OperationReceipt{Result: agentpb.ResultCode_RESULT_CODE_ACCEPTED}}, nil
|
|
}
|
|
func (s *uploadRPCStub) CompleteUpload(_ context.Context, _ *agentpb.CompleteUploadRequest) (*agentpb.CompleteUploadResponse, error) {
|
|
s.notifications++
|
|
if s.pending {
|
|
return nil, errors.New("notification pending")
|
|
}
|
|
return &agentpb.CompleteUploadResponse{State: agentpb.UploadState_UPLOAD_STATE_COMPLETED, Receipt: &agentpb.OperationReceipt{Result: agentpb.ResultCode_RESULT_CODE_ACCEPTED}}, nil
|
|
}
|
|
|
|
func TestRecordingNotificationRecoveryDoesNotPUTAgain(t *testing.T) {
|
|
puts := 0
|
|
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { puts++; w.WriteHeader(http.StatusOK) }))
|
|
defer server.Close()
|
|
root := t.TempDir()
|
|
path := filepath.Join(root, "audio.wav")
|
|
if err := os.WriteFile(path, []byte("audio"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sum := sha256.Sum256([]byte("audio"))
|
|
binding := &agentpb.ExecutionBinding{TenantId: "tenant-a", TenantKey: "tenant-a", ExecutionId: "execution-a", CallId: "call-a"}
|
|
asset := &agentpb.AssetDescriptor{AssetId: "recording-a", SizeBytes: 5, ChecksumSha256: hex.EncodeToString(sum[:])}
|
|
remote := &uploadRPCStub{pending: true, grant: &agentpb.UploadGrant{ObjectKey: "recording-a", Bucket: "mock-bucket", TargetUrl: server.URL, MaxBytes: 5, RequiredChecksumSha256: asset.ChecksumSha256, ExpiresAtUnixMs: time.Now().Add(time.Minute).UnixMilli()}}
|
|
spool, err := agent.NewSpool(root, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
uploader := agent.UploadClient{HTTPClient: server.Client()}
|
|
if _, err := uploadRecording(context.Background(), config.Config{}, remote, uploader, spool, binding, asset, path); err == nil {
|
|
t.Fatal("pending MQ notification reported complete")
|
|
}
|
|
remote.pending = false
|
|
restarted, err := agent.NewSpool(root, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := recoverUploadNotifications(context.Background(), config.Config{}, remote, restarted); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
record, err := restarted.LoadUploadAttempt(stableUploadID(binding, asset))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if record.State != "completed" || puts != 1 || remote.requests != 1 || remote.notifications != 2 {
|
|
t.Fatalf("state=%s PUT=%d grant=%d notification=%d", record.State, puts, remote.requests, remote.notifications)
|
|
}
|
|
if _, err := uploadRecording(context.Background(), config.Config{}, remote, uploader, restarted, binding, asset, path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if puts != 1 || remote.notifications != 2 {
|
|
t.Fatal("completed upload repeated side effects")
|
|
}
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Fatal("source recording removed")
|
|
}
|
|
}
|