From 290f60f3593218891e4121fbf5f3b23219b78ab1 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 24 Aug 2026 22:03:33 +0200 Subject: [PATCH 1/2] refactor(meshstack/noop): take runner fixtures from test_context The runner e2e module read its GCP project and the meshStack endpoint from `TF_VAR_*` in the environment, which the smoke-test harness had to keep wired in two files. That is how the module broke silently when one of the two lines was removed. Both values are environment facts the harness already carries in `test_context`, so read them from there. An e2e module should never take a fixture from the environment that `test_context` can hold. Co-Authored-By: Claude Opus 5 --- modules/meshstack/noop/e2e/runner/main.tf | 4 ++-- modules/meshstack/noop/e2e/runner/provider.tf | 2 +- .../meshstack/noop/e2e/runner/variables.tf | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/meshstack/noop/e2e/runner/main.tf b/modules/meshstack/noop/e2e/runner/main.tf index dd468f56..008440c4 100644 --- a/modules/meshstack/noop/e2e/runner/main.tf +++ b/modules/meshstack/noop/e2e/runner/main.tf @@ -2,8 +2,8 @@ module "backplane" { source = "../../backplane" meshstack_workspace_identifier = var.test_context.workspace - meshstack_endpoint = var.meshstack_endpoint - gcp_project_id = var.gcp_project_id + meshstack_endpoint = var.test_context.meshstack_endpoint + gcp_project_id = var.test_context.fixtures.gcp.project_id gcp_region = var.gcp_region gcp_resource_name_prefix = "noop-runner-${var.test_context.name_suffix}" runner_display_name = "smoke-test-noop-runner-${var.test_context.name_suffix}" diff --git a/modules/meshstack/noop/e2e/runner/provider.tf b/modules/meshstack/noop/e2e/runner/provider.tf index 7e8eebd1..cf5cded0 100644 --- a/modules/meshstack/noop/e2e/runner/provider.tf +++ b/modules/meshstack/noop/e2e/runner/provider.tf @@ -1,4 +1,4 @@ provider "google" { - project = var.gcp_project_id + project = var.test_context.fixtures.gcp.project_id region = var.gcp_region } diff --git a/modules/meshstack/noop/e2e/runner/variables.tf b/modules/meshstack/noop/e2e/runner/variables.tf index 6b9240f5..eadaf58e 100644 --- a/modules/meshstack/noop/e2e/runner/variables.tf +++ b/modules/meshstack/noop/e2e/runner/variables.tf @@ -4,22 +4,22 @@ variable "test_context" { workspace = string project = string name_suffix = string + + # Base URL of the meshStack API, written into the runner config for API polling. + meshstack_endpoint = string + + # Project the runner's Cloud Run service and Secret Manager secrets live in. + fixtures = object({ + gcp = object({ + project_id = string + }) + }) }) nullable = false } -variable "gcp_project_id" { - type = string - description = "GCP project ID for the runner Cloud Run service and Secret Manager secrets." -} - variable "gcp_region" { type = string default = "europe-west1" description = "GCP region for the Cloud Run service and Secret Manager replicas." } - -variable "meshstack_endpoint" { - type = string - description = "Base URL of the meshStack API. Written into the runner config for API polling." -} From c7f6d0e53252f92256df94d3b476107f9cc8949a Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 24 Aug 2026 22:03:35 +0200 Subject: [PATCH 2/2] docs(e2e-test): no fixtures from the environment Records the rule the runner change follows, and why: a `TF_VAR_` fixture has to be wired on the harness side too, so it has a second place to go missing. Secrets stay the exception, since GitHub can only mask values one by one. Co-Authored-By: Claude Opus 5 --- .agents/skills/e2e-test/SKILL.md | 13 +++++++++++++ AGENTS.md | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/.agents/skills/e2e-test/SKILL.md b/.agents/skills/e2e-test/SKILL.md index 891602b2..6013c79e 100644 --- a/.agents/skills/e2e-test/SKILL.md +++ b/.agents/skills/e2e-test/SKILL.md @@ -99,10 +99,22 @@ Conventions that keep this clean and correct: `test_context` forces the variant to be chosen before `tofu test` starts, which pushes a test-matrix concern out of the hub and into whatever invokes it. +- **Never read a fixture from the environment.** If `test_context` can carry the value, it carries + it — a `TF_VAR_*` has to be wired in the harness repo too (an Actions variable, a workflow line, a + `setup-env.sh` line), and nothing checks that those still agree. That is exactly how the + `meshstack/noop` runner test broke silently when one of two wiring lines was removed. The harness + side of this rule, including where a new fixture value comes from, is the four-branch rule in + `meshstack-smoke-test`'s `AGENTS.md`. + ### Provider authentication secrets Provider authentication secrets should come via standard environment variables expected by these providers, not grab-bag fields in `test_context`. +Secrets are the one exception to the rule above: GitHub masks values only one by one, so a secret +cannot ride in the `test_context` grab-bag. It arrives as a scalar `TF_VAR_` and the module +declares a matching top-level `sensitive` variable (`nullable`, `default = null`, so foundation mode +can omit it). + --- ## `e2e/main.tf` conventions @@ -464,6 +476,7 @@ source setup-override-provider.sh - [ ] `fixtures` is `optional()` with its inner shape fully required (no half-populated fixtures) - [ ] Always-shared fields (`workspace`, `name_suffix`, `hub_git_ref`) are required, not `optional()` - [ ] Cloud resource IDs sourced from `var.test_context.fixtures.*` (not flat `test_context` fields) +- [ ] No fixture read from the environment — only secrets arrive as `TF_VAR_*` - [ ] Scalar secrets are top-level `nullable` vars with `default = null` (foundation mode omits them) - [ ] Module sourced via relative path (not a GitHub URL), gated with `count = var.test_context.bbd_version_ref == null ? 1 : 0` - [ ] `hub.git_ref = var.test_context.hub_git_ref` — no hardcoded `"main"` diff --git a/AGENTS.md b/AGENTS.md index 2c5cec47..b3948d42 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -414,6 +414,10 @@ the diagram checklist. Modules that can be smoke-tested against a live meshStack instance should include an `e2e/` directory alongside the module root. +An `e2e/` module takes every environment fact from `var.test_context` and **never from the +environment** — only secrets arrive as scalar `TF_VAR_*`. A `TF_VAR_` fixture has to be wired in the +harness repo as well, which is a second place for it to go missing. + See [.agents/skills/e2e-test/SKILL.md](.agents/skills/e2e-test/SKILL.md) (the `e2e-test` skill) for the full e2e testing conventions, including the `e2e/` structure, `test_context` wiring, `e2e/main.tf` and `*.tftest.hcl` conventions, the new-test checklist, and how to run and debug tests via the smoke-test runner.