Testing
Winterbaume uses three complementary test layers. Each layer catches different classes of bugs; together they give strong confidence that service behaviour matches real AWS.
Test layers
| Layer | Location | What it catches |
|---|---|---|
| SDK integration tests | crates/winterbaume-{svc}/tests/integration_test.rs | API shape compatibility, state transitions, error responses |
| Moto parity ports | Same file | Behavioral correctness against a proven reference |
| Terraform E2E | crates/winterbaume-e2e-tests/tests/terraform/ | Provider-level compatibility, waiters, sub-resource reads |
SDK integration tests
Every service crate has an integration_test.rs that exercises the service through real aws-sdk-rust clients routed through a MockAws instance:
use aws_sdk_sqs as sqs;
use winterbaume_core::MockAws;
use winterbaume_sqs::SqsService;
use winterbaume_sts::StsService;
async fn build() -> (sqs::Client, MockAws) {
let mock = MockAws::builder()
.with_service(StsService::new())
.with_service(SqsService::new())
.build();
let config = mock.sdk_config("us-east-1").await;
(sqs::Client::new(&config), mock)
}
#[tokio::test]
async fn test_send_and_receive() {
let (client, _mock) = build().await;
let q = client.create_queue().queue_name("test").send().await.unwrap();
let url = q.queue_url().unwrap();
client.send_message()
.queue_url(url)
.message_body("hello")
.send().await.unwrap();
let recv = client.receive_message()
.queue_url(url)
.send().await.unwrap();
assert_eq!(recv.messages().len(), 1);
assert_eq!(recv.messages()[0].body().unwrap(), "hello");
}Running service tests
# All tests for one service
cargo test -p winterbaume-sqs
# A specific test by name
cargo test -p winterbaume-sqs test_send_and_receive
# Stop after first 5 failures
cargo test -p winterbaume-sqs -- --test-threads=1 2>&1 | head -100Moto parity ports
The strongest behavioural specs are the Python tests in moto's test suite. Port these to Rust to verify that winterbaume produces the same behaviour as moto.
Porting workflow
- Read the moto test in
tests/test_{service}/test_{service}.pyfrom the moto repository. - Translate each test function to a
#[tokio::test]inintegration_test.rs, using the same setup, inputs, and assertions. - Substitute boto3 calls with the equivalent
aws-sdk-rustcalls.
Example — original moto test:
@mock_aws
def test_create_queue():
sqs = boto3.client("sqs", region_name="us-east-1")
sqs.create_queue(QueueName="test-queue")
queues = sqs.list_queues()["QueueUrls"]
assert len(queues) == 1
assert "test-queue" in queues[0]Ported to Rust:
#[tokio::test]
async fn test_create_queue() {
let (client, _mock) = build().await;
client.create_queue().queue_name("test-queue").send().await.unwrap();
let resp = client.list_queues().send().await.unwrap();
assert_eq!(resp.queue_urls().len(), 1);
assert!(resp.queue_urls()[0].contains("test-queue"));
}The /port-moto-tests skill automates this workflow for a given service.
Terraform E2E tests
crates/winterbaume-e2e-tests/tests/terraform/ contains per-service HCL configurations and a Rust test harness that:
- Starts
winterbaume-serverin-process. - Runs
terraform init+terraform applyagainst it. - Asserts on resource creation, read-backs, and provider waiter behaviour.
- Runs
terraform destroyto verify deletion paths.
These tests exercise patterns that SDK tests miss: provider-level read callbacks after create, computed attributes, waiter retry loops, and sub-resource ordering.
Running E2E tests
# Run a single service suite
cargo test -p winterbaume-e2e-tests -- sqs
# Run all E2E tests (slow)
cargo test -p winterbaume-e2e-testsStale .terraform.tfstate.lock.info files will block re-runs. Always rm -rf the working directory before retrying a failed E2E run.
FIX(terraform-e2e) convention
When a handler or state bug is discovered through an E2E test failure, the fix site is marked:
// FIX(terraform-e2e): ELBv2 DescribeLoadBalancers must return DNSName in response
// Provider's read callback always re-reads after create and asserts on this field.A matching fast integration test should be added so the fix does not remain E2E-only. Grep for FIX(terraform-e2e) to find all current sites and verify they each have integration-test coverage.
Coverage counting
The /api-coverage skill generates coverage reports by counting implemented operations, excluding handlers with a // STUB[...] comment. Coverage counts are a prioritisation signal, not an acceptance criterion — behavioural correctness and provider compatibility are what matter.
Test organisation guidelines
- Each test function should cover one meaningful behaviour, not one API call.
- Tests that exercise error conditions are as valuable as happy-path tests.
- Use
#[tokio::test]with no shared global state — construct a freshMockAwsper test. - Long test suites should use helper functions like
build()to keep setup DRY. - When a bug is found and fixed, add a regression test that would have caught it.