Skip to content

Improve BDD feature readability with a common operations DSL #858

Description

@sbaum1994

Why

The live BDD suite has grown to 12 feature files, about 2,200 lines, and more than 550 Gherkin steps. It contains roughly 147 raw command steps, 116 separate exit-zero assertions, 60 individual environment-variable checks, 14 Helm list assertions, and 18 non-empty YAML-key assertions.

Raw commands remain an important escape hatch, but repeated command and assertion plumbing now obscures the operator workflow in several features. The DSL should hide shell mechanics while keeping the selected workflow, configuration, resource names, namespaces, Kubernetes contexts, timeouts, and expected state visible.

Goals

  • Make feature files easier to scan as operator workflows.
  • Consolidate repeated command plumbing and assertions.
  • Preserve meaningful inputs and outcomes in the Gherkin text or tables.
  • Keep step handlers thin and delegate pure behavior to testable DSL helpers.
  • Retain raw command steps for uncommon operations and command-specific tests.

Abstraction criteria

A new step should:

  • represent one operator action or one observable outcome;
  • be reused across features or replace a clearly repeated pattern;
  • expose every behaviorally meaningful input;
  • avoid hidden workflow branching or ambient Kubernetes context;
  • report failures against the specific table row or resource; and
  • preserve the existing CLI-versus-Helmfile workflow boundary.

First wave

Implement low-risk steps that remove mechanics without hiding the operation.

  • Add When I successfully run command: for commands whose required outcome is exit code 0. Preserve the command result for later output assertions and successful-command caching. Keep the existing run-plus-exit-code form for negative and exit-code-specific cases.
  • Add a table-driven required-environment-variable step.
  • Add a table-driven Helm release deployment assertion with explicit Kubernetes context, release name, namespace, and optional revision.
  • Add generic table-driven Kubernetes resource existence and absence assertions with explicit kind, name, namespace, and context. Replace the ServiceMonitor-specific step rather than adding more resource-specific existence steps.
  • Add a table-driven non-empty YAML-key assertion.
  • Add a Kubernetes resource YAML-subset assertion with explicit resource, namespace, and context. Use it instead of repeated kubectl get -o jsonpath command plumbing where the resource state is the contract.
  • Add a positive rendered-manifest content assertion alongside the existing negative assertion, including an explicit render directory and optional path filter.

Example vocabulary:

When I successfully run command:
  """
  make -C deploy/stacks/self-managed install HELMFILE_ENV=local-bdd
  """

Given these environment variables are set:
  | name            |
  | NGC_API_KEY     |
  | SAMPLE_NGC_ORG  |
  | SAMPLE_NGC_TEAM |

Then these Helm releases should be deployed using context "k3d-ncp-local":
  | name          | namespace     | revision |
  | nvca-operator | nvca-operator | 1        |

Then these Kubernetes resources should not exist in namespace "monitoring" using context "k3d-ncp-local":
  | kind           | name                         |
  | ServiceMonitor | nvcf-default-monitors-nvca   |
  | PodMonitor     | nvcf-default-monitors-worker |

Second wave

After the first wave is in use, add semantic steps where the hidden portion is stable implementation plumbing rather than user intent.

  • Authenticate Helm to an explicit OCI registry using the current NGC API key without exposing secret-bearing shell syntax.
  • Prepare an explicit self-managed secrets destination from the secrets template using the current NGC registry credential.
  • Prepare a named Helmfile environment for a named stack from an explicit fixture and visible values table.
  • Assert that a table of Gateway API routes becomes accepted and resolved using an explicit context and timeout.
  • Assert that a named NVCFBackend reports an explicit agent status using an explicit namespace, context, and timeout.
  • Assert that a named Kubernetes deployment completes rollout using an explicit namespace, context, and timeout.
  • Assert that a DNS name resolves within an explicit timeout.
  • Assert that a named Helm release contains a visible YAML values subset using an explicit namespace and context.

Out of scope

  • Opaque composite steps such as Given the stack is installed, Given the gateway is ready, or Given a sample function is running.
  • Combining control-plane and compute-plane installation into one step.
  • Hiding whether installation uses nvcf-cli, Helmfile, or a stack Makefile.
  • Hiding profile selections, Helmfile values, image sources, release revisions, resource identities, namespaces, contexts, or timeouts.
  • Replacing exact negative-command assertions when the exit code or error text is itself under test.
  • Abstracting selective Helmfile install order or selectors in image-source tests.
  • Moving destructive cleanup into step handlers or changing the existing cleanup authorization model.
  • Collapsing function create, deploy, API-key generation, and invocation into one lifecycle step. Individual product-oriented steps can be evaluated in separate follow-up work if their meaningful parameters remain visible.
  • Changing test coverage, supported deployment topologies, or the live-test execution policy as part of the DSL refactor.

Acceptance criteria

  • Update tests/bdd/AGENTS.md and tests/bdd/PLAN.md with the revised abstraction rules and new catalog entries before migrating features.
  • Add focused unit tests for every DSL helper and step handler.
  • Keep handlers limited to validation, argument assembly, ledger interaction, command execution, and result capture.
  • Migrate existing repeated patterns without weakening their assertions.
  • Keep Kubernetes context explicit in new Kubernetes and Helm steps.
  • Keep secret values out of command logs and failure messages.
  • Pass go test -short ./... under tests/bdd after each migration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions