feat: persist resumable client script uploads
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user