test(deploy): close the coverage gaps a whole-repo mutation pass found - #19
test(deploy): close the coverage gaps a whole-repo mutation pass found#19thedavidmeister wants to merge 1 commit into
Conversation
Probe every behaviour of LibRainDeploy by breaking one line at a time and running the whole suite. Add tests only for the mutants that survived, and tighten two existing tests whose names claimed coverage their assertions did not deliver. New behaviours pinned: - isStartBlock at block 0 and block 1, using a Base genesis allocation target - isStartBlock compares against the immediately preceding block - findDeployBlock returns the exact deploy block, including when it is the block the fork is at, and restores the fork when it reverts - deployZoltu rejects a codeless result, a failed call whose output buffer holds a live address, the zero address, and never forwards caller value - deployToNetworks accepts a dependency that has code - deployAndBroadcast broadcasts as the address derived from the private key testZoltuFactoryCodehash now covers every supported network rather than Arbitrum alone, so base_sepolia, flare and polygon are exercised at all. testDeployToNetworksMultipleNetworks now asserts the chain left selected and the deployed code hash, so a truncated loop cannot pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 16 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
🤖 ai:vetter |
|
🤖 ai:producer |
Test-only. No change to
src/.Adversarial mutation-testing pass over the whole repo
(
adversarial-mutation-testv0.27.0), scanning4422e29— 2 commits pastsol-v0.1.4.What was done
91 distinct mutations were applied one at a time to
src/lib/LibRainDeploy.sol, each run against the whole suite and restored viaVCS. Every behaviour of all seven functions plus the constants was probed: each
conditional, comparison, boundary, revert path, error argument order, assembly
operand, loop bound and fork restore. Three rounds were run; the third surfaced
no new fillable gap.
validated by that, and this PR does not touch them.
deterministic discriminator (both listed below); no test is added for those.
New tests
isStartBlockat block 0 checks only the code hash at that block, with no block before genesis to comparetestIsStartBlockGenesisAllocationAtBlockZeroisStartBlockat block 1 still compares against block 0testIsStartBlockGenesisAllocationAtBlockOneisStartBlockcompares against the immediately preceding blocktestIsStartBlockOneBlockAfterDeployBlockfindDeployBlockreturns the exact deploy block, including when it is the block the fork is attestFindDeployBlockExactZoltuBaseDeployBlockfindDeployBlockleaves the fork on its original block when it revertsDeployedBeforeStartBlocktestFindDeployBlockRestoresForkOnDeployedBeforeStartBlockRevertdeployZolturejects a successful factory call that left no codetestDeployZoltuRevertsEmptyCreationCodedeployZolturejects a failed factory call even when its output buffer holds the address of a contract that has codetestDeployZoltuRevertsWhenFactoryCallFailsWithAddressDatadeployZoltunever reports the zero address as a deploymenttestDeployZoltuRevertsZeroAddressWithCodedeployZoltudoes not forward the caller's value to the factorytestDeployZoltuDoesNotForwardValuedeployToNetworkstreats a dependency that has code as presenttestDeployToNetworksPresentDependencyDeploysdeployAndBroadcastbroadcasts as the address derived from the given private key, and forwards it todeployToNetworkstestDeployAndBroadcastUsesDeployerFromPrivateKeyThe new
isStartBlockblock-0 and block-1 cases use the OP Stack WETH9predeploy from the Base genesis allocation, which is the only way to reach a
target that already has its code at block 0.
testFindDeployBlockExactZoltu\ BaseDeployBlockasserts the exact block (1117029, resolved independently bybinary searching
eth_getCodeover the Base RPC) rather than round-trippingthrough
isStartBlock, so the two functions no longer only validate eachother.
Strengthened in place (no duplicates added)
testZoltuFactoryCodehashnow iteratessupportedNetworks()instead offorking Arbitrum only. Before this, mutating
BASE_SEPOLIA,FLAREorPOLYGONto a bogus alias survived the entire suite — three of the fivesupported networks were never forked by any test, so nothing pinned that
their names are real fork aliases or that the factory is actually deployed
there.
testDeployToNetworksMultipleNetworksnow asserts the chain left selectedand the deployed code hash. Its name claims it covers "deploy to every
network", but it only asserted the returned address — which is identical for
both networks, so truncating the loop to
i < 1, or forkingnetworks[0]every iteration, both survived it.
New fixture
MockAddressRevertingFactory— a factory stand-in whose calls always fail,reverting with exactly the twenty bytes of its own address. It is what makes
the
!successterm ofdeployZoltu's guard independently observable.Survivors deliberately left untested
Provably equivalent:
low = startBlock→startBlock + 1: the pre-check proves the target doesnot have the expected code hash at
startBlock, so the answer is alwaysgreater than
startBlock.deployBlock = low→high:low == highat loop exit.call(..., 12, 20)→call(..., 12, 32):mload(0)reads bytes[0, 32)either way, so the value is identical for any return-data length.
deployedAddress.codehash→expectedAddress.codehashin the final check:the two are provably equal at that point on both branches.
deployAndBroadcast's ownNoNetworksguard:deployToNetworksreverts withthe identical error, so the guard is redundant.
Not equivalent, but not deterministically discriminable from a test:
mstore(0, 0)indeployZoltu. It guards against stale scratchmemory bleeding into
deployedAddresswhen a factory returns fewer than 20bytes. Scratch happens to be zero at that point under the current compiler,
so its removal is currently unobservable, and a test cannot dirty
compiler-owned scratch space deterministically. (Actively dirtying it is
caught —
mstore(0, not(0))is killed by two existing tests.) The guard iscorrect and should stay.
Findings filed separately
The adversarial half of the pass produced three issues, each with a verified
repro: #16, #17, #18. None of them are enshrined by the tests here — in
particular
testDeployZoltuRevertsWhenFactoryCallFailsWithAddressDataassertsonly the
DeployFailedselector, not the reported address that #17 is about,so it stays green under that fix.
Scan record
audit/mutation-test-scans.jsonrecords the scan for org-wide health tracking.