Skip to content

Phase 2 -- Root Updates -- Verification

Test catalogue for Phase 2. Every requirement in requirements.md traces to one or more tests here. Every test traces back to a requirement.

Phase 2’s verification surface is purely static / unit: CDK Template-matcher assertions on the synthesized CloudFormation, plus grep / AST assertions on the source-code preservation comment, plus repo-state assertions on the documentation patches. There is no live dig or operator walkthrough in Phase 2. The first cross-account NS-record write happens in Phase 3 (Corporate) and is verified there.

CategoryWhat it assertsRunnerWhere the test lives
Unit (CDK Template)Synthesized CloudFormation matches expected resources, exports, and stack name.Jest + aws-cdk-lib/assertions (existing pattern — see src/main/cdk/stacks/purpose/image-storage.test.ts for a reference).src/main/cdk/stacks/root/root-dns-stack.test.ts (new).
Source-state grepThe CFN-name preservation comment is present at the constructor call site.Jest test using node:fs to read the source file (same pattern as platform.test.ts in Phase 1).src/main/cdk/apps/Root/root-app.test.ts (new) — or extend an existing test file in apps/Root/ if convenient.
Repo-state grepPhase 2 documentation patches landed (planning artefacts present, phases.md patched, DQ-R1-006 recorded).Local make test-links and human-readable diff review.documentation/ repo’s existing link-check pipeline.
Build / lint / synth gateThe repo still builds, lints, tests, and synthesises after the renames.npm run lint, npm run build, npm test, npx cdk synth --app ...Already in place; Phase 2 must keep them green.
RequirementTest ID(s)
REQ-ROOT-001 (ardamails.com zone declared)V-ROOT-001
REQ-ROOT-002 (zone ID exported)V-ROOT-002
REQ-ROOT-003 (existing zones preserved)V-ROOT-003
REQ-IAC-001 (apps folder rename)V-IAC-001
REQ-IAC-002 (stack class + file rename)V-IAC-002
REQ-IAC-003 (CFN name preserved + comment)V-IAC-003
REQ-IAC-004 (instances/Root/dns.ts exists)V-IAC-004
REQ-OPS-001 (deploy-root.sh path update)V-OPS-001
REQ-DOC-001 (planning artefacts published)V-DOC-001
REQ-DOC-002 (DQ-R1-006 and patches present)V-DOC-002

V-ROOT-001 — ardamails.com hosted zone is declared

Section titled “V-ROOT-001 — ardamails.com hosted zone is declared”

Type: CDK Template-matcher unit test in root-dns-stack.test.ts.

Setup: instantiate RootDnsStack with the standard test props (matching the existing RootConfigurationStack test pattern, if any; otherwise, mirror image-storage.test.ts’s cdk.App + new RootDnsStack(...) shape).

Assert:

template.hasResourceProperties("AWS::Route53::HostedZone", {
Name: "ardamails.com.",
});

Pass criterion: the synthesised template contains exactly one AWS::Route53::HostedZone whose Name property is "ardamails.com." (note trailing dot per Route53 convention).


V-ROOT-002 — ardamails.com zone ID is exported

Section titled “V-ROOT-002 — ardamails.com zone ID is exported”

Type: CDK Template-matcher unit test in root-dns-stack.test.ts.

Assert:

template.hasOutput("*", {
Export: { Name: "arda-ardamails-zone" },
});

(Or, equivalently, template.findOutputs("*", { Export: { Name: "arda-ardamails-zone" } }) and assert the result’s length is 1.)

Pass criterion: the synthesised template contains exactly one CloudFormation Output whose Export.Name is "arda-ardamails-zone". Its Value is a Ref to the ardamails.com hosted-zone resource.


V-ROOT-003 — Existing zones and exports preserved

Section titled “V-ROOT-003 — Existing zones and exports preserved”

Type: CDK Template-matcher unit test in root-dns-stack.test.ts.

