返回 DeepSeek-Reasonix
skill_watch_test.go
根目录 / internal / boot / skill_watch_test.go
1 package boot
2
3 import (
4 "context"
5 "io"
6 "testing"
7
8 "reasonix/internal/skill/skillwatch"
9 )
10
11 // A package test binary must never spawn a watcher helper: one per test would
12 // leave stray reasonix-desktop.exe processes behind. The host constructor is
13 // what the desktop calls, so it has to apply the same gate as Build.
14 func TestHostSkillWatchServiceIsDisabledInTestBinaries(t *testing.T) {
15 if !watchSkillsEnabled() {
16 if service := NewHostSkillWatchService(io.Discard); service != nil {
17 _ = service.Close()
18 t.Fatal("host skill watch service was created in a package test binary")
19 }
20 return
21 }
22 // A production-named binary (an installed desktop service) does own one.
23 service := NewHostSkillWatchService(io.Discard)
24 if service == nil {
25 t.Fatal("host skill watch service is nil in a production binary")
26 }
27 if err := service.Close(); err != nil {
28 t.Fatalf("close host skill watch service: %v", err)
29 }
30 }
31
32 // subscribeScanOnly adds one subscription so the counters can show whether a
33 // close happened. ScanOnly keeps this process-free, which a real helper would
34 // not: on Windows the helper is os.Executable(), and here that is this test
35 // binary — the child would re-run the suite.
36 func subscribeScanOnly(t *testing.T, service *skillwatch.Service) {
37 t.Helper()
38 sub := service.Subscribe(t.TempDir(), 1,
39 func(context.Context, string, int) ([]string, bool) { return nil, true },
40 func(context.Context, string, int) ([32]byte, int, bool) { return [32]byte{}, 0, true },
41 func(string) {})
42 t.Cleanup(sub.Release)
43 }
44
45 // A build must not close a caller-owned service: every other controller on the
46 // host is still subscribed to it, and closing it tears down the helper they all
47 // watch through.
48 func TestCloseSkillsWithWatcherLeavesCallerOwnedServiceOpen(t *testing.T) {
49 host := skillwatch.NewService(skillwatch.Options{ScanOnly: true, Stderr: io.Discard})
50 subscribeScanOnly(t, host)
51 if host.Diagnostics().LogicalSubscriptions != 1 {
52 t.Fatal("fixture: the subscription did not register")
53 }
54 ptr := host
55 closeSkillsWithWatcher(nil, nil, &host, true)
56 if host != nil {
57 t.Fatal("closeSkillsWithWatcher must clear the caller's pointer")
58 }
59 if ptr.Diagnostics().LogicalSubscriptions != 1 {
60 t.Fatal("a caller-owned service must outlive the build that subscribed to it")
61 }
62 if err := ptr.Close(); err != nil {
63 t.Fatalf("close caller-owned service: %v", err)
64 }
65 }
66
67 // The mirror case: a service the build created is closed with the build.
68 func TestCloseSkillsWithWatcherClosesABuildOwnedService(t *testing.T) {
69 owned := skillwatch.NewService(skillwatch.Options{ScanOnly: true, Stderr: io.Discard})
70 subscribeScanOnly(t, owned)
71 if owned.Diagnostics().LogicalSubscriptions != 1 {
72 t.Fatal("fixture: the subscription did not register")
73 }
74 ptr := owned
75 closeSkillsWithWatcher(nil, nil, &owned, false)
76 if ptr.Diagnostics().LogicalSubscriptions != 0 {
77 t.Fatal("a build-owned service must be closed with the build")
78 }
79 }
80
80 lines GO