Skip to content

Comments

test(dpp): add validation error path tests for identity_nonce, max_depth, and GroupV0#3123

Open
thepastaclaw wants to merge 4 commits intodashpay:v3.1-devfrom
thepastaclaw:test/validation-error-paths-simple
Open

test(dpp): add validation error path tests for identity_nonce, max_depth, and GroupV0#3123
thepastaclaw wants to merge 4 commits intodashpay:v3.1-devfrom
thepastaclaw:test/validation-error-paths-simple

Conversation

@thepastaclaw
Copy link
Contributor

@thepastaclaw thepastaclaw commented Feb 20, 2026

Issue Being Fixed

Tracker: thepastaclaw/tracker#14 (parent: #11)

Addresses findings C-07, I-01, and I-05 from the rs-dpp test quality audit.

What was done

Added missing validation error path tests for three validators that had little to no test coverage:

C-07: validate_new_identity_nonce — 0 tests → 5 tests

  • validate_new_identity_nonce_valid_zero — nonce=0, valid
  • validate_new_identity_nonce_valid_max_minus_one — nonce=23 (boundary), valid
  • validate_new_identity_nonce_invalid_at_max — nonce=24 (boundary), returns NonceTooFarInPast
  • validate_new_identity_nonce_invalid_above_max — nonce=25, returns error
  • validate_new_identity_nonce_invalid_large — nonce=1000, returns error

I-01: validate_max_depth_v0 — max-depth error path never tested → 1 test

  • should_return_error_when_max_depth_exceeded — dynamically builds a schema exceeding max_depth from platform version, asserts DataContractMaxDepthExceedError

I-05: GroupV0::validate — 1 test → 11 tests

  • test_group_exceeds_max_members — too many members
  • test_group_too_few_members_zero — empty group
  • test_group_too_few_members_one — single member
  • test_group_member_has_power_of_zero — member with power 0
  • test_group_member_power_over_limit — power > GROUP_POWER_LIMIT (u16::MAX)
  • test_group_member_power_exceeds_required — power > required_power
  • test_group_total_power_less_than_required — insufficient total power
  • test_group_non_unilateral_member_power_less_than_required — non-unilateral members can't meet threshold
  • test_group_required_power_zero — documents that required_power=0 hits power > required_power before the invalid-power check
  • test_group_required_power_over_limit — required_power > GROUP_POWER_LIMIT (65536), reached via 3 members at max power

All tests compile and pass (cargo test -p dpp --lib — 32 targeted tests, 0 failures).

Summary by CodeRabbit

  • Tests
    • Added comprehensive unit tests covering document-schema max-depth enforcement, group validation edge cases (member counts, power constraints, and related error scenarios), and identity nonce boundary checks across valid and invalid ranges.

Validation

  1. What was tested

    • cargo test -p dpp --lib
  2. Results

    • 32 targeted tests executed
    • 0 failures
  3. Environment

    • Local development environment for dashpay/platform (Rust test run)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

Adds unit tests for three validators—max-depth schema validation, GroupV0::validate, and validate_new_identity_nonce—that exercise error branches and boundary conditions without changing public APIs.

Changes

Cohort / File(s) Summary
Max Depth Validation Tests
packages/rs-dpp/src/data_contract/document_type/schema/validate_max_depth/v0/mod.rs
Adds a unit test that builds a nested JSON schema up to the configured max depth, invokes validate_max_depth_v0, and asserts the first error is DataContractMaxDepthExceedError with the reported max_depth equal to the configured maximum.
Group Validation Tests
packages/rs-dpp/src/data_contract/group/v0/mod.rs
Adds an extensive test suite for GroupV0::validate covering exceeding max members, too few members, various power constraints, unilateral power cases, and invalid required_power; tests assert specific ConsensusError::BasicError variants in results.
Identity Nonce Validation Tests
packages/rs-dpp/src/identity/identity_nonce.rs
Adds tests for validate_new_identity_nonce covering zero, max-1, at max, above max, and very large nonces; expects valid ranges to pass and out-of-range values to produce NonceTooFarInPast errors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through tests with eager eyes,

Depths, groups, nonces — all boundary-wise.
I counted members, measured depth with care,
Chased errant nonces here and there,
A tiny thump — the suite is fair.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding validation error path tests for three specific validators (identity_nonce, max_depth, and GroupV0).
Linked Issues check ✅ Passed The PR successfully addresses all three findings from issue #14: C-07 adds 5 identity_nonce tests, I-01 adds max_depth_v0 test, and I-05 adds 10 GroupV0::validate tests covering error branches.
Out of Scope Changes check ✅ Passed All changes are focused test additions directly addressing the three linked issues; no unrelated modifications or scope creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/rs-dpp/src/identity/identity_nonce.rs (1)