Setup: same instantiation as V-ROOT-001.

Assert (one assertion per existing zone + one for the role + one for each existing export):

template.hasResourceProperties("AWS::Route53::HostedZone", { Name: "app.arda.cards." });
template.hasResourceProperties("AWS::Route53::HostedZone", { Name: "io.arda.cards." });
template.hasResourceProperties("AWS::Route53::HostedZone", { Name: "auth.arda.cards." });
template.hasResourceProperties("AWS::Route53::HostedZone", { Name: "assets.arda.cards." });
template.hasResource("AWS::IAM::Role", {}); // AllowCreatingNSRecordsRole
// Exports
["arda-app-zone", "arda-io-zone", "arda-auth-zone", "arda-assets-zone", "arda-allow-create-ns-record-role"]
.forEach(name => template.hasOutput("*", { Export: { Name: name } }));

Pass criterion: every existing zone, the role, and every existing export are present in the synthesised template.


Type: filesystem grep test (Jest).

Assert:

expect(fs.existsSync("src/main/cdk/apps/Root/r53-zones.ts")).toBe(true);
expect(fs.existsSync("src/main/cdk/apps/Root/live-url.ts")).toBe(true);
expect(fs.existsSync("src/main/cdk/apps/rootConfiguration")).toBe(false);
// Plus: no source file under src/ references the old path.
const repoSources = walkRepoSources("src/");
const offendingFiles = repoSources.filter(f =>
fs.readFileSync(f, "utf8").includes("apps/rootConfiguration")
);
expect(offendingFiles).toEqual([]);

Pass criterion: the new path exists, the old path is gone, no source file references the old path.


Type: filesystem grep test (Jest) plus a TypeScript import-resolution check.

Assert:

expect(fs.existsSync("src/main/cdk/stacks/root/root-dns-stack.ts")).toBe(true);
expect(fs.existsSync("src/main/cdk/stacks/root/root-configuration-stack.ts")).toBe(false);
// No source file references the old class or the old file.
const repoSources = walkRepoSources("src/");
const offendingClassRefs = repoSources.filter(f =>
fs.readFileSync(f, "utf8").match(/\bRootConfigurationStack\b/)
);
const offendingPathRefs = repoSources.filter(f =>
fs.readFileSync(f, "utf8").includes("root-configuration-stack")
);
expect(offendingClassRefs).toEqual([]);
expect(offendingPathRefs).toEqual([]);

Plus: npx tsc --noEmit (run as part of npm run build) succeeds, which catches any missed import.

Pass criterion: the new file exists, the old file is gone, no source references the old class or path, tsc resolves cleanly.


V-IAC-003 — CFN stack name preserved with inline comment

Section titled “V-IAC-003 — CFN stack name preserved with inline comment”

Type: source-state grep test (Jest).

Assert: read src/main/cdk/apps/Root/r53-zones.ts and verify:

