test(daemon): use portsmocks MockClock/MockTimer for poller test
Replace the hand-rolled pollerFakeClock/pollerFakeTimer with a mockery-generated MockClock/MockTimer harness (portsmocks), following the existing coordinatorMockClock pattern in render_coordinator_test.go. CLAUDE.md prefers generated mocks over hand-written fakes, and this repo already has one for exactly this shape.
- commit
- bf74b023c61771c48b77db47ec3581ecc9cad930
- author
- brice · · 2026-07-20 08:36 UTC
- parent
- 83b5fe2
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +66 -60
M internal/usecase/daemon/bar_refresh_test.go +66 -60
| @@ -12,7 +12,9 @@ import ( | |||
| 12 | 12 | ||
| 13 | 13 | "github.com/bnema/vev/internal/domain" | |
| 14 | 14 | "github.com/bnema/vev/internal/ports" | |
| 15 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |
| 15 | 16 | "github.com/bnema/vev/internal/usecase/layout" | |
| 17 | + | "github.com/stretchr/testify/mock" | |
| 16 | 18 | "github.com/stretchr/testify/require" | |
| 17 | 19 | ) | |
| 18 | 20 | ||
| @@ -434,53 +436,49 @@ func TestBarScriptRefreshIsPerSession(t *testing.T) { | |||
| 434 | 436 | require.Equal(t, "bottom-b", stateB.bottomRight) | |
| 435 | 437 | } | |
| 436 | 438 | ||
| 437 | - | // pollerFakeTimer is a manually-fired timer for barScriptPoller tests. Its | |
| 438 | - | // channel is buffered so the test goroutine can push a tick before the | |
| 439 | - | // poller reaches its select without blocking. | |
| 440 | - | type pollerFakeTimer struct { | |
| 441 | - | ch chan time.Time | |
| 439 | + | // pollerMockClock and pollerMockTimer wrap the generated portsmocks.MockClock | |
| 440 | + | // and portsmocks.MockTimer (see internal/ports/mocks/clock_mock.go), following | |
| 441 | + | // the coordinatorMockClock pattern in render_coordinator_test.go: the mocks | |
| 442 | + | // supply the ports.Clock/ports.Timer implementations, while a small | |
| 443 | + | // hand-owned struct retains the real channel and requested duration for each | |
| 444 | + | // timer so the test goroutine can fire ticks and assert on re-arming. | |
| 445 | + | type pollerMockClock struct { | |
| 446 | + | clock *portsmocks.MockClock | |
| 447 | + | timers chan *pollerMockTimer | |
| 442 | 448 | } | |
| 443 | 449 | ||
| 444 | - | func (t *pollerFakeTimer) C() <-chan time.Time { return t.ch } | |
| 445 | - | func (t *pollerFakeTimer) Reset(time.Duration) bool { return true } | |
| 446 | - | func (t *pollerFakeTimer) Stop() bool { return true } | |
| 447 | - | ||
| 448 | - | // pollerFakeClock records every timer barScriptPoller creates, along with the | |
| 449 | - | // duration requested, so a test can assert on re-arming behavior without real | |
| 450 | - | // sleeps or waiting out domain.MinBarInterval. NewTimer runs on the poller | |
| 451 | - | // goroutine while the test goroutine reads via count/at, so all access is | |
| 452 | - | // mutex-guarded; nothing here calls testify from the poller goroutine. | |
| 453 | - | type pollerFakeClock struct { | |
| 454 | - | mu sync.Mutex | |
| 455 | - | timers []*pollerFakeTimer | |
| 456 | - | durs []time.Duration | |
| 457 | - | } | |
| 458 | - | ||
| 459 | - | func (c *pollerFakeClock) Now() time.Time { return time.Unix(0, 0) } | |
| 460 | - | ||
| 461 | - | func (c *pollerFakeClock) NewTimer(d time.Duration) ports.Timer { | |
| 462 | - | c.mu.Lock() | |
| 463 | - | defer c.mu.Unlock() | |
| 464 | - | t := &pollerFakeTimer{ch: make(chan time.Time, 1)} | |
| 465 | - | c.timers = append(c.timers, t) | |
| 466 | - | c.durs = append(c.durs, d) | |
| 467 | - | return t | |
| 468 | - | } | |
| 469 | - | ||
| 470 | - | func (c *pollerFakeClock) count() int { | |
| 471 | - | c.mu.Lock() | |
| 472 | - | defer c.mu.Unlock() | |
| 473 | - | return len(c.timers) | |
| 450 | + | type pollerMockTimer struct { | |
| 451 | + | mock *portsmocks.MockTimer | |
| 452 | + | ch chan time.Time | |
| 453 | + | duration time.Duration | |
| 474 | 454 | } | |
| 475 | 455 | ||
| 476 | - | func (c *pollerFakeClock) at(i int) (*pollerFakeTimer, time.Duration) { | |
| 477 | - | c.mu.Lock() | |
| 478 | - | defer c.mu.Unlock() | |
| 479 | - | return c.timers[i], c.durs[i] | |
| 456 | + | func newPollerMockClock(t *testing.T, capacity int) *pollerMockClock { | |
| 457 | + | t.Helper() | |
| 458 | + | clk := &pollerMockClock{ | |
| 459 | + | clock: portsmocks.NewMockClock(t), | |
| 460 | + | timers: make(chan *pollerMockTimer, capacity), | |
| 461 | + | } | |
| 462 | + | clk.clock.EXPECT().Now().Return(time.Unix(0, 0)).Maybe() | |
| 463 | + | clk.clock.EXPECT().NewTimer(mock.Anything).RunAndReturn(func(d time.Duration) ports.Timer { | |
| 464 | + | timer := &pollerMockTimer{ | |
| 465 | + | mock: portsmocks.NewMockTimer(t), | |
| 466 | + | duration: d, | |
| 467 | + | ch: make(chan time.Time, 1), | |
| 468 | + | } | |
| 469 | + | // Stop is called 0, 1, or 2 times per timer depending on whether it is | |
| 470 | + | // replaced mid-loop or torn down by the poller's deferred Stop on | |
| 471 | + | // exit, so its expectation is unconstrained. | |
| 472 | + | timer.mock.EXPECT().C().Maybe().Return((<-chan time.Time)(timer.ch)) | |
| 473 | + | timer.mock.EXPECT().Stop().Maybe().Return(true) | |
| 474 | + | clk.timers <- timer | |
| 475 | + | return timer.mock | |
| 476 | + | }).Maybe() | |
| 477 | + | return clk | |
| 480 | 478 | } | |
| 481 | 479 | ||
| 482 | 480 | // TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel drives barScriptPoller | |
| 483 | - | // directly (not through Serve) with a fake clock, covering the three | |
| 481 | + | // directly (not through Serve) with a mock clock, covering the three | |
| 484 | 482 | // behaviors the Task 5 rewrite touched but left untested: | |
| 485 | 483 | // | |
| 486 | 484 | // 1. after a tick fires, the loop arms a NEW timer instead of blocking | |
| @@ -489,9 +487,9 @@ func (c *pollerFakeClock) at(i int) (*pollerFakeTimer, time.Duration) { | |||
| 489 | 487 | // rather than the one the poller started with, and | |
| 490 | 488 | // 3. cancelling the context makes the goroutine return instead of leaking. | |
| 491 | 489 | func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | |
| 492 | - | clk := &pollerFakeClock{} | |
| 490 | + | clk := newPollerMockClock(t, 8) | |
| 493 | 491 | d := newBarRefreshTestDaemon(&fakeBarRunner{}, 2*time.Second) | |
| 494 | - | d.clock = clk | |
| 492 | + | d.clock = clk.clock | |
| 495 | 493 | d.barScripts.reload = make(chan struct{}, 1) | |
| 496 | 494 | ||
| 497 | 495 | ctx, cancel := context.WithCancel(context.Background()) | |
| @@ -502,16 +500,23 @@ func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | |||
| 502 | 500 | }() | |
| 503 | 501 | ||
| 504 | 502 | // Stage 1: the poller arms its first timer at the configured interval. | |
| 505 | - | require.Eventually(t, func() bool { return clk.count() >= 1 }, time.Second, time.Millisecond) | |
| 506 | - | _, dur0 := clk.at(0) | |
| 507 | - | require.Equal(t, 2*time.Second, dur0) | |
| 503 | + | var timer0 *pollerMockTimer | |
| 504 | + | select { | |
| 505 | + | case timer0 = <-clk.timers: | |
| 506 | + | case <-time.After(time.Second): | |
| 507 | + | t.Fatal("timed out waiting for the poller's initial timer") | |
| 508 | + | } | |
| 509 | + | require.Equal(t, 2*time.Second, timer0.duration) | |
| 508 | 510 | ||
| 509 | 511 | // Stage 2: firing that timer processes the tick and re-arms a new one. | |
| 510 | - | timer0, _ := clk.at(0) | |
| 511 | 512 | timer0.ch <- time.Unix(0, 0) | |
| 512 | - | require.Eventually(t, func() bool { return clk.count() >= 2 }, time.Second, time.Millisecond) | |
| 513 | - | _, dur1 := clk.at(1) | |
| 514 | - | require.Equal(t, 2*time.Second, dur1, "tick re-arm should keep the same interval when config is unchanged") | |
| 513 | + | var timer1 *pollerMockTimer | |
| 514 | + | select { | |
| 515 | + | case timer1 = <-clk.timers: | |
| 516 | + | case <-time.After(time.Second): | |
| 517 | + | t.Fatal("timed out waiting for the poller to re-arm after a tick") | |
| 518 | + | } | |
| 519 | + | require.Equal(t, 2*time.Second, timer1.duration, "tick re-arm should keep the same interval when config is unchanged") | |
| 515 | 520 | ||
| 516 | 521 | // Stage 3: changing the interval and signaling reload re-arms at the NEW | |
| 517 | 522 | // interval instead of waiting out the timer armed under the old one. | |
| @@ -519,20 +524,21 @@ func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | |||
| 519 | 524 | d.barScripts.cfg.interval = 7 * time.Second | |
| 520 | 525 | d.barScripts.mu.Unlock() | |
| 521 | 526 | d.barScripts.reload <- struct{}{} | |
| 522 | - | require.Eventually(t, func() bool { return clk.count() >= 3 }, time.Second, time.Millisecond) | |
| 523 | - | _, dur2 := clk.at(2) | |
| 524 | - | require.Equal(t, 7*time.Second, dur2, "reload should re-arm at the newly configured interval") | |
| 527 | + | var timer2 *pollerMockTimer | |
| 528 | + | select { | |
| 529 | + | case timer2 = <-clk.timers: | |
| 530 | + | case <-time.After(time.Second): | |
| 531 | + | t.Fatal("timed out waiting for the poller to re-arm after reload") | |
| 532 | + | } | |
| 533 | + | require.Equal(t, 7*time.Second, timer2.duration, "reload should re-arm at the newly configured interval") | |
| 525 | 534 | ||
| 526 | 535 | // Stage 4: cancelling the context exits the goroutine instead of leaking it. | |
| 527 | 536 | cancel() | |
| 528 | - | require.Eventually(t, func() bool { | |
| 529 | - | select { | |
| 530 | - | case <-done: | |
| 531 | - | return true | |
| 532 | - | default: | |
| 533 | - | return false | |
| 534 | - | } | |
| 535 | - | }, time.Second, time.Millisecond, "barScriptPoller should exit promptly after context cancellation") | |
| 537 | + | select { | |
| 538 | + | case <-done: | |
| 539 | + | case <-time.After(time.Second): | |
| 540 | + | t.Fatal("barScriptPoller did not exit after context cancellation") | |
| 541 | + | } | |
| 536 | 542 | } | |
| 537 | 543 | ||
| 538 | 544 | func TestApplyConfigSignalsPollerReload(t *testing.T) { | |
| @@ -12,7 +12,9 @@ import ( | |||||
| 12 | 12 | ||||
| 13 | "github.com/bnema/vev/internal/domain" | 13 | "github.com/bnema/vev/internal/domain" | ||
| 14 | "github.com/bnema/vev/internal/ports" | 14 | "github.com/bnema/vev/internal/ports" | ||
| 15 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |||
| 15 | "github.com/bnema/vev/internal/usecase/layout" | 16 | "github.com/bnema/vev/internal/usecase/layout" | ||
| 17 | + | "github.com/stretchr/testify/mock" | |||
| 16 | "github.com/stretchr/testify/require" | 18 | "github.com/stretchr/testify/require" | ||
| 17 | ) | 19 | ) | ||
| 18 | 20 | ||||
| @@ -434,53 +436,49 @@ func TestBarScriptRefreshIsPerSession(t *testing.T) { | |||||
| 434 | require.Equal(t, "bottom-b", stateB.bottomRight) | 436 | require.Equal(t, "bottom-b", stateB.bottomRight) | ||
| 435 | } | 437 | } | ||
| 436 | 438 | ||||
| 437 | - | // pollerFakeTimer is a manually-fired timer for barScriptPoller tests. Its | 439 | + | // pollerMockClock and pollerMockTimer wrap the generated portsmocks.MockClock |
| 438 | - | // channel is buffered so the test goroutine can push a tick before the | 440 | + | // and portsmocks.MockTimer (see internal/ports/mocks/clock_mock.go), following |
| 439 | - | // poller reaches its select without blocking. | 441 | + | // the coordinatorMockClock pattern in render_coordinator_test.go: the mocks |
| 440 | - | type pollerFakeTimer struct { | 442 | + | // supply the ports.Clock/ports.Timer implementations, while a small |
| 441 | - | ch chan time.Time | 443 | + | // hand-owned struct retains the real channel and requested duration for each |
| 444 | + | // timer so the test goroutine can fire ticks and assert on re-arming. | |||
| 445 | + | type pollerMockClock struct { | |||
| 446 | + | clock *portsmocks.MockClock | |||
| 447 | + | timers chan *pollerMockTimer | |||
| 442 | } | 448 | } | ||
| 443 | 449 | ||||
| 444 | - | func (t *pollerFakeTimer) C() <-chan time.Time { return t.ch } | 450 | + | type pollerMockTimer struct { |
| 445 | - | func (t *pollerFakeTimer) Reset(time.Duration) bool { return true } | 451 | + | mock *portsmocks.MockTimer |
| 446 | - | func (t *pollerFakeTimer) Stop() bool { return true } | 452 | + | ch chan time.Time |
| 447 | - | 453 | + | duration time.Duration | |
| 448 | - | // pollerFakeClock records every timer barScriptPoller creates, along with the | |||
| 449 | - | // duration requested, so a test can assert on re-arming behavior without real | |||
| 450 | - | // sleeps or waiting out domain.MinBarInterval. NewTimer runs on the poller | |||
| 451 | - | // goroutine while the test goroutine reads via count/at, so all access is | |||
| 452 | - | // mutex-guarded; nothing here calls testify from the poller goroutine. | |||
| 453 | - | type pollerFakeClock struct { | |||
| 454 | - | mu sync.Mutex | |||
| 455 | - | timers []*pollerFakeTimer | |||
| 456 | - | durs []time.Duration | |||
| 457 | - | } | |||
| 458 | - | ||||
| 459 | - | func (c *pollerFakeClock) Now() time.Time { return time.Unix(0, 0) } | |||
| 460 | - | ||||
| 461 | - | func (c *pollerFakeClock) NewTimer(d time.Duration) ports.Timer { | |||
| 462 | - | c.mu.Lock() | |||
| 463 | - | defer c.mu.Unlock() | |||
| 464 | - | t := &pollerFakeTimer{ch: make(chan time.Time, 1)} | |||
| 465 | - | c.timers = append(c.timers, t) | |||
| 466 | - | c.durs = append(c.durs, d) | |||
| 467 | - | return t | |||
| 468 | - | } | |||
| 469 | - | ||||
| 470 | - | func (c *pollerFakeClock) count() int { | |||
| 471 | - | c.mu.Lock() | |||
| 472 | - | defer c.mu.Unlock() | |||
| 473 | - | return len(c.timers) | |||
| 474 | } | 454 | } | ||
| 475 | 455 | ||||
| 476 | - | func (c *pollerFakeClock) at(i int) (*pollerFakeTimer, time.Duration) { | 456 | + | func newPollerMockClock(t *testing.T, capacity int) *pollerMockClock { |
| 477 | - | c.mu.Lock() | 457 | + | t.Helper() |
| 478 | - | defer c.mu.Unlock() | 458 | + | clk := &pollerMockClock{ |
| 479 | - | return c.timers[i], c.durs[i] | 459 | + | clock: portsmocks.NewMockClock(t), |
| 460 | + | timers: make(chan *pollerMockTimer, capacity), | |||
| 461 | + | } | |||
| 462 | + | clk.clock.EXPECT().Now().Return(time.Unix(0, 0)).Maybe() | |||
| 463 | + | clk.clock.EXPECT().NewTimer(mock.Anything).RunAndReturn(func(d time.Duration) ports.Timer { | |||
| 464 | + | timer := &pollerMockTimer{ | |||
| 465 | + | mock: portsmocks.NewMockTimer(t), | |||
| 466 | + | duration: d, | |||
| 467 | + | ch: make(chan time.Time, 1), | |||
| 468 | + | } | |||
| 469 | + | // Stop is called 0, 1, or 2 times per timer depending on whether it is | |||
| 470 | + | // replaced mid-loop or torn down by the poller's deferred Stop on | |||
| 471 | + | // exit, so its expectation is unconstrained. | |||
| 472 | + | timer.mock.EXPECT().C().Maybe().Return((<-chan time.Time)(timer.ch)) | |||
| 473 | + | timer.mock.EXPECT().Stop().Maybe().Return(true) | |||
| 474 | + | clk.timers <- timer | |||
| 475 | + | return timer.mock | |||
| 476 | + | }).Maybe() | |||
| 477 | + | return clk | |||
| 480 | } | 478 | } | ||
| 481 | 479 | ||||
| 482 | // TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel drives barScriptPoller | 480 | // TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel drives barScriptPoller | ||
| 483 | - | // directly (not through Serve) with a fake clock, covering the three | 481 | + | // directly (not through Serve) with a mock clock, covering the three |
| 484 | // behaviors the Task 5 rewrite touched but left untested: | 482 | // behaviors the Task 5 rewrite touched but left untested: | ||
| 485 | // | 483 | // | ||
| 486 | // 1. after a tick fires, the loop arms a NEW timer instead of blocking | 484 | // 1. after a tick fires, the loop arms a NEW timer instead of blocking | ||
| @@ -489,9 +487,9 @@ func (c *pollerFakeClock) at(i int) (*pollerFakeTimer, time.Duration) { | |||||
| 489 | // rather than the one the poller started with, and | 487 | // rather than the one the poller started with, and | ||
| 490 | // 3. cancelling the context makes the goroutine return instead of leaking. | 488 | // 3. cancelling the context makes the goroutine return instead of leaking. | ||
| 491 | func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | 489 | func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | ||
| 492 | - | clk := &pollerFakeClock{} | 490 | + | clk := newPollerMockClock(t, 8) |
| 493 | d := newBarRefreshTestDaemon(&fakeBarRunner{}, 2*time.Second) | 491 | d := newBarRefreshTestDaemon(&fakeBarRunner{}, 2*time.Second) | ||
| 494 | - | d.clock = clk | 492 | + | d.clock = clk.clock |
| 495 | d.barScripts.reload = make(chan struct{}, 1) | 493 | d.barScripts.reload = make(chan struct{}, 1) | ||
| 496 | 494 | ||||
| 497 | ctx, cancel := context.WithCancel(context.Background()) | 495 | ctx, cancel := context.WithCancel(context.Background()) | ||
| @@ -502,16 +500,23 @@ func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | |||||
| 502 | }() | 500 | }() | ||
| 503 | 501 | ||||
| 504 | // Stage 1: the poller arms its first timer at the configured interval. | 502 | // Stage 1: the poller arms its first timer at the configured interval. | ||
| 505 | - | require.Eventually(t, func() bool { return clk.count() >= 1 }, time.Second, time.Millisecond) | 503 | + | var timer0 *pollerMockTimer |
| 506 | - | _, dur0 := clk.at(0) | 504 | + | select { |
| 507 | - | require.Equal(t, 2*time.Second, dur0) | 505 | + | case timer0 = <-clk.timers: |
| 506 | + | case <-time.After(time.Second): | |||
| 507 | + | t.Fatal("timed out waiting for the poller's initial timer") | |||
| 508 | + | } | |||
| 509 | + | require.Equal(t, 2*time.Second, timer0.duration) | |||
| 508 | 510 | ||||
| 509 | // Stage 2: firing that timer processes the tick and re-arms a new one. | 511 | // Stage 2: firing that timer processes the tick and re-arms a new one. | ||
| 510 | - | timer0, _ := clk.at(0) | |||
| 511 | timer0.ch <- time.Unix(0, 0) | 512 | timer0.ch <- time.Unix(0, 0) | ||
| 512 | - | require.Eventually(t, func() bool { return clk.count() >= 2 }, time.Second, time.Millisecond) | 513 | + | var timer1 *pollerMockTimer |
| 513 | - | _, dur1 := clk.at(1) | 514 | + | select { |
| 514 | - | require.Equal(t, 2*time.Second, dur1, "tick re-arm should keep the same interval when config is unchanged") | 515 | + | case timer1 = <-clk.timers: |
| 516 | + | case <-time.After(time.Second): | |||
| 517 | + | t.Fatal("timed out waiting for the poller to re-arm after a tick") | |||
| 518 | + | } | |||
| 519 | + | require.Equal(t, 2*time.Second, timer1.duration, "tick re-arm should keep the same interval when config is unchanged") | |||
| 515 | 520 | ||||
| 516 | // Stage 3: changing the interval and signaling reload re-arms at the NEW | 521 | // Stage 3: changing the interval and signaling reload re-arms at the NEW | ||
| 517 | // interval instead of waiting out the timer armed under the old one. | 522 | // interval instead of waiting out the timer armed under the old one. | ||
| @@ -519,20 +524,21 @@ func TestBarScriptPollerRearmsTicksReloadsAndExitsOnCancel(t *testing.T) { | |||||
| 519 | d.barScripts.cfg.interval = 7 * time.Second | 524 | d.barScripts.cfg.interval = 7 * time.Second | ||
| 520 | d.barScripts.mu.Unlock() | 525 | d.barScripts.mu.Unlock() | ||
| 521 | d.barScripts.reload <- struct{}{} | 526 | d.barScripts.reload <- struct{}{} | ||
| 522 | - | require.Eventually(t, func() bool { return clk.count() >= 3 }, time.Second, time.Millisecond) | 527 | + | var timer2 *pollerMockTimer |
| 523 | - | _, dur2 := clk.at(2) | 528 | + | select { |
| 524 | - | require.Equal(t, 7*time.Second, dur2, "reload should re-arm at the newly configured interval") | 529 | + | case timer2 = <-clk.timers: |
| 530 | + | case <-time.After(time.Second): | |||
| 531 | + | t.Fatal("timed out waiting for the poller to re-arm after reload") | |||
| 532 | + | } | |||
| 533 | + | require.Equal(t, 7*time.Second, timer2.duration, "reload should re-arm at the newly configured interval") | |||
| 525 | 534 | ||||
| 526 | // Stage 4: cancelling the context exits the goroutine instead of leaking it. | 535 | // Stage 4: cancelling the context exits the goroutine instead of leaking it. | ||
| 527 | cancel() | 536 | cancel() | ||
| 528 | - | require.Eventually(t, func() bool { | 537 | + | select { |
| 529 | - | select { | 538 | + | case <-done: |
| 530 | - | case <-done: | 539 | + | case <-time.After(time.Second): |
| 531 | - | return true | 540 | + | t.Fatal("barScriptPoller did not exit after context cancellation") |
| 532 | - | default: | 541 | + | } |
| 533 | - | return false | |||
| 534 | - | } | |||
| 535 | - | }, time.Second, time.Millisecond, "barScriptPoller should exit promptly after context cancellation") | |||
| 536 | } | 542 | } | ||
| 537 | 543 | ||||
| 538 | func TestApplyConfigSignalsPollerReload(t *testing.T) { | 544 | func TestApplyConfigSignalsPollerReload(t *testing.T) { | ||