Skip to content

Conversation

@tilacog
Copy link

@tilacog tilacog commented Jan 6, 2026

Resolves: #3785

Description

This PR improves test coverage for the refunder crate by expanding the end-to-end tests covering many refund scenarios.

Other Changes

  1. Promoted the RefundStatus from tests to the main crate logic.
  2. Fixed compilation error in shared crate that was blocking this work.

Run the refunder E2E tests:

cargo nextest run -p e2e refunder

@gemini-code-assist
Copy link

Summary of Changes

Hello @tilacog, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the testability and coverage of the refunder crate. It introduces a modular design for the refund service by abstracting its interactions with the database and blockchain, allowing for more isolated and comprehensive unit tests. Concurrently, it expands the existing end-to-end test suite to validate various refund conditions and edge cases, ensuring the service's reliability. These changes collectively improve the robustness and maintainability of the refunding mechanism.

Highlights

  • Expanded E2E Tests: The end-to-end tests for the refunder crate have been significantly expanded to cover a wider array of refund scenarios, including various slippage and validity duration thresholds, and cases where orders are already invalidated or settled.
  • Trait Abstractions for Unit Testing: New trait abstractions (DbRead, ChainRead, and ChainWrite) have been introduced in the refunder crate. These traits decouple the refund service from concrete database and blockchain implementations, enabling robust unit testing using the mockall crate.
  • Refactored Refund Service: The RefundService struct has been refactored to be generic over the newly introduced traits, facilitating dependency injection and improving testability. Concrete implementations (AlloyChain for blockchain and Postgres for database) are now located in a new infra/ module.
  • Compilation Fix: A compilation error in the shared crate that was blocking development work has been resolved.
  • Dependency Updates: Several dependencies across the project, including winnow, indexmap, rustc_version, windows-sys, regex, and toml_edit, have been updated to newer versions. New dev-dependencies mockall and rstest were added for enhanced testing capabilities.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves the testability and robustness of the refunder crate by introducing trait-based abstractions for database and blockchain interactions. This refactoring enables comprehensive unit testing with mocks, which have been added and are very thorough. Additionally, the E2E test suite has been greatly expanded, covering numerous refund scenarios and edge cases, which increases confidence in the refunder's behavior.

My review focuses on the new abstractions and the refactored logic. The changes are excellent, but I have identified a couple of areas for improvement. One is a potential inconsistency in how refundable orders are processed, which could lead to misleading logs or wasteful transactions. The other is an opportunity to address a TODO left in the code for a performance enhancement. Overall, this is a high-quality contribution that substantially improves the codebase.

.or_default()
.push(uid);
}
let uid_with_latest_refundablility = futures::future::join_all(futures).await; // TODO: is it worth to switch to FuturesUnordered here?

Choose a reason for hiding this comment

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

medium

I see you've left a TODO here about switching to FuturesUnordered. This is a good idea for performance, as it would allow processing results as they complete rather than waiting for all futures to finish.

You're already using buffer_unordered from futures::StreamExt in send_out_refunding_tx, which provides similar benefits. You could use it here as well for consistency and improved performance. A concurrency limit (e.g., 10) is a reasonable starting point to avoid overwhelming the RPC endpoint.

Suggested change
let uid_with_latest_refundablility = futures::future::join_all(futures).await; // TODO: is it worth to switch to FuturesUnordered here?
let uid_with_latest_refundablility: Vec<_> = stream::iter(futures).buffer_unordered(10).collect().await;

Comment on lines 191 to 209
let futures = uids.iter().map(|uid| {
let (uid, self_) = (*uid, &self);
let (uid, database) = (*uid, &self.database);
async move {
self_
.get_ethflow_data_from_db(&uid)
database
.get_ethflow_order_data(&uid)
.await
.context(format!("uid {uid:?}"))
.inspect_err(|err| {
tracing::error!(?err, ?uid, "failed to get data from db")
})
}
});
let encoded_ethflow_orders: Vec<_> = stream::iter(futures)
.buffer_unordered(10)
.filter_map(|result| async {
result
.inspect_err(|err| tracing::error!(?err, "failed to get data from db"))
.ok()
})
.filter_map(|result| async { result.ok() })
.collect()
.await;
self.submitter
.submit(uids, encoded_ethflow_orders, contract)
.submit_refund(&uids, encoded_ethflow_orders, contract)
.await?;

Choose a reason for hiding this comment

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

medium

There's a potential issue where uids and encoded_ethflow_orders can become out of sync. If get_ethflow_order_data fails for some UIDs, encoded_ethflow_orders will contain fewer items than the uids slice passed to submit_refund. This could lead to misleading logs and potentially sending wasteful empty transactions if all database lookups fail.

To make this more robust, you could refactor this section to ensure the lists are consistent. The following suggestion filters out orders that fail the database lookup and only attempts to submit a refund if there are successful orders to process.

            let futures = uids.iter().map(|uid| async move {
                match self.database.get_ethflow_order_data(uid).await {
                    Ok(data) => Some((*uid, data)),
                    Err(err) => {
                        tracing::error!(?err, ?uid, "failed to get data from db");
                        None
                    }
                }
            });

            let successful_orders: Vec<_> = stream::iter(futures)
                .buffer_unordered(10)
                .filter_map(|result| async { result })
                .collect()
                .await;

            if !successful_orders.is_empty() {
                let (successful_uids, encoded_ethflow_orders): (Vec<_>, Vec<_>) =
                    successful_orders.into_iter().unzip();
                self.submitter
                    .submit_refund(&successful_uids, encoded_ethflow_orders, contract)
                    .await?;
            }

Copy link
Author

@tilacog tilacog Jan 6, 2026

Choose a reason for hiding this comment

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

