Skip to content

Latest commit

 

History

History

python-testcontainers

README.md

Python + Testcontainers

pytest · Testcontainers · uv · LocalStack · LocalStack for Azure

uv run pytest

Before you run: envilder.json maps LOCALSTACK_AUTH_TOKEN to the SSM key /envilder/development/localstack/authToken, so that key must hold your LocalStack token in your AWS account, and you need the AWS CLI installed and logged in (aws configure or aws sso login). See Store your LocalStack token.

What happens when you run it

flowchart LR
    subgraph cloud["☁️ Your AWS account"]
        token[("SSM<br/>LOCALSTACK_AUTH_TOKEN")]
    end

    subgraph docker["🐳 Docker, started by Testcontainers"]
        localstack["LocalStack<br/><i>emulated SSM</i>"]
        azurelocal["LocalStack for Azure<br/><i>emulated Key Vault</i>"]
    end

    token -- "Envilder.resolve_file('envilder.json')" --> localstack
    token -- "same token" --> azurelocal
    aws["🧪 test_aws_ssm.py"] --> localstack
    azure["🧪 test_azure_key_vault.py"] --> azurelocal
Loading

Three tests, two stories (see the root README):

Test Proves
Should_ActivateLicense_When_LocalStackStartsWithTokenResolvedByEnvilder Envilder fetched the real token and LocalStack accepted it
Should_ResolveSecretFromSsm_When_MapFilePointsToIt Envilder resolves envilder.test.aws.json against SSM
Should_ResolveSecretFromKeyVault_When_MapFilePointsToIt Envilder resolves envilder.test.azure.json against Key Vault

Read the code in this order

  1. test_aws_ssm.py: the localstack fixture is the whole integration in two statements, Envilder.resolve_file(...) and container.env.update(secrets). Then come the first two tests. In the second, look at the Act block: that is everything Envilder does.
  2. test_azure_key_vault.py is the same test against Key Vault. Its secrets fixture resolves the same token, and the emulator plumbing lives in localstack_azure.py, so skim it. The test itself only changes the map file and the client.

The line to look at

Your app resolves a map file in one call, and Envilder builds the cloud client for you:

secrets = Envilder.resolve_file("envilder.test.aws.json")

The tests need that client aimed at an emulator, so they build it themselves and hand it to Envilder:

sut = EnvilderClient(AwsSsmSecretProvider(ssm))
secrets = sut.resolve_secrets(map_file)

Everything after that line is the same code your app runs.

Emulator-only bits

You won't need these against real clouds:

What Why
aws_access_key_id="test", … LocalStack accepts any credentials
LOCALSTACK_AUTH_TOKEN LocalStack for Azure needs the same token as the AWS emulator, resolved with Envilder
a free host port bound to the same container port, GATEWAY_LISTEN, LOCALSTACK_HOST vault URLs embed the gateway port, so both ports must match
ClientSecretCredential("any-app", "dummy") with disable_instance_discovery=True the emulator accepts a placeholder service principal (all-zero tenant and subscription) on https://azure.localhost.localstack.cloud:<port>
resource group and vault created through ARM the emulator starts empty, so the test creates the vault and uses the returned vaultUri
IDENTITY_ENDPOINT / IDENTITY_HEADER a tiny local token endpoint lets DefaultAzureCredential work through managed identity, as on an App Service; competing AZURE_* variables are cleared while it runs

TLS verification and Key Vault challenge resource verification stay enabled: the emulator serves a trusted certificate on *.vault.azure.localhost.localstack.cloud.