const source = fs.readFileSync("src/main/cdk/apps/Root/r53-zones.ts", "utf8");
// 1. The constructor call passes "RootConfiguration" as the third positional arg.
expect(source).toMatch(/new\s+RootDnsStack\s*\(\s*[^,]+,\s*[^,]+,\s*"RootConfiguration"/);
// 2. The inline preservation comment is present somewhere above the call.
expect(source).toMatch(/CFN stack name MUST remain "RootConfiguration"/);

Pass criterion: both regexes match. The comment text must contain the exact phrase CFN stack name MUST remain "RootConfiguration" so future agents grep-find it.

Optional reinforcement: a CDK Template assertion on the Stack’s stackName property:

expect(stack.stackName).toBe("RootConfiguration");

(Not required if V-ROOT-001 already asserts the stack name implicitly via export naming, but recommended.)


V-IAC-004 — instances/Root/dns.ts exists and is imported

Section titled “V-IAC-004 — instances/Root/dns.ts exists and is imported”

Type: filesystem + import-graph test (Jest).

Assert:

expect(fs.existsSync("src/main/cdk/instances/Root/dns.ts")).toBe(true);
// Confirm at least one `apps/Root/*` file imports from it.
const appFiles = ["src/main/cdk/apps/Root/r53-zones.ts", "src/main/cdk/apps/Root/live-url.ts"];
const importsRootDns = appFiles.some(f =>
fs.existsSync(f) && fs.readFileSync(f, "utf8").includes("arda/instances/Root/dns")
);
expect(importsRootDns).toBe(true);

Plus: the file exports the expected named symbols (ARDAMAILS_ZONE_NAME or equivalent constant; the export-keys constant). The exact symbol set is fixed in exports.md.

Pass criterion: the file exists, at least one app entry imports from it, and the expected named exports are present.


V-OPS-001 — deploy-root.sh points at renamed app

Section titled “V-OPS-001 — deploy-root.sh points at renamed app”

Type: filesystem grep test (Jest or shell).

Assert:

Terminal window
grep -F "src/main/cdk/apps/Root/r53-zones.ts" deploy-root.sh
# (matches one line)
! grep -F "src/main/cdk/apps/rootConfiguration" deploy-root.sh
# (matches no lines)

Pass criterion: the new path appears once, the old path appears zero times.


V-DOC-001 — Phase 2 planning artefacts published

Section titled “V-DOC-001 — Phase 2 planning artefacts published”

Type: filesystem assertion in the documentation repo, plus link-checker (make test-links) inclusion.

Assert: the five files exist under src/content/docs/roadmap/in-progress/email-integration/2-root-updates/:

  • analysis.md
  • requirements.md
  • specification.md
  • verification.md
  • exports.md

Plus: each file has the standard Starlight frontmatter (title, tags, domain, maturity, author).

Pass criterion: all five files exist with valid frontmatter; make test-links passes.


V-DOC-002 — DQ-R1-006 and phases.md patch present

Section titled “V-DOC-002 — DQ-R1-006 and phases.md patch present”

Type: filesystem grep test against the documentation repo.

Assert:

Terminal window
grep -F "DQ-R1-006" decision-log.md
# (matches at least three lines: the table row, the section heading, the summary row)
grep -F "ardamails.com zone declaration" phases.md
# (matches the new Phase 2 row)
! grep -F "after Phase 3 lands the zone" phases.md
# (the dropped language is gone)

Pass criterion: DQ-R1-006 is present in the decision log; the Phase 2 NS-delegation row is gone and replaced; the “after Phase 3 lands the zone” phrasing is removed.

Phase 2’s tests are run together as part of the standard infrastructure pre-push gate. There is no operator-driven sequence:

OrderStepCommand
1Lintnpm run lint
2Type-check + buildnpm run build
3Unit tests (CDK Template + source-state grep)npm test
4Synth (manual confirmation)npx cdk synth --app "npx ts-node ... apps/Root/r53-zones.ts"
5cdk diff against deployed Root stack (manual confirmation)npx cdk diff --app ... (operator validates the diff is additive only)
6Documentation gatemake pr-checks (in the documentation repo)

Steps 1-4 are CI-gated. Steps 5-6 are PR-review gates — the human reviewer confirms the cdk diff output is additive only and the documentation pr-checks log is clean before approving the PR.

  • Live dig NS arda.ardamails.com: this assertion belongs to Phase 3 because Phase 3 is what creates the arda.ardamails.com zone and writes the parent NS record. Phase 2 only ensures the parent zone (ardamails.com) exists; it emits no NS records.
  • Live cross-account assume-role test: the IAM role’s trust policy and permissions are unchanged in Phase 2; coverage of that role’s behaviour rests on the existing partition IngressStack tests and on Phase 3 / Phase 4 deploys exercising it.
  • Operator walkthroughs: Phase 2 has no operator-driven steps. The operator’s only role is reviewing the PR and confirming the cdk diff output before merging.