Skip to content

Commit 0103dcc

Browse files
authored
Merge pull request #85 from ashioyajotham/test/health-worker-deterministic-sync-83
test(service): deterministic sync for health worker tests (#83)
2 parents 067b8dd + 4ea881a commit 0103dcc

1 file changed

Lines changed: 69 additions & 45 deletions

File tree

nexus-broker/internal/service/connection_health_test.go

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,37 @@ import (
1717
"github.com/Prescott-Data/nexus-framework/nexus-broker/internal/service"
1818
)
1919

20+
func runWorkerUntilSignal(t *testing.T, worker *service.ConnectionHealthWorker, done <-chan struct{}) {
21+
t.Helper()
22+
ctx, cancel := context.WithCancel(context.Background())
23+
workerDone := make(chan struct{})
24+
25+
go func() {
26+
defer close(workerDone)
27+
worker.Start(ctx)
28+
}()
29+
30+
select {
31+
case <-done:
32+
case <-time.After(2 * time.Second):
33+
cancel()
34+
select {
35+
case <-workerDone:
36+
case <-time.After(2 * time.Second):
37+
t.Fatal("timed out waiting for health worker to stop after signal timeout")
38+
}
39+
t.Fatal("timed out waiting for health worker signal")
40+
}
41+
42+
cancel()
43+
44+
select {
45+
case <-workerDone:
46+
case <-time.After(2 * time.Second):
47+
t.Fatal("timed out waiting for health worker to stop")
48+
}
49+
}
50+
2051
// Add missing mock methods to MockConnectionRepository
2152
func (m *MockConnectionRepository) GetForHealthCheck(ctx context.Context, limit int) ([]*domain.ConnectionWithProvider, error) {
2253
args := m.Called(ctx, limit)
@@ -126,15 +157,14 @@ func TestConnectionHealthWorker_OAuth2_Healthy(t *testing.T) {
126157
mockSvc.On("Refresh", mock.Anything, connID).Return(&service.RefreshResponse{}, nil).Once()
127158

128159
// Should update health to healthy
129-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "healthy").Return(nil).Once()
160+
done := make(chan struct{})
161+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "healthy").
162+
Run(func(args mock.Arguments) { close(done) }).
163+
Return(nil).Once()
130164

131165
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
132-
133-
ctx, cancel := context.WithCancel(context.Background())
134-
go worker.Start(ctx)
135-
136-
time.Sleep(50 * time.Millisecond) // Give it time to run at least once
137-
cancel()
166+
167+
runWorkerUntilSignal(t, worker, done)
138168

139169
mockRepo.AssertExpectations(t)
140170
mockSvc.AssertExpectations(t)
@@ -169,15 +199,14 @@ func TestConnectionHealthWorker_OAuth2_Expired(t *testing.T) {
169199
mockRepo.On("UpdateStatus", mock.Anything, connID, "expired").Return(nil).Once()
170200

171201
// Should update health to expired
172-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "expired").Return(nil).Once()
202+
done := make(chan struct{})
203+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "expired").
204+
Run(func(args mock.Arguments) { close(done) }).
205+
Return(nil).Once()
173206

174207
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
175-
176-
ctx, cancel := context.WithCancel(context.Background())
177-
go worker.Start(ctx)
178-
179-
time.Sleep(50 * time.Millisecond)
180-
cancel()
208+
209+
runWorkerUntilSignal(t, worker, done)
181210

182211
mockRepo.AssertExpectations(t)
183212
mockSvc.AssertExpectations(t)
@@ -213,15 +242,14 @@ func TestConnectionHealthWorker_OAuth2_ProviderDown_ShieldsExpiration(t *testing
213242

214243
// Should NOT call UpdateStatus (no expiration)
215244
// Should update health to "unhealthy" instead of "expired"
216-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "unhealthy").Return(nil).Once()
245+
done := make(chan struct{})
246+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "unhealthy").
247+
Run(func(args mock.Arguments) { close(done) }).
248+
Return(nil).Once()
217249

218250
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
219251

