Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/src/content/docs/local-testing/testing-harness.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <env> --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<ResourceMetadata>
```

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.

<Aside type="tip">
[`harness.e2e.test.ts`](https://github.com/INTENTIUS/chant/blob/main/examples/testing-harness-aws/harness.e2e.test.ts) asserts both shapes against a real deploy, and proves the marker check actually gates: the same live resource, read with a marker naming a different env, fails the assertion.
</Aside>

## What the project must declare

Two config requirements, both about identity:
Expand Down
Loading