Test the Holding parser - #49
Conversation
token::holding::Holding had no test. cbtc-lib carried a copy of the struct and also had none, so the parser that reads every utility-registry holding was untested on both sides. cbtc-lib has now deleted its copy, which leaves this the only one. The tests cover the four parsed fields, the six error messages and the lock check. Two mutations confirm they fail when the parser breaks: reading the instrument id from the top level, and treating a null lock as a lock. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
There is a minor but objective test naming typo (“holding holding”) that should be corrected before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds unit test coverage for the existing token::holding::Holding JSON parser and documents the addition in the changelog, without changing parser behavior seen by callers.
Changes:
- Added a dedicated
#[cfg(test)]module incrates/token/src/holding.rscovering successful parsing, error cases, and lock/null-lock behavior. - Added an
[Unreleased]changelog entry noting the new unit tests.
File summaries
| File | Description |
|---|---|
| crates/token/src/holding.rs | Adds 14 unit tests for Holding::from_active_contract and Holding::is_locked_in_contract, including null-lock semantics. |
| CHANGELOG.md | Documents the new unit tests under [Unreleased]. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The three sibling tests all read "a_holding_with_...", so the lock test now matches them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The changes are limited to test additions and a changelog entry, with only a minor test-name typo to optionally correct.
Review details
Suppressed comments (1)
crates/token/src/holding.rs:197
- Test name has a duplicated word ("a_holding_holding...") which makes the intent harder to read/search; rename to remove the repetition.
#[test]
fn a_holding_with_a_lock_is_locked() {
let mut argument = payload();
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The change is limited to adding tests and a changelog entry, with only a minor alignment nit between a test assertion and the PR’s stated intent.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
crates/token/src/holding.rs:152
- PR description says the unit tests assert exact error strings, but this test only checks
starts_with, so certain message changes could slip through. If the intent is to pin the full message, assert equality against the exact formatted error (while still deriving the parse-error detail from the same decimal parser).
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
The test checked a prefix, while the PR body claimed exact assertions. It now compares the full string, and derives the parser's own detail from DamlDecimal::parse rather than pinning wording this crate does not own. Rewording the format string fails the test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Round 2 came back with one suppressed finding, and it was right. The PR body said the tests assert exact error strings, but Fixed in 9aa9643, using the approach the comment suggested. The test now compares the whole message and derives the parser's own detail from Round 1's single finding, the doubled word in a test name, was fixed in 3cb3264.
🤖 Generated with Claude Code |
There was a problem hiding this comment.
🟢 Approval recommended
Changes are limited to tests and changelog updates, and the additions are low-risk and consistent with the stated PR intent.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
crates/token/src/holding.rs:156
- This test name suggests the amount is being rejected specifically because it’s a JSON number, but the parser currently treats any non-string
amountas if the field were missing (because it usesas_str()). Renaming the test to reflect that behavior will make failures easier to interpret without changing semantics.
crates/token/src/holding.rs:162 - The asserted error message says the field is missing even though it’s present but of the wrong type. Adding a brief explanation here will prevent future readers from “fixing” the test without realizing it’s documenting the current parser behavior.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Doesn't V2 holding have |
|
Closing unmerged. The 14 parser tests here move into the Holding struct fix, which rewrites the fixture: this branch's fixture names |
What this PR does
Old behavior
token::holding::Holdingparses a utility-registry holding contract out of theledger's JSON. It had no test.
cbtc-libcarried a copy of the same struct andalso had no test, so the parser was untested in both repositories.
New behavior
The parser has 14 unit tests. Nothing about the parser itself changes, so no
caller sees a difference.
Verification
cargo test --workspace— 184 passed, 0 failed, 39 ignored. The 39 are thelive devnet tests, which need credentials this run did not have.
cargo clippy --workspace --all-targets -- -D warnings— clean, in a freshtarget directory so the result is a real compile rather than a cache hit.
cargo fmt --check— clean.the instrument id from the top level instead of the nested object fails 3
tests. Treating a null lock as a lock fails 1 test. I reverted both and
confirmed the file matches its original byte for byte.
list_holdingsitself, which fetches the contracts thesetests parse. It still has no test, because a test needs a stub ledger.
The change that enables it
contract()helper builds aJsActiveContractcarrying acreateArgumentpayload.
Holdingreads the concrete template payload, not an interface view,so the existing fixture in
active_contracts.rsdoes not fit.Caveats
That is deliberate: the messages tell an operator which field the registry
omitted, so a silent rewording is worth catching.
serde_json, already a dev dependency.Details
What changed
crates/token/src/holding.rs— atestsmodule, 152 lines.from_active_contract: the four parsed fields, the nested instrument read,and all six error messages.
is_locked_in_contract: a real lock, a null lock, a missing lock field, anda contract with no
createArgument.CHANGELOG.md— an entry under[Unreleased].Why the null-lock case matters
A holding carries
lockas an explicit JSON null when it is unlocked. A parserthat checked only whether the field is present would call every holding locked,
and every transfer would then skip its own funds. One test pins that behavior,
and the mutation run confirms the test fails without it.
Companion: DLC-link/cbtc-lib#66
deletes the duplicated struct, which leaves this parser the only one.
🤖 Generated with Claude Code