Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 7 additions & 5 deletions .agents/skills/e2e-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ Conventions that keep this clean and correct:
- **Always-shared fields are required**: `workspace`, `name_suffix`, and `hub_git_ref` are used (or
statically evaluated) in both modes.
- **Cloud resource IDs live under `fixtures`** (e.g. `var.test_context.fixtures.stackit.project_id`),
never as a flat top-level field.
never as a flat top-level field. This ensures we have one common union type of fixture inputs reusable
across our hub modules.
- **`test_context` describes the environment, not the test case.** A flag that selects *which variant
of the module under test to build* (e.g. a sync vs async implementation) does not belong in
`test_context` — it belongs in a **root variable of the `e2e/` module**, pinned per test file. See
Expand All @@ -101,13 +102,13 @@ Conventions that keep this clean and correct:

### Secrets

**No secret is ever a `test_context` field.** The grab-bag is built from state a CI job can read, so
a secret in it would have to be persisted somewhere it does not belong.
**No secret go into the `test_context` field.** The test_context object is built from a tofu state
read and this must not store secrets.

A secret reaches the module one of two ways:

- The provider reads it from **its own standard environment variable** (cloud credentials). The
module declares nothing.
- The provider reads it from **its own standard environment variable**. The
module declares nothing and we rely on the e2e test harness to setup the environment accordingly.
- The module declares a **flat root variable** for it, when the value is also needed as an input to
the module under test (e.g. `stackit_git_forgejo_token`, `github_app_private_key`). The smoke-test
runner exports every secret it holds as `TF_VAR_<name>`, so declaring the variable is all it takes
Expand Down Expand Up @@ -489,6 +490,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"`
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,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`; 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.

<!-- scorecard-checks: no_buildingblock_tftest -->
Expand Down
2 changes: 1 addition & 1 deletion modules/meshstack/noop/e2e/runner/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module "backplane" {

meshstack_workspace_identifier = var.test_context.workspace
meshstack_endpoint = var.meshstack_endpoint
gcp_project_id = var.gcp_project_id
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}"
Expand Down
2 changes: 1 addition & 1 deletion modules/meshstack/noop/e2e/runner/provider.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
provider "google" {
project = var.gcp_project_id
project = var.test_context.fixtures.gcp.project_id
region = var.gcp_region
}
14 changes: 9 additions & 5 deletions modules/meshstack/noop/e2e/runner/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@ variable "test_context" {
workspace = string
project = string
name_suffix = 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."
}

# Not secret, so this belongs in test_context (see the e2e-test skill) — but the harness does not
# publish it there yet; it reaches CI through the secret pipe. Move it over once it does.
variable "meshstack_endpoint" {
type = string
description = "Base URL of the meshStack API. Written into the runner config for API polling."
Expand Down
Loading