189-226: Consider asserting additional error fields and replacing the magic number.

Two small improvements for completeness:

  1. Magic literal in validate_new_identity_nonce_invalid_large1000 is an arbitrary sentinel with no expressed relationship to MISSING_IDENTITY_REVISIONS_MAX_BYTES. A constant expression makes the intent self-documenting:
♻️ Suggested change for magic literal
-        let nonce = 1000;
+        let nonce = MISSING_IDENTITY_REVISIONS_MAX_BYTES * 42; // well above the limit
  1. Incomplete error field assertions — the three invalid tests only assert e.error. The struct also carries setting_identity_nonce (should equal the input nonce) and current_identity_nonce (should be None for a new identity). Adding these assertions would fully pin the error payload and catch regressions in those fields:
♻️ Suggested additional assertions (shown for the `at_max` test; pattern applies to all three)
         assert_eq!(e.error, MergeIdentityNonceResult::NonceTooFarInPast);
+        assert_eq!(e.setting_identity_nonce, nonce);
+        assert!(e.current_identity_nonce.is_none());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-dpp/src/identity/identity_nonce.rs` around lines 189 - 226,
Replace the magic literal in validate_new_identity_nonce_invalid_large with a
value derived from MISSING_IDENTITY_REVISIONS_MAX_BYTES (e.g.
MISSING_IDENTITY_REVISIONS_MAX_BYTES * 2) so the relationship to the boundary is
explicit, and in all three failing tests
(validate_new_identity_nonce_invalid_at_max,
validate_new_identity_nonce_invalid_above_max,
validate_new_identity_nonce_invalid_large) add assertions on the returned
InvalidIdentityNonceError fields: assert e.error ==
MergeIdentityNonceResult::NonceTooFarInPast, assert e.setting_identity_nonce ==
nonce, and assert e.current_identity_nonce.is_none() to fully pin the error
payload from validate_new_identity_nonce.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-dpp/src/data_contract/group/v0/mod.rs`:
- Around line 360-379: The check for self.required_power == 0 in
GroupV0::validate is unreachable because the per-member validation (the power >
self.required_power check in the member loop) will always fire first when
required_power == 0; either remove the unreachable branch that returns
GroupRequiredPowerIsInvalidError or refactor the per-member check so it doesn't
preempt the required_power == 0 validation (e.g., validate required_power early
in validate before iterating members); update references in tests that assert
GroupMemberHasPowerOverLimitError vs GroupRequiredPowerIsInvalidError to match
the chosen behavior.

---

Nitpick comments:
In `@packages/rs-dpp/src/identity/identity_nonce.rs`:
- Around line 189-226: Replace the magic literal in
validate_new_identity_nonce_invalid_large with a value derived from
MISSING_IDENTITY_REVISIONS_MAX_BYTES (e.g. MISSING_IDENTITY_REVISIONS_MAX_BYTES
* 2) so the relationship to the boundary is explicit, and in all three failing
tests (validate_new_identity_nonce_invalid_at_max,
validate_new_identity_nonce_invalid_above_max,
validate_new_identity_nonce_invalid_large) add assertions on the returned
InvalidIdentityNonceError fields: assert e.error ==
MergeIdentityNonceResult::NonceTooFarInPast, assert e.setting_identity_nonce ==
nonce, and assert e.current_identity_nonce.is_none() to fully pin the error
payload from validate_new_identity_nonce.

