libtftest

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

libtftest

Terraform module integration testing library. Wraps Terratest with opinionated, LocalStack-aware defaults so module authors write ~10 lines of Go instead of ~200.

What it does

libtftest manages the full lifecycle of a Terraform module integration test:

  1. Starts a LocalStack container (or reuses a shared one)
  2. Copies your module to a scratch workspace
  3. Injects provider and backend overrides so your .tf files stay untouched
  4. Runs terraform init + apply (or plan) against LocalStack
  5. Hands you pre-configured AWS SDK v2 clients for assertions
  6. Cleans up everything via t.Cleanup -- destroy, stop container, flush logs

The module also includes sneakystack, a gap-filling HTTP proxy for LocalStack blind spots (IAM Identity Center, Organizations, Control Tower). It ships as both an importable Go package and a standalone Docker container.

Install

go get github.com/donaldgifford/libtftest

Requirements: Go 1.26+ (uses testing.TB.Context(), requires 1.24 minimum), Docker (for running LocalStack containers), Terraform CLI (installed via mise or manually).

Quick Start

package test

import (
    "testing"

    "github.com/donaldgifford/libtftest"
    s3assert "github.com/donaldgifford/libtftest/assert/s3"
    "github.com/donaldgifford/libtftest/localstack"
)

func TestS3Module(t *testing.T) {
    t.Parallel()

    tc := libtftest.New(t, &libtftest.Options{
        Edition:   localstack.EditionCommunity,
        ModuleDir: "../../modules/s3-bucket",
    })
    tc.SetVar("bucket_name", tc.Prefix()+"-logs")

    tc.Apply()

    bucket := tc.Output("bucket_id")
    s3assert.BucketExists(t, tc.AWS(), bucket)
    s3assert.BucketHasVersioning(t, tc.AWS(), bucket)
}

Run it:

go test -tags=integration -v ./test/...

See docs/examples for more complete examples.

Features

  • Zero-boilerplate container lifecycle -- libtftest.New starts LocalStack, health checks, and cleans up automatically
  • Provider override injection -- writes _libtftest_override.tf.json so your .tf files stay untouched
  • Backend isolation -- forces backend "local" via _libtftest_backend_override.tf.json to prevent hitting real S3 backends
  • Parallel safety -- tc.Prefix() generates unique 10-char resource name prefixes (ltt- + 6 hex chars)
  • Shared container mode -- harness.Run in TestMain shares one container across an entire test package
  • Edition detection -- RequirePro(t) auto-skips tests on Community edition; Pro-only assertions call it internally
  • AWS SDK v2 clients -- pre-configured awsx constructors for S3, DynamoDB, IAM, SSM, Secrets Manager, SQS, SNS, Lambda, KMS, Kinesis, STS
  • Assertion helpers -- per-service packages under assert/: s3assert, ddbassert, iamassert (Pro), ssmassert, lambdaassert (importable as aliases to coexist with the AWS SDK)
  • Fixture seeding -- per-service packages under fixtures/: s3fix.SeedObject, ssmfix.SeedParameter, secretsfix.SeedSecret, sqsfix.SeedMessage with automatic t.Cleanup
  • Plan testing -- tc.Plan() returns parsed PlanResult with resource change counts for golden-file testing
  • Idempotency assertions -- tc.AssertIdempotent() runs a fresh Plan and fails the test if any changes are pending; tc.AssertIdempotentApply() performs the rigorous double-Apply check (Plan -> Apply -> Plan, both plans empty). Both ship *Context variants for per-call deadlines. See docs/examples/08-idempotency.md.
  • Tag propagation assertion -- tagsassert.PropagatesFromRoot calls the AWS Resource Groups Tagging API once and verifies a baseline tag map is present on every listed ARN. Subset check (extra tags allowed), aggregated failure messages, paired *Context variant. See docs/examples/09-tag-propagation.md.
  • JSON snapshot testing -- snapshot.JSONStrict and snapshot.JSONStructural lock down deterministic JSON payloads against a golden file. LIBTFTEST_UPDATE_SNAPSHOTS=1 regenerates snapshots in place. snapshot.ExtractIAMPolicies and snapshot.ExtractResourceAttribute pull policy documents out of terraform show -json plan.out output. See docs/examples/10-snapshot-iam.md.
  • terratest 1.0 *Context API -- every TestCase method, every per-service assertion, and every per-service fixture ships a paired *Context variant (ApplyContext, BucketExistsContext, SeedObjectContext, etc.); non-context forms are permanent shims that forward to the *Context variant with tb.Context(). Cleanup paths use context.WithoutCancel so destroy + teardown survive test-end cancellation. See docs/examples/07-cancellation.md.
  • sneakystack -- gap-filling proxy for LocalStack blind spots, usable as an in-process sidecar or standalone Docker container
  • Configurable image -- defaults to OSS LocalStack; override via Options.Image or LIBTFTEST_LOCALSTACK_IMAGE for Pro, airgapped, or custom images

