| 1 | package workspacestate |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | "path/filepath" |
| 6 | "testing" |
| 7 | ) |
| 8 | |
| 9 | func TestReadVersionsTrackSourceAndLifecycleWithoutPresentation(t *testing.T) { |
| 10 | state := newState() |
| 11 | path := filepath.Join(t.TempDir(), "legacy.jsonl") |
| 12 | state.SessionStates["one"] = SessionState{Lifecycle: Active, Generation: 3} |
| 13 | state.Presentation["one"] = Presentation{Title: "before"} |
| 14 | state.SourceMappings["adopted"] = SourceMapping{Path: path, HeadID: "branch", SessionID: "one", Fingerprint: "proof", RetainedArtifacts: []string{"checkpoint"}} |
| 15 | first := NewReadVersions(state) |
| 16 | source, err := first.Source(path) |
| 17 | if err != nil || source == "" || first.Session("one") == "" { |
| 18 | t.Fatalf("missing version: %q %v", source, err) |
| 19 | } |
| 20 | state.Presentation["one"] = Presentation{Title: "renamed", Pinned: true} |
| 21 | state.SessionStates["unrelated"] = SessionState{Lifecycle: Archived, Generation: 19} |
| 22 | unchanged := NewReadVersions(state) |
| 23 | if got, _ := unchanged.Source(path); got != source || unchanged.Session("one") != first.Session("one") { |
| 24 | t.Fatal("presentation or an unrelated session revoked a snapshot") |
| 25 | } |
| 26 | // Mutating an input slice must not change an already published token. |
| 27 | state.SourceMappings["adopted"].RetainedArtifacts[0] = "replaced" |
| 28 | replacement := NewReadVersions(state) |
| 29 | if got, _ := first.Source(path); got != source { |
| 30 | t.Fatal("input mutation changed retained versions") |
| 31 | } |
| 32 | if got, _ := replacement.Source(path); got == source || replacement.Session("one") == first.Session("one") { |
| 33 | t.Fatal("source mapping replacement did not revoke the old version") |
| 34 | } |
| 35 | state.SessionStates["one"] = SessionState{Lifecycle: Archived, Generation: 4} |
| 36 | archived := NewReadVersions(state) |
| 37 | if got, _ := archived.Source(path); got == source || archived.Session("one") == replacement.Session("one") { |
| 38 | t.Fatal("archive did not invalidate source and session readers") |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | func TestReadVersionsPreserveUnknownFieldsAndMappingOnlySources(t *testing.T) { |
| 43 | state := newState() |
| 44 | path := filepath.Join(t.TempDir(), "old.jsonl") |
| 45 | state.SourceMappings["old-writer"] = SourceMapping{Path: path, SessionID: "legacy"} |
| 46 | first := NewReadVersions(state) |
| 47 | if first.Session("legacy") == "" { |
| 48 | t.Fatal("mapping without explicit lifecycle has no token") |
| 49 | } |
| 50 | mapping := state.SourceMappings["old-writer"] |
| 51 | mapping.extra = map[string]json.RawMessage{"future-proof": json.RawMessage(`"changed"`)} |
| 52 | state.SourceMappings["old-writer"] = mapping |
| 53 | second := NewReadVersions(state) |
| 54 | if first.Session("legacy") == second.Session("legacy") { |
| 55 | t.Fatal("unknown source proof was omitted from invalidation") |
| 56 | } |
| 57 | delete(state.SourceMappings, "old-writer") |
| 58 | removed := NewReadVersions(state) |
| 59 | if version, err := removed.Source(path); version != "" || err != nil || removed.Session("legacy") != "" { |
| 60 | t.Fatalf("removed mapping retained a token: %q %v", version, err) |
| 61 | } |
| 62 | } |
| 63 |