返回 DeepSeek-Reasonix
session_metadata_guard_test.go
根目录 / desktop / internal / workspacestate / session_metadata_guard_test.go
1 package workspacestate
2
3 import (
4 "errors"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/filelock"
9 )
10
11 func TestMetadataCommitHoldsLifecycleFileLockAndRejectsArchiveRestore(t *testing.T) {
12 store := NewStore(filepath.Join(t.TempDir(), "state.json"))
13 if err := store.EnsureWorkspace(t.Context(), Workspace{ID: "global"}); err != nil {
14 t.Fatal(err)
15 }
16 if err := store.AttachSession(t.Context(), "", "global", "target", ""); err != nil {
17 t.Fatal(err)
18 }
19 state, err := store.Load(t.Context())
20 if err != nil {
21 t.Fatal(err)
22 }
23 generation := state.SessionStates["target"].Generation
24 if err := store.WithSessionUnchanged(t.Context(), "target", "global", generation, func() error {
25 release, err := filelock.TryAcquire(store.Path() + ".lock")
26 if err == nil {
27 release()
28 t.Fatal("metadata commit did not hold the cross-process lifecycle lock")
29 }
30 return nil
31 }); err != nil {
32 t.Fatal(err)
33 }
34 other := NewStore(store.Path())
35 if err := other.ArchiveSession(t.Context(), "target"); err != nil {
36 t.Fatal(err)
37 }
38 commit := func() error { t.Fatal("stale metadata callback was invoked"); return nil }
39 if err := store.WithSessionUnchanged(t.Context(), "target", "global", generation, commit); !errors.Is(err, ErrSessionNotFound) {
40 t.Fatalf("archive: %v", err)
41 }
42 if err := other.RestoreSession(t.Context(), "target"); err != nil {
43 t.Fatal(err)
44 }
45 if err := store.WithSessionUnchanged(t.Context(), "target", "global", generation, commit); !errors.Is(err, ErrMutationConflict) {
46 t.Fatalf("archive/restore ABA: %v", err)
47 }
48 }
49
49 lines GO