Shared Container Mode

For faster tests, share one LocalStack container across all tests in a package:

package test

import (
    "testing"

    "github.com/donaldgifford/libtftest/harness"
    "github.com/donaldgifford/libtftest/localstack"
)

func TestMain(m *testing.M) {
    harness.Run(m, harness.Config{
        Edition: localstack.EditionCommunity,
    })
}

// Individual tests automatically reuse the shared container.
func TestMyModule(t *testing.T) {
    t.Parallel()
    tc := libtftest.New(t, &libtftest.Options{
        ModuleDir: "../",
    })
    // ...
}

Package Overview

Package Purpose
libtftest Core API: TestCase, New, Apply, Plan, Output, SetVar
localstack Container lifecycle, edition detection, health polling
tf Workspace copy, provider/backend override, terraform.Options builder
awsx AWS SDK v2 client constructors configured for LocalStack
fixtures Pre-apply data seeding with automatic cleanup
assert Post-apply assertion helpers grouped by AWS service
harness Shared-container TestMain helper, Sidecar interface
sneakystack LocalStack gap-filling proxy with Store interface

Documentation

Doc Description
Examples Usage examples for common testing scenarios
Cancellation & ctx *Context paired API, deadlines, WithoutCancel cleanup
Idempotency tc.AssertIdempotent and tc.AssertIdempotentApply
CHANGELOG Released versions and migration notes
Feature Matrix Generated table of Pro / mockta / multi-tag gated functions
Development Guide How to develop, test, and contribute to libtftest
Design Doc (DESIGN-0001) Architecture and API design
Implementation Plan (IMPL-0001) Phased implementation plan
Skills Design (DESIGN-0002) Claude Code skills for authors and consumers
Skills Implementation (IMPL-0002) Phased plan for the skills above
Investigation (INV-0001) terratest 1.0 *Context migration analysis
Context Migration (IMPL-0003) Phased plan that produced the paired-method API

Using Claude Code with libtftest

Two sets of Claude Code skills accelerate libtftest workflows:

For libtftest maintainers — local skills in this repo's .claude/skills/ that scaffold the most common new-code paths (assertions, fixtures, sneakystack handlers, AWS clients) and a libtftest-reviewer agent that catches libtftest-specific mistakes (PortEndpoint vs Endpoint, RequirePro gating, tb naming). They activate automatically when working in this repo.

For Terraform module repos that consume libtftest — install the libtftest plugin from donaldgifford/claude-skills:

claude plugin install donaldgifford/claude-skills:libtftest

The plugin provides tftest:scaffold (bootstrap a test/ directory), tftest:setup-ci (wire the reusable GHA workflow), tftest:add-test, tftest:add-fixture, tftest:add-assertion, tftest:debug, tftest:enable-pro, tftest:enable-sneakystack, tftest:upgrade, plus a tftest-reviewer agent.

See docs/examples/README.md for the full skill list with descriptions.

License

See LICENSE for details.

Documentation

Overview

Package libtftest wraps Terratest with opinionated, LocalStack-aware defaults for Terraform module integration testing.

libtftest manages the LocalStack container lifecycle, injects provider and backend overrides into the workspace under test, provides pre-configured AWS SDK v2 clients via the awsx sub-package, and offers parallel-safe resource naming. The goal: module authors write ~10 lines of Go instead of ~200.

Typical use

