Skip to content

Commit e705003

Browse files
committed
fix(tools): fix data race in mockInteractor used by GWS test
Added mutex to mockInteractor and a linkCh channel so TestGWSExecute_MissingScopeSignaled no longer polls shared state.
1 parent b4ba20f commit e705003

File tree

2 files changed

+22
-14
lines changed

2 files changed

+22
-14
lines changed

agent/tools/guard_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,42 @@ package tools
33
import (
44
"context"
55
"errors"
6+
"sync"
67
"testing"
78
)
89

910
type mockInteractor struct {
11+
mu sync.Mutex
1012
notified []string
1113
links []struct{ text, url string }
1214
approvals []string
1315
approveAll bool
1416
approveErr error
1517
notifyErr error
18+
linkCh chan struct{ text, url string } // optional signal for NotifyLink
1619
}
1720

1821
func (m *mockInteractor) Notify(msg string) error {
22+
m.mu.Lock()
23+
defer m.mu.Unlock()
1924
m.notified = append(m.notified, msg)
2025
return m.notifyErr
2126
}
2227

2328
func (m *mockInteractor) NotifyLink(text, url string) error {
29+
m.mu.Lock()
2430
m.links = append(m.links, struct{ text, url string }{text, url})
31+
ch := m.linkCh
32+
m.mu.Unlock()
33+
if ch != nil {
34+
ch <- struct{ text, url string }{text, url}
35+
}
2536
return nil
2637
}
2738

2839
func (m *mockInteractor) RequestApproval(desc string) (bool, error) {
40+
m.mu.Lock()
41+
defer m.mu.Unlock()
2942
m.approvals = append(m.approvals, desc)
3043
if m.approveErr != nil {
3144
return false, m.approveErr

agent/tools/gws_execute_test.go

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ func TestGWSExecute_MissingScopeSignaled(t *testing.T) {
195195
TokenDBPath: dbPath,
196196
})
197197
bridge := NewTokenBridge(g, "user@test.com")
198-
inter := &mockInteractor{approveAll: false}
198+
linkCh := make(chan struct{ text, url string }, 1)
199+
inter := &mockInteractor{approveAll: false, linkCh: linkCh}
199200
runner := &mockRunner{outputs: map[string]string{"calendar events.list": `{"items":[]}`}}
200201

201202
waiter := google.NewScopeWaiter()
@@ -230,22 +231,16 @@ func TestGWSExecute_MissingScopeSignaled(t *testing.T) {
230231
close(done)
231232
}()
232233

233-
// Wait for the auth link to be sent.
234-
deadline := time.After(3 * time.Second)
235-
for {
236-
if len(inter.links) > 0 {
237-
break
238-
}
239-
select {
240-
case <-deadline:
241-
t.Fatal("timed out waiting for auth link")
242-
default:
243-
time.Sleep(10 * time.Millisecond)
244-
}
234+
// Wait for the auth link to be sent via channel (race-free).
235+
var link struct{ text, url string }
236+
select {
237+
case link = <-linkCh:
238+
case <-time.After(3 * time.Second):
239+
t.Fatal("timed out waiting for auth link")
245240
}
246241

247242
// Extract state from the auth link URL.
248-
linkURL := inter.links[0].url
243+
linkURL := link.url
249244
stateIdx := strings.Index(linkURL, "state=")
250245
if stateIdx < 0 {
251246
t.Fatal("auth link missing state param")

0 commit comments

Comments
 (0)