返回 DeepSeek-Reasonix
config_home_test.go
根目录 / internal / boot / config_home_test.go
1 package boot
2
3 import (
4 "context"
5 "path/filepath"
6 "sync"
7 "testing"
8 "time"
9
10 "reasonix/internal/config"
11 "reasonix/internal/history"
12 "reasonix/internal/historycatalog"
13 )
14
15 // isolateConfigHome redirects user config and cache paths to a per-test temp
16 // directory. The history projection is process-global, so it must be closed on
17 // both sides of the environment change instead of retaining a deleted cache
18 // path across repeated tests.
19 func isolateConfigHome(t *testing.T) string {
20 t.Helper()
21 closeBootTestHistoryCatalog(t)
22 dir := robustTempDir(t)
23 t.Setenv("HOME", dir)
24 t.Setenv("USERPROFILE", dir)
25 t.Setenv("XDG_CONFIG_HOME", dir)
26 t.Setenv("AppData", filepath.Join(dir, "AppData"))
27 t.Setenv("LocalAppData", filepath.Join(dir, "LocalAppData"))
28 t.Setenv("REASONIX_CREDENTIALS_STORE", "file")
29 t.Setenv(config.CompletionValidationModeEnv, config.CompletionValidationOff)
30 t.Cleanup(func() { closeBootTestHistoryCatalog(t) })
31 return dir
32 }
33
34 func closeBootTestHistoryCatalog(t *testing.T) {
35 t.Helper()
36 // Fixture teardown must await resource release before changing HOME. Its
37 // bound is the suite deadline; shutdown latency is tested by the owner.
38 ctx := context.Background()
39 if deadline, ok := t.Deadline(); ok {
40 var cancel context.CancelFunc
41 ctx, cancel = context.WithDeadline(ctx, deadline)
42 defer cancel()
43 }
44 if err := history.CloseSharedCatalog(ctx); err != nil {
45 t.Fatalf("close shared history catalog: %v", err)
46 }
47 }
48
49 // fenceBootTestHistoryCatalog releases a process-global projection inherited
50 // from an earlier test and closes the replacement before t.TempDir cleanup.
51 // Windows cannot remove a temporary REASONIX_HOME while SQLite still owns it.
52 func fenceBootTestHistoryCatalog(t *testing.T) {
53 t.Helper()
54 closeBootTestHistoryCatalog(t)
55 t.Cleanup(func() { closeBootTestHistoryCatalog(t) })
56 }
57
58 func bootTestHistoryIndexReady(t *testing.T) <-chan struct{} {
59 t.Helper()
60 ready := make(chan struct{})
61 var once sync.Once
62 history.RegisterCatalogObserver(func(status historycatalog.Status, _ []string, _ string) {
63 if status.Indexed > 0 {
64 once.Do(func() { close(ready) })
65 }
66 })
67 return ready
68 }
69
70 func waitForBootTestHistoryIndex(t *testing.T, ready <-chan struct{}) {
71 t.Helper()
72 select {
73 case <-ready:
74 case <-time.After(30 * time.Second):
75 t.Fatal("timed out waiting for history catalog to index the saved fixture")
76 }
77 }
78
78 lines GO