返回 DeepSeek-Reasonix
host_concurrency_test.go
根目录 / internal / plugin / host_concurrency_test.go
1 package plugin
2
3 import (
4 "errors"
5 "fmt"
6 "sync"
7 "testing"
8
9 "reasonix/internal/tool"
10 )
11
12 // TestHostConcurrentAccess hammers the Host's mutable state from many goroutines:
13 // writers churn the failures records while readers snapshot status. The mutex
14 // must keep every read internally consistent; before it, a concurrent slice
15 // append against a copy could tear the slice header and panic.
16 func TestHostConcurrentAccess(t *testing.T) {
17 h := &Host{}
18 // Seed a few "connected" servers so the read paths have data to walk. These
19 // methods only read name/transport/toolCatalog, never the (nil) transport.
20 for i := range 4 {
21 h.clients = append(h.clients, &Client{
22 name: fmt.Sprintf("srv-%d", i), transport: "stdio",
23 toolCatalog: toolCatalogSnapshot{listed: true, adapters: make([]tool.Tool, i)},
24 })
25 h.prompts = append(h.prompts, Prompt{Server: fmt.Sprintf("srv-%d", i), Name: "p"})
26 }
27
28 const workers = 24
29 var wg sync.WaitGroup
30 wg.Add(workers)
31 for w := range workers {
32 go func(w int) {
33 defer wg.Done()
34 for i := range 500 {
35 switch (w + i) % 6 {
36 case 0:
37 h.RecordFailure(Spec{Name: fmt.Sprintf("bad-%d", i%8), Type: "stdio"}, errors.New("boom"))
38 case 1:
39 _ = h.Failures()
40 case 2:
41 _ = h.Servers()
42 case 3:
43 _ = h.ServerNames()
44 case 4:
45 _ = h.has(fmt.Sprintf("srv-%d", i%4))
46 case 5:
47 _ = h.Prompts()
48 }
49 }
50 }(w)
51 }
52 wg.Wait()
53 }
54
54 lines GO