Skip to content

test(locked_outpoints): add unit tests for ChangeSet merge and is_empty#436

Open
KrishnaShuk wants to merge 2 commits into
bitcoindevkit:masterfrom
KrishnaShuk:test/locked-outpoints-changeset
Open

test(locked_outpoints): add unit tests for ChangeSet merge and is_empty#436
KrishnaShuk wants to merge 2 commits into
bitcoindevkit:masterfrom
KrishnaShuk:test/locked-outpoints-changeset

Conversation

@KrishnaShuk

@KrishnaShuk KrishnaShuk commented Apr 8, 2026

Copy link
Copy Markdown

Description

This PR adds unit test coverage for locked_outpoints::ChangeSet, which previously had zero inline tests. It tests the Merge trait implementations, ensuring that merge and is_empty behave correctly under various conditions.

Notes to the reviewers

  • Added 6 deterministic tests verifying:
    • Default initialization is empty
    • Merging with empty datasets are clean no-ops
    • Disjoint changeset merges successfully combine outpoints
    • Overwriting logic accurately fulfills the extend() semantics where the incoming other duplicate outpoints overwrite self
  • Uses idiomatic boolean assertions (assert!(...)) verified cleanly by cargo clippy.
  • Tests use Txid::from_byte_array internally to maintain deterministic speed without relying on rand.

Changelog notice

Checklists

All Submissions:

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

@ValuedMammal ValuedMammal added the tests New or improved tests label Apr 8, 2026
@codecov

codecov Bot commented Apr 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.68%. Comparing base (826b19f) to head (54e43c1).
⚠️ Report is 37 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #436      +/-   ##
==========================================
+ Coverage   80.04%   80.68%   +0.64%     
==========================================
  Files          24       24              
  Lines        5336     5504     +168     
  Branches      242      244       +2     
==========================================
+ Hits         4271     4441     +170     
+ Misses        987      984       -3     
- Partials       78       79       +1     
Flag Coverage Δ
rust 80.68% <100.00%> (+0.64%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lambaaryan011 lambaaryan011 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Issues Found

  1. Missing Test for Multiple Merges

    • The code only tests 2 merges (true → false)
    • Should test 3+ merges (true → false → true → false)
    • Example: What if merge happens 10 times?
  2. Missing Implementation Code

    • Tests check merge() but we can't see merge() code
    • Need to see the actual implementation
    • Can't verify tests are correct without it
  3. Unclear Checklist

    • "breaks the existing API" is confusing
    • Does it break or not? Need clarity

Good Things

  • Tests are written correctly
  • Uses assert!() properly
  • Deterministic (no random data)

@luisschwab

Copy link
Copy Markdown
Member

@lambaaryan011 you only waste everyone's time with this LLM review.

@lambaaryan011

Copy link
Copy Markdown

@luisschwab That’s fair — my earlier comment wasn’t very useful.

I’ll take a closer look at the implementation and follow up with more concrete feedback.

@ValuedMammal

Copy link
Copy Markdown
Collaborator

I would like to see more Merge tests like this for the wallet::ChangeSet type.

@ValuedMammal

Copy link
Copy Markdown
Collaborator

@KrishnaShuk GitHub is complaining that your signing key is not verified.

@KrishnaShuk

Copy link
Copy Markdown
Author

Sure, I'll add wallet::ChangeSet merge tests as a follow-up commit in this PR.

@KrishnaShuk GitHub is complaining that your signing key is not verified.

I've_ added my GPG key to GitHub. The commit is now showing as verified now.

@ValuedMammal ValuedMammal moved this to In Progress in BDK Wallet May 6, 2026
@ValuedMammal ValuedMammal added this to the Wallet 3.1.0 milestone May 6, 2026
@ValuedMammal

Copy link
Copy Markdown
Collaborator

While we're testing ChangeSet invariants, I think we should look into #470 for this PR as well.

@ValuedMammal ValuedMammal left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The debug_assert in ChangeSet::merge only fires in debug mode, meaning reassignment is actually permitted in release builds, which is surprising.

I think the correct merge semantics should be: "If self is_none and other is_some, then proceed with the assignment, else debug_assert other is none or self equals other (to catch bugs during development)."

cargo test -r --lib -- wallet::changeset::test

// Merging other changeset should succeed and ignore other descriptor
#[cfg(not(debug_assertions))]
#[test]
fn test_merge_must_not_change_descriptor() {
    use crate::test_utils::*;
    use bitcoin::secp256k1::Secp256k1;
    let secp = Secp256k1::new();
    let (desc_1, _) = Descriptor::parse_descriptor(&secp, get_test_tr_single_sig()).unwrap();
    let (desc_2, _) = Descriptor::parse_descriptor(&secp, get_test_wpkh()).unwrap();

    let mut changeset = ChangeSet {
        descriptor: Some(desc_1.clone()),
        ..Default::default()
    };
    let other_descriptor_changeset = ChangeSet {
        descriptor: Some(desc_2),
        ..Default::default()
    };
    Merge::merge(&mut changeset, other_descriptor_changeset);
    assert_eq!(
        changeset.descriptor,
        Some(desc_1),
        "descriptor must never change"
    );
}

Comment thread src/wallet/changeset.rs
}

#[test]
fn test_is_empty_with_descriptor() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would be more clear to the reader if the test name reflected the expectation.

Suggested change
fn test_is_empty_with_descriptor() {
fn test_is_non_empty_with_descriptor() {

Comment thread src/wallet/changeset.rs
}

#[test]
fn test_is_empty_with_network() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
fn test_is_empty_with_network() {
fn test_is_non_empty_with_network() {

}

#[test]
fn test_is_empty_with_entries() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
fn test_is_empty_with_entries() {
fn test_is_non_empty_with_entries() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When I saw the test name and read through what the test tests, I was confused. @ValuedMammal made a suggestion in src/wallet/changeset.rs to update the test names so they reflect the expected outcome rather than just the method being called. I think that applies to this too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests New or improved tests

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants