Skip to content

test(pricefeedwatcher): assert only one watch loop is started - #4032

Merged
j0sh merged 1 commit into
ja/golang-1.27from
rs/pricefeed-single-watch-loop-test
Aug 21, 2026
Merged

test(pricefeedwatcher): assert only one watch loop is started#4032
j0sh merged 1 commit into
ja/golang-1.27from
rs/pricefeed-single-watch-loop-test

Conversation

@rickstaa

Copy link
Copy Markdown
Member

Follow-up to #4031, targeting ja/golang-1.27 so it lands with the Go 1.27 bump.

#4031 removes the reflect-based cancel func comparison from TestPriceFeedWatcher_Subscribe. That removal is correct, but it leaves the "only 1 watch loop gets started" comment describing something nothing checks. This adds a test that actually checks it.

Why the old assertion could not work

It compared reflect.ValueOf(w.cancelWatch).Pointer() across subscriptions. For a func value that returns the address of the compiled function body, not the identity of the closure. Every cancelWatch comes from context.WithCancel, so all of them share one code address:

c1=0x4933a0 c2=0x4933a0  equal=true

Two unrelated cancel funcs, guaranteed equal. The assertion passed whether ensureWatchLocked assigned once or five times, so it could not detect the bug it was written for. reflect's docs say as much: for a Func the result "is not necessarily enough to identify a single function uniquely".

What this asserts instead

A duplicate watch loop is observable: each loop gets its own ticker and polls w.priceFeed once per priceUpdatePeriod. So N loops means N FetchPriceData calls per tick.

priceUpdatePeriod is 1 hour, which is why this was not testable before. testing/synctest (already used elsewhere in #4031) makes the hour instant, puts newTruncatedTicker on a deterministic boundary, and synctest.Wait() blocks until every bubble goroutine is idle. No sleeps, no flake.

Verified both directions

  • Against the real code: passes.
  • With the if w.cancelWatch != nil { return } guard in ensureWatchLocked removed: fails with Expected number of calls (1) does not match the actual number of calls (5).

🤖 Generated with Claude Code

The reflect-based cancel func comparison this replaces could never fail:
reflect.Value.Pointer returns a code pointer, and every cancelWatch comes
from context.WithCancel, so all of them compared equal regardless of how
many loops ran.

Each watch loop polls the price feed once per period, so a duplicate loop
is observable as a duplicate FetchPriceData call. synctest makes the hour
long period instant and the goroutine scheduling deterministic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the go Pull requests that update Go code label Aug 20, 2026
@j0sh
j0sh merged commit a1d2e90 into ja/golang-1.27 Aug 21, 2026
12 checks passed
@j0sh
j0sh deleted the rs/pricefeed-single-watch-loop-test branch August 21, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants