From b0ec096468ea71aa88267bf8d0cb5faf37b44ac3 Mon Sep 17 00:00:00 2001 From: Stephanie Baum Date: Thu, 20 Aug 2026 02:16:25 -0700 Subject: [PATCH 1/2] test(bdd): add Helm release deployment assertion Replace repeated Helm JSON plumbing with an explicit-context table step that preserves optional revision checks and names mismatched releases. Keep the local Helmfile fixtures deterministic against current observability and NVCA sizing defaults so the migrated live suites remain runnable. Refs #861 Signed-off-by: Stephanie Baum --- tests/bdd/PLAN.md | 21 +++- tests/bdd/dsl/helm.go | 104 ++++++++++++++++++ tests/bdd/dsl/helm_test.go | 92 ++++++++++++++++ .../multi-cluster-eks-helmfile.feature | 44 ++++---- .../features/multi-cluster-helmfile.feature | 56 +++++----- tests/bdd/features/observability-all.feature | 17 ++- .../features/observability-compute.feature | 17 ++- .../features/observability-control.feature | 15 ++- .../single-cluster-eks-helmfile.feature | 44 ++++---- ...e-cluster-helmfile-upstream-images.feature | 15 ++- .../features/single-cluster-helmfile.feature | 56 +++++----- .../single-cluster-up-oneclick.feature | 24 ++-- .../nvcf-compute-plane-local-bdd-multi.yaml | 8 ++ .../nvcf-compute-plane-local-bdd.yaml | 8 ++ tests/bdd/godog_test.go | 24 ++-- tests/bdd/steps/assertion_steps.go | 49 +++++++++ tests/bdd/steps/steps_test.go | 35 ++++++ 17 files changed, 464 insertions(+), 165 deletions(-) create mode 100644 tests/bdd/dsl/helm.go create mode 100644 tests/bdd/dsl/helm_test.go diff --git a/tests/bdd/PLAN.md b/tests/bdd/PLAN.md index 2f90cf257..be79b61bb 100644 --- a/tests/bdd/PLAN.md +++ b/tests/bdd/PLAN.md @@ -137,6 +137,7 @@ refactor in every consumer; that is a feature. | `Then the rendered manifests in {string} should contain:` (table) | Requires a `text` header and one or more fixed strings. Recursively inspects regular files under the repo-relative directory and fails if any listed string is absent. `${VAR}` expansion applies to the path and table values. | | `Then the rendered manifests in {string} under directories matching {string} should contain:` (table) | Positive rendered-manifest assertion scoped to files below a directory whose name matches the supplied shell pattern, such as `*-nats`. The render directory, directory-name pattern, and table values support `${VAR}` expansion. | | `Then the rendered manifests in {string} should not contain:` (table) | Requires a `text` header and one or more fixed strings. Recursively inspects regular files under the repo-relative directory and fails if any listed string appears. `${VAR}` expansion applies to the path and table values. | +| `Then these Helm releases should be deployed using context {string}:` (table) | Requires `name` and `namespace` headers, with an optional `revision` header. Runs one explicit-context, all-namespaces `helm list` and asserts that every listed release has status `deployed`; non-empty revision cells are also matched. | | `Then these ServiceMonitors should exist in namespace {string} using context {string}:` (table) | Requires a `name` header and one or more names. Runs one `kubectl get` with every named ServiceMonitor; exit code 0 proves every listed resource exists. | #### YAML comparison semantics @@ -256,8 +257,9 @@ Going away in `tests/bdd`: `Template`, `HelmfileTemplate`, `RegisterCluster`, `InstallNvcaOperator`). Replaced by `When I run command "make ..."`. - The Helm release readback methods (`HelmReleaseDeployed`, - `NVCAOperatorReady`, `NVCAAgentReady`). Replaced by `helm list -o json` - + `kubectl rollout status` / `kubectl wait` directly in Gherkin. + `NVCAOperatorReady`, `NVCAAgentReady`). Release deployment checks use the + table-driven Helm assertion; readiness remains explicit through + `kubectl rollout status` / `kubectl wait` in Gherkin. - The `harness.CLIHarness` interface and its five domain methods (`SelfHostedUp`, `SelfHostedInstallControlPlane`, `SelfHostedComputePlaneRegister`, `SelfHostedComputePlaneInstall`, @@ -460,6 +462,21 @@ func FilesDoNotContain(root string, needles []string) error // to files below a directory whose name matches the shell pattern. func FilesContain(root, directoryNamePattern string, needles []string) error +// HelmListCommand builds an explicit-context, all-namespaces Helm list command. +func HelmListCommand(kubeContext string) (string, error) + +// HelmReleaseExpectation identifies a deployed Helm release and optionally +// pins the expected revision. +type HelmReleaseExpectation struct { + Name string + Namespace string + Revision string +} + +// HelmReleasesDeployed asserts that every expected release exists in Helm's +// JSON output with status deployed and, when provided, the expected revision. +func HelmReleasesDeployed(raw string, expected []HelmReleaseExpectation) error + // ServiceMonitorExistenceCommand builds one kubectl get command whose // successful exit proves every named ServiceMonitor exists. func ServiceMonitorExistenceCommand(namespace, kubeContext string, names []string) (string, error) diff --git a/tests/bdd/dsl/helm.go b/tests/bdd/dsl/helm.go new file mode 100644 index 000000000..55385ff59 --- /dev/null +++ b/tests/bdd/dsl/helm.go @@ -0,0 +1,104 @@ +/* +SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dsl + +import ( + "encoding/json" + "fmt" + "strings" +) + +// HelmReleaseExpectation identifies a deployed Helm release and optionally +// pins the expected revision. +type HelmReleaseExpectation struct { + Name string + Namespace string + Revision string +} + +type helmRelease struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + Revision any `json:"revision"` + Status string `json:"status"` +} + +// HelmListCommand builds an explicit-context, all-namespaces Helm list command. +func HelmListCommand(kubeContext string) (string, error) { + kubeContext = strings.TrimSpace(Interpolate(kubeContext)) + if kubeContext == "" { + return "", fmt.Errorf("kube context is empty") + } + return "helm list --all-namespaces --kube-context " + quoteCommandArg(kubeContext) + " -o json", nil +} + +// HelmReleasesDeployed asserts that every expected release exists in Helm's +// JSON output with status deployed and, when provided, the expected revision. +func HelmReleasesDeployed(raw string, expected []HelmReleaseExpectation) error { + var actual []helmRelease + if err := json.Unmarshal([]byte(raw), &actual); err != nil { + return fmt.Errorf("parse helm list json: %w", err) + } + if len(expected) == 0 { + return fmt.Errorf("expected Helm releases are empty") + } + + for _, rawExpectation := range expected { + expectation := HelmReleaseExpectation{ + Name: strings.TrimSpace(Interpolate(rawExpectation.Name)), + Namespace: strings.TrimSpace(Interpolate(rawExpectation.Namespace)), + Revision: strings.TrimSpace(Interpolate(rawExpectation.Revision)), + } + if expectation.Name == "" { + return fmt.Errorf("helm release name is empty") + } + if expectation.Namespace == "" { + return fmt.Errorf("helm release %q namespace is empty", expectation.Name) + } + + release, found := findHelmRelease(actual, expectation.Name, expectation.Namespace) + if !found { + return describeMissingHelmRelease(actual, expectation) + } + if release.Status != "deployed" { + return fmt.Errorf("helm release %q in namespace %q status = %q, want %q", expectation.Name, expectation.Namespace, release.Status, "deployed") + } + if expectation.Revision != "" && fmt.Sprint(release.Revision) != expectation.Revision { + return fmt.Errorf("helm release %q in namespace %q revision = %q, want %q", expectation.Name, expectation.Namespace, fmt.Sprint(release.Revision), expectation.Revision) + } + } + return nil +} + +func findHelmRelease(releases []helmRelease, name, namespace string) (helmRelease, bool) { + for _, release := range releases { + if release.Name == name && release.Namespace == namespace { + return release, true + } + } + return helmRelease{}, false +} + +func describeMissingHelmRelease(releases []helmRelease, expected HelmReleaseExpectation) error { + for _, release := range releases { + if release.Name == expected.Name { + return fmt.Errorf("helm release %q namespace = %q, want %q", expected.Name, release.Namespace, expected.Namespace) + } + } + return fmt.Errorf("helm release %q in namespace %q is missing", expected.Name, expected.Namespace) +} diff --git a/tests/bdd/dsl/helm_test.go b/tests/bdd/dsl/helm_test.go new file mode 100644 index 000000000..5407f5e53 --- /dev/null +++ b/tests/bdd/dsl/helm_test.go @@ -0,0 +1,92 @@ +/* +SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dsl + +import ( + "strings" + "testing" +) + +const deployedHelmReleases = `[ + {"name":"nats","namespace":"nats-system","revision":"2","status":"deployed"}, + {"name":"api","namespace":"nvcf","revision":3,"status":"failed"} +]` + +func TestHelmListCommandUsesExplicitContext(t *testing.T) { + t.Setenv("BDD_TMP_CONTEXT", "k3d-ncp-local") + got, err := HelmListCommand("${BDD_TMP_CONTEXT}") + if err != nil { + t.Fatalf("build command: %v", err) + } + want := "helm list --all-namespaces --kube-context k3d-ncp-local -o json" + if got != want { + t.Fatalf("command = %q, want %q", got, want) + } +} + +func TestHelmListCommandRejectsEmptyContext(t *testing.T) { + if _, err := HelmListCommand(""); err == nil { + t.Fatal("expected empty context error") + } +} + +func TestHelmReleasesDeployedMatchesOptionalRevision(t *testing.T) { + expected := []HelmReleaseExpectation{{Name: "nats", Namespace: "nats-system", Revision: "2"}} + if err := HelmReleasesDeployed(deployedHelmReleases, expected); err != nil { + t.Fatalf("assert releases: %v", err) + } + + expected[0].Revision = "" + if err := HelmReleasesDeployed(deployedHelmReleases, expected); err != nil { + t.Fatalf("assert release without revision: %v", err) + } +} + +func TestHelmReleasesDeployedReportsMissingRelease(t *testing.T) { + err := HelmReleasesDeployed(deployedHelmReleases, []HelmReleaseExpectation{{Name: "missing", Namespace: "nvcf"}}) + if err == nil || !strings.Contains(err.Error(), `helm release "missing" in namespace "nvcf" is missing`) { + t.Fatalf("error = %v", err) + } +} + +func TestHelmReleasesDeployedReportsNamespaceMismatch(t *testing.T) { + err := HelmReleasesDeployed(deployedHelmReleases, []HelmReleaseExpectation{{Name: "nats", Namespace: "wrong"}}) + if err == nil || !strings.Contains(err.Error(), `namespace = "nats-system", want "wrong"`) { + t.Fatalf("error = %v", err) + } +} + +func TestHelmReleasesDeployedReportsStatusMismatch(t *testing.T) { + err := HelmReleasesDeployed(deployedHelmReleases, []HelmReleaseExpectation{{Name: "api", Namespace: "nvcf"}}) + if err == nil || !strings.Contains(err.Error(), `status = "failed", want "deployed"`) { + t.Fatalf("error = %v", err) + } +} + +func TestHelmReleasesDeployedReportsRevisionMismatch(t *testing.T) { + err := HelmReleasesDeployed(deployedHelmReleases, []HelmReleaseExpectation{{Name: "nats", Namespace: "nats-system", Revision: "1"}}) + if err == nil || !strings.Contains(err.Error(), `revision = "2", want "1"`) { + t.Fatalf("error = %v", err) + } +} + +func TestHelmReleasesDeployedRejectsMalformedJSON(t *testing.T) { + if err := HelmReleasesDeployed("not json", []HelmReleaseExpectation{{Name: "nats", Namespace: "nats-system"}}); err == nil { + t.Fatal("expected parse error") + } +} diff --git a/tests/bdd/features/multi-cluster-eks-helmfile.feature b/tests/bdd/features/multi-cluster-eks-helmfile.feature index 26e2f17f6..8a8624772 100644 --- a/tests/bdd/features/multi-cluster-eks-helmfile.feature +++ b/tests/bdd/features/multi-cluster-eks-helmfile.feature @@ -199,25 +199,24 @@ Feature: Install a multi-cluster NVCF stack across two pre-provisioned EKS clust When I run command "make -C deploy/stacks/self-managed install HELMFILE_ENV=eks-bdd-multi" Then the command exit code should be 0 - When I run command "helm list --all-namespaces --kube-context ${EKS_CONTEXT} -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cert-manager | cert-manager | deployed | - | openbao-server | vault-system | deployed | - | cassandra | cassandra-system | deployed | - | api-keys | api-keys | deployed | - | sis | sis | deployed | - | api | nvcf | deployed | - | nvct-api | nvcf | deployed | - | invocation-service | nvcf | deployed | - | grpc-proxy | nvcf | deployed | - | ess-api | ess | deployed | - | notary-service | nvcf | deployed | - | admin-issuer-proxy | api-keys | deployed | - | reval | nvcf | deployed | - | nats-auth-callout-service | nats-system | deployed | - | ingress | envoy-gateway-system | deployed | + Then these Helm releases should be deployed using context "${EKS_CONTEXT}": + | name | namespace | + | nats | nats-system | + | cert-manager | cert-manager | + | openbao-server | vault-system | + | cassandra | cassandra-system | + | api-keys | api-keys | + | sis | sis | + | api | nvcf | + | nvct-api | nvcf | + | invocation-service | nvcf | + | grpc-proxy | nvcf | + | ess-api | ess | + | notary-service | nvcf | + | admin-issuer-proxy | api-keys | + | reval | nvcf | + | nats-auth-callout-service | nats-system | + | ingress | envoy-gateway-system | # Confirm gateway-routes templated global.domain into the api # HTTPRoute hostname on the control-plane cluster. @@ -339,10 +338,9 @@ Feature: Install a multi-cluster NVCF stack across two pre-provisioned EKS clust """ Then the command exit code should be 0 - When I run command "helm list -n nvca-operator --kube-context ${EKS_COMPUTE_CONTEXT} -o json" - Then the json output should contain rows: - | name | namespace | status | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "${EKS_COMPUTE_CONTEXT}": + | name | namespace | + | nvca-operator | nvca-operator | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --context ${EKS_COMPUTE_CONTEXT} --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/multi-cluster-helmfile.feature b/tests/bdd/features/multi-cluster-helmfile.feature index 9f08c4809..98e1206d6 100644 --- a/tests/bdd/features/multi-cluster-helmfile.feature +++ b/tests/bdd/features/multi-cluster-helmfile.feature @@ -43,11 +43,13 @@ Feature: Install a local multi-cluster NVCF stack with Helmfile | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE | nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/stargate-client:0.2.0 | + | observability.profile | disabled | And I copy the file "tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml" to "deploy/stacks/nvcf-compute-plane/environments/local-bdd.yaml" And I update yaml file "deploy/stacks/nvcf-compute-plane/environments/local-bdd.yaml" with keys: - | global.imagePullSecrets[0].name | nvcr-pull-secret | - | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | global.imagePullSecrets[0].name | nvcr-pull-secret | + | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | observability.profile | disabled | And I copy the file "deploy/stacks/self-managed/secrets/secrets.yaml.template" to "deploy/stacks/self-managed/secrets/local-bdd-secrets.yaml" And I substitute "REPLACE_WITH_BASE64_DOCKER_CREDENTIAL" in file "deploy/stacks/self-managed/secrets/local-bdd-secrets.yaml" with base64 of "$oauthtoken:${NGC_API_KEY}" # Conflict precheck: single-cluster ncp-local's k3d serverlb @@ -84,27 +86,26 @@ Feature: Install a local multi-cluster NVCF stack with Helmfile When I run command "make -C deploy/stacks/self-managed install HELMFILE_ENV=local-bdd" Then the command exit code should be 0 - When I run command "helm list --all-namespaces --kube-context k3d-ncp-local-cp -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cert-manager | cert-manager | deployed | - | openbao-server | vault-system | deployed | - | cassandra | cassandra-system | deployed | - | api-keys | api-keys | deployed | - | sis | sis | deployed | - | api | nvcf | deployed | - | nvct-api | nvcf | deployed | - | invocation-service | nvcf | deployed | - | grpc-proxy | nvcf | deployed | - | ess-api | ess | deployed | - | notary-service | nvcf | deployed | - | admin-issuer-proxy | api-keys | deployed | - | reval | nvcf | deployed | - | nats-auth-callout-service | nats-system | deployed | - | ingress | envoy-gateway-system | deployed | - | llm-request-router | nvcf | deployed | - | llm-api-gateway | nvcf | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local-cp": + | name | namespace | + | nats | nats-system | + | cert-manager | cert-manager | + | openbao-server | vault-system | + | cassandra | cassandra-system | + | api-keys | api-keys | + | sis | sis | + | api | nvcf | + | nvct-api | nvcf | + | invocation-service | nvcf | + | grpc-proxy | nvcf | + | ess-api | ess | + | notary-service | nvcf | + | admin-issuer-proxy | api-keys | + | reval | nvcf | + | nats-auth-callout-service | nats-system | + | ingress | envoy-gateway-system | + | llm-request-router | nvcf | + | llm-api-gateway | nvcf | # These routes are installed by ncp-local before the Helmfile # stack, then become fully resolved once the control-plane @@ -213,10 +214,9 @@ Feature: Install a local multi-cluster NVCF stack with Helmfile """ Then the command exit code should be 0 - When I run command "helm list -n nvca-operator --kube-context k3d-ncp-local-compute-1 -o json" - Then the json output should contain rows: - | name | namespace | status | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local-compute-1": + | name | namespace | + | nvca-operator | nvca-operator | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --context k3d-ncp-local-compute-1 --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/observability-all.feature b/tests/bdd/features/observability-all.feature index 84050469a..68ddfa932 100644 --- a/tests/bdd/features/observability-all.feature +++ b/tests/bdd/features/observability-all.feature @@ -104,15 +104,14 @@ Feature: Install local Helmfile observability for both planes # Revision 1 proves the compute install did not reinstall or upgrade the # shared observability releases created by the control-plane install. - When I run command "helm list --all-namespaces --kube-context k3d-ncp-local -o json" - Then the json output should contain rows: - | name | namespace | revision | status | - | prometheus-operator-crds | monitoring | 1 | deployed | - | opentelemetry-operator | monitoring | 1 | deployed | - | victoria-metrics | monitoring | 1 | deployed | - | otel-collector | monitoring | 1 | deployed | - | default-monitors | monitoring | 1 | deployed | - | nvca-operator | nvca-operator | 1 | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | revision | + | prometheus-operator-crds | monitoring | 1 | + | opentelemetry-operator | monitoring | 1 | + | victoria-metrics | monitoring | 1 | + | otel-collector | monitoring | 1 | + | default-monitors | monitoring | 1 | + | nvca-operator | nvca-operator | 1 | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --context k3d-ncp-local --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/observability-compute.feature b/tests/bdd/features/observability-compute.feature index 84a66e3ba..7e2cf102d 100644 --- a/tests/bdd/features/observability-compute.feature +++ b/tests/bdd/features/observability-compute.feature @@ -118,15 +118,14 @@ Feature: Install local Helmfile observability with the compute profile kubectl --context k3d-ncp-local-compute-1 delete pod --namespace nvca-system --selector app.kubernetes.io/name=nvca --wait=false """ - When I run command "helm list --all-namespaces --kube-context k3d-ncp-local-compute-1 -o json" - Then the json output should contain rows: - | name | namespace | status | - | prometheus-operator-crds | monitoring | deployed | - | opentelemetry-operator | monitoring | deployed | - | victoria-metrics | monitoring | deployed | - | otel-collector | monitoring | deployed | - | default-monitors | monitoring | deployed | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local-compute-1": + | name | namespace | + | prometheus-operator-crds | monitoring | + | opentelemetry-operator | monitoring | + | victoria-metrics | monitoring | + | otel-collector | monitoring | + | default-monitors | monitoring | + | nvca-operator | nvca-operator | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --context k3d-ncp-local-compute-1 --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/observability-control.feature b/tests/bdd/features/observability-control.feature index 78ec6baa8..8bc861e90 100644 --- a/tests/bdd/features/observability-control.feature +++ b/tests/bdd/features/observability-control.feature @@ -54,14 +54,13 @@ Feature: Install local Helmfile observability with the control profile Scenario: Control profile installs shared infrastructure and control monitors When I successfully run command "make -C deploy/stacks/self-managed install HELMFILE_ENV=local-bdd-observability-control" - When I run command "helm list --all-namespaces --kube-context k3d-ncp-local -o json" - Then the json output should contain rows: - | name | namespace | status | - | prometheus-operator-crds | monitoring | deployed | - | opentelemetry-operator | monitoring | deployed | - | victoria-metrics | monitoring | deployed | - | otel-collector | monitoring | deployed | - | default-monitors | monitoring | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | prometheus-operator-crds | monitoring | + | opentelemetry-operator | monitoring | + | victoria-metrics | monitoring | + | otel-collector | monitoring | + | default-monitors | monitoring | When I successfully run command "kubectl get opentelemetrycollector nvcf-observability -n monitoring --context k3d-ncp-local -o jsonpath='{.spec.targetAllocator.enabled}'" And the command output should contain "true" diff --git a/tests/bdd/features/single-cluster-eks-helmfile.feature b/tests/bdd/features/single-cluster-eks-helmfile.feature index 3f1b14a12..e5fab01ea 100644 --- a/tests/bdd/features/single-cluster-eks-helmfile.feature +++ b/tests/bdd/features/single-cluster-eks-helmfile.feature @@ -184,25 +184,24 @@ Feature: Install a single-cluster NVCF stack on a pre-provisioned EKS cluster wi When I run command "make -C deploy/stacks/self-managed install HELMFILE_ENV=eks-bdd" Then the command exit code should be 0 - When I run command "helm list --all-namespaces --kube-context ${EKS_CONTEXT} -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cert-manager | cert-manager | deployed | - | openbao-server | vault-system | deployed | - | cassandra | cassandra-system | deployed | - | api-keys | api-keys | deployed | - | sis | sis | deployed | - | api | nvcf | deployed | - | nvct-api | nvcf | deployed | - | invocation-service | nvcf | deployed | - | grpc-proxy | nvcf | deployed | - | ess-api | ess | deployed | - | notary-service | nvcf | deployed | - | admin-issuer-proxy | api-keys | deployed | - | reval | nvcf | deployed | - | nats-auth-callout-service | nats-system | deployed | - | ingress | envoy-gateway-system | deployed | + Then these Helm releases should be deployed using context "${EKS_CONTEXT}": + | name | namespace | + | nats | nats-system | + | cert-manager | cert-manager | + | openbao-server | vault-system | + | cassandra | cassandra-system | + | api-keys | api-keys | + | sis | sis | + | api | nvcf | + | nvct-api | nvcf | + | invocation-service | nvcf | + | grpc-proxy | nvcf | + | ess-api | ess | + | notary-service | nvcf | + | admin-issuer-proxy | api-keys | + | reval | nvcf | + | nats-auth-callout-service | nats-system | + | ingress | envoy-gateway-system | # Verify gateway-routes templated global.domain into the api # HTTPRoute hostname. Confirms the env-file global.domain value @@ -268,10 +267,9 @@ Feature: Install a single-cluster NVCF stack on a pre-provisioned EKS cluster wi """ Then the command exit code should be 0 - When I run command "helm list -n nvca-operator --kube-context ${EKS_CONTEXT} -o json" - Then the json output should contain rows: - | name | namespace | status | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "${EKS_CONTEXT}": + | name | namespace | + | nvca-operator | nvca-operator | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --context ${EKS_CONTEXT} --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/single-cluster-helmfile-upstream-images.feature b/tests/bdd/features/single-cluster-helmfile-upstream-images.feature index a951622fc..849ece593 100644 --- a/tests/bdd/features/single-cluster-helmfile-upstream-images.feature +++ b/tests/bdd/features/single-cluster-helmfile-upstream-images.feature @@ -110,14 +110,13 @@ Feature: Install a local single-cluster stack with upstream supporting images When I run command "env HELM_REGISTRY_CONFIG=${REPO_ROOT}/tools/ncp-local-cluster/secrets/docker-config.json make -C deploy/stacks/self-managed install HELMFILE_ENV=local-bdd HELMFILE_SELECTOR=name=api" Then the command exit code should be 0 - When I run command "helm list --all-namespaces -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cassandra | cassandra-system | deployed | - | ess-api | ess | deployed | - | nats-auth-callout-service | nats-system | deployed | - | api | nvcf | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | nats | nats-system | + | cassandra | cassandra-system | + | ess-api | ess | + | nats-auth-callout-service | nats-system | + | api | nvcf | When I run command: """ diff --git a/tests/bdd/features/single-cluster-helmfile.feature b/tests/bdd/features/single-cluster-helmfile.feature index a38ea42f7..7efeccd26 100644 --- a/tests/bdd/features/single-cluster-helmfile.feature +++ b/tests/bdd/features/single-cluster-helmfile.feature @@ -23,11 +23,13 @@ Feature: Install a local single-cluster NVCF stack with Helmfile | global.imagePullSecrets[0].name | nvcr-pull-secret | | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | observability.profile | disabled | And I copy the file "tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml" to "deploy/stacks/nvcf-compute-plane/environments/local-bdd.yaml" And I update yaml file "deploy/stacks/nvcf-compute-plane/environments/local-bdd.yaml" with keys: - | global.imagePullSecrets[0].name | nvcr-pull-secret | - | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | global.imagePullSecrets[0].name | nvcr-pull-secret | + | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | + | observability.profile | disabled | And I copy the file "deploy/stacks/self-managed/secrets/secrets.yaml.template" to "deploy/stacks/self-managed/secrets/local-bdd-secrets.yaml" # Only ${VAR} is interpolated; bare $oauthtoken stays literal. And I substitute "REPLACE_WITH_BASE64_DOCKER_CREDENTIAL" in file "deploy/stacks/self-managed/secrets/local-bdd-secrets.yaml" with base64 of "$oauthtoken:${NGC_API_KEY}" @@ -72,27 +74,26 @@ Feature: Install a local single-cluster NVCF stack with Helmfile Then the command exit code should be 0 - When I run command "helm list --all-namespaces -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cert-manager | cert-manager | deployed | - | openbao-server | vault-system | deployed | - | cassandra | cassandra-system | deployed | - | api-keys | api-keys | deployed | - | sis | sis | deployed | - | api | nvcf | deployed | - | nvct-api | nvcf | deployed | - | invocation-service | nvcf | deployed | - | grpc-proxy | nvcf | deployed | - | ess-api | ess | deployed | - | notary-service | nvcf | deployed | - | admin-issuer-proxy | api-keys | deployed | - | reval | nvcf | deployed | - | nats-auth-callout-service | nats-system | deployed | - | ingress | envoy-gateway-system | deployed | - | llm-request-router | nvcf | deployed | - | llm-api-gateway | nvcf | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | nats | nats-system | + | cert-manager | cert-manager | + | openbao-server | vault-system | + | cassandra | cassandra-system | + | api-keys | api-keys | + | sis | sis | + | api | nvcf | + | nvct-api | nvcf | + | invocation-service | nvcf | + | grpc-proxy | nvcf | + | ess-api | ess | + | notary-service | nvcf | + | admin-issuer-proxy | api-keys | + | reval | nvcf | + | nats-auth-callout-service | nats-system | + | ingress | envoy-gateway-system | + | llm-request-router | nvcf | + | llm-api-gateway | nvcf | Rule: Helmfile installs NVCA on the same local cluster after registration via the stack Makefile @@ -137,10 +138,9 @@ Feature: Install a local single-cluster NVCF stack with Helmfile """ Then the command exit code should be 0 - When I run command "helm list -n nvca-operator -o json" - Then the json output should contain rows: - | name | namespace | status | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | nvca-operator | nvca-operator | When I run command "kubectl rollout status deployment/nvca-operator -n nvca-operator --timeout=10m" Then the command exit code should be 0 diff --git a/tests/bdd/features/single-cluster-up-oneclick.feature b/tests/bdd/features/single-cluster-up-oneclick.feature index 55e47c34b..b70ee9c90 100644 --- a/tests/bdd/features/single-cluster-up-oneclick.feature +++ b/tests/bdd/features/single-cluster-up-oneclick.feature @@ -81,21 +81,19 @@ Feature: Bring up a local single-cluster NVCF stack with the self-hosted up one- Then the command exit code should be 0 # Control plane releases are deployed on the single k3d cluster. - When I run command "helm list --all-namespaces --kube-context k3d-ncp-local -o json" - Then the json output should contain rows: - | name | namespace | status | - | nats | nats-system | deployed | - | cassandra | cassandra-system | deployed | - | openbao-server | vault-system | deployed | - | api-keys | api-keys | deployed | - | sis | sis | deployed | - | api | nvcf | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | nats | nats-system | + | cassandra | cassandra-system | + | openbao-server | vault-system | + | api-keys | api-keys | + | sis | sis | + | api | nvcf | # The compute plane (NVCA operator) is deployed on the same cluster. - When I run command "helm list -n nvca-operator --kube-context k3d-ncp-local -o json" - Then the json output should contain rows: - | name | namespace | status | - | nvca-operator | nvca-operator | deployed | + Then these Helm releases should be deployed using context "k3d-ncp-local": + | name | namespace | + | nvca-operator | nvca-operator | # The agent registered by up reports healthy. When I run command "kubectl wait nvcfbackend ncp-local -n nvca-operator --context k3d-ncp-local --for=jsonpath={.status.agentStatus}=healthy --timeout=10m" diff --git a/tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml b/tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml index a1d897af6..165fa38dd 100644 --- a/tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml +++ b/tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml @@ -32,6 +32,14 @@ global: # in the multi-cluster topology. nvcaOperator: selfManaged: + # The simulated GPU density exceeds laptop CPU and memory ratios. Keep + # dynamic discovery's 1x type so NVCA can report local GPU capacity. + featureGateValues: + - -InfraResourceOverhead + - -EnforceHelmFunctionResourceLimits + - -EnforceContainerFunctionResourceLimits + - -EnforceHelmTaskResourceLimits + - -EnforceContainerTaskResourceLimits icmsServiceURL: http://api.sis.svc.cluster.local:8080 revalServiceURL: http://reval.nvcf.svc.cluster.local:8080 natsURL: nats://nats.nats-system.svc.cluster.local:4222 diff --git a/tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml b/tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml index 5ab7f37ac..8ecaf2607 100644 --- a/tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml +++ b/tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml @@ -26,6 +26,14 @@ global: nvcaOperator: selfManaged: + # The simulated GPU density exceeds laptop CPU and memory ratios. Keep + # dynamic discovery's 1x type so NVCA can report local GPU capacity. + featureGateValues: + - -InfraResourceOverhead + - -EnforceHelmFunctionResourceLimits + - -EnforceContainerFunctionResourceLimits + - -EnforceHelmTaskResourceLimits + - -EnforceContainerTaskResourceLimits icmsServiceURL: http://api.sis.svc.cluster.local:8080 revalServiceURL: http://reval.nvcf.svc.cluster.local:8080 natsURL: nats://nats.nats-system.svc.cluster.local:4222 diff --git a/tests/bdd/godog_test.go b/tests/bdd/godog_test.go index 56da25ad9..85c7b9e35 100644 --- a/tests/bdd/godog_test.go +++ b/tests/bdd/godog_test.go @@ -305,9 +305,9 @@ func TestSingleClusterUpFeatureFileWiresToSteps(t *testing.T) { // TestSingleClusterUpOneClickFeatureFileWiresToSteps runs the // self-hosted up one-click feature against a fake CommandRunner. The -// helm-list canned outputs carry --kube-context k3d-ncp-local so the -// control-plane and nvca-operator json-rows assertions have something to -// parse; the conflict-precheck k3d-get returns exit 1. +// helm-list canned output carries --kube-context k3d-ncp-local so the +// control-plane and nvca-operator assertions have something to parse; +// the conflict-precheck k3d-get returns exit 1. func TestSingleClusterUpOneClickFeatureFileWiresToSteps(t *testing.T) { t.Setenv("NVCF_CLI", "/usr/bin/nvcf-cli") t.Setenv("NGC_API_KEY", "test-key") @@ -315,7 +315,6 @@ func TestSingleClusterUpOneClickFeatureFileWiresToSteps(t *testing.T) { t.Setenv("SAMPLE_NGC_TEAM", "test-team") suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ "helm list --all-namespaces --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, - "helm list -n nvca-operator --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, // Conflict precheck: feature asserts the multi-cluster // control-plane is absent. "k3d cluster get ncp-local-cp": {ExitCode: 1}, @@ -389,8 +388,7 @@ func TestMultiClusterUpFeatureFileWiresToSteps(t *testing.T) { // single-cluster-helmfile.feature against a fake runner. The fixture // the feature copies from is seeded into the wiring suite's RepoRoot // so the I copy / I update yaml chain has a real source file. The -// fake runner is pre-loaded with canned JSON for the `helm list` step -// so the json-rows assertion has something to parse. +// fake runner is pre-loaded with canned JSON for the Helm release assertion. func TestSingleClusterHelmfileFeatureFileWiresToSteps(t *testing.T) { t.Setenv("NGC_API_KEY", "test-key") t.Setenv("SAMPLE_NGC_ORG", "test-org") @@ -398,8 +396,7 @@ func TestSingleClusterHelmfileFeatureFileWiresToSteps(t *testing.T) { t.Setenv("NVCF_CLI", "/usr/bin/nvcf-cli") t.Setenv("REPO_ROOT", "/repo-root-placeholder") suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ - "helm list --all-namespaces -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, - "helm list -n nvca-operator -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, + "helm list --all-namespaces --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, "/usr/bin/nvcf-cli --config /repo-root-placeholder/tests/bdd/fixtures/nvcf-cli-local.yaml function invoke --request-body '{\"message\":\"bdd-echo\",\"repeats\":1}' --timeout 120 --poll-duration 5": { ExitCode: 0, Stdout: "Function invocation completed!\n\nResponse:\n{\"rawResponse\":\"bdd-echo\"}\n", @@ -835,7 +832,7 @@ func TestMultiClusterHelmfileFeatureFileWiresToSteps(t *testing.T) { t.Setenv("REPO_ROOT", "/repo-root-placeholder") suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ "helm list --all-namespaces --kube-context k3d-ncp-local-cp -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, - "helm list -n nvca-operator --kube-context k3d-ncp-local-compute-1 -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, + "helm list --all-namespaces --kube-context k3d-ncp-local-compute-1 -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, "/usr/bin/nvcf-cli --config /repo-root-placeholder/tests/bdd/fixtures/nvcf-cli-local.yaml function invoke --request-body '{\"message\":\"bdd-echo\",\"repeats\":1}' --timeout 120 --poll-duration 5": { ExitCode: 0, Stdout: "Function invocation completed!\n\nResponse:\n{\"rawResponse\":\"bdd-echo\"}\n", @@ -929,7 +926,7 @@ func TestSingleClusterHelmfileUpstreamImagesFeatureFileWiresToSteps(t *testing.T upstreamReloader := "docker.io/natsio/nats-server-config-reloader:0.23.0" suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ "k3d cluster get ncp-local-cp": {ExitCode: 1}, - "helm list --all-namespaces -o json": { + "helm list --all-namespaces --kube-context k3d-ncp-local -o json": { ExitCode: 0, Stdout: helmListAllNamespacesJSON(), }, @@ -1070,7 +1067,8 @@ func helmListAllNamespacesJSON() string { {"name":"nats-auth-callout-service","namespace":"nats-system","status":"deployed"}, {"name":"ingress","namespace":"envoy-gateway-system","status":"deployed"}, {"name":"llm-request-router","namespace":"nvcf","status":"deployed"}, -{"name":"llm-api-gateway","namespace":"nvcf","status":"deployed"} +{"name":"llm-api-gateway","namespace":"nvcf","status":"deployed"}, +{"name":"nvca-operator","namespace":"nvca-operator","status":"deployed"} ]` } @@ -1349,8 +1347,6 @@ func TestSingleClusterEKSHelmfileFeatureFileWiresToSteps(t *testing.T) { "helm list --all-namespaces --kube-context " + eksContext + " -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, // @control-plane: httproute jsonpath assertion expects api.. "kubectl --context " + eksContext + " get httproute nvcf-api -n envoy-gateway -o jsonpath={.spec.hostnames[0]}": {ExitCode: 0, Stdout: "api." + wiringGatewayLB}, - // @nvca-registration: helm list confirms nvca-operator deployed. - "helm list -n nvca-operator --kube-context " + eksContext + " -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, })) seedStackBaseYaml(t, suite.Config.RepoRoot) seedComputePlaneBaseYaml(t, suite.Config.RepoRoot) @@ -1447,7 +1443,7 @@ func TestMultiClusterEKSHelmfileFeatureFileWiresToSteps(t *testing.T) { NVCT_GLOBAL_FQDN_GRPC: http://worker-tasks.` + wiringGatewayDomain + ` `}, // compute nvca-operator helm list assertion. - "helm list -n nvca-operator --kube-context " + computeContext + " -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, + "helm list --all-namespaces --kube-context " + computeContext + " -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, // @function-lifecycle: function invoke returns the echo payload. "/usr/bin/nvcf-cli --config /repo-root-placeholder/tests/bdd/out/nvcf-cli-eks-bdd-multi.yaml function invoke --request-body '{\"message\":\"bdd-echo\",\"repeats\":1}' --timeout 120 --poll-duration 5": { ExitCode: 0, diff --git a/tests/bdd/steps/assertion_steps.go b/tests/bdd/steps/assertion_steps.go index 1a3ed8fd2..daba7c55d 100644 --- a/tests/bdd/steps/assertion_steps.go +++ b/tests/bdd/steps/assertion_steps.go @@ -45,6 +45,7 @@ func registerAssertionSteps(ctx *godog.ScenarioContext, sc *ScenarioContext) { ctx.Step(`^the rendered manifests in "([^"]*)" should contain:$`, sc.renderedManifestsShouldContain) ctx.Step(`^the rendered manifests in "([^"]*)" under directories matching "([^"]*)" should contain:$`, sc.renderedManifestsUnderMatchingDirectoriesShouldContain) ctx.Step(`^the rendered manifests in "([^"]*)" should not contain:$`, sc.renderedManifestsShouldNotContain) + ctx.Step(`^these Helm releases should be deployed using context "([^"]*)":$`, sc.helmReleasesShouldBeDeployed) ctx.Step(`^these ServiceMonitors should exist in namespace "([^"]*)" using context "([^"]*)":$`, sc.serviceMonitorsShouldExist) } @@ -196,6 +197,54 @@ func tableToSingleColumn(table *godog.Table, header string) ([]string, error) { return values, nil } +func (sc *ScenarioContext) helmReleasesShouldBeDeployed(ctx context.Context, kubeContext string, table *godog.Table) error { + expected, err := tableToHelmReleaseExpectations(table) + if err != nil { + return err + } + command, err := dsl.HelmListCommand(kubeContext) + if err != nil { + return err + } + if err := sc.runAndRecord(ctx, command); err != nil { + return err + } + if err := sc.commandExitCodeShouldBe(0); err != nil { + return err + } + return dsl.HelmReleasesDeployed(sc.LastResult.Stdout, expected) +} + +func tableToHelmReleaseExpectations(table *godog.Table) ([]dsl.HelmReleaseExpectation, error) { + if table == nil || len(table.Rows) < 2 { + return nil, fmt.Errorf("table must have name and namespace headers and at least one data row") + } + headers := table.Rows[0].Cells + withRevision := len(headers) == 3 + if len(headers) != 2 && !withRevision { + return nil, fmt.Errorf("table headers must be name, namespace, and optional revision") + } + if strings.TrimSpace(headers[0].Value) != "name" || strings.TrimSpace(headers[1].Value) != "namespace" || withRevision && strings.TrimSpace(headers[2].Value) != "revision" { + return nil, fmt.Errorf("table headers must be name, namespace, and optional revision") + } + + expected := make([]dsl.HelmReleaseExpectation, 0, len(table.Rows)-1) + for index, row := range table.Rows[1:] { + if len(row.Cells) != len(headers) { + return nil, fmt.Errorf("row %d has %d cells, expected %d", index+1, len(row.Cells), len(headers)) + } + release := dsl.HelmReleaseExpectation{ + Name: row.Cells[0].Value, + Namespace: row.Cells[1].Value, + } + if withRevision { + release.Revision = row.Cells[2].Value + } + expected = append(expected, release) + } + return expected, nil +} + func (sc *ScenarioContext) serviceMonitorsShouldExist(ctx context.Context, namespace, kubeContext string, table *godog.Table) error { names, err := tableToSingleColumn(table, "name") if err != nil { diff --git a/tests/bdd/steps/steps_test.go b/tests/bdd/steps/steps_test.go index e77604512..608fa195a 100644 --- a/tests/bdd/steps/steps_test.go +++ b/tests/bdd/steps/steps_test.go @@ -567,6 +567,41 @@ func TestServiceMonitorsShouldExistRunsSingleExplicitGet(t *testing.T) { } } +func TestHelmReleasesShouldBeDeployedRunsSingleExplicitList(t *testing.T) { + sc, fake := newScenarioContext(t) + fake.result = harness.Result{ExitCode: 0, Stdout: `[{"name":"nats","namespace":"nats-system","revision":"1","status":"deployed"}]`} + table := docTable(t, [][]string{ + {"name", "namespace", "revision"}, + {"nats", "nats-system", "1"}, + }) + + if err := sc.helmReleasesShouldBeDeployed(context.Background(), "k3d-ncp-local", table); err != nil { + t.Fatalf("assert Helm releases: %v", err) + } + if len(fake.runs) != 1 { + t.Fatalf("runs = %d, want 1", len(fake.runs)) + } + want := "helm list --all-namespaces --kube-context k3d-ncp-local -o json" + if fake.runs[0].command != want { + t.Fatalf("command = %q, want %q", fake.runs[0].command, want) + } +} + +func TestHelmReleaseTableAcceptsNameAndNamespace(t *testing.T) { + table := docTable(t, [][]string{ + {"name", "namespace"}, + {"nats", "nats-system"}, + }) + + got, err := tableToHelmReleaseExpectations(table) + if err != nil { + t.Fatalf("parse table: %v", err) + } + if len(got) != 1 || got[0].Name != "nats" || got[0].Namespace != "nats-system" || got[0].Revision != "" { + t.Fatalf("expectations = %#v", got) + } +} + func TestRegisterAllRunsAFeatureFile(t *testing.T) { // End-to-end smoke check that RegisterAll wires every category. A // minimal in-memory feature is driven through a Godog TestSuite so From 4298b6ee6681ace6c3e6bf97c03f036bbd5c888f Mon Sep 17 00:00:00 2001 From: Stephanie Baum Date: Thu, 20 Aug 2026 10:26:01 -0700 Subject: [PATCH 2/2] test(bdd): verify local resource sizing gates Signed-off-by: Stephanie Baum --- tests/bdd/fixtures_test.go | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/bdd/fixtures_test.go b/tests/bdd/fixtures_test.go index 3a5dd51ca..56b9c31ed 100644 --- a/tests/bdd/fixtures_test.go +++ b/tests/bdd/fixtures_test.go @@ -24,6 +24,7 @@ import ( "os/exec" "path/filepath" "regexp" + "slices" "strings" "testing" @@ -149,6 +150,45 @@ func TestNVCFCLILocalFixtureTargetsLocalGRPCGateway(t *testing.T) { } } +func TestComputePlaneLocalBDDFixturesDisableResourceSizingFeatureGates(t *testing.T) { + want := []string{ + "-InfraResourceOverhead", + "-EnforceHelmFunctionResourceLimits", + "-EnforceContainerFunctionResourceLimits", + "-EnforceHelmTaskResourceLimits", + "-EnforceContainerTaskResourceLimits", + } + + for _, fixturePath := range []string{ + "fixtures/nvcf-compute-plane-local-bdd.yaml", + "fixtures/nvcf-compute-plane-local-bdd-multi.yaml", + } { + t.Run(filepath.Base(fixturePath), func(t *testing.T) { + fixtureBytes, err := os.ReadFile(fixturePath) + if err != nil { + t.Fatalf("read compute-plane fixture %s: %v", fixturePath, err) + } + var fixture struct { + Global struct { + NVCAOperator struct { + SelfManaged struct { + FeatureGateValues []string `yaml:"featureGateValues"` + } `yaml:"selfManaged"` + } `yaml:"nvcaOperator"` + } `yaml:"global"` + } + if err := yaml.Unmarshal(fixtureBytes, &fixture); err != nil { + t.Fatalf("parse compute-plane fixture %s: %v", fixturePath, err) + } + + got := fixture.Global.NVCAOperator.SelfManaged.FeatureGateValues + if !slices.Equal(got, want) { + t.Fatalf("featureGateValues = %q, want %q", got, want) + } + }) + } +} + func TestSelfManagedLocalBDDMultiFixtureWiresGRPCWorkerCallback(t *testing.T) { fixtureBytes, err := os.ReadFile("fixtures/self-managed-local-bdd-multi.yaml") if err != nil {