Files: contracts/token/src/lib.rs lines 1227 to 1265; contracts/vesting/src/lib.rs lines 732 to 770
Issue: #340 asked for a test that stops docs/events.md, the contract and the indexer drifting apart, and the implementation is genuinely clever: scripts/generate_events_doc.py --check runs in CI, and each contract carries a test asserting its topic set. The test does it by reading its own source as a string:
const NEEDLE: &str = "symbol_short!(\"";
...
.split_once("#[cfg(test)]\nmod test {")
Three weaknesses follow, and the same logic is duplicated verbatim in both contracts.
It is brittle in the wrong direction. The split_once depends on the exact bytes #[cfg(test)]\nmod test {. Any reformatting of that line — a cargo fmt change, an added attribute, a doc comment between them — makes the split fail, and the failure mode is a confusing panic in an unrelated-looking test rather than a clear message.
It cannot see what it claims to check. It finds symbol_short! literals, so a topic built from a variable or a constant is invisible to it, and a publish call inside a branch that never executes still counts as an emitted event. It proves a string appears in the file, which is not the same as the contract emitting that topic.
It does not check payloads. #339's whole subject was that mint, clawback and approve had the wrong topic tuples and data. A literal scan cannot detect a regression there, so the test that exists to prevent event drift does not cover the axis on which event drift last happened.
Fix: Assert against the host instead of the source. Exercise each entry point in a test and read env.events().all(), comparing the full (topics, data) shape to a checked-in fixture — which also gives docs/events.json a real source of truth and lets generate_events_doc.py derive payload columns. Share the comparison helper between the two contracts via a small contracts/common test-support crate rather than copying it a third time when the factory contract in open issue #368 lands.
Files:
contracts/token/src/lib.rslines 1227 to 1265;contracts/vesting/src/lib.rslines 732 to 770Issue: #340 asked for a test that stops
docs/events.md, the contract and the indexer drifting apart, and the implementation is genuinely clever:scripts/generate_events_doc.py --checkruns in CI, and each contract carries a test asserting its topic set. The test does it by reading its own source as a string:Three weaknesses follow, and the same logic is duplicated verbatim in both contracts.
It is brittle in the wrong direction. The
split_oncedepends on the exact bytes#[cfg(test)]\nmod test {. Any reformatting of that line — acargo fmtchange, an added attribute, a doc comment between them — makes the split fail, and the failure mode is a confusing panic in an unrelated-looking test rather than a clear message.It cannot see what it claims to check. It finds
symbol_short!literals, so a topic built from a variable or a constant is invisible to it, and apublishcall inside a branch that never executes still counts as an emitted event. It proves a string appears in the file, which is not the same as the contract emitting that topic.It does not check payloads. #339's whole subject was that
mint,clawbackandapprovehad the wrong topic tuples and data. A literal scan cannot detect a regression there, so the test that exists to prevent event drift does not cover the axis on which event drift last happened.Fix: Assert against the host instead of the source. Exercise each entry point in a test and read
env.events().all(), comparing the full(topics, data)shape to a checked-in fixture — which also givesdocs/events.jsona real source of truth and letsgenerate_events_doc.pyderive payload columns. Share the comparison helper between the two contracts via a smallcontracts/commontest-support crate rather than copying it a third time when the factory contract in open issue #368 lands.