返回 DeepSeek-Reasonix
session_composer_compatibility_test.go
根目录 / desktop / session_composer_compatibility_test.go
1 package main
2
3 import (
4 "testing"
5
6 "reasonix/desktop/internal/sessionui"
7 "reasonix/internal/provider"
8 )
9
10 func TestComposerReupgradeDetectsHistoryWithoutSubmissionReceipts(t *testing.T) {
11 a, ref := lifecycleFixture(t)
12 t.Cleanup(func() { _ = a.sessionUIStore().Close() })
13 initial, err := a.GetSessionComposerState(ref)
14 if err != nil {
15 t.Fatal(err)
16 }
17 saved, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: initial.Revision, ContentVersion: 1, ContentJSON: `{"text":"unsent before downgrade"}`})
18 if err != nil {
19 t.Fatal(err)
20 }
21 path := a.sessionUIStore().Path()
22 if err := a.sessionUIStore().Close(); err != nil {
23 t.Fatal(err)
24 }
25 // Simulate an old/imported history writer that does not know session-ui or
26 // attach a SubmissionID. The independent input file must remain unchanged.
27 binding, err := a.desktopSessionService("").Open(t.Context(), ref)
28 if err != nil {
29 t.Fatal(err)
30 }
31 appendSessionTestMessage(t, binding.Runtime(), "old-writer", provider.Message{ID: "old-writer-user", Role: provider.RoleUser, Content: "continued in old version"})
32 if err := binding.Release(t.Context()); err != nil {
33 t.Fatal(err)
34 }
35 a.sessionUI = sessionui.New(path)
36 recovered, err := a.GetSessionComposerState(ref)
37 if err != nil || !recovered.HistoryChanged || recovered.ContentJSON != saved.ContentJSON {
38 t.Fatalf("unsafe reupgrade: %+v %v", recovered, err)
39 }
40 if _, err := a.BeginSessionComposerSubmission(ref, recovered.Revision, "must-not-send", `{}`); err == nil {
41 t.Fatal("unreviewed old input became sendable")
42 }
43 }
44
45 func TestComposerArchiveRestoreAndPurgeKeepLifecycleOwnership(t *testing.T) {
46 a, ref := lifecycleFixture(t)
47 t.Cleanup(func() { _ = a.sessionUIStore().Close() })
48 input, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: "0", ContentVersion: 1, ContentJSON: `{"text":"archive input","attachments":[{"path":"missing-file"}]}`})
49 if err != nil {
50 t.Fatal(err)
51 }
52 if _, err := a.ArchiveSessionTarget(SessionSelector{Ref: &ref}); err != nil {
53 t.Fatal(err)
54 }
55 archived, err := a.GetSessionComposerState(ref)
56 if err != nil || archived.ContentJSON != input.ContentJSON {
57 t.Fatalf("archive lost input: %+v %v", archived, err)
58 }
59 if _, err := a.RestoreSessionTarget(SessionSelector{Ref: &ref}); err != nil {
60 t.Fatal(err)
61 }
62 restored, err := a.GetSessionComposerState(ref)
63 if err != nil || restored.ContentJSON != input.ContentJSON {
64 t.Fatalf("restore lost input: %+v %v", restored, err)
65 }
66 if _, err := a.ArchiveSessionTarget(SessionSelector{Ref: &ref}); err != nil {
67 t.Fatal(err)
68 }
69 if err := a.PurgeCanonicalSession(ref); err != nil {
70 t.Fatal(err)
71 }
72 row, err := a.sessionUIStore().Get(t.Context(), "composer", composerRecordKey(ref))
73 if err != nil || row.Revision != "0" {
74 t.Fatalf("purge retained input: %+v %v", row, err)
75 }
76 if _, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: "0", ContentVersion: 1, ContentJSON: `{"text":"late save"}`}); err == nil {
77 t.Fatal("late save revived purged input")
78 }
79 }
80
81 func TestComposerSubmissionIdentityCannotClearAnotherInputVersion(t *testing.T) {
82 a, ref := lifecycleFixture(t)
83 t.Cleanup(func() { _ = a.sessionUIStore().Close() })
84 saved, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: "0", ContentVersion: 1, ContentJSON: `{"text":"first"}`})
85 if err != nil {
86 t.Fatal(err)
87 }
88 pending, err := a.BeginSessionComposerSubmission(ref, saved.Revision, "fixed-submission", `{"submit":"first"}`)
89 if err != nil {
90 t.Fatal(err)
91 }
92 if repeated, err := a.BeginSessionComposerSubmission(ref, saved.Revision, "fixed-submission", `{"submit":"first"}`); err != nil || repeated.Revision != pending.Revision {
93 t.Fatalf("identical retry changed association: %+v %v", repeated, err)
94 }
95 if _, err := a.BeginSessionComposerSubmission(ref, saved.Revision, "fixed-submission", `{"submit":"different"}`); err == nil {
96 t.Fatal("changed request reused frozen identity")
97 }
98 done, err := a.CompleteSessionComposerSubmission(ref, "fixed-submission", "accepted")
99 if err != nil {
100 t.Fatal(err)
101 }
102 next, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: done.Revision, ContentVersion: 1, ContentJSON: `{"text":"second"}`})
103 if err != nil {
104 t.Fatal(err)
105 }
106 if _, err := a.BeginSessionComposerSubmission(ref, next.Revision, "fixed-submission", `{"submit":"second"}`); err == nil {
107 t.Fatal("old receipt can clear a new input")
108 }
109 late, err := a.CompleteSessionComposerSubmission(ref, "fixed-submission", "accepted")
110 if err != nil || late.ContentJSON != next.ContentJSON {
111 t.Fatalf("late acceptance cleared new input: %+v %v", late, err)
112 }
113 }
114
114 lines GO