返回 DeepSeek-Reasonix
session_catalog_topic_helpers_test.go
根目录 / desktop / session_catalog_topic_helpers_test.go
1 package main
2
3 import (
4 "testing"
5 "time"
6 )
7
8 const sessionCatalogTestDeadline = 30 * time.Second
9
10 func waitForInitialCatalogReconcile(t *testing.T, app *App) bool {
11 t.Helper()
12 // Own cleanup before admission can fail. Publication/readiness assertions
13 // must not leave a late SQLite opener running into temporary-dir cleanup.
14 t.Cleanup(func() {
15 if !app.stopSessionCatalog(sessionCatalogTestDeadline) {
16 t.Error("session catalog fixture did not stop cleanly")
17 }
18 })
19 app.catalogLifecycleMu.Lock()
20 alreadyStarted := app.catalogInitialReconcileDone != nil
21 app.catalogLifecycleMu.Unlock()
22 app.startSessionCatalog()
23 app.catalogLifecycleMu.Lock()
24 done := app.catalogInitialReconcileDone
25 app.catalogLifecycleMu.Unlock()
26 if done == nil {
27 t.Fatal("session catalog initial reconcile was not armed")
28 }
29 select {
30 case <-done:
31 case <-time.After(sessionCatalogTestDeadline):
32 t.Fatal("session catalog initial reconcile did not finish")
33 }
34 if app.sessionCatalog.Load() == nil {
35 t.Fatal("session catalog exited before publication")
36 }
37 return alreadyStarted
38 }
39
40 func waitForCatalogReconcileJobs(t *testing.T, app *App) {
41 t.Helper()
42 jobs := make([]<-chan struct{}, 0)
43 for _, target := range app.sessionCatalogTargets() {
44 if !app.requestSessionCatalogReconcile(target.Path) {
45 t.Fatalf("request catalog reconcile for %q", target.Path)
46 }
47 key := projectRootKey(target.Path)
48 app.catalogReconcileMu.Lock()
49 if job := app.catalogReconcileJobs[key]; job != nil {
50 jobs = append(jobs, job.done)
51 }
52 app.catalogReconcileMu.Unlock()
53 }
54 deadline := time.Now().Add(sessionCatalogTestDeadline)
55 for _, done := range jobs {
56 if !waitChannelBefore(done, deadline) {
57 t.Fatal("explicit session catalog reconcile did not finish")
58 }
59 }
60 }
61
62 func waitForCatalogTopic(t *testing.T, app *App, scope, workspaceRoot, topicID string) []ProjectNode {
63 t.Helper()
64 waitForInitialCatalogReconcile(t, app)
65 waitForCatalogReconcileJobs(t, app)
66 nodes := mustListProjectTree(t, app)
67 for _, folder := range nodes {
68 if scope == "project" && (!sameProjectRoot(folder.Root, workspaceRoot) || folder.Kind != "project") {
69 continue
70 }
71 if scope != "project" && folder.Kind != "global_folder" {
72 continue
73 }
74 for _, topic := range folder.Children {
75 if topic.TopicID == topicID {
76 return nodes
77 }
78 }
79 }
80 t.Fatalf("catalog topic %q did not become visible after reconciliation: %#v", topicID, nodes)
81 return nil
82 }
83
84 func waitForCatalogTreeCondition(t *testing.T, app *App, description string, matches func([]ProjectNode) bool) []ProjectNode {
85 t.Helper()
86 waitForInitialCatalogReconcile(t, app)
87 waitForCatalogReconcileJobs(t, app)
88 nodes := mustListProjectTree(t, app)
89 if matches(nodes) {
90 return nodes
91 }
92 t.Fatalf("catalog did not reach %s: %#v", description, nodes)
93 return nil
94 }
95
95 lines GO