返回 DeepSeek-Reasonix
tagged_previous_writer_test.go
根目录 / desktop / tagged_previous_writer_test.go
1 package main
2
3 import (
4 "bytes"
5 "encoding/json"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "testing"
10
11 "reasonix/desktop/internal/sessionui"
12 "reasonix/internal/provider"
13 )
14
15 // Opt-in integration gate: executables come from the tagged fixture generator,
16 // not from a current implementation pretending to be a previous reader.
17 func TestTaggedPreviousWriterRoundtrip(t *testing.T) {
18 writers := os.Getenv("REASONIX_TAGGED_WRITERS")
19 if writers == "" {
20 t.Skip("run generate-session-history-fixtures.py --writers-dir and set REASONIX_TAGGED_WRITERS")
21 }
22 for _, version := range []string{"1.38.10", "1.38.11"} {
23 t.Run(version, func(t *testing.T) {
24 a, ref := lifecycleFixture(t)
25 binding, err := a.desktopSessionService("").Open(t.Context(), ref)
26 if err != nil {
27 t.Fatal(err)
28 }
29 appendSessionTestMessage(t, binding.Runtime(), "new-writer", provider.Message{ID: "new-user", Role: provider.RoleUser, Content: "New-version history before downgrade"})
30 if err := binding.Release(t.Context()); err != nil {
31 t.Fatal(err)
32 }
33 saved, err := a.SaveSessionComposerState(SessionComposerSaveRequest{Ref: ref, ExpectedRevision: "0", ContentVersion: 1, ContentJSON: `{"text":"input retained while old binary runs"}`})
34 if err != nil {
35 t.Fatal(err)
36 }
37 path := a.sessionUIStore().Path()
38 if err := a.sessionUIStore().Close(); err != nil {
39 t.Fatal(err)
40 }
41 before, err := os.ReadFile(path)
42 if err != nil {
43 t.Fatal(err)
44 }
45 a.closeSessionServices()
46 sources := migrationSourceSnapshot(t, canonicalMigrationSourceFiles(a.desktopSessions.root, ref.SessionID))
47 command := exec.CommandContext(t.Context(), filepath.Join(writers, version), filepath.Join(a.desktopSessions.root, ref.SessionID), ref.SessionID)
48 output, err := command.Output()
49 if err != nil {
50 t.Fatalf("tagged writer failed: %v\n%s", err, output)
51 }
52 var result struct{ Status string }
53 if err := json.Unmarshal(output, &result); err != nil {
54 t.Fatal(err)
55 }
56 after, err := os.ReadFile(path)
57 if err != nil || !bytes.Equal(before, after) {
58 t.Fatal("previous writer changed the independent UI database")
59 }
60 a.sessionUI = sessionui.New(path)
61 t.Cleanup(func() { _ = a.sessionUIStore().Close() })
62 recovered, err := a.GetSessionComposerState(ref)
63 if version == "1.38.10" {
64 // #10545 raised physical storage revision 2 to 3. Registry v3
65 // readability alone never promised whole-session downgrade safety.
66 if result.Status != "unsupported_version" || err != nil || recovered.HistoryChanged || recovered.ContentJSON != saved.ContentJSON {
67 t.Fatalf("unsafe old-reader rejection: %+v %+v %v", result, recovered, err)
68 }
69 assertMigrationSourceSnapshot(t, sources)
70 return
71 }
72 if result.Status != "continued" {
73 t.Fatalf("old reader could not continue: %+v", result)
74 }
75 if err != nil || !recovered.HistoryChanged || recovered.ContentJSON != saved.ContentJSON {
76 t.Fatalf("unsafe reupgrade after real old write: %+v %v", recovered, err)
77 }
78 if _, err := a.BeginSessionComposerSubmission(ref, recovered.Revision, "must-not-send", `{}`); err == nil {
79 t.Fatal("stale input became sendable without review")
80 }
81 history, err := a.desktopSessionService("").Query().History(t.Context(), ref)
82 if err != nil || len(history) != 3 || history[0].ID != "user" || history[1].ID != "new-user" || history[2].ID != "old-version-user" {
83 t.Fatalf("roundtrip history changed: %d messages %v", len(history), err)
84 }
85 })
86 }
87 }
88
88 lines GO