acctest

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MPL-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package acctest provides acceptance-test scaffolding shared across resource packages. Lives outside internal/testutil because it imports internal/provider, which transitively imports every resource package -- a testutil-side import would create a cycle for any resource whose unit tests already use the fixtures in testutil.

Helpers here are only invoked from TestAcc_* functions, which the terraform-plugin-testing framework gates on TF_ACC=1. Without TF_ACC, `go test` skips the framework-managed bodies and these helpers are never called -- meaning a developer running `task test:unit` on a machine with no Hyper-V host pays nothing for their existence.

See docs/contributing/acceptance-tests.md for the workbench setup (env vars, pre-placed fixtures) and PLAN.md S9 Tier 2 for the test strategy this implements.

Index

Constants

View Source
const AccTestPrefix = "tfacc"

AccTestPrefix is the resource-name prefix for everything created by an acceptance test run. Sweepers (a follow-up PR -- see acceptance-tests.md) will target this prefix; until then it gives a clear pattern for manual cleanup of orphans on the bench.

Variables

View Source
var ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
	"hyperv": providerserver.NewProtocol6WithError(provider.New("test")()),
}

ProtoV6ProviderFactories registers the in-process provider under the short name `hyperv`. terraform-plugin-testing wires this factory into the test-driven Terraform CLI invocations so acceptance test Steps don't shell out to a real `terraform-provider-hyperv` binary -- they run the same code we just compiled.

The version "test" is what main.version receives at non-release builds; keeping it consistent with `task install` (which uses "0.0.0-dev") is not load-bearing here -- the framework only cares about the protocol version (6) the factory advertises.

Functions

func AccCtx

func AccCtx(t *testing.T) context.Context

AccCtx returns a context.Background bound to the test's lifetime. Use this in t.Cleanup hooks that need to call into the typed Hyper-V client after the test body has completed -- e.g. an extra sanity check that the resource is gone after CheckDestroy already passed. The framework runs CheckDestroy with its own ctx, so this is for ad-hoc cleanup only.

func CheckResourceGone

func CheckResourceGone[T any](resourceType string, get func(context.Context, string) (*T, error)) resource.TestCheckFunc

CheckResourceGone returns a TestCheckFunc suitable for the `CheckDestroy:` field of a resource.TestCase. For every state resource of the given type, it calls `get(id)` against the bench and expects ErrNotFound. Any other outcome (cmdlet error, or the resource still existing) fails the test.

Generic on the return type of the getter so callers can pass `client.GetVMSwitch`, `client.GetVHD`, etc. without an adapter closure -- the assertion only cares about the error path, not the returned struct.

Use the inverse pattern (`expect resource still exists`) for resources whose Delete is documented as a no-op on the underlying object -- e.g. hyperv_image_file in host_path mode. Those tests inline a custom CheckDestroy rather than using this helper.

Per-call context budget: 30 seconds. resource.TestCheckFunc's signature has no *testing.T, so we can't piggyback on the test-scoped context AccCtx provides. A bare context.Background would let a dropped bench connection between Terraform's destroy and this Get hang for the full process-level `go test -timeout` (120 minutes per the Taskfile), turning a transient blip into a two-hour wait with no intermediate signal. 30s is generous for any single Get against a healthy bench and bounds the worst case.

func NewClient

func NewClient(t *testing.T) *hyperv.Client

NewClient builds a *hyperv.Client from the bench's HYPERV_* env vars. Used by CheckDestroy assertions that need to query Hyper-V directly -- the provider's own client is owned by the framework's per-test providerserver and isn't reachable from outside the resource.Test closure.

Mirrors the resolution in internal/provider/backend_select.go but stays inline here. We deliberately don't import provider/backend_select because that path is exported through provider.Configure and threading it through testutil for shared use would re-introduce the import-cycle concern the acctest package was created to avoid.

Two-tier behavior matching RequireEnv:

  • TF_ACC unset → t.Skip (not an acc run; nothing to build).
  • TF_ACC set, env misconfigured → t.Fatalf with the specific gap.

Connection is opened on construction and Closed via t.Cleanup.

func PreCheck

func PreCheck(t *testing.T)

PreCheck fails fast with a readable error when the bench's HYPERV_* env vars aren't set, instead of letting the framework spawn `terraform` and surface an opaque Configure-time diagnostic. Called as the PreCheck closure on every resource.TestCase.

Two-tier check:

  • HYPERV_BACKEND must be set (no implicit default for acc tests -- a missing value usually means .env.local wasn't loaded).
  • Per-backend dependent vars: ssh/winrm need host+username; local has no required vars beyond backend itself.

TF_ACC gating is handled by the framework (resource.Test skips when TF_ACC is unset), so we don't re-check it here.

func RandomName

func RandomName(scenario string) string

RandomName returns a unique resource name that's identifiable as belonging to an acc test run. Format: `tfacc-<scenario>-<8-random-lower>`.

The `scenario` arg disambiguates across tests in the same package (e.g. RandomName("vswitch-private") vs RandomName("vswitch-internal")) so a partial-cleanup scenario doesn't conflate them.

Lowercase alpha-numeric only -- Hyper-V switch and VM names tolerate dashes but not underscores or spaces in some cmdlet contexts, and uppercase complicates the case-insensitive sweep filter.

func RequireEnv

func RequireEnv(t *testing.T, key string) string

RequireEnv is the exported form of `require` for per-test fixtures that aren't part of the common provider config. Resource acc tests call this to assert HYPERV_TEST_VHD_DIR, HYPERV_TEST_HOST_FILE, etc. before generating their HCL configs.

Two-tier behavior, both required for clean `task test:unit` runs:

  • TF_ACC unset → t.Skip. The framework's resource.Test() skips for the same reason, but it does so AFTER the test body has run -- if RequireEnv panicked or t.Fatal'd before that point, a non-acc run of `go test ./...` would fail. Skipping early keeps unit-only runs green even when bench-only env vars are unset.
  • TF_ACC set but `key` unset → t.Fatalf. A maintainer running acceptance tests with a misconfigured .env.local should see an immediate, actionable error instead of an opaque resource-creation failure on the bench.

func RunnerIPForBench

func RunnerIPForBench(benchHost string) (string, error)

RunnerIPForBench returns the local IP address the runner OS would use as source when routing to benchHost. An httptest.Server bound to that address is reachable from the bench (assuming a flat LAN or at least symmetric routing).

Implementation: UDP-"dial" the destination. net.Dial with UDP doesn't actually send packets but does run the routing table lookup, so LocalAddr after the dial reveals the source IP that would have been used. Standard idiom -- the alternative (enumerate all interfaces and guess) is fragile on multi-homed hosts (Wi-Fi + ethernet + VPN).

For backend=local (benchHost is empty), returns "127.0.0.1": the bench IS the runner, so the loopback address suffices.

Port 80 in the dial target is arbitrary; only routing is consulted.

func ServeFixture

func ServeFixture(t *testing.T, ip string, body []byte) *httptest.Server

ServeFixture stands up an httptest.Server bound to ip:0 (random free port) that serves body on every GET regardless of path. The caller appends a cosmetic path suffix (e.g. "/fixture.bin") to the returned URL for readability in HCL configs and Terraform diffs.

t.Cleanup tears the server down at end-of-test; no defer in the caller. The server runs concurrently with the test's apply step -- the bench downloads from the URL while the test thread waits in terraform-plugin-testing's apply loop.

Why bind to a specific IP rather than 0.0.0.0: keeps the firewall surface tight and makes the URL the bench downloads from explicitly the runner's LAN address. Binding to all interfaces would also work but invites confusion about which path the bench actually takes.

ReadHeaderTimeout is set to defend against the gosec G112 finding that httptest.Server's defaults are unbounded. 5s is generous for any sane HTTP client.

Types

This section is empty.

Jump to

Keyboard shortcuts

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