返回 DeepSeek-Reasonix
session_catalog_initial_reconcile_test.go
根目录 / desktop / session_catalog_initial_reconcile_test.go
1 package main
2
3 import (
4 "context"
5 "database/sql"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "reasonix/internal/config"
12 "reasonix/internal/sessioncatalog"
13 )
14
15 func TestSessionCatalogInitialReconcileSignalFollowsRestoredTabs(t *testing.T) {
16 isolateDesktopUserDirs(t)
17 app := NewApp()
18 app.tabsRestored = make(chan struct{})
19 app.startSessionCatalog()
20 t.Cleanup(func() { app.stopSessionCatalog(time.Second) })
21 _ = waitForSessionCatalogForTest(t, app, nil)
22
23 app.catalogLifecycleMu.Lock()
24 done := app.catalogInitialReconcileDone
25 app.catalogLifecycleMu.Unlock()
26 if done == nil {
27 t.Fatal("initial reconcile signal was not armed")
28 }
29 select {
30 case <-done:
31 t.Fatal("initial reconcile completed before restored tabs were published")
32 default:
33 }
34
35 app.markTabsRestored()
36 select {
37 case <-done:
38 case <-time.After(sessionCatalogTestDeadline):
39 t.Fatal("initial reconcile did not complete after restored tabs were published")
40 }
41 }
42
43 func TestSessionCatalogAdmissionDoesNotWaitForMetadataWriter(t *testing.T) {
44 isolateDesktopUserDirs(t)
45 app := NewApp()
46 ctx, cancel := context.WithCancel(t.Context())
47 defer cancel()
48 app.ctx = ctx
49 app.tabsRestored = make(chan struct{})
50 path := filepath.Join(t.TempDir(), "catalog.sqlite")
51 catalog, err := sessioncatalog.Open(ctx, sessioncatalog.Options{Path: path, MetadataOnly: true, StartPaused: true})
52 if err != nil {
53 t.Fatal(err)
54 }
55 t.Cleanup(func() { _ = catalog.Close(context.Background()) })
56 db, err := sql.Open("sqlite", path)
57 if err != nil {
58 t.Fatal(err)
59 }
60 defer db.Close()
61 conn, err := db.Conn(ctx)
62 if err != nil {
63 t.Fatal(err)
64 }
65 defer conn.Close()
66 if _, err := conn.ExecContext(ctx, "BEGIN IMMEDIATE"); err != nil {
67 t.Fatal(err)
68 }
69 defer conn.ExecContext(context.Background(), "ROLLBACK")
70 // Both metadata synchronization and the restored path's database write
71 // are held behind a real SQLite writer. Neither belongs to admission.
72 dir := config.SessionDir()
73 if err := os.MkdirAll(dir, 0700); err != nil {
74 t.Fatal(err)
75 }
76 source := filepath.Join(dir, "restored.jsonl")
77 if err := os.WriteFile(source, []byte("unread body\n"), 0600); err != nil {
78 t.Fatal(err)
79 }
80 app.tabs = map[string]*WorkspaceTab{"current": {ID: "current", Scope: "global", SessionPath: source}}
81 admitted := make(chan struct{})
82 done := make(chan struct{})
83 go func() {
84 defer close(done)
85 app.watchSessionCatalog(ctx, catalog, nil, func() { close(admitted) })
86 }()
87 defer func() {
88 cancel()
89 _, _ = conn.ExecContext(context.Background(), "ROLLBACK")
90 select {
91 case <-done:
92 case <-time.After(sessionCatalogTestDeadline):
93 t.Error("catalog watcher did not join shutdown")
94 }
95 }()
96 app.markTabsRestored()
97 select {
98 case <-admitted:
99 case <-time.After(sessionCatalogTestDeadline):
100 t.Fatal("startup admission waited for a database writer")
101 }
102 }
103
103 lines GO