I think this is the same problem that’s been reported here:

  1. /// DB errors for individual orders are skipped; other orders proceed.
    ///
    /// # Current Behavior (documented, not necessarily ideal)
    ///
    /// When a DB lookup fails for an order:
    /// - The error is logged and the order data is excluded from the submission
    /// - However, the UID is still included in the submission
    ///
    /// This means `submit_refund` receives:
    /// - `uids`: ALL original UIDs (including those with failed lookups)
    /// - `orders`: Only the order data for successful lookups
    ///
    /// This creates a mismatch between UIDs and order data. See the TODO in
    /// `test_send_out_refunding_tx_all_db_calls_fail_still_submits` for
    /// discussion of potential fixes.
    #[tokio::test]
    async fn test_send_out_refunding_tx_db_error_skips_order() {
  2. /// If every DB lookup fails, we still call the submitter with the original
    /// UIDs but without any order data.
    ///
    /// What actually happens:
    /// - Each failed order‑data fetch is logged and ignored (it doesn’t stop
    /// the whole batch).
    /// - The submitter gets the same list of UIDs we started with, but the
    /// `orders` slice may be empty (or contain fewer entries) because some or
    /// all lookups failed.
    ///
    /// TODO: Is this the behavior we really want? Submitting a refund that
    /// contains UIDs but no order details feels off. Possible fixes:
    /// 1. Skip the submission entirely when `encoded_ethflow_orders` is empty.
    /// 2. Return an error if *all* order‑data lookups fail.
    /// 3. Filter the UID list so it only includes IDs with successful lookups.
    ///
    /// NOTE: This test complements
    /// `test_send_out_refunding_tx_db_error_skips_order`. That test covers
    /// partial DB failure (some lookups succeed); this one covers
    /// total DB failure (all lookups fail). Together they verify that DB errors
    /// are non-fatal and UIDs are always preserved regardless of lookup
    /// success.
    #[tokio::test]
    async fn test_send_out_refunding_tx_all_db_calls_fail_still_submits() {

I wasn't aiming for any behavioral changes with this PR, but happy to review and discuss this during the review.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO we should fix in a separate PR

@tilacog tilacog marked this pull request as ready for review January 6, 2026 18:28
@tilacog tilacog requested a review from a team as a code owner January 6, 2026 18:28
Copy link
Contributor

@jmg-duarte jmg-duarte left a comment

Choose a reason for hiding this comment

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

IMO & ideally, this should be broken into two parts: the refactor and the tests

Would make review much easier.

Left some comments

Comment on lines 191 to 209
let futures = uids.iter().map(|uid| {
let (uid, self_) = (*uid, &self);
let (uid, database) = (*uid, &self.database);
async move {
self_
.get_ethflow_data_from_db(&uid)
database
.get_ethflow_order_data(&uid)
.await
.context(format!("uid {uid:?}"))
.inspect_err(|err| {
tracing::error!(?err, ?uid, "failed to get data from db")
})
}
});
let encoded_ethflow_orders: Vec<_> = stream::iter(futures)
.buffer_unordered(10)
.filter_map(|result| async {
result
.inspect_err(|err| tracing::error!(?err, "failed to get data from db"))
.ok()
})
.filter_map(|result| async { result.ok() })
.collect()
.await;
self.submitter
.submit(uids, encoded_ethflow_orders, contract)
.submit_refund(&uids, encoded_ethflow_orders, contract)
.await?;
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO we should fix in a separate PR

This commit reverts the trait-based refactoring in the refunder crate
to facilitate code review by splitting the work across two PRs.

The trait-based architecture will be re-introduced in a follow-up PR.
This reverts commit b6b7d8c.

Also adds the "rand" feature to the shared crate, resolving the build error.
…sts--pr

# Conflicts:
#	crates/shared/Cargo.toml
#	crates/shared/src/sources/balancer_v2/pool_fetching/registry.rs
@tilacog tilacog requested a review from jmg-duarte January 7, 2026 16:52
@tilacog tilacog changed the title refunder: expand E2E tests and add trait abstractions for unit testing refunder: expand E2E tests Jan 7, 2026
@tilacog
Copy link
Author

tilacog commented Jan 7, 2026

For future reference, I've reverted the commit f098c48 to split this PR in two.

It'll be reintroduced in a follow-up PR.

Comment on lines 56 to 57
owner if owner == NO_OWNER => Self::Invalid,
owner if owner == INVALIDATED_OWNER => Self::Refunded,
Copy link
Contributor

Choose a reason for hiding this comment

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

this should work

Suggested change
owner if owner == NO_OWNER => Self::Invalid,
owner if owner == INVALIDATED_OWNER => Self::Refunded,
NO_OWNER => Self::Invalid,
INVALIDATED_OWNER => Self::Refunded,

/// Alternatives to leaking inlcude:
/// - Transmuting, but requires unsafe and introduces some drop complexity
/// - Borrow services & onchain structs
/// - Use Rc/Arc
Copy link
Contributor

Choose a reason for hiding this comment

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

To be honest, I prefer the Arc solution, I find that the leak may be a footgun

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree. Why not simply use Arc?

Copy link
Author

Choose a reason for hiding this comment

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

After giving it a bit more thought, it seems we can't use Arc/Rc here because RefunderTestSetup> struct can't hold both Services<'a>, which in turn borrows &'a Onchain.

However, if we were to modify the Services and Onchain structs themselves, I think we could use Arc/Rc, but that seems like a PR on its own.

In the meantime, I managed to get it working quite easily using ouroboros for the self-referencing part.

What do you guys think? Should I stick with ouroboros, or do we want to refactor Services/Onchain to use Arc/Rc instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is probably now what you want to read but IMO we should instead (if possible):

  1. refactor the test to match the structure of the other ones (that don't require these tricks — read ouroboros, leaks, etc)
  2. fix the E2E testing structure in general (if there's space for it) cc'ing @extrawurst for discussion/visibility
  • since it would tackle a big part of the tech debt
  • at the same time we could shift the E2E specialization from Ilya to you since you'd literally be learning and re-writing them

Fixing the E2E doesn't need to be wholesale, it can be similar to the alloy migration, bit by bit, and with E2E there's no risk of adding new tests and infrastructure for said tests and break previous ones; so we could do new proper structures and migrate the E2E tests bit by bit

I suggest this because:

  • while the Box::leak usage in tests is most likely ok, it's a footgun waiting to blow
  • ouroboros would fix this now but we'd add more tech debt to our existing one
  • if you're eventually adding traits etc (like in the first iteration of the PR), it's just a matter of time until you hit this again -> adding more leak/ouroboros -> meaning more debt

Copy link
Author

Choose a reason for hiding this comment

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

100% agree.

I'm working on removing the setup struct entirely, so tests will follow the e2e pattern and we won't bump into this issue anymore.

Copy link
Author

Choose a reason for hiding this comment

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

I removed the helper/offending struct in the last commit (bb5233f) and replaced it with a builder that just borrows both Services and Onchain, so there's no more lifetime wrangling. Hopefully that makes more sense now.

@tilacog tilacog requested a review from squadgazzz January 7, 2026 17:49
Copy link
Contributor

@squadgazzz squadgazzz left a comment

Choose a reason for hiding this comment

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

Amazing PR 🚀
Just a few clarification questions before approving it.

Comment on lines +284 to +285
// Tests that orders with slippage below, at, or above the min_price_deviation
// threshold are refunded according to the SQL >= check.
Copy link
Contributor

Choose a reason for hiding this comment

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

Does the result depend only on a SQL query? If so, can this be tested in a more lightweight setup? Maybe expanding the database tests would be enough here? And keep only 1-2 cases with a local node involved. My worry is that each test executes in 5-7s, which adds ~40s to the CI job execution. No strong opinion tho. Especially, if the alternative solution will require almost the same time to execute.

Copy link
Author

Choose a reason for hiding this comment

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

Fair point.

Considering SQL alone, then I believe yes. I can see order expiration and slippage being tested at the postgres_refundable_ethflow_orders test

The current test operates one layer above and verifies the full integration, so I dare to say there's a bit more of value being added here.

Nonetheless, I'm still not sure what's the best way forward here.

Maybe we can remove the boundary test cases, since they are already covered in the database tests?

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean, we don't need to drop all the test cases, we should keep at least 1. But for example, if changing some test case params only depends on the SQL query output, we should test it in the DB tests to avoid spinning up the protocol, etc, just to ensure the query returns a reasonable result. If changing the test case params involves some other logic on the refunder side, then we should keep everything as is for sure.

Copy link
Author

Choose a reason for hiding this comment

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

I’m on the fence about this. The current build (bb5233f) tests three scenarios for each threshold:

  1. Refund should be triggered
  2. Refund should not be triggered
  3. Edge case, outcome depends on the specific attribute

I'm OK about dropping the edge cases, but ideally we should still test both the triggering and non-triggering paths...
I'm totally with you on the impact this has on our overall test time though, so I'm not sure how to move forward here.

Copy link
Author

Choose a reason for hiding this comment

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

That said, I think we could get the same confidence about triggering behavior from unit tests, which would be way lighter. We'd need some refactoring involving new traits to get there, but once we have them it should be much better. Do you think it's OK to leave these as they are for now and revisit once we have the unit tests in place?

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's leave it as is then. If this requires additional work, I think we can live with an extra 30s of CI running.

.create_and_index_ethflow_order(slippage.order, validity.order)
.await;

advance_time_past_expiration(&web3, valid_to).await;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I understand what stops the order from being settled. Could you add a comment in the code?

Copy link
Author

Choose a reason for hiding this comment

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

Currently, settlement won't happen because of the time distance between the chain (2020) and autopilot (2026, current).

All tests' orders are expired at creation time by default because they are created based on chain time. The exception being the refunder_skips_settled_orders test, which manually creates valid orders.

I'll document this more thoroughly 👍

Copy link
Author

Choose a reason for hiding this comment

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

I hope the last version (bb5233f) is sufficiently documented, please let me know if I should expand/elaborate more.

@tilacog tilacog force-pushed the tiago/refunder-e2e-tests branch from e696c98 to bb5233f Compare January 12, 2026 15:38
@tilacog
Copy link
Author

tilacog commented Jan 12, 2026

(force pushed just the branch tip, only modified the services/crates/e2e/tests/e2e/refunder.rs file)

Copy link
Contributor

@jmg-duarte jmg-duarte left a comment

Choose a reason for hiding this comment

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

I find the changes from invalidated to refunded kind of confusing

Comment on lines 165 to 166
///
/// Returns: (ExtendedEthFlowOrder, OrderUid, valid_to timestamp)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't add information one isn't getting from the function signature

Copy link
Author

Choose a reason for hiding this comment

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

removed in e7d9392

Comment on lines 253 to 254
/// For tests requiring actual settlement, use `Utc::now()` for `valid_to`.
/// See `refunder_skips_settled_orders` for an example.
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel that this should be part of the mod documentation as it pertains to how to writing tests, not attached to a single function

Copy link
Author

Choose a reason for hiding this comment

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

Addressed in cfbf74f

let ethflow_order_2 =
ExtendedEthFlowOrder::from_quote(&quote_response, valid_to).include_slippage_bps(9999);
)
.with_slippage_bps(slippage.order) // Explicit: testing this SQL filter
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused as to what this comment means, you're testing this filter?

Copy link
Author

Choose a reason for hiding this comment

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

Possibly a leftover comment from a previous version, my bad.
Removed in 5c493b2

Comment on lines 309 to 315
// Verify order is not yet invalidated
assert_ne!(
ethflow_order
.status(onchain.contracts(), ethflow_contract)
.await,
RefundStatus::Refunded
);
Copy link
Contributor

Choose a reason for hiding this comment

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

The docstring kind of mismatches the code, it's checking if the order wasn't refunded yet, meaning it can actually be invalid.

If you're expecting the order to not be invalid, you should check for that instead (?)

Copy link
Author

Choose a reason for hiding this comment

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

Bad wording on my end, and also poor logic.

What we should be testing here can be reworded to "checking if the order is still eligible for refund (not yet reimbursed)" or "check if order has not been refunded yet".

Fixed the comment in a760a89

Comment on lines +446 to +452
assert_eq!(
ethflow_order
.status(onchain.contracts(), ethflow_contract)
.await,
RefundStatus::Refunded,
"Order should already be invalidated by user"
);
Copy link
Contributor

Choose a reason for hiding this comment

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

If the order is invalidated on chain, why are you checking against refunded?

Copy link
Author

@tilacog tilacog Jan 12, 2026

Choose a reason for hiding this comment

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

The RefundStatus::Refunded variant is a bit confusing because it's semantic overloaded. It represents both:

  • Orders refunded by the refunder service, and
  • Orders invalidated/cancelled by the user

So, Refunded is the variant we have for a refund in a terminal state.

/// Status of an EthFlow order refund eligibility.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefundStatus {
/// Order has already been refunded or cancelled.
Refunded,
/// Order is still active and eligible for refund, with the given owner
/// address.
NotYetRefunded(Address),
/// Order is invalid (never created, already freed, or owner cannot receive
/// ETH).
Invalid,
}
impl From<CoWSwapEthFlow::CoWSwapEthFlow::ordersReturn> for RefundStatus {
fn from(value: CoWSwapEthFlow::CoWSwapEthFlow::ordersReturn) -> Self {
match value.owner {
NO_OWNER => Self::Invalid,
INVALIDATED_OWNER => Self::Refunded,
owner => Self::NotYetRefunded(owner),
}
}
}

Note: The impl From above was originally at crates/e2e/tests/e2e/ethflow.rs#L796-L804 in the main branch, before I consolidated the EthFlowOrderOnchainStatus enum w/ the RefundStatus I took from test code. I tried to not loose anything in translation, but now I'm having second thoughts.

Not sure how to go from here. Should I add more comments to explain this further? I'd rather avoid expanding/specializing the variants in this PR.

Copy link
Author

Choose a reason for hiding this comment

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

(Not sure I'm missing something here too)

Comment on lines +460 to +466
// The order should still be invalidated (status unchanged)
assert_eq!(
ethflow_order
.status(onchain.contracts(), ethflow_contract)
.await,
RefundStatus::Refunded
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

Copy link
Author

Choose a reason for hiding this comment

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

I'm covering this in my previous reply.

Comment on lines +559 to +565
assert_ne!(
ethflow_order
.status(onchain.contracts(), ethflow_contract)
.await,
RefundStatus::Refunded,
"Refunder should not have invalidated the already-settled order"
);
Copy link
Contributor

Choose a reason for hiding this comment

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

There doesn't seem to be a proper status for settled orders. Should this actually be invalid in the face of the refunder since it can't be refunded?

Copy link
Author

Choose a reason for hiding this comment

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

I'm covering this in my previous reply.

@tilacog tilacog closed this Jan 12, 2026
@tilacog tilacog deleted the tiago/refunder-e2e-tests branch January 12, 2026 17:48
@github-actions github-actions bot locked and limited conversation to collaborators Jan 12, 2026
@tilacog tilacog restored the tiago/refunder-e2e-tests branch January 12, 2026 17:50
@tilacog tilacog reopened this Jan 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: Refunder e2e tests

4 participants