220-
ctx, cancel := context.WithCancel(context.Background())
221-
go worker.Start(ctx)
222-
223-
time.Sleep(50 * time.Millisecond)
224-
cancel()
252+
runWorkerUntilSignal(t, worker, done)
225253

226254
mockRepo.AssertExpectations(t)
227255
mockSvc.AssertExpectations(t)
@@ -267,15 +295,14 @@ func TestConnectionHealthWorker_APIKey_Expired(t *testing.T) {
267295
mockRepo.On("UpdateStatus", mock.Anything, connID, "expired").Return(nil).Once()
268296

269297
// Should update health to expired
270-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "expired").Return(nil).Once()
298+
done := make(chan struct{})
299+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "expired").
300+
Run(func(args mock.Arguments) { close(done) }).
301+
Return(nil).Once()
271302

272303
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
273-
274-
ctx, cancel := context.WithCancel(context.Background())
275-
go worker.Start(ctx)
276-
277-
time.Sleep(50 * time.Millisecond)
278-
cancel()
304+
305+
runWorkerUntilSignal(t, worker, done)
279306

280307
mockRepo.AssertExpectations(t)
281308
mockSvc.AssertExpectations(t)
@@ -304,15 +331,14 @@ func TestConnectionHealthWorker_OAuth2_Upstream5xx_MarksUnhealthy(t *testing.T)
304331

305332
// Should set health_status to "unhealthy", NOT "expired"
306333
// Should NOT call UpdateStatus — connection status stays "active"
307-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "unhealthy").Return(nil).Once()
334+
done := make(chan struct{})
335+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "unhealthy").
336+
Run(func(args mock.Arguments) { close(done) }).
337+
Return(nil).Once()
308338

309339
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
310340

311-
ctx, cancel := context.WithCancel(context.Background())
312-
go worker.Start(ctx)
313-
314-
time.Sleep(50 * time.Millisecond)
315-
cancel()
341+
runWorkerUntilSignal(t, worker, done)
316342

317343
mockRepo.AssertExpectations(t)
318344
mockSvc.AssertExpectations(t)
@@ -340,15 +366,14 @@ func TestConnectionHealthWorker_OAuth2_403_MarksDegraded(t *testing.T) {
340366
mockSvc.On("Refresh", mock.Anything, connID).Return(&service.RefreshResponse{StatusCode: 403}, errors.New("forbidden")).Once()
341367

342368
// Should set health_status to "degraded", NOT "expired"
343-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "degraded").Return(nil).Once()
369+
done := make(chan struct{})
370+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "degraded").
371+
Run(func(args mock.Arguments) { close(done) }).
372+
Return(nil).Once()
344373

345374
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
346375

347-
ctx, cancel := context.WithCancel(context.Background())
348-
go worker.Start(ctx)
349-
350-
time.Sleep(50 * time.Millisecond)
351-
cancel()
376+
runWorkerUntilSignal(t, worker, done)
352377

353378
mockRepo.AssertExpectations(t)
354379
mockSvc.AssertExpectations(t)
@@ -376,15 +401,14 @@ func TestConnectionHealthWorker_OAuth2_NetworkError_MarksDegraded(t *testing.T)
376401
mockSvc.On("Refresh", mock.Anything, connID).Return((*service.RefreshResponse)(nil), errors.New("connection refused")).Once()
377402

378403
// Should set health_status to "degraded" (we don't know if credential is valid)
379-
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "degraded").Return(nil).Once()
404+
done := make(chan struct{})
405+
mockRepo.On("UpdateHealthStatus", mock.Anything, connID, "degraded").
406+
Run(func(args mock.Arguments) { close(done) }).
407+
Return(nil).Once()
380408

381409
worker := service.NewConnectionHealthWorker(mockRepo, mockSvc, mockHealth, 10*time.Millisecond)
382410

383-
ctx, cancel := context.WithCancel(context.Background())
384-
go worker.Start(ctx)
385-
386-
time.Sleep(50 * time.Millisecond)
387-
cancel()
411+
runWorkerUntilSignal(t, worker, done)
388412

389413
mockRepo.AssertExpectations(t)
390414
mockSvc.AssertExpectations(t)

0 commit comments

Comments
 (0)