Skip to content

Simplify contract event testing#1638

Merged
mootz12 merged 17 commits intomainfrom
issues/1566-simplify-event-testing-3
Jan 9, 2026
Merged

Simplify contract event testing#1638
mootz12 merged 17 commits intomainfrom
issues/1566-simplify-event-testing-3

Conversation

@mootz12
Copy link
Copy Markdown
Contributor

@mootz12 mootz12 commented Dec 11, 2025

What

Adds a testutils struct ContractEvents to be returned by env.events().all() that now returns the XDR variants of the emitted contract events. To simplify testing, Events defined by contractevents can be converted to the XDR variant with to_contract_event.

Additional partial eq impl were added to maintain backwards compatibility with users who test with existing methods. This will impact tests where users directly check the length of the events array, via all().len(), who will now need to write all().events.len().

Lastly, this also allows the debug information when testing events to display actual values, making it easier to determine what went wrong during test failures:

Before:

left: Vec(Ok((Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM), Vec(Ok(Symbol(transfer)), Ok(Address(obj#35)), Ok(Address(obj#37))), Map(obj#43))))
right: Vec(Ok((Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM), Vec(Ok(Symbol(transfer)), Ok(Address(obj#11)), Ok(Address(obj#29))), Map(obj#53))))

After:

left: [ContractEvent { ext: V0, contract_id: Some(ContractId(Hash(0000000000000000000000000000000000000000000000000000000000000001))), type_: Contract, body: V0(ContractEventV0 { topics: VecM([Symbol(ScSymbol(StringM(transfer))), Address(Contract(ContractId(Hash(0000000000000000000000000000000000000000000000000000000000000002)))), Address(Account(AccountId(PublicKeyTypeEd25519(Uint256(0000000000000000000000000000000000000000000000000000000000000003)))))]), data: Map(Some(ScMap(VecM([ScMapEntry { key: Symbol(ScSymbol(StringM(amount))), val: I128(Int128Parts { hi: 0, lo: 1 }) }, ScMapEntry { key: Symbol(ScSymbol(StringM(to_muxed_id))), val: U64(1) }])))) }) }]
right: [ContractEvent { ext: V0, contract_id: Some(ContractId(Hash(0000000000000000000000000000000000000000000000000000000000000001))), type_: Contract, body: V0(ContractEventV0 { topics: VecM([Symbol(ScSymbol(StringM(transfer))), Address(Contract(ContractId(Hash(0000000000000000000000000000000000000000000000000000000000000002)))), Address(Account(AccountId(PublicKeyTypeEd25519(Uint256(0000000000000000000000000000000000000000000000000000000000000003)))))]), data: Map(Some(ScMap(VecM([ScMapEntry { key: Symbol(ScSymbol(StringM(amount))), val: I128(Int128Parts { hi: 0, lo: 0 }) }, ScMapEntry { key: Symbol(ScSymbol(StringM(to_muxed_id))), val: U64(1) }])))) }) }]

An example test:

    #[test]
    fn test_event() {
        let env = Env::default();
        let contract_id = env.register(Contract, ());
        let client = ContractClient::new(&env, &contract_id);

        let from = Address::generate(&env);
        let to = MuxedAddress::generate(&env);
        let amount = 1i128;

        client.transfer(&from, &to, &amount);

        assert_eq!(
            env.events().all(),
            std::vec![Transfer {
                from: from.clone(),
                to: to.address(),
                amount,
                to_muxed_id: to.id(),
            }
            .to_contract_event(&env, &contract_id)],
        );
    }

Why

Solves #1566. Makes it easier to validate events during tests.

Known limitations

None

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request simplifies event testing in the Soroban SDK by introducing a new ContractEvents struct that wraps XDR contract events. The change improves test debuggability by displaying actual event values instead of opaque object references when assertions fail. The new API returns XDR contract events directly while maintaining backward compatibility through PartialEq implementations.

Key Changes

  • Introduced ContractEvents struct that wraps std::vec::Vec<xdr::ContractEvent> with filtering and comparison capabilities
  • Changed env.events().all() return type from Vec<(Address, Vec<Val>, Val)> to ContractEvents
  • Added to_contract_event() method to the Event trait for converting event structs to XDR format in tests

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
soroban-sdk/src/testutils.rs Adds ContractEvents struct with PartialEq implementations for backward compatibility, includes filter_by_contract_id method
soroban-sdk/src/events.rs Updates Events trait implementation to return ContractEvents, adds to_contract_event method to Event trait
soroban-sdk/src/address.rs Changes contract_id and from_contract_id methods from pub(crate) to pub visibility
tests/events/src/lib.rs Updates test examples to use new ContractEvents API with to_contract_event
tests/events_ref/src/lib.rs Updates test examples using reference fields to use new API
soroban-sdk/src/tests/contract_event.rs Refactors all event tests to use XDR format and adds new tests for backwards compatibility and filtering
tests-expanded/*.rs Shows macro expansion results with updated event testing patterns
soroban-sdk/test_snapshots/* Adds snapshot files for new test cases

Comment thread soroban-sdk/src/events.rs
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/events.rs Outdated
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs
Copy link
Copy Markdown
Member

@leighmcculloch leighmcculloch left a comment

Choose a reason for hiding this comment

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

👏🏻 I like this approach. It breaks the API, but softens that break for the common use cases. Few comments inline. Some of copilots comments seem like worth addressing as minor improvements too.

Comment thread soroban-sdk/src/address.rs Outdated
Comment thread soroban-sdk/src/events.rs Outdated
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread soroban-sdk/src/testutils.rs Outdated
Comment thread tests/events/src/lib.rs
Comment thread soroban-sdk/src/tests/contract_event.rs
@leighmcculloch
Copy link
Copy Markdown
Member

Looking at the OZ failures, the testutils::ContractEvent can adopt some additional functionality to further soften the breakage by implementing an into iterator, etc.

@mootz12
Copy link
Copy Markdown
Contributor Author

mootz12 commented Dec 11, 2025

Looking at the OZ failures, the testutils::ContractEvent can adopt some additional functionality to further soften the breakage by implementing an into iterator, etc.

I thought about this, but ultimately decided against it. It felt weird adding arbitrary vec methods to ContractEvents, without adding all read-only methods. Otherwise some tests will break and need .event and some won't.

@leighmcculloch
Copy link
Copy Markdown
Member

leighmcculloch commented Dec 14, 2025

@ozgunozerk @brozorec It looks like from the failures when we test this change with the OZ contracts repo (see the CI failures above) that the OZ contracts repo has some test utilities built around the .all(), which I think signals a couple things:

  • Since you folks have already been building event assertions utilities, what do you think of the change in this PR that would expose utilities for asserting on events using contractevent types and their xdr counterparts instead of the tuple list?

  • If we introduced the breakage, changing the return value of .all(), how disruptive would that be? We can of course make it a new function, .all2() or something else.

@brozorec
Copy link
Copy Markdown

  • Since you folks have already been building event assertions utilities, what do you think of the change in this PR that would expose utilities for asserting on events using contractevent types and their xdr counterparts instead of the tuple list?

I'm supporting this change. Being able to directly use contractevent is more intuitive than getting and asserting the event data from the tuple.

  • If we introduced the breakage, changing the return value of .all(), how disruptive would that be? We can of course make it a new function, .all2() or something else.

I'd say no need to have both variants. We'll refactor once that's out.

@mootz12 mootz12 changed the base branch from main to update-p25 January 2, 2026 15:25
Base automatically changed from update-p25 to release/v25-preview January 2, 2026 17:41
Copy link
Copy Markdown
Member

@leighmcculloch leighmcculloch left a comment

Choose a reason for hiding this comment

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

Looks good.

I pushed a couple tweaks that aren't critical. I had tried them locally because I wasn't sure if they were possible and they worked so pushed them instead of posting comments with them. If they cut across anything you were planning though we can revert them.

@mootz12 mootz12 force-pushed the issues/1566-simplify-event-testing-3 branch from 7be2477 to 9b288d7 Compare January 9, 2026 14:55
@mootz12 mootz12 changed the base branch from release/v25-preview to main January 9, 2026 14:55
@mootz12 mootz12 changed the title Issues/1566 simplify event testing 3 Simplify contract event testing Jan 9, 2026
@mootz12 mootz12 added this pull request to the merge queue Jan 9, 2026
Merged via the queue into main with commit a60b7e8 Jan 9, 2026
87 of 94 checks passed
@mootz12 mootz12 deleted the issues/1566-simplify-event-testing-3 branch January 9, 2026 19:08
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.

4 participants