Skip to content

fix(keeper): prevent mock race in Test_RegistrySynchronizer1_2_UpkeepReceivedLog#22216

Open
Tofel wants to merge 1 commit intodevelopfrom
fix/flaky-CRE-3895-2026-04-28
Open

fix(keeper): prevent mock race in Test_RegistrySynchronizer1_2_UpkeepReceivedLog#22216
Tofel wants to merge 1 commit intodevelopfrom
fix/flaky-CRE-3895-2026-04-28

Conversation

@Tofel
Copy link
Copy Markdown
Contributor

@Tofel Tofel commented Apr 28, 2026

Summary

  • Fixes flaky test Test_RegistrySynchronizer1_2_UpkeepReceivedLog in core/services/keeper/ (CRE-3895)
  • Changes registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Once().Maybe() at registry1_2_synchronizer_test.go:537
  • The fullSync goroutine (configured with getActiveUpkeepIDsTime=2) can make a second getUpkeep call after WaitForCount(upkeep_registrations, 1) returns, consuming the .Once() mock intended for the log handler — leaving handleUpkeepReceived without a response and causing WaitForCount(upkeep_registrations, 2) to time out
  • .Maybe() makes the mock a permanent fallback; the WaitForCount(..., 2) assertion still fully validates correctness

Test plan

  • CI runs Test_RegistrySynchronizer1_2_UpkeepReceivedLog with -race -shuffle=on (requires CL_DATABASE_URL — not runnable locally without a full DB setup)
  • Verify no regressions in sibling keeper synchronizer tests

🤖 Generated with Claude Code

…ReceivedLog

The fullSync goroutine (with getActiveUpkeepIDsTime=2) can consume the
getUpkeep mock intended for handleUpkeepReceived. Changing .Once() to
.Maybe() ensures the log-handler path always has a valid mock response
while preserving the WaitForCount(upkeep_registrations, 2) assertion.

Fixes CRE-3895

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 28, 2026 13:03
@Tofel Tofel requested a review from a team as a code owner April 28, 2026 13:03
@github-actions
Copy link
Copy Markdown
Contributor

👋 Tofel, thanks for creating this pull request!

To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team.

Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks!

@github-actions
Copy link
Copy Markdown
Contributor

✅ No conflicts with other open PRs targeting develop

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to eliminate flakiness in the keeper synchronizer test Test_RegistrySynchronizer1_2_UpkeepReceivedLog by relaxing the getUpkeep contract-call mock expectation so concurrent/overlapping calls don’t exhaust a .Once() expectation.

Changes:

  • Update the getUpkeep mock expectation in Test_RegistrySynchronizer1_2_UpkeepReceivedLog from .Once() to .Maybe() to avoid mock-consumption races.
  • Preserve the primary correctness assertion by still requiring upkeep_registrations to reach a count of 2.


registryMock := cltest.NewContractMockReceiver(t, ethMock, keeper.Registry1_2ABI, contractAddress)
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Once()
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Maybe()
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description attributes the flake to the fullSync goroutine making an extra getUpkeep call after WaitForCount(...) returns, but this test setup uses SyncInterval = 1000*time.Hour (and the resettable timer isn’t initially started) so a second scheduled full sync shouldn’t be able to run during the test. Consider updating the PR description to reflect the actual source of the extra getUpkeep call (or the current test timing) so future readers aren’t misled.

Copilot uses AI. Check for mistakes.

registryMock := cltest.NewContractMockReceiver(t, ethMock, keeper.Registry1_2ABI, contractAddress)
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Once()
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Maybe()
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching this mock to .Maybe() removes any upper/lower bound on how many getUpkeep RPCs are made during the test, which can mask regressions (e.g., unexpected repeated getUpkeep calls). If the goal is to avoid the race while keeping the assertion tight, consider using a bounded expectation (e.g., .Times(n) if the max is known) or a more specific matcher via MockMatchedResponse so only the UpkeepReceived-triggered call is constrained.

Suggested change
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Maybe()
registryMock.MockResponse("getUpkeep", upkeepConfig1_2).Once()

Copilot uses AI. Check for mistakes.
@trunk-io
Copy link
Copy Markdown

trunk-io Bot commented Apr 28, 2026

Static BadgeStatic BadgeStatic BadgeStatic Badge

Failed Test Failure Summary Logs
Test_EVMChainsController_Index The test failed without a specific error message, likely due to a known flaky test or an unrelated issue. Logs ↗︎
TestFunctionsConfigPoller/ThresholdPlugin Logs ↗︎
TestSyncer_V2_DBIntegration Logs ↗︎
TestFunctionsConfigPoller Logs ↗︎

View Full Report ↗︎Docs

@cl-sonarqube-production
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants