返回 DeepSeek-Reasonix
commands_test.go
根目录 / desktop / internal / workspacestate / commands_test.go
1 package workspacestate
2
3 import (
4 "encoding/json"
5 "errors"
6 "path/filepath"
7 "testing"
8 )
9
10 func TestPurgeCommandAdmissionDoesNotRelaxOtherCommands(t *testing.T) {
11 s, expected := seedArchivedProcessState(t)
12 if err := s.RenameWorkspace(t.Context(), GlobalWorkspaceID, "unrelated"); err != nil {
13 t.Fatal(err)
14 }
15 if err := s.BeginCommand(t.Context(), "command-restore", "restore", json.RawMessage(`{"action":"restore"}`), expected); !errors.Is(err, ErrMutationConflict) {
16 t.Fatalf("restore gate relaxed: %v", err)
17 }
18 if err := s.BeginPurgeCommand(t.Context(), "command-wrong-action", "restore", json.RawMessage(`{"action":"restore"}`), expected); !errors.Is(err, ErrMutationConflict) {
19 t.Fatalf("purge admitted restore: %v", err)
20 }
21 if err := s.BeginPurgeCommand(t.Context(), "command-future", "future", json.RawMessage(`{"action":"purge"}`), expected+100); !errors.Is(err, ErrMutationConflict) {
22 t.Fatalf("future version admitted: %v", err)
23 }
24 if err := s.BeginPurgeCommand(t.Context(), "command-purge", "purge", json.RawMessage(`{"action":"purge"}`), expected); err != nil {
25 t.Fatal(err)
26 }
27 state, err := s.Load(t.Context())
28 if err != nil {
29 t.Fatal(err)
30 }
31 if state.PendingOperations["command-purge"].ExpectedGeneration != expected || state.SessionStates["victim"].Lifecycle != Archived {
32 t.Fatal("admission changed intent or lifecycle")
33 }
34 }
35
36 func TestCommandChildRejectsInterveningLifecycle(t *testing.T) {
37 s := NewStore(filepath.Join(t.TempDir(), "registry.json"))
38 ctx := t.Context()
39 if err := s.EnsureWorkspace(ctx, Workspace{ID: "global", Root: t.TempDir(), Visible: true}); err != nil {
40 t.Fatal(err)
41 }
42 if err := s.AttachSession(ctx, "", "global", "session", ""); err != nil {
43 t.Fatal(err)
44 }
45 state, _ := s.Load(ctx)
46 if err := s.BeginCommand(ctx, "command-test", "fingerprint", json.RawMessage(`{}`), state.Generation); err != nil {
47 t.Fatal(err)
48 }
49 if err := s.ArchiveSession(ctx, "session"); err != nil {
50 t.Fatal(err)
51 }
52 latest, _ := s.Load(ctx)
53 // The RPC has a fresh snapshot, but still represents the old intent.
54 err := s.BeginOperation(ctx, Operation{ID: "command-test-0", Kind: "restore", Lifecycle: Active, WorkspaceID: "global", SessionIDs: []string{"session"}, ExpectedGeneration: latest.Generation})
55 if !errors.Is(err, ErrMutationConflict) {
56 t.Fatalf("stale command admitted: %v", err)
57 }
58 if err := s.BeginPurge(ctx, "session", state.Generation); !errors.Is(err, ErrMutationConflict) {
59 t.Fatalf("stale purge admitted: %v", err)
60 }
61 if err := s.SaveCommandResult(ctx, "command-test", json.RawMessage(`{"generation":1}`), true); err != nil {
62 t.Fatal(err)
63 }
64 latest, _ = s.Load(ctx)
65 var result struct {
66 Generation uint64 `json:"generation"`
67 }
68 if err := json.Unmarshal(latest.PendingOperations["command-test"].Result, &result); err != nil {
69 t.Fatal(err)
70 }
71 if result.Generation != latest.Generation {
72 t.Fatalf("receipt generation %d, commit %d", result.Generation, latest.Generation)
73 }
74 }
75
76 func TestHistoricalArchiveCommitPreservesUnknownTime(t *testing.T) {
77 s := NewStore(filepath.Join(t.TempDir(), "registry.json"))
78 ctx := t.Context()
79 if err := s.EnsureWorkspace(ctx, Workspace{ID: "global", Root: t.TempDir(), Visible: true}); err != nil {
80 t.Fatal(err)
81 }
82 op := Operation{ID: "legacy-trash", Kind: "archive-import", Lifecycle: Archived, WorkspaceID: "global", SessionIDs: []string{"old"}}
83 if err := s.BeginOperation(ctx, op); err != nil {
84 t.Fatal(err)
85 }
86 mapping := &SourceMapping{SourceKey: "old-source", Path: "/legacy/old.jsonl", Format: "legacy", Fingerprint: "proof", SessionID: "old"}
87 if err := s.PrepareOperationContent(ctx, op.ID, op.SessionIDs, mapping, nil); err != nil {
88 t.Fatal(err)
89 }
90 if err := s.CommitOperation(ctx, op.ID); !errors.Is(err, ErrMutationConflict) {
91 t.Fatalf("batch guard lost: %v", err)
92 }
93 if err := s.CommitHistoricalArchive(ctx, op.ID); err != nil {
94 t.Fatal(err)
95 }
96 state, _ := s.Load(ctx)
97 if state.SessionStates["old"].Lifecycle != Archived || state.SessionStates["old"].ArchivedAt != 0 || state.PendingOperations[op.ID].Phase != "committed" {
98 t.Fatalf("invalid historical commit: %+v", state)
99 }
100 }
101
101 lines GO