Files
go-sip/internal/agent/result_journal_test.go
T

139 lines
4.5 KiB
Go

package agent
import (
"context"
"errors"
"os"
"path/filepath"
"testing"
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
)
func TestResultJournalResumesOnlyConfirmedResults(t *testing.T) {
root := t.TempDir()
if err := os.Chmod(root, 0700); err != nil {
t.Fatal(err)
}
j := ResultJournal{Root: root}
entry := ResultEntry{SourceEventID: "call-1", DispatcherID: "11111111-1111-4111-8111-111111111111", TenantID: 1, Payload: []byte(`{"event_id":"call-1","recording":{}}`)}
if err := j.SaveReady(entry); err != nil {
t.Fatal(err)
}
attempts := 0
restarted := ResultJournal{Root: root}
if err := restarted.Recover(context.Background(), func(_ context.Context, got ResultEntry) error {
attempts++
if got.SourceEventID != entry.SourceEventID || string(got.Payload) != string(entry.Payload) || got.Upload != nil {
t.Fatalf("changed original result: %+v", got)
}
if attempts == 1 {
return errors.New("Dispatcher response unknown")
}
return nil
}); err == nil {
t.Fatal("unconfirmed result must remain for a later retry")
}
if err := restarted.Recover(context.Background(), func(ctx context.Context, got ResultEntry) error {
attempts++
return nil
}); err != nil {
t.Fatal(err)
}
if attempts != 2 {
t.Fatalf("expected one retry, got %d attempts", attempts)
}
if err := restarted.Recover(context.Background(), func(context.Context, ResultEntry) error { t.Fatal("confirmed result repeated"); return nil }); err != nil {
t.Fatal(err)
}
}
func TestResultJournalNeverReportsPreparedUpload(t *testing.T) {
root := t.TempDir()
if err := os.Chmod(root, 0700); err != nil {
t.Fatal(err)
}
j := ResultJournal{Root: root}
entry := ResultEntry{SourceEventID: "call-2", DispatcherID: "11111111-1111-4111-8111-111111111111", TenantID: 1, Payload: []byte(`{"event_id":"call-2","recording":{"status":"uploaded"}}`)}
if err := j.SavePrepared(entry); err != nil {
t.Fatal(err)
}
if err := (&ResultJournal{Root: root}).Recover(context.Background(), func(context.Context, ResultEntry) error {
t.Fatal("a PUT with unknown outcome cannot generate a final result")
return nil
}); err == nil {
t.Fatal("prepared result must remain visibly blocked")
}
observation := &agentpb.UploadObservation{UploadId: "upload-2", RecordingId: "recording-2", PutStatusCode: 200, SizeBytes: 10, ChecksumSha256: "digest"}
if err := j.MarkUploaded("call-2", observation); err != nil {
t.Fatal(err)
}
if err := j.DiscardPrepared("call-2"); err == nil {
t.Fatal("a ready result must not be discarded as an incomplete upload")
}
if err := (&ResultJournal{Root: root}).Recover(context.Background(), func(_ context.Context, got ResultEntry) error {
if got.Upload == nil || got.Upload.GetUploadId() != observation.GetUploadId() {
t.Fatal("uploaded proof missing after restart")
}
return nil
}); err != nil {
t.Fatal(err)
}
if _, err := os.ReadDir(filepath.Join(root, ".results")); err != nil {
t.Fatal(err)
}
}
func TestResultJournalRejectsTrailingCorruptData(t *testing.T) {
root := t.TempDir()
if err := os.Chmod(root, 0700); err != nil {
t.Fatal(err)
}
j := ResultJournal{Root: root}
entry := ResultEntry{SourceEventID: "call-corrupt", DispatcherID: "11111111-1111-4111-8111-111111111111", TenantID: 1, Payload: []byte(`{"recording":{}}`)}
if err := j.SaveReady(entry); err != nil {
t.Fatal(err)
}
path, err := j.path(entry.SourceEventID)
if err != nil {
t.Fatal(err)
}
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
t.Fatal(err)
}
if _, err := file.WriteString("\n{}"); err != nil {
t.Fatal(err)
}
if err := file.Close(); err != nil {
t.Fatal(err)
}
if err := j.Recover(context.Background(), func(context.Context, ResultEntry) error {
t.Fatal("corrupt result was reported")
return nil
}); err == nil {
t.Fatal("corrupt result must remain visibly blocked")
}
}
func TestResultJournalDiscardPreparedAfterRecovery(t *testing.T) {
root := t.TempDir()
if err := os.Chmod(root, 0700); err != nil {
t.Fatal(err)
}
j := ResultJournal{Root: root}
entry := ResultEntry{SourceEventID: "call-3", DispatcherID: "11111111-1111-4111-8111-111111111111", TenantID: 1, Payload: []byte(`{"recording":{"status":"uploaded"}}`)}
if err := j.SavePrepared(entry); err != nil {
t.Fatal(err)
}
if err := j.DiscardPrepared("call-3"); err != nil {
t.Fatal(err)
}
if err := j.DiscardPrepared("call-3"); err != nil {
t.Fatal(err)
}
if items, err := os.ReadDir(filepath.Join(root, ".results")); err != nil || len(items) != 0 {
t.Fatalf("confirmed recovered result still has a prepared journal: %v %v", items, err)
}
}