返回 DeepSeek-Reasonix
historical_archive_recovered_test.go
根目录 / desktop / historical_archive_recovered_test.go
1 package main
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/config"
13 "reasonix/internal/provider"
14 "reasonix/internal/sessioncatalog"
15 )
16
17 // A write conflict leaves one recovery copy per writer, each listed as its own
18 // row of the same conversation, so archiving any of them archives them all.
19 func TestArchivingRecoveredLegacyRowArchivesEveryCopy(t *testing.T) {
20 isolateDesktopUserDirs(t)
21 dir := config.SessionDir()
22 parent := filepath.Join(dir, "20260826-101500.000000000-fake-model.jsonl")
23 p := agent.NewSession("system")
24 p.Add(provider.Message{ID: "q0", Role: provider.RoleUser, Content: "shops from my search"})
25 if err := p.Save(parent); err != nil {
26 t.Fatal(err)
27 }
28 meta := agent.BranchMeta{Scope: "global", TopicID: "topic_jd", TopicTitle: "shops from my search"}
29 for _, answer := range []string{"first writer", "second writer", "third writer"} {
30 s := agent.NewSession("system")
31 s.Add(provider.Message{ID: "q0", Role: provider.RoleUser, Content: "shops from my search"})
32 s.Add(provider.Message{ID: "a-" + answer, Role: provider.RoleAssistant, Content: answer})
33 if _, err := s.SaveConflictRecoveryBranch(agent.RecoveryBranchOptions{OriginalPath: parent, Reason: "conflict", BranchMeta: meta}); err != nil {
34 t.Fatal(err)
35 }
36 }
37 entries, _ := os.ReadDir(dir)
38 for _, e := range entries {
39 if strings.HasPrefix(e.Name(), "20260826-101500.000000000-fake-model.") {
40 _ = os.Remove(filepath.Join(dir, e.Name()))
41 }
42 }
43 // An unrelated recovered conversation must survive.
44 other := filepath.Join(dir, "20260826-120000.000000000-fake-model.jsonl")
45 o := agent.NewSession("system")
46 o.Add(provider.Message{ID: "q0", Role: provider.RoleUser, Content: "another conversation"})
47 if err := o.Save(other); err != nil {
48 t.Fatal(err)
49 }
50 if _, err := o.SaveConflictRecoveryBranch(agent.RecoveryBranchOptions{OriginalPath: other, Reason: "conflict",
51 BranchMeta: agent.BranchMeta{Scope: "global", TopicID: "topic_other", TopicTitle: "another conversation"}}); err != nil {
52 t.Fatal(err)
53 }
54
55 app := NewApp()
56 app.ctx = t.Context()
57 pinDesktopSessionRoot(t, app)
58 installNoopRuntimeEvents(app)
59 t.Cleanup(app.closeSessionServices)
60 catalog, err := sessioncatalog.Open(context.Background(), sessioncatalog.Options{InMemory: true, DisableRepair: true, MetadataOnly: true})
61 if err != nil {
62 t.Fatal(err)
63 }
64 app.sessionCatalog.Store(catalog)
65 t.Cleanup(func() { app.stopSessionCatalog(time.Second) })
66 recoveredRows := func(topic string) []ProjectNode {
67 t.Helper()
68 if err := catalog.ReconcileDirectory(context.Background(), sessioncatalog.DirectoryTarget{Path: dir, Scope: "global"}); err != nil {
69 t.Fatal(err)
70 }
71 page, err := app.ListProjectTopics(ProjectTopicPageRequest{Scope: "global", Limit: 50})
72 if err != nil {
73 t.Fatal(err)
74 }
75 app.ReleaseReadSnapshot(page.SnapshotID)
76 var rows []ProjectNode
77 for _, n := range page.Items {
78 if n.Recovered && n.TopicID == topic {
79 rows = append(rows, n)
80 }
81 }
82 return rows
83 }
84 rows := recoveredRows("topic_jd")
85 if len(rows) != 3 {
86 t.Fatalf("recovered rows before archive = %d, want the 3 copies", len(rows))
87 }
88 target := rows[0]
89 if _, err := app.ArchiveSessionTarget(SessionSelector{Ref: target.Session, Source: target.Source, SessionPath: target.SessionPath}); err != nil {
90 t.Fatal(err)
91 }
92 if left := recoveredRows("topic_jd"); len(left) != 0 {
93 t.Fatalf("archiving one recovered row left %d copies of the same conversation listed", len(left))
94 }
95 if kept := recoveredRows("topic_other"); len(kept) != 1 {
96 t.Fatalf("an unrelated recovered conversation was archived too: %d rows left, want 1", len(kept))
97 }
98 }
99
99 lines GO