Skip to content

test: assert [rpc_endpoints] and [etherscan] are exactly supportedNetworks() - #112

Open
thedavidmeister wants to merge 1 commit into
mainfrom
47-supported-networks-fully-configured
Open

test: assert [rpc_endpoints] and [etherscan] are exactly supportedNetworks()#112
thedavidmeister wants to merge 1 commit into
mainfrom
47-supported-networks-fully-configured

Conversation

@thedavidmeister

Copy link
Copy Markdown
Contributor

Closes #47

What was wrong

"Which networks this org deploys to" was spelled three times with nothing joining them:

  • LibRainDeploy.supportedNetworks() (src/lib/LibRainDeploy.sol:237-245)
  • [rpc_endpoints] (foundry.toml:60-65)
  • [etherscan] (foundry.toml:71-76)

Only one pairing was enforced, and only incidentally: testZoltuFactoryCodehash forks every name supportedNetworks() returns, so a name with no [rpc_endpoints] alias fails there. Nothing in the repo read [etherscan] at all — grep over src/, script/ and test/ for readFile|parseToml|keyExists finds only .package.version, artifact JSON reads and snapshot record reads.

The consequence is the one foundry.toml:67-70 already documents and then leaves to memory: rainix-manual-sol-artifacts passes --verify, so a network in supportedNetworks() with no [etherscan] entry broadcasts, spends the gas, and then fails with no API key configured for the chain.

The fix

testSupportedNetworksAreFullyConfigured in test/src/lib/LibRainDeploy.t.sol reads foundry.toml and asserts both sections are EXACTLY supportedNetworks().

Asserted in both directions, by membership:

  • every supported network has an [rpc_endpoints] alias and an [etherscan] key — a missing one is the broadcast-then-fail above;
  • every [rpc_endpoints] alias and every [etherscan] key is a supported network — a stray one is config nothing ever reads.

One direction alone is not enough: containment forwards passes for a section carrying an alias nothing deploys to, and containment backwards passes for a network with no config at all. Membership both ways makes the sets equal, so the three lists can only be one list.

Membership rather than the key counts the issue proposed. It is strictly stronger — equal counts plus forward containment is only set equality if supportedNetworks() has no duplicates, which this test would then be leaning on testSupportedNetworks to guarantee — and the failure names the offending alias instead of only reporting that two numbers differ.

The raw file is read rather than forge's resolved config, because the values are ${VAR} interpolations that only exist in CI. The keys are the whole contract here and they are in the text — so the test needs no RPC and fails on the PR that drifts, rather than at dispatch time with a hot key.

fs_permissions already granted { access = "read", path = "./foundry.toml" } and LibRainDeploySnapshot.deployTag already reads that file the same way, so no config change was needed.

The only non-test change is a comment on [etherscan] naming the test that now enforces it, so the next person editing that section learns it is checked rather than rediscovering the hazard.

Mutation evidence

Five mutants, one per drift direction. Every run was checked for 1 total tests as well as for [FAIL, so a mutant that failed to compile could not be miscounted as killed.

# mutant killed with
M1 6th network in supportedNetworks(), no config at all supported network has no [rpc_endpoints] alias: optimism
M2 6th network + [rpc_endpoints] alias, [etherscan] missed — the hazard in the issue supported network has no [etherscan] key: optimism
M3 stray [rpc_endpoints] alias [rpc_endpoints] alias is not a supported network: optimism
M4 stray [etherscan] key [etherscan] key is not a supported network: optimism
M5 [etherscan] entry deleted supported network has no [etherscan] key: polygon

M2 is the scenario both findings describe end to end, and it is the one nothing in the repo caught before this test.

Scope

Test-only plus one comment. No behaviour change: supportedNetworks(), [rpc_endpoints] and [etherscan] are all untouched and already agree.

QA

  • Discriminating tests: testSupportedNetworksAreFullyConfigured — fails on base under each of the five drifts below (verified by applying each mutation to a base tree via .fixes/mutate.sh and running forge test --match-test; each run asserted 1 total tests actually RAN as well as [FAIL, so a non-compiling mutant cannot be miscounted as killed). It is a new test: before it nothing in the repo read [etherscan], so the base suite is green on every mutant below.
  • Mutations applied: LibRainDeploy.supportedNetworks() new string[](5)new string[](6) + networks[5] = "optimism" → killed by testSupportedNetworksAreFullyConfigured (supported network has no [rpc_endpoints] alias: optimism); that same mutation plus optimism added to [rpc_endpoints] — the exact hazard the issue describes — → killed by testSupportedNetworksAreFullyConfigured (supported network has no [etherscan] key: optimism); foundry.toml [rpc_endpoints] += optimism with the library untouched → killed ([rpc_endpoints] alias is not a supported network: optimism); foundry.toml [etherscan] += optimism with the library untouched → killed ([etherscan] key is not a supported network: optimism); foundry.toml [etherscan] −= polygon → killed (supported network has no [etherscan] key: polygon). 5 mutants, 5 killed.
  • Oracle: LibRainDeploy.supportedNetworks(), which is what the deploy actually iterates (RainDeployBroadcast.deployNetworks() defaults to it). The expected key sets are never restated in the test — they are read from supportedNetworks() at runtime and compared against keys parsed out of the foundry.toml text, so the test cannot drift from the list it is about. The consequence being guarded is stated independently by foundry.toml:67-70 and by rainix-manual-sol-artifacts passing --verify.
  • Category check: the issue asks for both [rpc_endpoints] and [etherscan] asserted against supportedNetworks() in both directions; covered — [rpc_endpoints] forward and reverse, [etherscan] forward and reverse. Implemented by membership rather than the proposed key counts: strictly stronger (count equality is only set equality given no duplicates in supportedNetworks(), which the count form would be leaning on testSupportedNetworks to supply) and it names the offending alias on failure instead of only reporting that two numbers differ. No fs_permissions change was needed, as both findings predicted.

…works()

The supported-network list was spelled three times — supportedNetworks(),
[rpc_endpoints] and [etherscan] — with nothing joining them. Only the RPC
pairing was enforced, and only incidentally, by the fork tests failing on a
name with no alias. Nothing anywhere read [etherscan], so a network added
without an explorer key broadcast, spent the gas, and only then failed
--verify with no API key configured for the chain.

testSupportedNetworksAreFullyConfigured reads foundry.toml and asserts both
sections against supportedNetworks() by membership in both directions, so the
three lists can only be one list: a missing entry is the broadcast-then-fail,
and a stray entry is config nothing reads. Reading the raw file rather than
the resolved config keeps the ${VAR} values out of it, so the test needs no
RPC and fails on the PR that drifts rather than at dispatch time.

Closes #47

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Aug 15, 2026
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thedavidmeister, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7cb92cb7-ab9c-49a9-83b0-ccd45c2a5159

📥 Commits

Reviewing files that changed from the base of the PR and between 86f8d96 and b3e5c25.

📒 Files selected for processing (2)
  • foundry.toml
  • test/src/lib/LibRainDeploy.t.sol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

The supported-network list lives in three places and only the RPC half is enforced

1 participant