func TestS3ModuleApply(t *testing.T) {
	tc := libtftest.New(t, &libtftest.Options{
		ModuleDir: "../",
	})
	tc.SetVar("bucket_name", tc.Prefix()+"-bucket")
	tc.Apply()

	s3assert.BucketExists(t, tc.AWS(), tc.Prefix()+"-bucket")
}

Package layout

libtftest is organised as a small root package plus several sub-packages, each focused on a single concern:

  • assert/<service> — post-apply assertions, one package per AWS service (s3, dynamodb, iam, ssm, lambda, tags, snapshot)
  • fixtures/<service> — pre-apply data seeding, one package per AWS service (s3, ssm, secretsmanager, sqs)
  • awsx — flat package of typed AWS SDK v2 client constructors
  • harness — TestMain helpers for shared-container suites
  • localstack — testcontainers-go wrapper for LocalStack
  • tf — Terraform workspace management, override rendering
  • sneakystack — LocalStack gap-filling HTTP proxy

See DESIGN-0001 for the architectural overview and DESIGN-0003 for the v0.2.0 layout refactor that introduced the per-service sub-packages.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RequirePro

func RequirePro(tb testing.TB)

RequirePro skips the test when the running container is Community edition.

func RequireServices

func RequireServices(tb testing.TB, _ ...string)

RequireServices skips the test when any of the named services is not available in the running container's edition.

Types

type Options

type Options struct {
	Edition          localstack.Edition
	Services         []string
	Image            string
	ModuleDir        string
	Vars             map[string]any
	Reuse            *localstack.Container
	PersistOnFailure bool
	InitHooks        []localstack.InitHook
	AutoPrefixVars   bool
	EdgeURLOverride  string
}

Options configure a TestCase.

type PlanChanges

type PlanChanges struct {
	Add     int // Resources to create.
	Change  int // Resources to update in-place.
	Destroy int // Resources to destroy.
}

PlanChanges summarizes the resource-level diff from a plan.

type PlanResult

type PlanResult struct {
	JSON     []byte      // Raw `terraform show -json` output.
	FilePath string      // Path to the binary plan file.
	Changes  PlanChanges // Parsed summary of resource changes.
}

PlanResult holds the output of a terraform plan.

type TestCase

type TestCase struct {
	// contains filtered or unexported fields
}

TestCase is the primary handle returned from New. It owns a LocalStack container (or a reference to a shared one), a scratch workspace, and the AWS SDK config used for seeding and assertions.

func New

func New(tb testing.TB, opts *Options) *TestCase

New creates a TestCase. It starts LocalStack (or attaches to a shared one), copies the module into a scratch workspace, writes the provider override, and registers cleanup with t.Cleanup. It calls t.Fatal on any setup error.

func (*TestCase) AWS

func (tc *TestCase) AWS() aws.Config

AWS returns a cached aws.Config pointed at the LocalStack container.

func (*TestCase) Apply

func (tc *TestCase) Apply() *terraform.Options

Apply is a shim that calls ApplyContext with tb.Context().

func (*TestCase) ApplyContext added in v0.1.0

func (tc *TestCase) ApplyContext(ctx context.Context) *terraform.Options

ApplyContext runs terraform init + terraform apply with the supplied context and returns the terraform.Options so callers can chain additional operations.

func (*TestCase) ApplyContextE added in v0.1.0

func (tc *TestCase) ApplyContextE(ctx context.Context) (*terraform.Options, error)

ApplyContextE is the error-returning variant of ApplyContext.

func (*TestCase) ApplyE

func (tc *TestCase) ApplyE() (*terraform.Options, error)

ApplyE is a shim that calls ApplyContextE with tb.Context().

func (*TestCase) AssertIdempotent added in v0.2.0

func (tc *TestCase) AssertIdempotent()

AssertIdempotent is a shim that calls AssertIdempotentContext with tb.Context().

func (*TestCase) AssertIdempotentApply added in v0.2.0

func (tc *TestCase) AssertIdempotentApply()

AssertIdempotentApply is a shim that calls AssertIdempotentApplyContext with tb.Context().

func (*TestCase) AssertIdempotentApplyContext added in v0.2.0

func (tc *TestCase) AssertIdempotentApplyContext(ctx context.Context)

