What a large test suite actually buys a bot with a wallet
A bot that controls a wallet can turn a small state bug into an irreversible transaction. My test suite has about 473 tests, but the count is not the useful property. The useful property is that it separates deterministi
A bot that controls a wallet can turn a small state bug into an irreversible transaction. My test suite has about 473 tests, but the count is not the useful property. The useful property is that it separates deterministic business rules from effects that cross a trust boundary.
The split that matters: pure-logic tests versus anything that touches a network or a wallet, and why the second group is stubbed at the boundary instead of mocked everywhere
Pure-logic tests cover price conversion, listing state transitions, metadata validation, supply limits, and copy gates. They receive values and return values. These tests are fast, deterministic, and do not need Base, a wallet, or a pinned asset.
Network and wallet tests stop at an explicit boundary. I stub the RPC client, signer, storage client, and copywriter adapter. The application code still builds the request, evaluates the response, and updates local state normally.
I do not mock internal functions throughout the call graph. Deep mocks encode implementation details: rename a helper or move validation into another module, and unrelated tests fail. A boundary stub encodes the contract that matters.
rpc.send_transaction.return_value = receipt
signer.sign.return_value = signed_transaction
result = restocker.run(candidate)
assert result.status == "listed"
assert signer.sign.called
assert rpc.send_transaction.called
The cost is that a stub cannot prove Base accepted a real transaction. I keep a smaller manual deployment check for that. The main suite answers a different question: given each boundary response, does the bot make the correct decision before spending funds?
Three tests that paid for themselves: price rounding, retry-after-failure, and the length gate
| Test | Bug caught | Assertion |
|---|---|---|
| Price rounding | A floating-point conversion produced the wrong base-unit amount before a listing was signed. | Convert through an exact decimal representation and compare the final integer amount. |
| Retry after failure | A failed network call advanced local state, so the next daemon pass treated unfinished work as complete. | Failure leaves the candidate retryable; success alone commits the state transition. |
| Length gate | Generated copy passed the fact gate but exceeded the configured field limit. | Reject text above the configured maximum before metadata is pinned or signed. |
The price test protects both listing construction and the on-chain preload calculation. The preload is about 0.0000008 ETH per piece, so conversion code must not silently reinterpret decimal input.
The retry test matters because the daemon restocks unattended. A timeout is not proof of success, and an exception is not permission to discard work. The test feeds a boundary failure into the same state machine used in production, then verifies that the candidate remains eligible.
The length test sits after the LLM and fact gate. Factual text can still be operationally invalid. The gate measures the final serialized value, not an earlier draft, because normalization can change what is actually stored.
The rule for adding a test: it must fail on the bug you just fixed, plus the command that has to be green before a deploy
My rule is mechanical: reproduce the bug as a test, run it against the broken code, and confirm that it fails for the expected reason. Then apply the fix and run the entire suite.
pytest
That command must be green before a deploy. A test written only after the fix, without demonstrating the failure, may merely confirm the implementation that already exists. It does not prove that the original defect is covered.
The cost is maintenance. Boundary contracts must stay explicit, fixtures must remain readable, and obsolete tests must be removed when behavior intentionally changes. About 473 tests are useful only while each failure points to a real decision.
Practical takeaway: identify the wallet and network boundaries in your bot today, stub those boundaries, then turn the next production bug into a failing regression test before editing the implementation.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.