Comment on lines 360 to 379
#[test]
fn test_group_required_power_zero() {
let group = GroupV0 {
members: [(Identifier::random(), 1), (Identifier::random(), 1)].into(),
required_power: 0,
};

let result = group
.validate(None, PlatformVersion::latest())
.expect("should not error");

// Required power of zero is currently intercepted by the per-member `power > required_power`
// check before `GroupRequiredPowerIsInvalidError` is evaluated.
let Some(ConsensusError::BasicError(BasicError::GroupMemberHasPowerOverLimitError(
_,
))) = result.errors.first()
else {
panic!("expected GroupMemberHasPowerOverLimitError");
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n packages/rs-dpp/src/data_contract/group/v0/mod.rs | sed -n '120,170p'

Repository: dashpay/platform

Length of output: 2686


🏁 Script executed:

cat -n packages/rs-dpp/src/data_contract/group/v0/mod.rs | sed -n '100,125p'

Repository: dashpay/platform

Length of output: 1256


The required_power == 0 check at line 162 is unreachable dead code.

When required_power == 0, the per-member validation at lines 125–129 (power > self.required_power) always triggers before line 162 is reached. Since every member must have power > 0 (line 115), and required_power == 0, the condition power > required_power is always true, causing an error to be returned immediately.

The self.required_power == 0 branch should either be removed from line 162 (since it cannot be reached), or the per-member check should be refactored to allow this branch to execute. This is a valid finding, though as a test-only addition it is out of scope for the current PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-dpp/src/data_contract/group/v0/mod.rs` around lines 360 - 379,
The check for self.required_power == 0 in GroupV0::validate is unreachable
because the per-member validation (the power > self.required_power check in the
member loop) will always fire first when required_power == 0; either remove the
unreachable branch that returns GroupRequiredPowerIsInvalidError or refactor the
per-member check so it doesn't preempt the required_power == 0 validation (e.g.,
validate required_power early in validate before iterating members); update
references in tests that assert GroupMemberHasPowerOverLimitError vs
GroupRequiredPowerIsInvalidError to match the chosen behavior.

@thepastaclaw
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/rs-dpp/src/data_contract/group/v0/mod.rs`:
- Around line 358-376: This test correctly documents current behavior and
requires no code changes: leave the test in mod.rs as-is, keeping the inline
comment and the assertion that GroupV0::validate produces a
BasicError::GroupMemberHasPowerOverLimitError when required_power == 0; ensure
references to the two relevant errors (GroupMemberHasPowerOverLimitError and
GroupRequiredPowerIsInvalidError) and the GroupV0::validate function remain
unchanged so future fixes to validation order will surface as test failures
requiring assertion updates.

@thepastaclaw thepastaclaw force-pushed the test/validation-error-paths-simple branch from 4f89e9a to db23345 Compare February 21, 2026 18:02
@github-actions github-actions bot added this to the v3.1.0 milestone Feb 21, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/rs-dpp/src/identity/identity_nonce.rs (1)

215-226: validate_new_identity_nonce_invalid_large is redundant with validate_new_identity_nonce_invalid_above_max.

validate_new_identity_nonce has exactly two branches (>= MAX / < MAX). Both are fully covered by valid_zero and invalid_at_max; neither invalid_above_max nor invalid_large add new coverage. Consider dropping invalid_large or replacing it with a semantically distinct case (e.g., max u64 to document overflow behaviour isn't possible).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-dpp/src/identity/identity_nonce.rs` around lines 215 - 226, The
test validate_new_identity_nonce_invalid_large duplicates coverage of
validate_new_identity_nonce already exercised by
validate_new_identity_nonce_invalid_above_max and valid_zero; remove
validate_new_identity_nonce_invalid_large or replace it with a semantically
distinct scenario (e.g., use u64::MAX as the nonce to document overflow/edge
behavior) so it adds unique coverage; update or delete the test function
validate_new_identity_nonce_invalid_large accordingly and ensure
references/assertions still target validate_new_identity_nonce and
MergeIdentityNonceResult::NonceTooFarInPast if keeping a meaningful new case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/rs-dpp/src/data_contract/group/v0/mod.rs`:
- Around line 358-376: The validate logic in GroupV0 currently checks per-member
power limits before validating required_power, making the required_power == 0
branch (which should produce GroupRequiredPowerIsInvalidError) unreachable and
leaving dead code; update GroupV0::validate to validate required_power (and emit
GroupRequiredPowerIsInvalidError when required_power == 0 or otherwise invalid)
before iterating members for GroupMemberHasPowerOverLimitError so the intended
error path is reachable, and adjust the tests to expect
GroupRequiredPowerIsInvalidError for the zero required_power case or keep the
existing test as a regression test if you prefer to assert the new behavior.

---

Nitpick comments:
In `@packages/rs-dpp/src/identity/identity_nonce.rs`:
- Around line 215-226: The test validate_new_identity_nonce_invalid_large
duplicates coverage of validate_new_identity_nonce already exercised by
validate_new_identity_nonce_invalid_above_max and valid_zero; remove
validate_new_identity_nonce_invalid_large or replace it with a semantically
distinct scenario (e.g., use u64::MAX as the nonce to document overflow/edge
behavior) so it adds unique coverage; update or delete the test function
validate_new_identity_nonce_invalid_large accordingly and ensure
references/assertions still target validate_new_identity_nonce and
MergeIdentityNonceResult::NonceTooFarInPast if keeping a meaningful new case.

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.

1 participant