AssertIdempotentApplyContext performs the canonical double-Apply check: runs Plan (fails if non-empty), runs Apply again, then runs Plan a second time (fails if non-empty). Catches a strictly larger class of bugs than AssertIdempotentContext — including computed-vs-known mismatches that only surface on the second Apply — at the cost of one extra terraform apply round-trip.

Use this after the caller's initial Apply has completed.

func (*TestCase) AssertIdempotentContext added in v0.2.0

func (tc *TestCase) AssertIdempotentContext(ctx context.Context)

AssertIdempotentContext runs Plan and fails the test if the plan reports any resource changes (add, change, or destroy). It does NOT call Apply itself — call it after the caller's initial Apply has completed. It calls tb.Errorf on a non-zero change count; the test continues so additional assertions can surface their own failures.

Catches: bad ignore_changes, provider refresh-time drift, and known-after-apply placeholders that didn't resolve.

func (*TestCase) Output

func (tc *TestCase) Output(name string) string

Output is a shim that calls OutputContext with tb.Context().

func (*TestCase) OutputContext added in v0.1.0

func (tc *TestCase) OutputContext(ctx context.Context, name string) string

OutputContext reads a single Terraform output value with the supplied context.

func (*TestCase) Plan

func (tc *TestCase) Plan() *PlanResult

Plan is a shim that calls PlanContext with tb.Context().

func (*TestCase) PlanContext added in v0.1.0

func (tc *TestCase) PlanContext(ctx context.Context) *PlanResult

PlanContext runs terraform init + terraform plan -out with the supplied context and returns a PlanResult.

func (*TestCase) PlanContextE added in v0.1.0

func (tc *TestCase) PlanContextE(ctx context.Context) (*PlanResult, error)

PlanContextE is the error-returning variant of PlanContext.

func (*TestCase) PlanE

func (tc *TestCase) PlanE() (*PlanResult, error)

PlanE is a shim that calls PlanContextE with tb.Context().

func (*TestCase) Prefix

func (tc *TestCase) Prefix() string

Prefix returns the unique string for this test case.

func (*TestCase) SetVar

func (tc *TestCase) SetVar(key string, val any)

SetVar sets or overrides a single Terraform variable.

Directories

