From 1b494d4e69780eb1c6f65dc7b53c4041bfac762b Mon Sep 17 00:00:00 2001 From: lex00 <121451605+lex00@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:44:05 -0600 Subject: [PATCH] docs(local-testing): assertLive gets a section on the testing-harness page #1857's third acceptance item never shipped: PR #1997 added assertLive to the DeployedStack handle and the worked example used it, but touched no docs, so the local-testing/testing-harness page had zero mentions of it. The new "Asserting the deploy is live" section covers the signature, the two call shapes, the three failure modes (observed absent, not observed, foreign marker) and that each throws rather than passes, endpoint resolution being identical to deploy/destroy, and the v1 boundary against deep observation (#1014). The example is lifted from examples/testing-harness-aws/harness.e2e.test.ts, the worked example named in both issues. Fixes #2075. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FoXyD9UvKQ5ZdhiR9JT1yB --- .../docs/local-testing/testing-harness.mdx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/src/content/docs/local-testing/testing-harness.mdx b/docs/src/content/docs/local-testing/testing-harness.mdx index 5ed7cccef..ae5f5dc2c 100644 --- a/docs/src/content/docs/local-testing/testing-harness.mdx +++ b/docs/src/content/docs/local-testing/testing-harness.mdx @@ -51,10 +51,49 @@ The resolved handle is a `DeployedStack`: - `outputs` — the built outputs, keyed by lexicon. What was deployed, available for template-level assertions. - `entities` — the discovered entities, keyed by name. - `env` — the environment this deploy targeted. The teardown key. +- `assertLive(name, options?)`, observation-backed assertions against the live stack. Covered below. - `destroy()` — the marker-scoped sweep of that environment, in-process. The same operation as [`chant lifecycle teardown --yes`](/chant/guide/reconciling-lifecycle/). `destroy()` throws a `TeardownIncompleteError` when any candidate failed to delete or the plan had holes (something chant may own could not be read). An environment that cannot be called clean is a test failure, never a silent leak. It is safe to call again: teardown is stateless, and a second call over a clean environment plans nothing. +## Asserting the deploy is live + +`entities.has("taskQueue")` and the `outputs` map both check what was declared and built, not what is actually running. `assertLive` is the other half, calling `describeResources()` against exactly one named entity and reporting back with either a resolved value or a thrown error. Reach for it whenever a test needs to know the live stack, not just the plan that was sent to it. + +```typescript +assertLive(name: string, options?: { status?: string }): Promise +``` + +The call takes two forms. + +```typescript +// exists, marker-verified +const bucket = await stack.assertLive("dataBucket"); + +// exists, marker-verified, and reports this status +const queue = await stack.assertLive("taskQueue", { status: "CREATE_COMPLETE" }); +``` + +On success it resolves to the entity's `ResourceMetadata`. On failure it throws rather than returning a falsy result, for three distinct reasons. + +- The entity is observed absent. `describeResources` looked, and the environment reported no such resource, so `assertLive` throws `LiveAssertionError`. +- The entity is not observed at all, an unmapped kind, a failed call, missing credentials. That counts as neither a pass nor an absence, the same tri-state [the observation contract](/chant/lexicon-authoring/observation-contract/#the-observation-tri-state-absent-is-not-the-same-as-unread) draws for `lifecycle plan`, so `assertLive` throws `UnobservedAssertionError`, a distinct type so a suite can catch could-not-tell apart from a confirmed miss. +- A same-named resource is observed, but it carries another deploy's `{ stack, env }` marker, or no marker at all where the lexicon's ownership channel says one should be present, so `assertLive` throws `LiveAssertionError` naming what it found. + +Both error types are exported from `@intentius/chant/testing`: + +```typescript +import { LiveAssertionError, UnobservedAssertionError } from "@intentius/chant/testing"; +``` + +Endpoint resolution is identical to `deployStack` and `destroy()`: ambient endpoint variables win, then the target environment's declared `endpoint`, then the real cloud. See [Emulator or real cloud](#emulator-or-real-cloud) below. + +`assertLive` checks existence and status only. Property-level assertions, comparing a live resource's actual configuration against what was declared, wait on deep observation (chant #1014); `status` is as deep as v1 goes. + + + ## What the project must declare Two config requirements, both about identity: