Skip to content

feat: use testcontainers for integration tests#1572

Draft
cylewitruk wants to merge 16 commits intomainfrom
feat/docker-integration-tests
Draft

feat: use testcontainers for integration tests#1572
cylewitruk wants to merge 16 commits intomainfrom
feat/docker-integration-tests

Conversation

@cylewitruk
Copy link
Contributor

@cylewitruk cylewitruk commented Apr 3, 2025

Description

Supersedes: #1223

Probably 80% of the changes here are updating tests to use testcontainers vs. regtest::initialize_blockchain().

Changes

  • Introduces the testcontainers crate and implements images for bitcoin core and emily & co. I stuffed these into a separate crate sbtc-docker-testing at the moment, I'm on the fence if we should bake it into the sbtc crate or not...
  • Refactors the sbtc::testing::regtest module a bit to get rid of static references (it made sense when there was only a single bitcoind...).
  • Refactors [almost] all integration tests.
  • Includes some refactoring of the ZMQ tests due to trying to figure out what was going on with them. They ended up in a "better/more robust" state than they were in to begin with, so I left them the way they are.
  • Adds a nextest.toml and updates the Makefile commands to use it. This lets us specify specific test filters that nextest will use to group tests, run certain tests serially, using more/less threads, etc.
    • Configuration file documentation here
    • Test groups and mutual exclusion docs here
  • Updated CI (WIP)

Note that the current structure is:

  • sbtc-docker-testing crate: contains the base testcontainers integration
  • sbtc crate: builds on sbtc-docker-testing adding "common" regtest functionality (faucet, etc). Needed here to support the integration tests in this crate.
  • signer crate: builds on both sbtc and sbtc-docker-testing, adding additional support for e.g. BitcoinCoreClient, BitcoinCoreMessageStream etc. which are local to this crate.

Testing Information

  • Make sure you're running the latest nextest (cargo install nextest --locked) as some of the config file options are only available in newer versions.
  • Otherwise, just use the normal make testing commands as usual.

P.S. still working on getting CI to work properly

Checklist:

  • I have performed a self-review of my code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@cylewitruk cylewitruk self-assigned this Apr 3, 2025
@cylewitruk cylewitruk added this to sBTC Apr 3, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in sBTC Apr 3, 2025
Copy link
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 PR introduces the use of the testcontainers crate for integration tests by creating a dedicated crate (sbtc-docker-testing) and refactors existing regtest and integration test modules to support asynchronous testing with Tokio. It also updates the CI configuration and Nextest settings.

  • Introduced sbtc-docker-testing for container image management and integration testing.
  • Refactored the sbtc regtest module and integration tests to remove static references and leverage async tests.
  • Updated Cargo.toml and CI workflows to include the new testing crate and nextest configuration.

Reviewed Changes

Copilot reviewed 36 out of 38 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
signer/src/testing/docker.rs Added Docker test utilities for BitcoinCore.
signer/src/bitcoin/rpc.rs Added AsRef and Deref implementations for BitcoinCoreClient.
signer/Cargo.toml Updated testing features to include sbtc-docker-testing.
sbtc/tests/integration/validation.rs Refactored integration tests to use async Tokio tests with testcontainers.
sbtc/src/testing/regtest.rs Refactored regtest module and faucet initialization.
sbtc/Cargo.toml Added sbtc-docker-testing dependency to the testing feature.
nextest.toml Added configuration for test groups and overrides.
docker/docker-compose.test.yml Commented out the bitcoind service configuration.
docker-testing/* Added new testcontainers integration modules (images, logging, error).
Cargo.toml Added docker-testing as a workspace member and dependency.
.github/workflows/on-push.yaml Updated CI configuration with the new nextest version.
Files not reviewed (2)
  • .github/actions/docker.override.conf: Language not supported
  • Makefile: Language not supported
Comments suppressed due to low confidence (1)

sbtc/src/testing/regtest.rs:211

  • The signature for Faucet::new has been changed from taking a static reference to owning a Client, which might affect usage elsewhere. Confirm that this change is intentional and update documentation or related code if needed.
fn new(secret_key: &str, kind: AddressType, rpc: Client) -> Self {

fn container_name(name: &str) -> String {
let suffix: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(12)
Copy link

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

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

The documentation for container_name states an 8-character random suffix, but the code generates a 12-character suffix. Please update the comment or modify the code to ensure consistency.

Copilot uses AI. Check for mistakes.
@cylewitruk cylewitruk moved this from Needs Triage to In Progress in sBTC Apr 3, 2025
@cylewitruk cylewitruk added CI Relates to CI testing Relates to testing. labels Apr 3, 2025
@djordon djordon moved this from In Progress to Todo in sBTC Apr 4, 2025
}
})
.await
.expect("ZMQ endpooint not accessible within allotted timeout");

Choose a reason for hiding this comment

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

Suggested change
.expect("ZMQ endpooint not accessible within allotted timeout");
.expect("ZMQ endpoint not accessible within allotted timeout");

/// # Returns
/// * `Ok(())` if connection was established successfully
/// * `Err(Error)` if timeout occurred before successful connection
pub async fn wait_for_tcp_connectivity(host: &str, port: u16, timeout: Duration) {

Choose a reason for hiding this comment

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

Why not use a circuit breaker here (like failsafe-rs or resilient) with exponential back-offs instead of manually handling timeouts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We want the tests to be able to continue ASAP, so exponential back-off isn't really applicable, at least in the current use case. This is almost instantaneous in the current usages as well, so bringing in another dependency for something tokio already gives us isn't very appealing.

.map(char::from)
.collect();

format!("sbtc-test-{name}-{suffix}")

Choose a reason for hiding this comment

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

Could we include the test name in the container name for easier debugging?

  • Current: sbtc-test-bitcoind-abc123
  • Maybe: sbtc-test-bitcoind-test_get_block_works-abc123?

In this example, test_get_block_works is the test name (can probably be derived).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using a derive macro, sure, but otherwise we're stuck either providing the name as a string, or using a (questionably reliable) hack like any::type_name<fn()>() and stripping module prefix/suffixes...

more-asserts.workspace = true
sbtc = { path = ".", features = ["testing"] }
sbtc-docker-testing.workspace = true
tokio.workspace = true No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: new line here

assert_matches.workspace = true
more-asserts.workspace = true No newline at end of file
more-asserts.workspace = true
sbtc = { path = ".", features = ["testing"] }
Copy link
Contributor

Choose a reason for hiding this comment

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

? Is this necessary?

}

// Return an immediately-resolved future
async move {}.boxed()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can return std::future::ready(()) here.

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

Labels

CI Relates to CI testing Relates to testing.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants