package spool import ( "context" "crypto/sha256" "errors" "path/filepath" "testing" "time" ) func TestScriptUploadDurableExactReplayAndCommit_HP_SCRIPT_01(t *testing.T) { t.Parallel() ctx := context.Background() directory := filepath.Join(t.TempDir(), "spool") store := openTestStore(t, ctx, directory, DefaultTombstoneLimit) issue := testUUID(t, "019c46f1-1d02-7000-8000-000000000031") if _, err := store.AcceptCommand(ctx, testCommand(issue, []byte("script command")), time.Now().UTC()); err != nil { t.Fatal(err) } raw := []byte("first chunk; second chunk") descriptor := ScriptDescriptor{SizeBytes: uint64(len(raw)), SHA256: sha256.Sum256(raw)} status, err := store.BeginScript(ctx, issue, descriptor) if err != nil || status.ReceivedBytes != 0 || status.Committed { t.Fatalf("BeginScript = %#v, %v", status, err) } duplicate, err := store.BeginScript(ctx, issue, descriptor) if err != nil || !duplicate.Duplicate { t.Fatalf("matching BeginScript replay = %#v, %v", duplicate, err) } first := raw[:12] status, err = store.AppendScriptChunk(ctx, issue, 0, first, sha256.Sum256(first)) if err != nil || status.ReceivedBytes != uint64(len(first)) { t.Fatalf("first ScriptChunk = %#v, %v", status, err) } duplicate, err = store.AppendScriptChunk(ctx, issue, 0, first, sha256.Sum256(first)) if err != nil || !duplicate.Duplicate || duplicate.ReceivedBytes != uint64(len(first)) { t.Fatalf("matching ScriptChunk replay = %#v, %v", duplicate, err) } if _, err := store.AppendScriptChunk(ctx, issue, uint64(len(first)+1), raw[len(first):], sha256.Sum256(raw[len(first):])); !errors.Is(err, ErrScriptHole) { t.Fatalf("script hole error = %v, want ErrScriptHole", err) } if _, err := store.AppendScriptChunk(ctx, issue, 0, []byte("different one"), sha256.Sum256([]byte("different one"))); !errors.Is(err, ErrScriptOverlap) { t.Fatalf("changed overlap error = %v, want ErrScriptOverlap", err) } if _, err := store.CommitScript(ctx, issue, descriptor); !errors.Is(err, ErrScriptBounds) { t.Fatalf("early commit error = %v, want ErrScriptBounds", err) } if err := store.Close(); err != nil { t.Fatal(err) } store = openTestStore(t, ctx, directory, DefaultTombstoneLimit) rest := raw[len(first):] status, err = store.AppendScriptChunk(ctx, issue, uint64(len(first)), rest, sha256.Sum256(rest)) if err != nil || status.ReceivedBytes != uint64(len(raw)) { t.Fatalf("resumed ScriptChunk = %#v, %v", status, err) } status, err = store.CommitScript(ctx, issue, descriptor) if err != nil || !status.Committed { t.Fatalf("ScriptCommit = %#v, %v", status, err) } duplicate, err = store.CommitScript(ctx, issue, descriptor) if err != nil || !duplicate.Duplicate || !duplicate.Committed { t.Fatalf("ScriptCommit replay = %#v, %v", duplicate, err) } duplicate, err = store.AppendScriptChunk(ctx, issue, 0, raw[:len(first)], sha256.Sum256(raw[:len(first)])) if err != nil || !duplicate.Duplicate || !duplicate.Committed || duplicate.ReceivedBytes != uint64(len(raw)) { t.Fatalf("post-commit ScriptChunk replay = %#v, %v", duplicate, err) } if _, err := store.AppendScriptChunk(ctx, issue, uint64(len(raw)), []byte("!"), sha256.Sum256([]byte("!"))); !errors.Is(err, ErrScriptTerminal) { t.Fatalf("post-commit chunk error = %v, want ErrScriptTerminal", err) } var compression uint32 var stored []byte if err := store.db.QueryRowContext(ctx, `SELECT compression, stored_data FROM scripts WHERE issue_uuid = ?`, issue[:]).Scan(&compression, &stored); err != nil { t.Fatal(err) } if compression != 2 || string(stored) == string(raw) { t.Fatalf("script was not stored as zstd data: compression=%d stored=%q", compression, stored) } } func TestScriptUploadRejectsDescriptorDigestAndBounds_BH_SCRIPT_01(t *testing.T) { t.Parallel() ctx := context.Background() store := openTestStore(t, ctx, filepath.Join(t.TempDir(), "spool"), DefaultTombstoneLimit) issue := testUUID(t, "019c46f1-1d02-7000-8000-000000000032") if _, err := store.AcceptCommand(ctx, testCommand(issue, []byte("script command")), time.Now().UTC()); err != nil { t.Fatal(err) } raw := []byte("abc") descriptor := ScriptDescriptor{SizeBytes: uint64(len(raw)), SHA256: sha256.Sum256(raw)} if _, err := store.BeginScript(ctx, issue, descriptor); err != nil { t.Fatal(err) } wrong := sha256.Sum256([]byte("wrong")) if _, err := store.AppendScriptChunk(ctx, issue, 0, raw, wrong); !errors.Is(err, ErrScriptDigest) { t.Fatalf("chunk digest error = %v, want ErrScriptDigest", err) } if _, err := store.AppendScriptChunk(ctx, issue, 0, []byte("abcd"), sha256.Sum256([]byte("abcd"))); !errors.Is(err, ErrScriptBounds) { t.Fatalf("out-of-bounds chunk error = %v, want ErrScriptBounds", err) } conflicting := descriptor conflicting.SHA256 = wrong if _, err := store.BeginScript(ctx, issue, conflicting); !errors.Is(err, ErrScriptConflict) { t.Fatalf("descriptor conflict error = %v, want ErrScriptConflict", err) } if err := store.MarkTerminal(ctx, issue, 5); err != nil { t.Fatal(err) } if _, err := store.AppendScriptChunk(ctx, issue, 0, raw, sha256.Sum256(raw)); !errors.Is(err, ErrScriptTerminal) { t.Fatalf("terminal command chunk error = %v, want ErrScriptTerminal", err) } }