From 10df9393c348d5617e7beabe25ef72a6dd35fbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Mon, 27 Jul 2026 13:56:10 +0200 Subject: [PATCH 1/2] e2e: add test labels and profiles for selective execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add category/speed/scenario labels to all e2e tests for selective filtering. Introduce profile system with priority-based registration for downstream overrides. Labels: category (lifecycle, configuration, resilience, networking, observability), speed (fast, moderate, slow), scenario (crud, spec-update, drift, ownership, storage, port, security, metadata, failure, recovery). Profiles: smoke (lifecycle+configuration/fast), extended (+resilience/+moderate). Speed labels validated against CI execution times from PR #271. Assisted-by: 🤖 claude-opus-4-6@default --- test/e2e/configuration_test.go | 68 +++++++++------ test/e2e/failure_scenarios_test.go | 43 ++++++---- .../e2e/framework/labels/category/category.go | 27 ++++++ .../e2e/framework/labels/scenario/scenario.go | 37 +++++++++ test/e2e/framework/labels/speed/speed.go | 25 ++++++ test/e2e/framework/profiles.go | 83 +++++++++++++++++++ test/e2e/framework/profiles_defaults.go | 39 +++++++++ test/e2e/lifecycle_test.go | 13 ++- test/e2e/main_test.go | 5 ++ test/e2e/manager_test.go | 8 +- test/e2e/mcp_handshake_test.go | 6 +- test/e2e/networkpolicy_test.go | 14 ++-- test/e2e/reconciliation_test.go | 53 +++++++----- 13 files changed, 345 insertions(+), 76 deletions(-) create mode 100644 test/e2e/framework/labels/category/category.go create mode 100644 test/e2e/framework/labels/scenario/scenario.go create mode 100644 test/e2e/framework/labels/speed/speed.go create mode 100644 test/e2e/framework/profiles.go create mode 100644 test/e2e/framework/profiles_defaults.go diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index d4fc8c91..8add7ac0 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -33,14 +33,18 @@ import ( mcpv1alpha1 "github.com/kubernetes-sigs/mcp-lifecycle-operator/api/v1alpha1" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/scenario" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) // --- Storage Tests --- func TestStorageConfigMap(t *testing.T) { feature := features.New("MCPServer with ConfigMap storage"). - WithLabel("type", "configuration"). - WithLabel("config", "storage-configmap"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Storage). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -110,8 +114,9 @@ func TestStorageConfigMap(t *testing.T) { func TestStorageSecret(t *testing.T) { feature := features.New("MCPServer with Secret storage"). - WithLabel("type", "configuration"). - WithLabel("config", "storage-secret"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Storage). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -181,8 +186,9 @@ func TestStorageSecret(t *testing.T) { func TestStorageEmptyDir(t *testing.T) { feature := features.New("MCPServer with EmptyDir storage"). - WithLabel("type", "configuration"). - WithLabel("config", "storage-emptydir"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Storage). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { sizeLimit := resource.MustParse("100Mi") return f.SetupMCPServer(ctx, t, cfg, "storage-empty", true, @@ -242,8 +248,9 @@ func TestStorageEmptyDir(t *testing.T) { func TestStorageRecursiveReadOnly(t *testing.T) { feature := features.New("MCPServer with RecursiveReadOnly storage"). - WithLabel("type", "configuration"). - WithLabel("config", "storage-recursive-readonly"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Storage). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -308,8 +315,9 @@ func TestStorageRecursiveReadOnly(t *testing.T) { func TestStorageMultipleMounts(t *testing.T) { feature := features.New("MCPServer with multiple storage mounts"). - WithLabel("type", "configuration"). - WithLabel("config", "storage-multi"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Storage). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -410,8 +418,9 @@ func TestStorageMultipleMounts(t *testing.T) { func TestCustomPort(t *testing.T) { feature := features.New("MCPServer with custom non-default port"). - WithLabel("type", "configuration"). - WithLabel("config", "port-custom"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Port). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { // Use port 9090 at creation time. The test image only listens on 3001, // so the pod won't pass readiness, but we verify port propagation. @@ -480,8 +489,9 @@ func TestCustomPort(t *testing.T) { func TestSamePortDifferentNamespaces(t *testing.T) { feature := features.New("MCPServers with same port in different namespaces"). - WithLabel("type", "configuration"). - WithLabel("config", "port-namespaces"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Slow). + WithLabel(scenario.Label, scenario.Port). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { // Server A in the default test namespace ctx = f.SetupMCPServer(ctx, t, cfg, "server-a", true) @@ -562,8 +572,9 @@ func TestSamePortDifferentNamespaces(t *testing.T) { func TestDefaultSecurityContext(t *testing.T) { feature := features.New("MCPServer with default security context"). - WithLabel("type", "configuration"). - WithLabel("config", "security-default"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Security). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "sec-default", true) }). @@ -610,8 +621,9 @@ func TestDefaultSecurityContext(t *testing.T) { func TestCustomSecurityContext(t *testing.T) { feature := features.New("MCPServer with custom security context"). - WithLabel("type", "configuration"). - WithLabel("config", "security-custom"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Security). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "sec-custom", true, f.WithSecurityContext(&corev1.SecurityContext{ @@ -666,8 +678,9 @@ func TestCustomSecurityContext(t *testing.T) { func TestPodSecurityContext(t *testing.T) { feature := features.New("MCPServer with pod security context"). - WithLabel("type", "configuration"). - WithLabel("config", "security-pod"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Security). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "sec-pod", true, f.WithPodSecurityContext(&corev1.PodSecurityContext{ @@ -708,8 +721,9 @@ func TestPodSecurityContext(t *testing.T) { func TestCustomLabelsAndAnnotations(t *testing.T) { feature := features.New("MCPServer with custom labels and annotations"). - WithLabel("type", "configuration"). - WithLabel("config", "metadata-custom"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Metadata). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "meta-custom", true, f.WithExtraLabels(map[string]string{"team": "platform", "env": "test"}), @@ -782,8 +796,9 @@ func TestCustomLabelsAndAnnotations(t *testing.T) { func TestReservedLabelFiltering(t *testing.T) { feature := features.New("MCPServer reserved label filtering"). - WithLabel("type", "configuration"). - WithLabel("config", "metadata-reserved"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Metadata). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "meta-reserved", true, f.WithExtraLabels(map[string]string{ @@ -825,8 +840,9 @@ func TestReservedLabelFiltering(t *testing.T) { func TestCustomMetadataUpdate(t *testing.T) { feature := features.New("MCPServer custom metadata update"). - WithLabel("type", "configuration"). - WithLabel("config", "metadata-update"). + WithLabel(category.Label, category.Configuration). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Metadata). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "meta-update", true, f.WithExtraLabels(map[string]string{"team": "alpha"}), diff --git a/test/e2e/failure_scenarios_test.go b/test/e2e/failure_scenarios_test.go index a82fb529..4cc1aedb 100644 --- a/test/e2e/failure_scenarios_test.go +++ b/test/e2e/failure_scenarios_test.go @@ -32,12 +32,16 @@ import ( mcpv1alpha1 "github.com/kubernetes-sigs/mcp-lifecycle-operator/api/v1alpha1" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/scenario" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) func TestImagePullFailure(t *testing.T) { feature := features.New("MCPServer image pull failure"). - WithLabel("type", "failure"). - WithLabel("failure", "image-pull"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Slow). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "img-pull-fail", false, f.WithImage("invalid.example.com/nonexistent/image:v0.0.1"), @@ -92,8 +96,9 @@ func TestImagePullFailure(t *testing.T) { func TestContainerCrashLoop(t *testing.T) { feature := features.New("MCPServer container crash loop"). - WithLabel("type", "failure"). - WithLabel("failure", "crash-loop"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "crash-loop", false, f.WithImage("docker.io/library/busybox:1.37"), @@ -132,8 +137,9 @@ func TestContainerCrashLoop(t *testing.T) { func TestMCPHandshakeFailure(t *testing.T) { feature := features.New("MCPServer handshake failure"). - WithLabel("type", "failure"). - WithLabel("failure", "mcp-handshake"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "handshake-fail", false, f.WithPath("/not-mcp"), @@ -184,8 +190,9 @@ func TestMCPHandshakeFailure(t *testing.T) { func TestMissingConfigMapReference(t *testing.T) { feature := features.New("MCPServer missing ConfigMap reference"). - WithLabel("type", "failure"). - WithLabel("failure", "missing-configmap"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "missing-cm", false, f.WithEnvFrom(corev1.EnvFromSource{ @@ -238,8 +245,9 @@ func TestMissingConfigMapReference(t *testing.T) { func TestMissingSecretReference(t *testing.T) { feature := features.New("MCPServer missing Secret reference"). - WithLabel("type", "failure"). - WithLabel("failure", "missing-secret"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "missing-secret", false, f.WithEnvFrom(corev1.EnvFromSource{ @@ -292,8 +300,9 @@ func TestMissingSecretReference(t *testing.T) { func TestMissingStorageConfigMapReference(t *testing.T) { feature := features.New("MCPServer missing storage ConfigMap reference"). - WithLabel("type", "failure"). - WithLabel("failure", "missing-storage-configmap"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Failure). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "missing-storage", false, f.WithStorage(mcpv1alpha1.StorageMount{ @@ -339,8 +348,9 @@ func TestMissingStorageConfigMapReference(t *testing.T) { func TestRecoveryFromMissingConfigMap(t *testing.T) { feature := features.New("MCPServer recovery from missing ConfigMap"). - WithLabel("type", "recovery"). - WithLabel("failure", "missing-configmap"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Recovery). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "recovery-cm", false, f.WithEnvFrom(corev1.EnvFromSource{ @@ -402,8 +412,9 @@ func TestRecoveryFromMissingConfigMap(t *testing.T) { func TestRecoveryFromImagePullFailure(t *testing.T) { feature := features.New("MCPServer recovery from image pull failure"). - WithLabel("type", "recovery"). - WithLabel("failure", "image-pull"). + WithLabel(category.Label, category.Resilience). + WithLabel(speed.Label, speed.Slow). + WithLabel(scenario.Label, scenario.Recovery). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "recovery-img", false, f.WithImage("invalid.example.com/nonexistent/image:v0.0.1"), diff --git a/test/e2e/framework/labels/category/category.go b/test/e2e/framework/labels/category/category.go new file mode 100644 index 00000000..b9ace6de --- /dev/null +++ b/test/e2e/framework/labels/category/category.go @@ -0,0 +1,27 @@ +/* +Copyright 2026 The Kubernetes Authors + +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 category + +const ( + Label = "category" + + Lifecycle = "lifecycle" + Configuration = "configuration" + Resilience = "resilience" + Networking = "networking" + Observability = "observability" +) diff --git a/test/e2e/framework/labels/scenario/scenario.go b/test/e2e/framework/labels/scenario/scenario.go new file mode 100644 index 00000000..85ce9926 --- /dev/null +++ b/test/e2e/framework/labels/scenario/scenario.go @@ -0,0 +1,37 @@ +/* +Copyright 2026 The Kubernetes Authors + +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 scenario + +const ( + Label = "scenario" + + // Lifecycle scenarios. + CRUD = "crud" + SpecUpdate = "spec-update" + Drift = "drift" + Ownership = "ownership" + + // Configuration scenarios. + Storage = "storage" + Port = "port" + Security = "security" + Metadata = "metadata" + + // Resilience scenarios. + Failure = "failure" + Recovery = "recovery" +) diff --git a/test/e2e/framework/labels/speed/speed.go b/test/e2e/framework/labels/speed/speed.go new file mode 100644 index 00000000..f31ccf46 --- /dev/null +++ b/test/e2e/framework/labels/speed/speed.go @@ -0,0 +1,25 @@ +/* +Copyright 2026 The Kubernetes Authors + +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 speed + +const ( + Label = "speed" + + Fast = "fast" + Moderate = "moderate" + Slow = "slow" +) diff --git a/test/e2e/framework/profiles.go b/test/e2e/framework/profiles.go new file mode 100644 index 00000000..8555c784 --- /dev/null +++ b/test/e2e/framework/profiles.go @@ -0,0 +1,83 @@ +/* +Copyright 2026 The Kubernetes Authors + +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 framework + +import ( + "flag" + "fmt" + "os" + "sort" +) + +// Profile is a named label selector for filtering e2e tests. +// Higher Priority wins when multiple registrations use the same Name. +// Upstream defaults use priority 0; downstream overrides should use > 0. +type Profile struct { + Name string + Priority int + Labels map[string][]string +} + +var ( + profiles = map[string]Profile{} + profileFlag string +) + +// RegisterProfile adds a profile to the global registry. +// If a profile with the same name already exists, the higher priority wins. +// Equal priority overwrites (last-write-wins). +func RegisterProfile(p Profile) { + existing, ok := profiles[p.Name] + if !ok || p.Priority >= existing.Priority { + profiles[p.Name] = p + } +} + +// LookupProfile returns a profile by name. +func LookupProfile(name string) (Profile, bool) { + p, ok := profiles[name] + return p, ok +} + +// ProfileNames returns sorted profile names. +func ProfileNames() []string { + names := make([]string, 0, len(profiles)) + for n := range profiles { + names = append(names, n) + } + sort.Strings(names) + return names +} + +// RegisterProfileFlag registers the -profile flag. Call before envconf.NewFromFlags(). +func RegisterProfileFlag() { + flag.StringVar(&profileFlag, "profile", "", "Test profile (smoke, extended, or distribution-specific)") +} + +// ResolveProfile returns the labels for the selected profile, or nil if +// no profile was selected. Exits with an error if the profile is unknown. +func ResolveProfile() map[string][]string { + if profileFlag == "" { + return nil + } + p, ok := LookupProfile(profileFlag) + if !ok { + fmt.Fprintf(os.Stderr, "unknown profile %q, available: %v\n", profileFlag, ProfileNames()) + os.Exit(1) + } + return p.Labels +} diff --git a/test/e2e/framework/profiles_defaults.go b/test/e2e/framework/profiles_defaults.go new file mode 100644 index 00000000..016e7b6d --- /dev/null +++ b/test/e2e/framework/profiles_defaults.go @@ -0,0 +1,39 @@ +/* +Copyright 2026 The Kubernetes Authors + +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 framework + +import ( + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" +) + +func init() { + RegisterProfile(Profile{ + Name: "smoke", + Labels: map[string][]string{ + category.Label: {category.Lifecycle, category.Configuration}, + speed.Label: {speed.Fast}, + }, + }) + RegisterProfile(Profile{ + Name: "extended", + Labels: map[string][]string{ + category.Label: {category.Lifecycle, category.Configuration, category.Resilience}, + speed.Label: {speed.Fast, speed.Moderate}, + }, + }) +} diff --git a/test/e2e/lifecycle_test.go b/test/e2e/lifecycle_test.go index 55594bc1..d91d847f 100644 --- a/test/e2e/lifecycle_test.go +++ b/test/e2e/lifecycle_test.go @@ -31,12 +31,16 @@ import ( mcpv1alpha1 "github.com/kubernetes-sigs/mcp-lifecycle-operator/api/v1alpha1" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/scenario" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) func TestMCPServerHappyPath(t *testing.T) { feature := features.New("MCPServer happy path"). - WithLabel("type", "lifecycle"). - WithLabel("component", "mcpserver"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.CRUD). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "test-server", false) }). @@ -110,8 +114,9 @@ func TestMCPServerHappyPath(t *testing.T) { func TestMCPServerUpdatePort(t *testing.T) { feature := features.New("MCPServer port update"). - WithLabel("type", "update"). - WithLabel("component", "mcpserver"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.CRUD). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "test-server", true) }). diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go index 875de6dc..ed523dc5 100644 --- a/test/e2e/main_test.go +++ b/test/e2e/main_test.go @@ -38,11 +38,16 @@ import ( var testenv env.Environment func TestMain(m *testing.M) { + f.RegisterProfileFlag() cfg, err := envconf.NewFromFlags() if err != nil { panic(err) } + if labels := f.ResolveProfile(); labels != nil { + cfg.WithLabels(labels) + } + testenv = env.NewWithConfig(cfg) // Register MCPServer types so the client can work with them. diff --git a/test/e2e/manager_test.go b/test/e2e/manager_test.go index ffaf0174..68f909c3 100644 --- a/test/e2e/manager_test.go +++ b/test/e2e/manager_test.go @@ -39,6 +39,8 @@ import ( "sigs.k8s.io/e2e-framework/pkg/features" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) const ( @@ -50,7 +52,8 @@ const ( func TestManagerPodRunning(t *testing.T) { feature := features.New("Manager pod is running"). - WithLabel("type", "manager"). + WithLabel(category.Label, category.Observability). + WithLabel(speed.Label, speed.Fast). Assess("controller-manager pod is Running", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { pod := f.FindPodByLabel(ctx, t, cfg, operatorNamespace, "control-plane=controller-manager") t.Logf("controller-manager pod %s is Running", pod.Name) @@ -63,7 +66,8 @@ func TestManagerPodRunning(t *testing.T) { func TestMetricsEndpoint(t *testing.T) { feature := features.New("Metrics endpoint serves data"). - WithLabel("type", "manager"). + WithLabel(category.Label, category.Observability). + WithLabel(speed.Label, speed.Fast). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { r := cfg.Client().Resources() diff --git a/test/e2e/mcp_handshake_test.go b/test/e2e/mcp_handshake_test.go index 75807281..b3abf068 100644 --- a/test/e2e/mcp_handshake_test.go +++ b/test/e2e/mcp_handshake_test.go @@ -32,14 +32,16 @@ import ( "sigs.k8s.io/e2e-framework/pkg/features" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) func TestMCPHandshake(t *testing.T) { const mcpServerPort = 3001 feature := features.New("MCP handshake with everything-mcp-server"). - WithLabel("type", "mcp"). - WithLabel("component", "mcpserver"). + WithLabel(category.Label, category.Networking). + WithLabel(speed.Label, speed.Moderate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "everything-mcp-server", true) }). diff --git a/test/e2e/networkpolicy_test.go b/test/e2e/networkpolicy_test.go index 4758d747..538375d0 100644 --- a/test/e2e/networkpolicy_test.go +++ b/test/e2e/networkpolicy_test.go @@ -35,12 +35,14 @@ import ( mcpv1alpha1 "github.com/kubernetes-sigs/mcp-lifecycle-operator/api/v1alpha1" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) func TestNetworkPolicyCreated(t *testing.T) { feature := features.New("MCPServer creates NetworkPolicy"). - WithLabel("type", "networkpolicy"). - WithLabel("component", "mcpserver"). + WithLabel(category.Label, category.Networking). + WithLabel(speed.Label, speed.Fast). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "netpol-test", true) }). @@ -106,8 +108,8 @@ func TestNetworkPolicyCreated(t *testing.T) { func TestNetworkPolicyPortUpdate(t *testing.T) { feature := features.New("MCPServer NetworkPolicy port update"). - WithLabel("type", "networkpolicy"). - WithLabel("scenario", "port-update"). + WithLabel(category.Label, category.Networking). + WithLabel(speed.Label, speed.Slow). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "netpol-port", true) }). @@ -158,8 +160,8 @@ func TestNetworkPolicyPortUpdate(t *testing.T) { func TestNetworkPolicyGarbageCollected(t *testing.T) { feature := features.New("MCPServer NetworkPolicy garbage collection"). - WithLabel("type", "networkpolicy"). - WithLabel("scenario", "gc"). + WithLabel(category.Label, category.Networking). + WithLabel(speed.Label, speed.Moderate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "netpol-gc", true) }). diff --git a/test/e2e/reconciliation_test.go b/test/e2e/reconciliation_test.go index 001451f2..293d063c 100644 --- a/test/e2e/reconciliation_test.go +++ b/test/e2e/reconciliation_test.go @@ -37,6 +37,9 @@ import ( mcpv1alpha1 "github.com/kubernetes-sigs/mcp-lifecycle-operator/api/v1alpha1" f "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/category" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/scenario" + "github.com/kubernetes-sigs/mcp-lifecycle-operator/test/e2e/framework/labels/speed" ) const configHashAnnotation = "mcp.x-k8s.io/config-hash" @@ -47,8 +50,9 @@ func TestImageUpdate(t *testing.T) { digestRef := "quay.io/matzew/mcp-everything@sha256:537cdedad807bb56140caca9c332d3577b16e533584164bbc3f27abac7b5ba15" feature := features.New("MCPServer image update"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "image-update"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Slow). + WithLabel(scenario.Label, scenario.SpecUpdate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "img-update", true) }). @@ -95,8 +99,9 @@ func TestImageUpdate(t *testing.T) { func TestStorageAddition(t *testing.T) { feature := features.New("MCPServer storage addition"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "storage-add"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.SpecUpdate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -182,8 +187,9 @@ func TestStorageAddition(t *testing.T) { func TestStorageRemoval(t *testing.T) { feature := features.New("MCPServer storage removal"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "storage-remove"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.SpecUpdate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() @@ -260,8 +266,9 @@ func TestStorageRemoval(t *testing.T) { func TestReplicaDrift(t *testing.T) { feature := features.New("MCPServer replica drift correction"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "drift-replicas"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Drift). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "drift-repl", true, f.WithReplicas(1)) }). @@ -305,8 +312,9 @@ func TestReplicaDrift(t *testing.T) { func TestServicePortDrift(t *testing.T) { feature := features.New("MCPServer Service port drift correction"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "drift-service-port"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Drift). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "drift-port", true) }). @@ -350,8 +358,9 @@ func TestServicePortDrift(t *testing.T) { func TestDeploymentDeletion(t *testing.T) { feature := features.New("MCPServer Deployment recreation after deletion"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "drift-deployment-deleted"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Slow). + WithLabel(scenario.Label, scenario.Drift). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "drift-dep", true) }). @@ -400,8 +409,9 @@ func TestDeploymentDeletion(t *testing.T) { func TestServiceDeletion(t *testing.T) { feature := features.New("MCPServer Service recreation after deletion"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "drift-service-deleted"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Drift). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "drift-svc", true) }). @@ -451,8 +461,9 @@ func TestServiceDeletion(t *testing.T) { func TestOwnerReferences(t *testing.T) { feature := features.New("MCPServer OwnerReferences on child resources"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "ownership"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Fast). + WithLabel(scenario.Label, scenario.Ownership). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "owner-ref", true) }). @@ -502,8 +513,9 @@ func assertOwnerReference(t *testing.T, refs []metav1.OwnerReference, expectedNa func TestCascadingDeletion(t *testing.T) { feature := features.New("MCPServer cascading deletion"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "cascading-delete"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Ownership). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "cascade-del", true) }). @@ -551,8 +563,9 @@ func TestCascadingDeletion(t *testing.T) { func TestConfigMapDataUpdateTriggersRestart(t *testing.T) { feature := features.New("MCPServer config hash update on ConfigMap change"). - WithLabel("type", "reconciliation"). - WithLabel("scenario", "config-hash"). + WithLabel(category.Label, category.Lifecycle). + WithLabel(speed.Label, speed.Moderate). + WithLabel(scenario.Label, scenario.Drift). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { ns := ctx.Value(f.NsKey).(string) r := cfg.Client().Resources() From 0b96770d145e412399ce62d8c90e7e985b52a41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Mon, 27 Jul 2026 17:21:44 +0200 Subject: [PATCH 2/2] e2e: rename scenario CRUD to Deploy, fix UpdatePort label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename scenario.CRUD to scenario.Deploy for clarity. CRUD is too broad. Deploy means create+verify. Fix TestMCPServerUpdatePort to use scenario.SpecUpdate instead of scenario.Deploy since it modifies Spec.Config.Port. Add package doc to scenario explaining it is an optional secondary label for slicing larger categories. Assisted-by: 🤖 claude-opus-4-6@default --- test/e2e/framework/labels/scenario/scenario.go | 6 +++++- test/e2e/lifecycle_test.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/e2e/framework/labels/scenario/scenario.go b/test/e2e/framework/labels/scenario/scenario.go index 85ce9926..543c11a2 100644 --- a/test/e2e/framework/labels/scenario/scenario.go +++ b/test/e2e/framework/labels/scenario/scenario.go @@ -14,13 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package scenario provides the optional third label dimension for e2e tests. +// Scenarios slice larger categories (lifecycle, configuration, resilience) into +// finer groups. Categories with few tests (networking, observability) intentionally +// omit scenario labels. package scenario const ( Label = "scenario" // Lifecycle scenarios. - CRUD = "crud" + Deploy = "deploy" SpecUpdate = "spec-update" Drift = "drift" Ownership = "ownership" diff --git a/test/e2e/lifecycle_test.go b/test/e2e/lifecycle_test.go index d91d847f..a697251c 100644 --- a/test/e2e/lifecycle_test.go +++ b/test/e2e/lifecycle_test.go @@ -40,7 +40,7 @@ func TestMCPServerHappyPath(t *testing.T) { feature := features.New("MCPServer happy path"). WithLabel(category.Label, category.Lifecycle). WithLabel(speed.Label, speed.Fast). - WithLabel(scenario.Label, scenario.CRUD). + WithLabel(scenario.Label, scenario.Deploy). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "test-server", false) }). @@ -116,7 +116,7 @@ func TestMCPServerUpdatePort(t *testing.T) { feature := features.New("MCPServer port update"). WithLabel(category.Label, category.Lifecycle). WithLabel(speed.Label, speed.Fast). - WithLabel(scenario.Label, scenario.CRUD). + WithLabel(scenario.Label, scenario.SpecUpdate). Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { return f.SetupMCPServer(ctx, t, cfg, "test-server", true) }).