package artifacts import ( "bytes" "context" "strings" "testing" "gitea.maximumdirect.net/eric/narratio/internal/adapters/storage" ) func TestRemoteCommitManifestRejectsUnsupportedVersion(t *testing.T) { commit := testRemoteCommit(t, "20260519T010203Z-a1b2c3d4") pointer := testCommitPointer(t, testCurrentSessionPrefix(), commit) commit.FormatVersion++ if _, err := EncodeRemoteCommitManifest(commit); err == nil || !strings.Contains(err.Error(), "unsupported remote commit format version") { t.Fatalf("EncodeRemoteCommitManifest() error = %v, want unsupported version", err) } pointer.FormatVersion++ if _, err := EncodeCurrentCommitPointer(pointer); err == nil || !strings.Contains(err.Error(), "unsupported current commit pointer format version") { t.Fatalf("EncodeCurrentCommitPointer() error = %v, want unsupported version", err) } } func TestRemoteCommitDecodersRejectUnknownFields(t *testing.T) { commit := testRemoteCommit(t, "20260519T010203Z-a1b2c3d4") commitData, err := EncodeRemoteCommitManifest(commit) if err != nil { t.Fatalf("EncodeRemoteCommitManifest() error = %v", err) } commitData = appendUnknownJSONField(t, commitData) if _, err := DecodeRemoteCommitManifest(commitData); err == nil || !strings.Contains(err.Error(), "unknown field") { t.Fatalf("DecodeRemoteCommitManifest() error = %v, want unknown-field rejection", err) } pointer := testCommitPointer(t, testCurrentSessionPrefix(), commit) pointerData, err := EncodeCurrentCommitPointer(pointer) if err != nil { t.Fatalf("EncodeCurrentCommitPointer() error = %v", err) } pointerData = appendUnknownJSONField(t, pointerData) if _, err := DecodeCurrentCommitPointer(pointerData); err == nil || !strings.Contains(err.Error(), "unknown field") { t.Fatalf("DecodeCurrentCommitPointer() error = %v, want unknown-field rejection", err) } } func TestLoadCurrentStateReadsVerifiedRemoteCommit(t *testing.T) { store := &storage.FakeBackend{} commit := testRemoteCommit(t, "20260519T010203Z-a1b2c3d4") seedRemoteCommit(t, store, testCurrentSessionPrefix(), commit, "commit-generation", "manifest-generation") state, err := LoadCurrentState(context.Background(), store, testCurrentSessionPrefix(), CurrentStateValidation{ ExpectedCampaign: commit.Campaign, ExpectedSessionID: commit.SessionID, ValidateRunID: true, }) if err != nil { t.Fatalf("LoadCurrentState() error = %v", err) } if state.Commit == nil || state.Pointer == nil { t.Fatalf("state does not expose selected immutable commit: %#v", state) } if state.CurrentPointerKey != S3CurrentCommitPointerKey(testCurrentSessionPrefix()) { t.Fatalf("CurrentPointerKey = %q", state.CurrentPointerKey) } if state.Manifest.RunID != commit.RunID { t.Fatalf("manifest run ID = %q, want %q", state.Manifest.RunID, commit.RunID) } } func TestLoadCurrentStateRejectsPointerCommitIdentityMismatch(t *testing.T) { store := &storage.FakeBackend{} prefix := testCurrentSessionPrefix() pointerRunID := "20260519T010203Z-a1b2c3d4" commit := testRemoteCommit(t, "20260520T010203Z-a1b2c3d4") commitData, err := EncodeRemoteCommitManifest(commit) if err != nil { t.Fatalf("EncodeRemoteCommitManifest() error = %v", err) } store.SeedObject(storage.FakeObject{Key: S3RunCommitKey(prefix, pointerRunID), Data: commitData, ETag: "commit-generation"}) pointer := CurrentCommitPointer{ FormatVersion: RemoteCommitFormatVersion, Campaign: commit.Campaign, SessionID: commit.SessionID, RunID: pointerRunID, CommitKey: S3RunCommitKey(prefix, pointerRunID), CommitSHA256: remoteObjectSHA256(commitData), CommitSize: int64(len(commitData)), CommitGeneration: "commit-generation", } pointerData, err := EncodeCurrentCommitPointer(pointer) if err != nil { t.Fatalf("EncodeCurrentCommitPointer() error = %v", err) } store.SeedObject(storage.FakeObject{Key: S3CurrentCommitPointerKey(prefix), Data: pointerData}) _, err = LoadCurrentState(context.Background(), store, prefix, CurrentStateValidation{}) if err == nil || !strings.Contains(err.Error(), "does not match selected remote commit") { t.Fatalf("error = %v, want pointer/commit identity mismatch", err) } } func TestLoadCurrentStateRejectsCommitManifestIdentityMismatch(t *testing.T) { store := &storage.FakeBackend{} commit := testRemoteCommit(t, "20260519T010203Z-a1b2c3d4") pointer := seedRemoteCommit(t, store, testCurrentSessionPrefix(), commit, "commit-generation", "manifest-generation") wrongManifest := testManifestJSON(t, "2026-05-03", "wrong-campaign", commit.RunID) store.SeedObject(storage.FakeObject{ Key: commit.Artifacts[0].DestinationKey, Data: wrongManifest, ETag: "manifest-generation", }) commit.Artifacts[0].SHA256 = remoteObjectSHA256(wrongManifest) commit.Artifacts[0].Size = int64(len(wrongManifest)) commitData, err := EncodeRemoteCommitManifest(commit) if err != nil { t.Fatalf("EncodeRemoteCommitManifest() error = %v", err) } store.SeedObject(storage.FakeObject{Key: pointer.CommitKey, Data: commitData, ETag: "commit-generation"}) pointer.CommitSHA256 = remoteObjectSHA256(commitData) pointer.CommitSize = int64(len(commitData)) pointerData, err := EncodeCurrentCommitPointer(*pointer) if err != nil { t.Fatalf("EncodeCurrentCommitPointer() error = %v", err) } store.SeedObject(storage.FakeObject{Key: S3CurrentCommitPointerKey(testCurrentSessionPrefix()), Data: pointerData}) _, err = LoadCurrentState(context.Background(), store, testCurrentSessionPrefix(), CurrentStateValidation{}) if err == nil || !strings.Contains(err.Error(), "does not match committed session manifest") { t.Fatalf("error = %v, want commit/manifest identity mismatch", err) } } func TestLoadCurrentStateRejectsCommitChecksumOrGenerationMismatch(t *testing.T) { tests := []struct { name string mutate func(*CurrentCommitPointer) want string }{ { name: "checksum", mutate: func(pointer *CurrentCommitPointer) { pointer.CommitSHA256 = strings.Repeat("0", 64) }, want: "checksum mismatch", }, { name: "generation", mutate: func(pointer *CurrentCommitPointer) { pointer.CommitGeneration = "stale-generation" }, want: "generation mismatch", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { store := &storage.FakeBackend{} commit := testRemoteCommit(t, "20260519T010203Z-a1b2c3d4") pointer := seedRemoteCommit(t, store, testCurrentSessionPrefix(), commit, "commit-generation", "manifest-generation") test.mutate(pointer) pointerData, err := EncodeCurrentCommitPointer(*pointer) if err != nil { t.Fatalf("EncodeCurrentCommitPointer() error = %v", err) } store.SeedObject(storage.FakeObject{Key: S3CurrentCommitPointerKey(testCurrentSessionPrefix()), Data: pointerData}) _, err = LoadCurrentState(context.Background(), store, testCurrentSessionPrefix(), CurrentStateValidation{}) if err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("error = %v, want %q", err, test.want) } }) } } func testRemoteCommit(t *testing.T, runID string) RemoteCommitManifest { t.Helper() prefix := testCurrentSessionPrefix() manifestData := testManifestJSON(t, "2026-05-03", "sample-campaign", runID) return RemoteCommitManifest{ FormatVersion: RemoteCommitFormatVersion, Campaign: "sample-campaign", SessionID: "2026-05-03", RunID: runID, Artifacts: []RemoteArtifact{{ Type: RemoteArtifactTypeSessionManifest, Source: "session.manifest", DestinationKey: S3RunRelativeDestinationKey(S3RunPrefix(prefix, runID), "session-manifest.json"), SHA256: remoteObjectSHA256(manifestData), Size: int64(len(manifestData)), Generation: "manifest-generation", }}, } } func testCommitPointer(t *testing.T, prefix string, commit RemoteCommitManifest) CurrentCommitPointer { t.Helper() commitData, err := EncodeRemoteCommitManifest(commit) if err != nil { t.Fatalf("EncodeRemoteCommitManifest() error = %v", err) } return CurrentCommitPointer{ FormatVersion: RemoteCommitFormatVersion, Campaign: commit.Campaign, SessionID: commit.SessionID, RunID: commit.RunID, CommitKey: S3RunCommitKey(prefix, commit.RunID), CommitSHA256: remoteObjectSHA256(commitData), CommitSize: int64(len(commitData)), CommitGeneration: "commit-generation", } } func seedRemoteCommit(t *testing.T, store *storage.FakeBackend, prefix string, commit RemoteCommitManifest, commitGeneration, manifestGeneration string) *CurrentCommitPointer { t.Helper() manifestData := testManifestJSON(t, commit.SessionID, commit.Campaign, commit.RunID) commit.Artifacts[0].SHA256 = remoteObjectSHA256(manifestData) commit.Artifacts[0].Size = int64(len(manifestData)) commit.Artifacts[0].Generation = manifestGeneration commitData, err := EncodeRemoteCommitManifest(commit) if err != nil { t.Fatalf("EncodeRemoteCommitManifest() error = %v", err) } store.SeedObject(storage.FakeObject{ Key: S3RunCommitKey(prefix, commit.RunID), Data: commitData, ETag: commitGeneration, }) store.SeedObject(storage.FakeObject{ Key: commit.Artifacts[0].DestinationKey, Data: manifestData, ETag: manifestGeneration, }) pointer := testCommitPointer(t, prefix, commit) pointer.CommitGeneration = commitGeneration pointerData, err := EncodeCurrentCommitPointer(pointer) if err != nil { t.Fatalf("EncodeCurrentCommitPointer() error = %v", err) } store.SeedObject(storage.FakeObject{Key: S3CurrentCommitPointerKey(prefix), Data: pointerData}) return &pointer } func appendUnknownJSONField(t *testing.T, data []byte) []byte { t.Helper() trimmed := bytes.TrimSpace(data) if len(trimmed) < 2 || trimmed[len(trimmed)-1] != '}' { t.Fatalf("invalid JSON object %q", trimmed) } return append(append([]byte(nil), trimmed[:len(trimmed)-1]...), []byte(`,"unexpected":true}`)...) }