返回 DeepSeek-Reasonix
indexed_lifecycle_test.go
根目录 / internal / history / indexed_lifecycle_test.go
1 package history
2
3 import (
4 "context"
5 "testing"
6 "time"
7
8 "reasonix/internal/historycatalog"
9 )
10
11 func TestIndexedCatalogManagerCloseFencesOpenAndAllowsRestart(t *testing.T) {
12 manager := &indexedCatalogManager{}
13 started := make(chan struct{})
14 release := make(chan struct{})
15 manager.open = func(ctx context.Context, opts historycatalog.Options) (*historycatalog.Catalog, error) {
16 close(started)
17 <-release
18 opts.Path, opts.InMemory = "", true
19 return historycatalog.Open(ctx, opts)
20 }
21 manager.register([]historycatalog.Root{{Path: t.TempDir(), Scope: "global"}})
22 <-started
23
24 closed := make(chan error, 1)
25 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
26 defer cancel()
27 go func() { closed <- manager.close(ctx) }()
28 select {
29 case err := <-closed:
30 t.Fatalf("close returned before in-flight open exited: %v", err)
31 default:
32 }
33 close(release)
34 if err := <-closed; err != nil {
35 t.Fatalf("close after open fence: %v", err)
36 }
37 if catalog := manager.get(); catalog != nil {
38 t.Fatal("stale open published a catalog after close")
39 }
40
41 manager.mu.Lock()
42 manager.open = func(ctx context.Context, opts historycatalog.Options) (*historycatalog.Catalog, error) {
43 opts.Path, opts.InMemory = "", true
44 return historycatalog.Open(ctx, opts)
45 }
46 manager.mu.Unlock()
47 manager.register([]historycatalog.Root{{Path: t.TempDir(), Scope: "global"}})
48 manager.mu.RLock()
49 var reopened chan struct{}
50 for _, done := range manager.opening {
51 reopened = done
52 }
53 manager.mu.RUnlock()
54 if reopened == nil {
55 t.Fatal("manager did not start a new generation after close")
56 }
57 <-reopened
58 if catalog := manager.get(); catalog == nil {
59 t.Fatal("manager did not publish the restarted catalog")
60 }
61 if err := manager.close(ctx); err != nil {
62 t.Fatalf("close restarted catalog: %v", err)
63 }
64 }
65
65 lines GO