Path Synopsis
Package assert is deprecated.
Package assert is deprecated.
dynamodb
Package dynamodb provides post-apply assertions for AWS DynamoDB resources created by Terraform modules under test.
Package dynamodb provides post-apply assertions for AWS DynamoDB resources created by Terraform modules under test.
iam
Package iam provides post-apply assertions for AWS IAM resources created by Terraform modules under test.
Package iam provides post-apply assertions for AWS IAM resources created by Terraform modules under test.
lambda
Package lambda provides post-apply assertions for AWS Lambda resources created by Terraform modules under test.
Package lambda provides post-apply assertions for AWS Lambda resources created by Terraform modules under test.
s3
Package s3 provides post-apply assertions for AWS S3 resources created by Terraform modules under test.
Package s3 provides post-apply assertions for AWS S3 resources created by Terraform modules under test.
snapshot
Package snapshot provides JSON snapshot testing for Terraform plans and other deterministic JSON payloads, plus a small extraction toolkit for the IAM-heavy use cases that motivate it.
Package snapshot provides JSON snapshot testing for Terraform plans and other deterministic JSON payloads, plus a small extraction toolkit for the IAM-heavy use cases that motivate it.
ssm
Package ssm provides post-apply assertions for AWS Systems Manager (SSM) Parameter Store resources created by Terraform modules under test.
Package ssm provides post-apply assertions for AWS Systems Manager (SSM) Parameter Store resources created by Terraform modules under test.
tags
Package tags provides service-agnostic tag-propagation assertions backed by the AWS Resource Groups Tagging API.
Package tags provides service-agnostic tag-propagation assertions backed by the AWS Resource Groups Tagging API.
Package awsx provides AWS SDK v2 client constructors configured for LocalStack-backed Terraform module tests.
Package awsx provides AWS SDK v2 client constructors configured for LocalStack-backed Terraform module tests.
cmd
libtftest command
Package main is the entry point for the libtftest CLI.
Package main is the entry point for the libtftest CLI.
sneakystack command
Package main is the entry point for the sneakystack standalone proxy binary.
Package main is the entry point for the sneakystack standalone proxy binary.
Package fixtures is deprecated.
Package fixtures is deprecated.
s3
Package s3 provides pre-apply data-seeding fixtures for AWS S3 resources in LocalStack-backed Terraform module tests.
Package s3 provides pre-apply data-seeding fixtures for AWS S3 resources in LocalStack-backed Terraform module tests.
secretsmanager
Package secretsmanager provides pre-apply data-seeding fixtures for AWS Secrets Manager resources in LocalStack-backed Terraform module tests.
Package secretsmanager provides pre-apply data-seeding fixtures for AWS Secrets Manager resources in LocalStack-backed Terraform module tests.
sqs
Package sqs provides pre-apply data-seeding fixtures for AWS SQS resources in LocalStack-backed Terraform module tests.
Package sqs provides pre-apply data-seeding fixtures for AWS SQS resources in LocalStack-backed Terraform module tests.
ssm
Package ssm provides pre-apply data-seeding fixtures for AWS Systems Manager (SSM) Parameter Store resources in LocalStack- backed Terraform module tests.
Package ssm provides pre-apply data-seeding fixtures for AWS Systems Manager (SSM) Parameter Store resources in LocalStack- backed Terraform module tests.
Package harness provides shared-container TestMain helpers and the Sidecar interface for plugging auxiliary services (e.g.
Package harness provides shared-container TestMain helpers and the Sidecar interface for plugging auxiliary services (e.g.
internal
dockerx
Package dockerx provides Docker daemon detection and error classification helpers used by libtftest's container lifecycle code.
Package dockerx provides Docker daemon detection and error classification helpers used by libtftest's container lifecycle code.
logx
Package logx provides structured logging configuration and test artifact dumping used by libtftest's TestCase lifecycle.
Package logx provides structured logging configuration and test artifact dumping used by libtftest's TestCase lifecycle.
naming
Package naming provides deterministic, parallel-safe resource name prefixes for libtftest's TestCase fixture.
Package naming provides deterministic, parallel-safe resource name prefixes for libtftest's TestCase fixture.
testfake
Package testfake provides a minimal in-memory testing.TB stand-in used by libtftest's per-service test packages to verify that assertions and fixtures route failures through the test handle rather than panicking, returning errors, or silently swallowing problems.
Package testfake provides a minimal in-memory testing.TB stand-in used by libtftest's per-service test packages to verify that assertions and fixtures route failures through the test handle rather than panicking, returning errors, or silently swallowing problems.
Package localstack manages LocalStack container lifecycle via testcontainers-go.
Package localstack manages LocalStack container lifecycle via testcontainers-go.
Package sneakystack provides a LocalStack gap-filling HTTP proxy with an in-memory store, designed to surface AWS APIs that LocalStack itself does not cover (IAM Identity Center, Organizations, Control Tower, etc.).
Package sneakystack provides a LocalStack gap-filling HTTP proxy with an in-memory store, designed to surface AWS APIs that LocalStack itself does not cover (IAM Identity Center, Organizations, Control Tower, etc.).
services
Package services hosts the per-service handlers for sneakystack's LocalStack-gap-filling proxy.
Package services hosts the per-service handlers for sneakystack's LocalStack-gap-filling proxy.
Package tf handles Terraform workspace management, override rendering, and terraform.Options construction for libtftest's TestCase fixture.
Package tf handles Terraform workspace management, override rendering, and terraform.Options construction for libtftest's TestCase fixture.
Package tools is a parent directory marker for repo-local Go tooling that is built but not redistributed as part of the public libtftest module surface.
Package tools is a parent directory marker for repo-local Go tooling that is built but not redistributed as part of the public libtftest module surface.
docgen command
Command docgen scans the libtftest repo for `// libtftest:requires <tag>[,<tag>...] <reason>` marker comments, emits a JSON intermediate representation, renders the human-readable feature matrix to docs/feature-matrix.md, and gates CI by verifying that every function calling libtftest.RequirePro carries a marker.
Command docgen scans the libtftest repo for `// libtftest:requires <tag>[,<tag>...] <reason>` marker comments, emits a JSON intermediate representation, renders the human-readable feature matrix to docs/feature-matrix.md, and gates CI by verifying that every function calling libtftest.RequirePro carries a marker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL