返回 DeepSeek-Reasonix
tagged_damaged_history_test.go
根目录 / desktop / tagged_damaged_history_test.go
1 package main
2
3 import (
4 "encoding/json"
5 "errors"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "reasonix/desktop/internal/workspacestate"
11 "reasonix/internal/config"
12 )
13
14 func TestTaggedDamagedHistoryIsolatesFailureAndRetainsRecoveryEvidence(t *testing.T) {
15 for _, version := range []string{"1.38.8", "1.38.9", "1.38.10", "1.38.11"} {
16 for _, damage := range []string{"future-revision", "truncated-commit"} {
17 t.Run(version+"/"+damage, func(t *testing.T) {
18 isolateDesktopUserDirs(t)
19 fixture := taggedHistoryFixture(t, version)
20 root := config.SessionStoreDir()
21 original := copyTaggedDirectory(t, filepath.Join(fixture, "canonical"), root)
22 path := filepath.Join(root, "canonical-complete", "manifest.json")
23 var body []byte
24 if damage == "future-revision" {
25 var manifest map[string]any
26 readTaggedJSON(t, path, &manifest)
27 manifest["storageRevision"] = 999
28 body, _ = json.Marshal(manifest)
29 } else {
30 path = filepath.Join(root, "canonical-complete", "events.frames")
31 body = original[path][:len(original[path])-12]
32 }
33 if err := os.WriteFile(path, body, 0600); err != nil {
34 t.Fatal(err)
35 }
36 app := NewApp()
37 t.Cleanup(app.closeSessionServices)
38 healthy := map[string]string{}
39 for attempt := range 3 {
40 err := app.migrateDesktopSessionsV5(t.Context())
41 if attempt == 2 && damage == "truncated-commit" {
42 // Export failed after reserving the original source fingerprint.
43 // Repair changes that fingerprint. Keep the old operation as
44 // conflicting evidence; never certify it against different bytes.
45 if !errors.Is(err, workspacestate.ErrMutationConflict) {
46 t.Fatalf("changed reserved source lost its conflict: %v", err)
47 }
48 } else if (attempt < 2) != (err != nil) {
49 t.Fatalf("attempt %d: expected isolated failure before repair: %v", attempt, err)
50 }
51 ledger, err := readDesktopMigrationLedger()
52 if err != nil {
53 t.Fatal(err)
54 }
55 for _, name := range []string{"canonical-compacted", "canonical-interrupted"} {
56 record := ledger.Records[desktopCanonicalMigrationKey(root, name)]
57 if record.Status != "completed" || record.TargetSessionID == "" || (attempt > 0 && healthy[name] != record.TargetSessionID) {
58 t.Fatalf("healthy sibling lost or duplicated: %+v", record)
59 }
60 healthy[name] = record.TargetSessionID
61 }
62 // Even a torn committed frame is an immutable import source.
63 assertMigrationSourceSnapshot(t, map[string][]byte{path: body})
64 infos, listErr := listAllCanonicalSessionInfo(t.Context(), app.desktopSessionService("").Query())
65 if attempt < 2 && (listErr != nil || len(infos) != 2) {
66 t.Fatalf("damaged source was silently published: %d %v", len(infos), listErr)
67 }
68 if attempt == 1 {
69 body = original[path]
70 if err := os.WriteFile(path, body, 0600); err != nil {
71 t.Fatal(err)
72 }
73 }
74 app.closeSessionServices()
75 app = NewApp()
76 t.Cleanup(app.closeSessionServices)
77 }
78 infos, err := listAllCanonicalSessionInfo(t.Context(), app.desktopSessionService("").Query())
79 if err != nil || len(infos) != 3 {
80 t.Fatalf("repair duplicated or lost sessions: %d %v", len(infos), err)
81 }
82 assertMigrationSourceSnapshot(t, original)
83 })
84 }
85 }
86 }
87
87 lines GO