返回 DeepSeek-Reasonix
session_v5_migration_submission_test.go
根目录 / desktop / session_v5_migration_submission_test.go
1 package main
2
3 import (
4 "bytes"
5 "encoding/json"
6 "path/filepath"
7 "testing"
8
9 "reasonix/internal/config"
10 "reasonix/internal/provider"
11 "reasonix/internal/session"
12 )
13
14 // Submission receipts introduced by #10392 are host metadata. Migration must
15 // preserve the event bytes, keep receipt scope tied to the session identity,
16 // and keep both title/history consumers and model messages compatible.
17 func TestDesktopV5UpgradeSubmissionIdentity(t *testing.T) {
18 for _, collision := range []bool{false, true} {
19 name := "same-identity"
20 if collision {
21 name = "independent-conflict-identity"
22 }
23 t.Run(name, func(t *testing.T) {
24 isolateDesktopUserDirs(t)
25 root := config.SessionStoreDir()
26 id := "accepted-source"
27 writer, err := session.CreateStore(filepath.Join(root, id), id)
28 if err != nil {
29 t.Fatal(err)
30 }
31 receipt := session.SubmissionReceipt{SessionID: id, SubmissionID: "send", Fingerprint: "request-fingerprint", TurnID: "turn", MessageID: "user"}
32 receiptBody, _ := json.Marshal(receipt)
33 message := provider.Message{ID: "user", Role: provider.RoleUser, Content: "question for the title"}
34 messageBody, _ := json.Marshal(map[string]any{"message": message})
35 if _, err := writer.Append(t.Context(), session.Batch{OperationID: "accept", TurnID: "turn", Events: []session.Event{
36 {Kind: "turn/start"},
37 {Kind: "submission/accepted", Optional: true, Payload: receiptBody},
38 {Kind: "message/complete", Payload: messageBody},
39 {Kind: "turn/end", Payload: json.RawMessage(`{"status":"completed"}`)},
40 }}); err != nil {
41 t.Fatal(err)
42 }
43 if err := writer.Close(t.Context()); err != nil {
44 t.Fatal(err)
45 }
46 before := migrationSourceSnapshot(t, canonicalMigrationSourceFiles(root, id))
47 app := NewApp()
48 t.Cleanup(app.closeSessionServices)
49 if collision {
50 other, err := app.desktopSessionService("").Create(t.Context(), session.CreateOptions{SessionID: id})
51 if err != nil {
52 t.Fatal(err)
53 }
54 appendMigrationTestMessage(t, app.desktopSessionService(""), other.Ref(), "unrelated existing work")
55 }
56 if err := app.migrateDesktopSessionsV5(t.Context()); err != nil {
57 t.Fatal(err)
58 }
59 ledger, err := readDesktopMigrationLedger()
60 if err != nil {
61 t.Fatal(err)
62 }
63 targetID := ledger.Records[desktopCanonicalMigrationKey(root, id)].TargetSessionID
64 if (targetID != id) != collision {
65 t.Fatalf("unexpected identity: %q", targetID)
66 }
67 ref := session.SessionRef{HostID: localDesktopHostID, SessionID: targetID}
68 query := app.desktopSessionService("").Query()
69 // This uses #10389's new canonical title reader and waits for the
70 // same history index that #10392 annotates with submission IDs.
71 titles, err := query.TitleMessages(t.Context(), ref, 3)
72 if err != nil || len(titles) != 1 || titles[0].Content != message.Content {
73 t.Fatalf("migrated title input: %+v, %v", titles, err)
74 }
75 page, err := query.ReadHistoryWindow(t.Context(), ref, session.HistoryWindowRequest{Anchor: "newest", Limit: 10})
76 if err != nil || page.Status != "ready" || len(page.Messages) != 1 {
77 t.Fatalf("migrated history: %+v, %v", page, err)
78 }
79 wantSubmission := "send"
80 if collision {
81 wantSubmission = ""
82 }
83 if page.Messages[0].SubmissionID != wantSubmission {
84 t.Fatalf("submission scope: %+v", page.Messages[0])
85 }
86 snapshot, err := query.Snapshot(t.Context(), ref)
87 if err != nil {
88 t.Fatal(err)
89 }
90 receipts, _ := json.Marshal(snapshot.Projection.Submissions)
91 if !bytes.Contains(receipts, receiptBody) {
92 t.Fatalf("source receipt lost: %s", receipts)
93 }
94 model, _ := json.Marshal(snapshot.Projection.Messages)
95 if bytes.Contains(model, []byte("submissionId")) || bytes.Contains(model, []byte("fingerprint")) {
96 t.Fatal("submission metadata entered model messages")
97 }
98 assertMigrationSourceSnapshot(t, before)
99 assertLineageRestart(t, app, 1)
100 })
101 }
102 }
103
103 lines GO