diff --git a/.golangci.yml b/.golangci.yml index 0989c6f07fa7..926187d5e48b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,6 +32,7 @@ linters: - hcpstatuspatch - testcasename - testfuncname + - testfuncstructure - sippyannotation - guestcluster - contextbackground diff --git a/Makefile b/Makefile index af3905b76d35..17caf9dcf562 100644 --- a/Makefile +++ b/Makefile @@ -119,11 +119,11 @@ UPSTREAM_REMOTE ?= $(shell git remote -v 2>/dev/null | grep 'openshift/hypershif PULL_BASE_SHA ?= $(if $(UPSTREAM_REMOTE),$(shell git rev-parse $(UPSTREAM_REMOTE)/main),$(shell git rev-parse main)) .PHONY: api-lint -api-lint: $(GOLANGCI_LINT) $(KUBEAPILINTER_PLUGIN) +api-lint: $(GOLANGCI_LINT) $(KUBEAPILINTER_PLUGIN) $(HYPERSHIFTLINTER_PLUGIN) cd api && $(GOLANGCI_LINT) run --config ./.golangci.yml --modules-download-mode=readonly -v --new-from-rev=${PULL_BASE_SHA} .PHONY: api-lint-fix -api-lint-fix: $(GOLANGCI_LINT) $(KUBEAPILINTER_PLUGIN) +api-lint-fix: $(GOLANGCI_LINT) $(KUBEAPILINTER_PLUGIN) $(HYPERSHIFTLINTER_PLUGIN) cd api && $(GOLANGCI_LINT) run --config ./.golangci.yml --fix -v --new-from-rev=${PULL_BASE_SHA} .PHONY: precommit-api-lint-fix @@ -156,7 +156,7 @@ hypershift-lint-all: $(GOLANGCI_LINT) $(HYPERSHIFTLINTER_PLUGIN) .PHONY: test-linter test-linter: - cd $(TOOLS_DIR) && $(GO) test ./hypershiftlinter/analyzers/... -count=1 + cd $(TOOLS_DIR) && $(GO) test ./hypershiftlinter/... -count=1 .PHONY: verify-git-clean verify-git-clean: diff --git a/TESTING.md b/TESTING.md index fc1a0f6a9e49..28db3f6b7d01 100644 --- a/TESTING.md +++ b/TESTING.md @@ -45,7 +45,25 @@ func TestBuildAzureMachineTemplate(t *testing.T) { ... } Do not use generic or disconnected test function names like `TestNodePoolFeatures`, `TestAzureIntegration`, or `TestVariousCases`. -Each test function should map 1:1 to the function under test. If a function is complex enough to warrant many test cases, use table-driven tests within a single `Test` function with the "When...it should..." naming for each case. +Each production function or method must map to at most one top-level unit test function. If a function is complex enough to warrant many test cases, use table-driven tests or `t.Run` subtests within a single test function, with the "When...it should..." naming for each case. + +This limit includes tests in both the production package and its external `_test` package. + +Prefer `Test_` for method tests so methods with common names such as `Reconcile`, `Validate`, and `Run` map unambiguously: + +```go +// Testing (*NodePoolReconciler).Reconcile +func TestNodePoolReconciler_Reconcile(t *testing.T) { ... } +``` + +For compatibility, `Test` and `Test` are also accepted when they resolve to exactly one production method. + +The one-to-one rule applies to ordinary unit tests. It does not apply to `TestMain`, benchmarks, fuzz tests, examples, generated files, integration/e2e/envtest/request-serving entry points, or package-level behavioral tests that do not correspond to one production function. For an intentional exception that still exercises a production function, add a declaration-level suppression with a specific reason: + +```go +//nolint:hypershiftlinter // testfuncstructure: validates compatibility across multiple functions +func TestSerializationCompatibility(t *testing.T) { ... } +``` ## Test Placement diff --git a/api/.golangci.yml b/api/.golangci.yml index 921572150302..ca6417f4d303 100644 --- a/api/.golangci.yml +++ b/api/.golangci.yml @@ -3,11 +3,19 @@ run: allow-parallel-runners: true linters: enable: + - hypershiftlinter - kubeapilinter disable: - staticcheck settings: custom: + hypershiftlinter: + path: ../hack/tools/bin/hypershiftlinter.so + description: "Enforces HyperShift unit test function structure" + settings: + analyzers: + enable: + - testfuncstructure kubeapilinter: path: ../hack/tools/bin/kube-api-linter.so description: kubeapilinter is the Kube-API-Linter and lints Kube like APIs based on API conventions and best practices. diff --git a/hack/tools/hypershiftlinter/README.md b/hack/tools/hypershiftlinter/README.md index 23fd1a410e15..0c66c5104d42 100644 --- a/hack/tools/hypershiftlinter/README.md +++ b/hack/tools/hypershiftlinter/README.md @@ -30,7 +30,7 @@ relying on reviewer memory. That matters for several reasons: ## Analyzers -The plugin ships 9 analyzers, scoped so each rule only fires where it applies. +The plugin ships 10 analyzers, scoped so each rule only fires where it applies. ### Unit test conventions (`TESTING.md`, unit tests only) @@ -38,6 +38,7 @@ The plugin ships 9 analyzers, scoped so each rule only fires where it applies. | -------------- | ----------------------------------------------------------------------------------------- | | `testcasename` | Test case name fields match `When , it should `. | | `testfuncname` | Test functions do not use the `Test_` prefix; use `TestFunctionName` instead. | +| `testfuncstructure` | Top-level unit tests map one-to-one to production functions and methods. | ### E2E conventions (`test/e2e/v2/` only) @@ -69,12 +70,63 @@ Relevant Makefile targets: - `make hypershift-lint-all` — run the enabled custom analyzers against the current tree. - `make lint` — run normal lint checks, including these analyzers. -- `make test-linter` — run the analyzers' own unit tests - (`go test ./hypershiftlinter/analyzers/...`). +- `make test-linter` — run the plugin and analyzers' unit tests + (`go test ./hypershiftlinter/...`). Each analyzer has [`analysistest`](https://pkg.go.dev/golang.org/x/tools/go/analysis/analysistest)-based unit tests with good/bad `testdata/` fixtures. +## Unit test function structure and exceptions + +`testfuncstructure` maps ordinary `func TestXxx(*testing.T)` declarations to +production functions and methods using package type information. Package +functions use `Test`. The preferred method form is +`Test_`; a compact receiver form or bare method name +is accepted only when it resolves unambiguously. Longer exact production names +take precedence over scenario suffixes, so `TestReconcileErrors` maps to a real +`ReconcileErrors` function before it can be treated as another `Reconcile` +scenario. + +External tests are matched to the actual package-under-test import path, not +another dependency with the same package name. Analysis facts carry production +declaration provenance and internal-test mappings into the external test +variant. This excludes exported test helpers and generated methods from the +production index and enforces the one-test limit across both package variants. + +The analyzer is enabled by both the root and `api/` module lint configurations. + +When several top-level tests map to one production symbol, consolidate their +scenarios into table cases or `t.Run` subtests under one test function. The +analyzer does not infer runtime coverage or require tests for every production +function. It ignores test-only helpers, package-level behavioral tests without +a production-function call, special Go test entry points, generated files, and +integration-style suites. + +For an intentional exception, use the golangci-lint plugin name on the test +declaration and explain why no one-function mapping is appropriate: + +```go +//nolint:hypershiftlinter // testfuncstructure: validates compatibility across multiple functions +func TestSerializationCompatibility(t *testing.T) { ... } +``` + +`testfuncstructure` is an analyzer inside the `hypershiftlinter` plugin, not a +standalone golangci-lint linter, so `//nolint:testfuncstructure` does not work. +The suppression affects every `hypershiftlinter` diagnostic on that declaration; +keep it declaration-scoped and specific. + +Existing findings present when this rule was enabled are listed exactly by +package, file, and declaration in `analyzers/testfuncstructure/legacy.go`. These +entries are migration debt, not approved patterns. New declarations remain +enforced, including a new canonical declaration added beside a baselined split +test. Remove an entry when its test is renamed or consolidated. An accepted +limitation is that an existing declaration remains exempt while its baseline +entry exists, even if its body changes. + +The linter test suite checks that every baseline entry names an existing test +declaration. Delete obsolete entries when tests are moved, renamed, or removed; +otherwise `make test-linter` fails. + ## Legacy E2E utility migration exceptions `e2eutilallowlist` prevents new v2 dependencies on `test/e2e/util` and its diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy.go new file mode 100644 index 000000000000..5dcaff5d1d6c --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy.go @@ -0,0 +1,972 @@ +package testfuncstructure + +import "k8s.io/apimachinery/pkg/util/sets" + +// legacyExceptions contains exact pre-existing test declarations that do not +// satisfy TESTING.md. Remove an entry when the corresponding test is migrated. +// Validated against main at 364a78f3d7001048a72e7c1c894c040bc7a43c04. +var legacyExceptions = sets.Set[string]{ + "api/util/ipnet/ipnet_test.go|TestUnmarshal": {}, + "cmd/cluster/agent/create_test.go|TestCreateCluster": {}, + "cmd/cluster/aws/create_test.go|TestCreateCluster": {}, + "cmd/cluster/aws/destroy_test.go|TestNewDestroyCommandClientProvider": {}, + "cmd/cluster/aws/destroy_test.go|TestValidateCredentialInfo": {}, + "cmd/cluster/aws/destroy_test.go|TestValidateCredentialInfoWithoutClient": {}, + "cmd/cluster/azure/create_test.go|TestCreateCluster": {}, + "cmd/cluster/azure/create_test.go|TestCreateClusterAutoNodeRequiresKarpenterClientID": {}, + "cmd/cluster/azure/create_test.go|TestCredentialSecretHasDeleteWithClusterLabel": {}, + "cmd/cluster/azure/create_test.go|TestDNSZoneRGValidation": {}, + "cmd/cluster/azure/create_test.go|TestRoleAssignmentWithInfraJSON": {}, + "cmd/cluster/azure/create_test.go|TestValidateEndpointAccess": {}, + "cmd/cluster/azure/create_test.go|TestValidateOAuthPublishingStrategy": {}, + "cmd/cluster/azure/destroy_test.go|TestDestroyClusterSetsCloudFromHostedCluster": {}, + "cmd/cluster/azure/destroy_test.go|TestDestroyClusterSetsGracePeriodFromTopology": {}, + "cmd/cluster/azure/destroy_test.go|TestNewDestroyCommandClientProvider": {}, + "cmd/cluster/core/create_test.go|TestAllocateNodeCIDRsFlag": {}, + "cmd/cluster/core/create_test.go|TestCachedClientProvider": {}, + "cmd/cluster/core/create_test.go|TestDisableMultiNetworkFlag": {}, + "cmd/cluster/core/create_test.go|TestGetServicePublishingStrategyMapping": {}, + "cmd/cluster/core/create_test.go|TestOVNKubernetesMTUFlag": {}, + "cmd/cluster/core/create_test.go|TestPrototypeResources": {}, + "cmd/cluster/core/destroy_test.go|TestDestroyCluster": {}, + "cmd/cluster/core/destroy_test.go|TestForceRemoveAllFinalizers": {}, + "cmd/cluster/core/destroy_test.go|TestForceRemoveAllFinalizersErrors": {}, + "cmd/cluster/gcp/create_test.go|TestCreateCluster": {}, + "cmd/cluster/gcp/create_test.go|TestValidateGCPOptions": {}, + "cmd/cluster/kubevirt/create_test.go|TestCreateCluster": {}, + "cmd/cluster/none/create_test.go|TestCreateCluster": {}, + "cmd/cluster/openstack/create_test.go|TestCreateCluster": {}, + "cmd/cluster/openstack/create_test.go|TestCreateOptions_Validate": {}, + "cmd/cluster/openstack/destroy_test.go|TestNewDestroyCommand": {}, + "cmd/cluster/openstack/destroy_test.go|TestNewDestroyCommandClientProvider": {}, + "cmd/cluster/powervs/create_test.go|TestCreateCluster": {}, + "cmd/cluster/powervs/destroy_test.go|TestNewDestroyCommandClientProvider": {}, + "cmd/consolelogs/azure/api_journals_test.go|TestAPIJournalCollectorForNodes": {}, + "cmd/consolelogs/azure/collection_test.go|TestSDKComputeClientBootDiagnosticsState": {}, + "cmd/consolelogs/azure/getlogs_test.go|TestSDKComputeClientRetrieveSerialConsoleLogURI": {}, + "cmd/consolelogs/azure/journals_test.go|TestBoundedJournalWriter": {}, + "cmd/consolelogs/azure/journals_test.go|TestSSHAddress": {}, + "cmd/fix/dr_oidc_iam_test.go|TestDrOidcIamOptions_Defaults": {}, + "cmd/fix/dr_oidc_iam_test.go|TestDrOidcIamOptions_RunDryRun": {}, + "cmd/fix/dr_oidc_iam_test.go|TestOidcDiscoveryURL": {}, + "cmd/infra/aws/create_operator_roles_test.go|TestCreateOperatorRoles": {}, + "cmd/infra/aws/create_operator_roles_test.go|TestCreateOperatorRolesValidate": {}, + "cmd/infra/aws/destroy_test.go|TestEmptyBucket": {}, + "cmd/infra/aws/destroy_test.go|TestEmptyBucket_Pagination": {}, + "cmd/infra/aws/route53_test.go|TestCreatePrivateZone": {}, + "cmd/infra/aws/route53_test.go|TestCreatePrivateZoneUsesStableCallerReferenceAcrossRetries": {}, + "cmd/infra/aws/util/sts_test.go|TestNewSTSSession_Validation": {}, + "cmd/infra/aws/util/sts_test.go|TestNewSTSSession_ValidationOrder": {}, + "cmd/infra/aws/util/sts_test.go|TestParseSTSCredentialsFile": {}, + "cmd/infra/aws/util/sts_test.go|TestParseSTSCredentialsFile_RealWorldFormat": {}, + "cmd/infra/aws/util/util_test.go|TestGetSession": {}, + "cmd/infra/aws/util/util_test.go|TestGetSession_DefaultChainRoleAssumption": {}, + "cmd/infra/aws/util/util_test.go|TestNewSession": {}, + "cmd/infra/aws/util/util_test.go|TestNewSession_ContextPropagation": {}, + "cmd/infra/aws/util/util_test.go|TestNewSession_UserAgent": {}, + "cmd/infra/azure/identities_test.go|TestGetWorkloadIdentityDefinitions": {}, + "cmd/infra/azure/identities_test.go|TestKarpenterWorkloadIdentityDefinition": {}, + "cmd/infra/azure/rbac_test.go|TestAssignRole": {}, + "cmd/infra/azure/rbac_test.go|TestAssignRoleCreateParameters": {}, + "cmd/infra/gcp/create_infra_test.go|TestNetworkManagerFormatNames": {}, + "cmd/infra/gcp/destroy_iam_test.go|TestDestroyIAMNewDestroyIAMCommand": {}, + "cmd/install/assets/crds/assets_test.go|TestCapiResources": {}, + "cmd/install/assets/crds/assets_test.go|TestCustomResourceDefinitionsStorageVersion": {}, + "cmd/install/assets/crds/assets_test.go|TestGetCustomResourceDefinition": {}, + "cmd/install/assets/hypershift_operator_test.go|TestExternalDNSDeployment_Build": {}, + "cmd/install/assets/hypershift_operator_test.go|TestHyperShiftOperatorClusterRole_WebhookRBAC": {}, + "cmd/install/assets/hypershift_operator_test.go|TestHyperShiftOperatorDeployment_Build": {}, + "cmd/install/assets/hypershift_operator_test.go|TestHyperShiftOperatorDeployment_Build_WorkloadIdentityLabel": {}, + "cmd/install/assets/resource_requirements_test.go|TestExternalDNSDeployment_HasResourceRequests": {}, + "cmd/install/assets/resource_requirements_test.go|TestHyperShiftOperatorDeployment_HasResourceRequests": {}, + "cmd/install/client_test.go|TestApplyWithNilClient": {}, + "cmd/install/client_test.go|TestDryRunValidateCRDsWithNilClient": {}, + "cmd/install/client_test.go|TestWaitUntilAvailableWithNilClient": {}, + "cmd/install/client_test.go|TestWaitUntilEstablishedWithNilClient": {}, + "cmd/install/install_test.go|TestApplyDefaults": {}, + "cmd/install/install_test.go|TestCRDIncludeFilter": {}, + "cmd/install/install_test.go|TestHyperShiftOperatorManifests_SharedIngress": {}, + "cmd/install/install_test.go|TestHyperShiftOperatorManifests_WebhookFlags": {}, + "cmd/install/install_test.go|TestHyperShiftOperatorPodDisruptionBudget": {}, + "cmd/install/install_test.go|TestNewInstallOptionsWithDefaults": {}, + "cmd/install/install_test.go|TestNewInstallOptionsWithDefaults_InstallScope": {}, + "cmd/install/install_test.go|TestOutputsHelpers": {}, + "cmd/install/install_test.go|TestRenderHyperShiftOperator": {}, + "cmd/install/install_test.go|TestRenderHyperShiftOperator_RenderSensitive": {}, + "cmd/install/install_test.go|TestRenderOutputsScope": {}, + "cmd/install/install_test.go|TestSetupCRDs": {}, + "cmd/install/install_test.go|TestSetupCRDs_CAPIStorageVersionMigrationGuard": {}, + "cmd/nodepool/aws/create_test.go|TestCreateNodePool_When_flags_are_parsed_it_should_generate_correct_nodepool": {}, + "cmd/nodepool/aws/create_test.go|TestUpdateNodePool_When_instance_profile_is_empty_it_should_default_to_infraID": {}, + "cmd/nodepool/aws/create_test.go|TestUpdateNodePool_When_instance_type_is_empty_it_should_default_based_on_arch": {}, + "cmd/nodepool/aws/create_test.go|TestUpdateNodePool_When_subnet_is_empty_it_should_use_hostedcluster_subnet": {}, + "cmd/nodepool/aws/create_test.go|TestValidate_When_root_volume_size_is_too_small_it_should_return_error": {}, + "cmd/nodepool/aws/create_test.go|TestValidate_When_root_volume_size_is_valid_it_should_succeed": {}, + "cmd/nodepool/azure/create_test.go|TestAzureBoundaryConditions": {}, + "cmd/nodepool/azure/create_test.go|TestCreateNodePool_When_flags_are_parsed_it_should_generate_correct_nodepool": {}, + "cmd/nodepool/azure/create_test.go|TestNodePoolPlatformImageGeneration": {}, + "cmd/nodepool/azure/create_test.go|TestValidateIPForwarding": {}, + "cmd/nodepool/azure/create_test.go|TestValidateImageGeneration": {}, + "cmd/nodepool/gcp/create_test.go|TestCLIFlow": {}, + "cmd/nodepool/kubevirt/create_test.go|TestCreateNodePool_When_flags_are_parsed_it_should_generate_correct_nodepool": {}, + "cmd/nodepool/kubevirt/create_test.go|TestNodePoolPlatform_When_memory_is_set_it_should_parse_correctly": {}, + "cmd/nodepool/kubevirt/create_test.go|TestRawKubevirtPlatformCreateOptions_Validate": {}, + "cmd/nodepool/openstack/create_test.go|TestCreateNodePool_When_flags_are_parsed_it_should_generate_correct_nodepool": {}, + "cmd/nodepool/openstack/create_test.go|TestRawOpenStackPlatformCreateOptions_Validate": {}, + "cmd/oadp/backup_test.go|TestBackupCommandIncludedNamespacesFlag": {}, + "cmd/oadp/backup_test.go|TestCreateOptionsDefaults": {}, + "cmd/oadp/backup_test.go|TestGenerateBackupObject": {}, + "cmd/oadp/backup_test.go|TestGenerateBackupObjectComprehensive": {}, + "cmd/oadp/backup_test.go|TestGenerateBackupObjectWithCustomName": {}, + "cmd/oadp/backup_test.go|TestGenerateBackupObjectWithIncludedNamespaces": {}, + "cmd/oadp/backup_test.go|TestGenerateBackupObjectWithKubeVirtResourcePolicy": {}, + "cmd/oadp/backup_test.go|TestRunBackupClientProvider": {}, + "cmd/oadp/common_test.go|TestApplyPlatformBackupSpec": {}, + "cmd/oadp/common_test.go|TestApplyPlatformBackupSpecPreservesExistingFields": {}, + "cmd/oadp/restore_test.go|TestBuildIncludedNamespaces": {}, + "cmd/oadp/restore_test.go|TestGenerateRestoreObjectBasic": {}, + "cmd/oadp/restore_test.go|TestGenerateRestoreObjectWithSchedule": {}, + "cmd/oadp/restore_test.go|TestValidateBackupExistsNotFound": {}, + "cmd/oadp/restore_test.go|TestValidateBackupExistsWithPhaseValidation": {}, + "cmd/oadp/schedule_test.go|TestBuildIncludedNamespacesForSchedule": {}, + "cmd/oadp/schedule_test.go|TestCreateScheduleOptionsDefaults": {}, + "cmd/oadp/schedule_test.go|TestGenerateScheduleObject": {}, + "cmd/oadp/schedule_test.go|TestGenerateScheduleObjectComprehensive": {}, + "cmd/oadp/schedule_test.go|TestGenerateScheduleObjectWithIncludedNamespaces": {}, + "cmd/oadp/schedule_test.go|TestGenerateScheduleObjectWithKubeVirtResourcePolicy": {}, + "cmd/oadp/schedule_test.go|TestRunSchedule": {}, + "cmd/oadp/schedule_test.go|TestRunScheduleClientProvider": {}, + "cmd/oadp/schedule_test.go|TestScheduleCommandIncludedNamespacesFlag": {}, + "cmd/oadp/schedule_test.go|TestScheduleNameValidation": {}, + "cmd/util/client_test.go|TestClientProviderErrors": {}, + "cmd/util/client_test.go|TestConfigFor": {}, + "cmd/util/client_test.go|TestControllerRuntimeClientFor": {}, + "cmd/util/client_test.go|TestImpersonatedClientFor": {}, + "cmd/util/client_test.go|TestKubernetesClientSetFor": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestDeleteLocalZoneCleanup": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestHCPExternalNames": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestReconcileDeletion": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestReconcileDeletionSharedVPC": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestReconcileDeletion_AfterControllerRestart": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestRecordForService": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestVPCEndpointPort": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestVPCEndpointSecurityGroupFilter": {}, + "control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go|TestVPCEndpointSecurityGroupName": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestDNSZoneConfigErrMsgQualifier": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestEnsureHCPFinalizer_WhenAlreadyPresent_ItShouldNotModify": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestEnsureHCPFinalizer_WhenNotPresent_ItShouldAddFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestEnsureHCPFinalizer_WhenPatchConflicts_ItShouldRequeue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestErrMsgQualifier": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestExtractPrivateEndpointIP": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestExtractPrivateEndpointIP_WhenNICHasNilElements_ItShouldSkipThem": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestHandleAzureError_WhenStatusPatchFails_ItShouldReturnPatchError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestHcpExternalPrivateServices_WhenBothRouteHostnamesConfigured_ItShouldReturnBoth": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestHcpExternalPrivateServices_WhenNoRouteStrategy_ItShouldReturnEmpty": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestHcpExternalPrivateServices_WhenRouteHasNoHostname_ItShouldSkipIt": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestMapHCPToAzurePLS": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestMapHCPToAzurePLS_WhenObjectIsNotHCP_ItShouldReturnNil": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileBaseDomainDNS_WhenDNSZoneCreateFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileBaseDomainDNS_WhenOAuthCR_ItShouldOnlyCreateOAuthRecord": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileBaseDomainDNS_WhenPrivateRouterWithNoSibling_ItShouldCreateBothRecords": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileBaseDomainDNS_WhenPrivateRouterWithSiblingOAuth_ItShouldOnlyCreateAPIRecord": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNSZone_WhenStatusPatchFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenARecordCreateFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenDNSZoneCreateFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenDNSZonePollerFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenDNSZonePollerSucceeds_ItShouldCreateLinkAndRecords": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenPEIPNotAvailable_ItShouldRequeue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenVNetAlreadyLinked_ItShouldContinue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenVNetLinkCreateFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDNS_WhenVNetLinkPollerFails_ItShouldRequeueAfterError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenARecordDeleteReturnsNon404Error_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenAllDeletesReturn404_ItShouldCompleteSuccessfully": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainButClusterNameCannotBeDerived_ItShouldSkipRecordDeletion": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainPollersSucceed_ItShouldDeleteAllBaseDomainResources": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainRecordDeleteFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainVNetLinkDeleteFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainVNetLinkPollerFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainZoneDeleteFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenBaseDomainZonePollerFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenDNSZoneDeleteReturnsNon404Error_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenDNSZoneNameNotSet_ItShouldSkipDNSCleanup": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenDNSZonePollerFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenDeletePollersReturn404_ItShouldSucceed": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenNoSiblingCRs_ItShouldDeleteBaseDomainZone": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenNonPrivateRouterWithBaseDomain_ItShouldDeleteOAuthRecordOnly": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenPEDeleteReturnsNon404Error_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenPEPollerFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenPollersSucceed_ItShouldDeleteAllResources": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenSiblingCRsExist_ItShouldNotDeleteBaseDomainZone": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenVNetLinkDeleteReturnsNon404Error_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileDelete_WhenVNetLinkPollerFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalService_ItShouldSetAllRequiredFields": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalService_WhenServiceHasExistingLabelsAndAnnotations_ItShouldPreserveThem": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalServices_WhenCreateOrUpdateFails_ItShouldReturnAggregateError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalServices_WhenPrivateWithExternalNames_ItShouldCreateServices": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalServices_WhenPrivateWithNoExternalNames_ItShouldNotCreateServices": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileExternalServices_WhenPublic_ItShouldDeleteExistingServices": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenAzureCleanupFails_ItShouldReturnErrorAndPreserveFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenBaseDomainZoneDeletionFails_ItShouldPreserveFinalizers": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenHCPDoesNotHaveFinalizer_ItShouldBeNoOp": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenHCPIsBeingDeleted_ItShouldCleanUpAndRemoveFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenMultipleCRsExist_ItShouldRemoveAllPerCRFinalizers": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenMultipleCRsShareBaseDomain_ItShouldDeleteBaseDomainZone": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenPatchConflicts_ItShouldRequeue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenSiblingCleanupFails_ItShouldPreserveAllFinalizers": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcileHCPDeletion_WhenSingleCRHasBaseDomain_ItShouldDeleteBaseDomainZoneIdempotently": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcilePrivateEndpoint_WhenCreateFails_ItShouldSetErrorCondition": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcilePrivateEndpoint_WhenGetReturnsNon404Error_ItShouldSetErrorCondition": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcilePrivateEndpoint_WhenPEAlreadyExists_ItShouldUpdateStatus": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcilePrivateEndpoint_WhenPollerFails_ItShouldSetErrorCondition": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcilePrivateEndpoint_WhenPollerSucceeds_ItShouldUpdateStatusWithPEInfo": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenAllResourcesAreCreated_ItShouldSetAvailableCondition": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenAvailableConditionPatchFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenBaseDomainDNSFails_ItShouldReturnRequeueResult": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRAlreadyDeleted_ItShouldReturnNoError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRDeletionFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRDeletionSucceeds_ItShouldRemoveFinalizerAndCleanUp": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRDeletionWithFinalizer_ItShouldCleanUpAndRemoveFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRIsDeleted_ItShouldCleanUpPEAndDNS": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenCRNotFound_ItShouldReturnNoError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenDNSReconcileFails_ItShouldReturnRequeueResult": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenFinalizerAddConflicts_ItShouldRequeue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenFinalizerNotPresent_ItShouldAddFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenGetHCPFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenHCPIsBeingDeleted_ItShouldNotReAddPerCRFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenHCPIsBeingDeleted_ItShouldTriggerCleanupInsteadOfCreation": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenHCPIsGone_ItShouldRemoveOrphanedPerCRFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenNonPrivateRouterCR_ItShouldSkipHypershiftLocalDNS": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenNonPrivateRouterDNSZoneNamePatchFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenNonPrivateRouterWithDNSZoneNameAlreadySet_ItShouldPreserveExistingDNSZoneName": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPEConnectionNotApproved_ItShouldRequeueWithWarning": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPEIsCreated_ItShouldCreateDNSZoneAndARecord": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPEReconcileFails_ItShouldReturnRequeueResult": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPLSAliasIsAvailable_ItShouldAddHCPFinalizer": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPLSAliasIsAvailable_ItShouldCreatePrivateEndpoint": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPLSAliasIsNotYetAvailable_ItShouldRequeue": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestReconcile_WhenPrivateRouterWithBaseDomain_ItShouldReconcileBaseDomainDNS": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestUpdatePrivateEndpointStatus_WhenConnectionPendingAndPatchFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestUpdatePrivateEndpointStatus_WhenStatusPatchFails_ItShouldReturnError": {}, + "control-plane-operator/controllers/azureprivatelinkservice/controller_test.go|TestVNetLinkName": {}, + "control-plane-operator/controllers/azureprivatelinkservice/observer_test.go|TestReconcile": {}, + "control-plane-operator/controllers/azureprivatelinkservice/observer_test.go|TestReconcile_WhenServiceNotFound_ItShouldReturnNoError": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/dns_test.go|TestGenerateZoneNames": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/dns_test.go|TestGenerateZoneNamesValidationErrors": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/dns_test.go|TestZoneNameLengthConstraints": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/observer_test.go|TestReconcileIntegration": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestConstructEndpointName": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestConstructIPAddressName": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestDNSEndpointNameTrimming": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestDNSEndpointNaming": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestEndpointNameUniqueness": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestEnqueueOnResourceLabelsChange": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestEnsureIPAddress": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestEnsureIPAddressContinuesWhenLabelReconciliationFails": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestEnsureIPAddressCreatesAddressWithLabels": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestHCPExternalNamesGCP": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestIPAddressNameUniqueness": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestNameserverTrailingDotTrimming": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestNamingFunctionConsistency": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestReconcilePSCEndpointContinuesWhenLabelReconciliationFails": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestReconcilePSCEndpointCreatesAndContinuesWhenLabelReconciliationFails": {}, + "control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller_test.go|TestReconcilePSCEndpointCreatesThenLabelsForwardingRule": {}, + "control-plane-operator/controllers/healthcheck/aws_test.go|TestAWSHealthCheckIdentityProviderEC2ClientNil": {}, + "control-plane-operator/controllers/healthcheck/aws_test.go|TestAWSHealthCheckIdentityProviderWithMockEC2": {}, + "control-plane-operator/controllers/healthcheck/aws_test.go|TestClassifyIdentityProviderError": {}, + "control-plane-operator/controllers/healthcheck/aws_test.go|TestClassifyIdentityProviderErrorMessageStability": {}, + "control-plane-operator/controllers/healthcheck/aws_test.go|TestUpdateRunsAWSIdentityCheckDuringDeletion": {}, + "control-plane-operator/controllers/healthcheck/gcp_test.go|TestGCPHealthCheckConditionDifferentiation": {}, + "control-plane-operator/controllers/healthcheck/gcp_test.go|TestGCPHealthCheckIdentityProviderConditionLogic": {}, + "control-plane-operator/controllers/healthcheck/gcp_test.go|TestUpdateRunsGCPIdentityCheckDuringDeletion": {}, + "control-plane-operator/controllers/hostedcontrolplane/aws_ingress_dns_test.go|TestReconcileIngressDNSSharedVPC": {}, + "control-plane-operator/controllers/hostedcontrolplane/aws_ingress_dns_test.go|TestReconcileNSDelegationNoDelegation": {}, + "control-plane-operator/controllers/hostedcontrolplane/aws_ingress_dns_test.go|TestReconcilePrivateZone": {}, + "control-plane-operator/controllers/hostedcontrolplane/aws_ingress_dns_test.go|TestReconcilePublicZone": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_pruning_test.go|TestPruneHistory": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_pruning_test.go|TestPruneHistory_CapAtMaxHistory": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_pruning_test.go|TestPruneHistory_DeterministicResults": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_pruning_test.go|TestPruneHistory_ExactlyMaxHistoryUnchanged": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_pruning_test.go|TestPruneHistory_UnderMaxHistoryUnchanged": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_AllComponentsComplete": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_ComponentFailure": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_ControlPlaneReleaseImage": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_FirstPopulation": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_ImageOnlyChange": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_NewComponentMidUpgrade": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_NewDesiredRelease": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_NoComponents": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_ObservedGeneration": {}, + "control-plane-operator/controllers/hostedcontrolplane/controlplaneversion_test.go|TestReconcileControlPlaneVersion_SupersededPartial": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestAWSSecurityGroupTags": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestControlPlaneComponents": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestEventHandling": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestGCPFirewallSkipOnWIFUnavailableReason": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestHealthCheckKASEndpoint": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestHealthCheckKASEndpointMetrics": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestKASHealthCheckURL": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestMarkGCPFirewallProvisioned": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestNonReadyInfraTriggersRequeueAfter": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileDefaultSecurityGroup": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileDefaultSecurityGroup_GenerationConflict": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileDeletion": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileDeletionAWSEndpointServices": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileFailsWhenCombinedPullSecretCreateFails": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestReconcileIgnitionServer": {}, + "control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go|TestRouterComponentComesAfterRouteCreatingComponents": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileInfrastructure": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileInfrastructure_ErrorCases": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileInfrastructure_WhenTransitioningFromPrivateToPublic_ItShouldCleanUpPrivateResources": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileInfrastructure_WhenTransitioningFromPublicToPrivate_ItShouldCleanUpPublicResources": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileOAuthService": {}, + "control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go|TestReconcileOAuthService_AzureLoadBalancer": {}, + "control-plane-operator/controllers/hostedcontrolplane/ingress/router_test.go|TestReconcileRouterServiceAnnotations": {}, + "control-plane-operator/controllers/hostedcontrolplane/ingress/router_test.go|TestReconcileRouterService_AppliesLoadBalancerSourceRanges": {}, + "control-plane-operator/controllers/hostedcontrolplane/ingress/router_test.go|TestReconcileRouterService_GCPInternalLoadBalancer": {}, + "control-plane-operator/controllers/hostedcontrolplane/ingress/router_test.go|TestReconcileRouterService_WhenExternalAWS_ItShouldSetInternetFacingScheme": {}, + "control-plane-operator/controllers/hostedcontrolplane/ingress/router_test.go|TestReconcileRouterService_WhenSwitchingMode_ItShouldRemoveConflictingAnnotation": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/healthcheck_test.go|TestGetHealthcheckEndpoint": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/metrics_test.go|TestKASHealthMetricsOperations": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go|TestKonnectivityServiceReconcile": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go|TestReconcilePrivateService": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go|TestReconcilePrivateServiceAWSNLBAnnotation": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go|TestReconcileService": {}, + "control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go|TestReconcileServiceAzureInternalLB": {}, + "control-plane-operator/controllers/hostedcontrolplane/manifests/etcd_test.go|TestEtcdBackupJobManifests": {}, + "control-plane-operator/controllers/hostedcontrolplane/oauth/idp_convert_test.go|TestConvertIdentityProviders_ErrorWrapping": {}, + "control-plane-operator/controllers/hostedcontrolplane/oauth/idp_convert_test.go|TestConvertProviderConfigToIDPData_Routing": {}, + "control-plane-operator/controllers/hostedcontrolplane/oauth/idp_convert_test.go|TestConvertProviderConfigToIDPData_UnsupportedType": {}, + "control-plane-operator/controllers/hostedcontrolplane/oauth/idp_convert_test.go|TestOpenIDProviderConversion": {}, + "control-plane-operator/controllers/hostedcontrolplane/oauth/service_test.go|TestOauthServiceReconcile": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/azure_workload_identity_webhook_test.go|TestReconcileAzureWorkloadIdentityWebhookServingCert": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/azure_workload_identity_webhook_test.go|TestReconcileAzureWorkloadIdentityWebhookServingCertIdempotent": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_DoesNotRetainRemovedAdditionalCAs": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_EmptyAdditionalSource": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_EmptyPreviousBundle": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_EvictsExpiredCAs": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_LegacyAggregateFailsClosedAfterAdditionalRemoval": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_LegacyAggregateRetainsUnambiguousDirectCA": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_NoDuplicatesOnSteadyState": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_PreservesPreviousCAsOnRotation": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_ProvenanceDisambiguatesSameSubjectSources": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_ProvenanceMetadata": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_RefusesEmptyBundle": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_SequentialRotation": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_SkipsMalformedPreviousCertificate": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/ca_test.go|TestReconcileTotalClientCA_WithAdditionalConfigMaps": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/gcp_workload_identity_federation_webhook_test.go|TestReconcileGCPWorkloadIdentityFederationWebhookServingCert": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/gcp_workload_identity_federation_webhook_test.go|TestReconcileGCPWorkloadIdentityFederationWebhookServingCertIdempotent": {}, + "control-plane-operator/controllers/hostedcontrolplane/pki/konnectivity_test.go|TestKonnectivitySeparateSigners": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/assets/assets_test.go|TestForEachManifest": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/assets/assets_test.go|TestForEachManifestWithActionError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/assets/assets_test.go|TestKonnectivityServerAuthenticatesAgents": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/assets/assets_test.go|TestLoadDeploymentManifest": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/autoscaler/component_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/autoscaler/component_test.go|TestAdaptDeploymentWithClusterAutoscalerImage": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_manager/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_manager/deployment_test.go|TestAdaptDeployment_ParseVersionError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/deployment_test.go|TestAdaptDeployment_WithNilAnnotations": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/deployment_test.go|TestAdaptDeployment_WithProxyEnvVars": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/role_test.go|TestAdaptRole": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/role_test.go|TestAdaptRole_WithNilAnnotations": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/aws/config_test.go|TestConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/aws/config_test.go|TestConfigErrorStates": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/aws/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/aws/deployment_test.go|TestAdaptDeploymentTLS": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config_test.go|TestConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config_test.go|TestConfigErrorStates": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config_test.go|TestConfigSecretDoesNotContainAcrMI": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/gcp/config_test.go|TestConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/gcp/config_test.go|TestConfigErrorStates": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/kubevirt/config_test.go|TestConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/component_test.go|TestPowerVSOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/config_test.go|TestAdaptConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/config_test.go|TestAdaptConfigErrorStates": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/config_test.go|TestAdaptConfigMapDataStructure": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/config_test.go|TestAdaptConfigTemplateExecution": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/deployment_test.go|TestAdaptDeploymentErrorStates": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/deployment_test.go|TestAdaptDeploymentTLSArgs": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/powervs/deployment_test.go|TestAdaptDeploymentWithAssets": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_credential_operator/component_test.go|TestCloudCredentialOperatorOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_credential_operator/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_credential_operator/deployment_test.go|TestAdaptDeploymentTLS": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cloud_credential_operator/deployment_test.go|TestAdaptDeploymentUpdatesContainer": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/configoperator/deployment_test.go|TestAdaptDeploymentIBMCloudControllerSelection": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/controlplaneoperator/deployment_test.go|TestAdaptDeploymentImageOverrides": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/controlplaneoperator/deployment_test.go|TestAdaptDeploymentWatchListClientEnv": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/csi/kubevirt/config_test.go|TestAdaptConfigMapConsistentOrdering": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/csi/kubevirt/config_test.go|TestAdaptConfigMapEmptyMappings": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment_test.go|TestAdaptDeploymentClusterVersionCapabilities": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/dnsoperator/component_test.go|TestComponentOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/etcd/etcd_test.go|TestAdaptStatefulSet": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/etcd/etcd_test.go|TestAdaptStatefulSetEtcdLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/etcd/shard_test.go|TestAdaptStatefulSetForShard": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/etcd/shard_test.go|TestAdaptStatefulSetForShard_IPv6WithDefrag": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/etcd/shard_test.go|TestEtcdShardInterface": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/fg/job_test.go|TestAdaptJob": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/fg/job_test.go|TestAdaptJobPreservesExistingEnvVars": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver/route_test.go|TestIgnitionRouteAdapt": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver/service_test.go|TestIgnitionServiceReconcile": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/deployment_test.go|TestAdaptDeploymentWithProxyEnvVars": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/deployment_test.go|TestAdaptHAProxyConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/deployment_test.go|TestAdaptHAProxyConfigErrors": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/service_test.go|TestAdaptService": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/service_test.go|TestAdaptServiceMultiplePorts": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/service_test.go|TestAdaptServicePreservesNodePort": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/service_test.go|TestAdaptServiceValidatesStrategyType": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ignitionserver_proxy/service_test.go|TestServicePublishingStrategyByTypeForHCP": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/karpenteroperator/component_test.go|TestKarpenterCredentialsSecretReconcile": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/karpenteroperator/deployment_test.go|TestAdaptStandaloneDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/karpenteroperator/deployment_test.go|TestAdaptStandaloneDeploymentEndpoint": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go|TestAdaptDeploymentKASLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go|TestKonnectivityServerTLSMinVersion": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/gcp_workload_identity_federation_webhook_test.go|TestApplyGCPWorkloadIdentityFederationWebhookContainer": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/gcp_workload_identity_federation_webhook_test.go|TestApplyGCPWorkloadIdentityFederationWebhookContainerError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSEncryptionConfig_Azure": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_ActiveContainerArgs": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_BackupContainerArgs": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_BackupKey": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_ContainerPorts": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_ImagePullPolicy": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_KASContainerMutation": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_LivenessProbe": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_Managed": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_NoBackupContainerWithoutBackupKey": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_ResourceRequests": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_SelfManaged": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kms/azure_test.go|TestGenerateKMSPodConfig_VolumeMountPaths": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kubeconfig_test.go|TestGenerateKubeConfigWithServingCerts": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kubeconfig_test.go|TestGenerateKubeConfigWithServingCerts_WhenClientCertIsMissing": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/kubeconfig_test.go|TestGenerateKubeConfigWithServingCerts_WhenRootCAIsMissing": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/params_test.go|TestNewAPIServerParamsAPIAdvertiseAddressAndPort": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/params_test.go|TestNewConfigParams": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/secretencryption_test.go|TestReconcileKMSEncryptionConfigAWS": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/secretencryption_test.go|TestReconcileKMSEncryptionConfigAzure": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kas/secretencryption_test.go|TestReconcileKMSEncryptionConfigAzureSelfManaged": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kcm/component_test.go|TestKubeControllerManagerOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kcm/config_test.go|TestGetServiceServingCA": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kcm/config_test.go|TestGetServiceServingCAError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kcm/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kcm/deployment_test.go|TestAdaptDeploymentKCMLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/component_test.go|TestKubeSchedulerOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/config_test.go|TestGenerateConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/deployment_test.go|TestAdaptDeploymentSchedulerLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/machine_approver/component_test.go|TestPredicate": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/machine_approver/component_test.go|TestPredicateError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment_test.go|TestAdaptDeployment_MetricsSetOverride": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigMissingService": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigMixedMonitors": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigNamedTargetPort": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigPodMonitorMissingPod": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigPodMonitorNameMismatch": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestAdaptScrapeConfigWithPodMonitors": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestComponentFromPodMonitor_EdgeCases": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/scrape_config_test.go|TestComponentFromServiceMonitor_EdgeCases": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/component_test.go|TestClusterNodeTuningOperatorOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/deployment_test.go|TestAdaptDeploymentUpdatesExistingEnvVars": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/servicemonitor_test.go|TestAdaptServiceMonitor": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/servicemonitor_test.go|TestAdaptServiceMonitorErrorCases": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/nto/servicemonitor_test.go|TestAdaptServiceMonitorMetricsSetConfigurations": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oapi/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oapi/deployment_test.go|TestAdaptDeploymentOAPILogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oapi/deployment_test.go|TestReconcileOpenshiftAPIServerDeploymentTrustBundle": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/deployment_test.go|TestAdaptDeploymentOAuthLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/idp_convert_test.go|TestConvertIdentityProviders_ErrorWrapping": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/idp_convert_test.go|TestConvertProviderConfigToIDPData_Routing": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/idp_convert_test.go|TestConvertProviderConfigToIDPData_UnsupportedType": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth/idp_convert_test.go|TestOpenIDProviderConversion": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth_apiserver/component_test.go|TestOpenshiftOAuthAPIServerOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth_apiserver/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth_apiserver/deployment_test.go|TestAdaptDeploymentMultipleConfigurations": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth_apiserver/deployment_test.go|TestAdaptDeploymentOAuthAPIServerLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/oauth_apiserver/deployment_test.go|TestAdaptDeploymentWithInvalidEtcdURL": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ocm/config_test.go|TestAdaptConfig_DisabledImageRegistryCapability": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ocm/config_test.go|TestAdaptConfig_PreservesExistingControllers": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ocm/config_test.go|TestReconcileOpenShiftControllerManagerConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/ocm/deployment_test.go|TestAdaptDeploymentOCMLogLevel": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/olm/catalogs/deployment_test.go|TestCheckCatalogImageOverrides": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/olm/collect_profiles/cronjob_test.go|TestGenerateModularDailyCronSchedule": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/olm/collect_profiles/cronjob_test.go|TestGenerateModularDailyCronScheduleProperties": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/pkioperator/component_test.go|TestPKIOperatorOptions": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/pkioperator/deployment_test.go|TestAdaptDeployment": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/pkioperator/deployment_test.go|TestAdaptDeploymentCombinedEnvVars": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/pkioperator/deployment_test.go|TestAdaptDeploymentReturnsNoError": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/routecm/config_test.go|TestReconcileOpenShiftRouteControllerManagerConfig": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/router/util/util_test.go|TestUseHCPRouter": {}, + "control-plane-operator/controllers/hostedcontrolplane/v2/router/util/util_test.go|TestUseHCPRouterAroHCP": {}, + "control-plane-operator/endpoint-resolver/server_test.go|TestResolverHandler": {}, + "control-plane-operator/endpoint-resolver/server_test.go|TestResolverHandlerEmptySelector": {}, + "control-plane-operator/endpoint-resolver/server_test.go|TestResolverHandlerInvalidBody": {}, + "control-plane-operator/endpoint-resolver/server_test.go|TestResolverHandlerMethodNotAllowed": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/hcpstatus/hcpstatus_test.go|TestHCPStatusReconciler": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileInPlaceUpgradeAnnotatesMachineWithNodePoolVersion": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileInPlaceUpgradeDegradedNodeErrorMessage": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileReturnsRequeueAfterDuringUpgrade": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileUpgradePods": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileUpgradePodsDeleteNotFoundContinues": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileUpgradePodsMultiNodeMixedStates": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader_test.go|TestReconcileUpgradePodsReturnsErrorWhenDeleteFails": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/machine/machine_test.go|TestReconcileDefaultIngressEndpoints": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/reencryption/reencryption_test.go|TestReconcile": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/reencryption/reencryption_test.go|TestReconcile_PatchStatusError": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure_workload_identity_webhook_test.go|TestReconcileAzureIdentityWebhook": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure_workload_identity_webhook_test.go|TestReconcileAzureIdentityWebhookIdempotent": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/configuration/cluster_version_test.go|TestReconcileClusterVersion": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/configuration/cluster_version_test.go|TestReconcileClusterVersionWhenGuestCVOHasOlderCapabilities": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/configuration/cluster_version_test.go|TestReconcileClusterVersionWithDisabledCapabilities": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/configuration/cluster_version_test.go|TestReconcileClusterVersionWithEnabledCapabilities": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/foundation_order_test.go|TestReconcile": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp/gcp_test.go|TestReconcileGCPCredentials_EmptySecretKey": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp/gcp_test.go|TestSetupOperandCredentials": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp/gcp_test.go|TestSetupOperandCredentials_Storage": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp_lb_service_annotations_test.go|TestReconcileGCPLoadBalancerServiceAnnotations": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp_lb_service_annotations_test.go|TestReconcileGCPLoadBalancerServiceAnnotationsErrors": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp_lb_service_annotations_test.go|TestReconcileGCPLoadBalancerServiceAnnotationsOptimisticLock": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp_workload_identity_federation_webhook_test.go|TestReconcileGCPIdentityWebhook": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/gcp_workload_identity_federation_webhook_test.go|TestReconcileGCPIdentityWebhookIdempotent": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile_test.go|TestConfigurationPriority": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile_test.go|TestReconcileDefaultIngressController": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile_test.go|TestReconcileDefaultIngressControllerWithCustomEndpointPublishingStrategy": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/monitoring/metricsforwarder_test.go|TestReconcileMetricsForwarderDeployment": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/monitoring/metricsforwarder_test.go|TestReconcileMetricsForwarderDeployment_ConfigHashChangeTrigersRollout": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/network/reconcile_test.go|TestReconcileDefaultIngressController": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/oapi/encryption_status_test.go|TestReconcileEncryptionCondition": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/oapi/encryption_status_test.go|TestReconcileEncryptionConditionIdempotent": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/olm/catalogs_test.go|TestReconcileCatalogSourcePreservesExistingAnnotations": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestConnectionErrorTracking": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestDestroyCloudResources": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestDestroyCloudResourcesWithKASUnavailable": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestReconcileErrorHandling": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestReconcileIngressControllerCertSource": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestReconcileRegistryAndIngress_ServiceAccountPullSecretsController": {}, + "control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go|TestReconcileRequeueBehavior": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestFilter_Apply": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestFilter_Apply_ConcurrentAccess": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestFilter_getOrCompile": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestFilter_getOrCompile_HandlesEmptyRegex": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestGetKeepRegexForComponent": {}, + "control-plane-operator/metrics-proxy/filter_test.go|TestGetKeepRegexForComponent_ValidRegex": {}, + "control-plane-pki-operator/certificaterevocationcontroller/certificaterevocationcontroller_test.go|TestCertificateRevocationController_processCertificateRevocationRequest": {}, + "control-plane-pki-operator/certificaterevocationcontroller/certificaterevocationcontroller_test.go|TestEnsureNewSignerCertificatePropagated_KASVerification": {}, + "control-plane-pki-operator/certificaterevocationcontroller/certificaterevocationcontroller_test.go|TestEnsureOldSignerCertificateRevoked_KASVerification": {}, + "control-plane-pki-operator/certificaterevocationcontroller/certificaterevocationcontroller_test.go|TestSyncCertificateRevocationRequest_RequeueBehavior": {}, + "control-plane-pki-operator/certificatesigningcontroller/certificatesigningcontroller_test.go|TestCertificateSigningController_processCertificateSigningRequest": {}, + "control-plane-pki-operator/certificatesigningcontroller/certificatesigningcontroller_test.go|TestSign": {}, + "control-plane-pki-operator/certificatesigningcontroller/certificatesigningcontroller_test.go|TestSign_WhenCSRSignatureIsCorrupted_ItShouldReturnAnError": {}, + "control-plane-pki-operator/certificatesigningrequestapprovalcontroller/certificatesigningrequestapprovalcontroller_test.go|TestCertificateSigningRequestApprovalController_processCertificateSigningRequest": {}, + "etcd-backup/etcdbackup_test.go|TestNewStartCommand": {}, + "etcd-backup/etcdbackup_test.go|TestNewStartCommandDefaults": {}, + "etcd-upload/azure_uploader_test.go|TestAzureBlobUploader": {}, + "etcd-upload/azure_uploader_test.go|TestAzureCredential": {}, + "etcd-upload/s3_uploader_test.go|TestS3Uploader": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_FieldNameCollision": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_FullFileSkip": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_FuncDecl": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_GenDecl": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_NoStrippedSymbols": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_TransitiveCascade": {}, + "hack/copy-capi-types/main_test.go|TestCrossFileCascade_UpdatesAllStrippedSymbols": {}, + "hack/copy-capi-types/main_test.go|TestNodeReferencesAny_GenDecl": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_BannedVarDecl": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_DotImport": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_FullFileSkip": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_ImportCleanup": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_MethodReceiver": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_MultiSpecVarBlock": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_NoBannedImports": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_SkipByName": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_SkipBySuffix": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_StripBannedFunctions": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_StripBannedTypes": {}, + "hack/copy-capi-types/main_test.go|TestProcessFile_WithinFileCascade": {}, + "hack/copy-capi-types/main_test.go|TestReferencesAny": {}, + "hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go|TestSnapshotReconciler_createSnapshot": {}, + "hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go|TestSnapshotReconciler_manageRetention": {}, + "hypershift-operator/controllers/etcdbackup/credentials_test.go|TestCredentialBehavioralMethods": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestCleanupResources": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestCleanupResourcesWithDeleteErrors": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestEnforceRetention": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestEnforceRetentionDeleteRace": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestEnsureRBAC": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestEnsureRBACIdempotency": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestEnsureServiceAccountWithCredentials": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestHandleJobStatus": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestHandleJobStatusEdgeCases": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcile": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileHappyPath": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileHappyPathWithAzureWI": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileHappyPathWithSTS": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileMonitorsExistingJob": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileRecoversWhenCredentialSecretAppears": {}, + "hypershift-operator/controllers/etcdbackup/reconciler_test.go|TestReconcileTerminalCleansUpAndEnforcesRetention": {}, + "hypershift-operator/controllers/hostedcluster/enqueue_secret_test.go|TestEnqueueHostedClustersForReferencedSecret": {}, + "hypershift-operator/controllers/hostedcluster/gcp_firewall_condition_test.go|TestGCPFirewallRulesReadyCondition": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestDeleteOrcImagesDuringHostedClusterDeletion": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestDestroyGracePeriod": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestHasBeenAvailable": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestHostedClusterWatchesEverythingItCreates": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestIsUpgradeable": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestKubevirtETCDEncKey": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileCLISecrets": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileCLISecretsErrors": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileComponents": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneAPINetwork": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneAdditionalTrustBundle": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneConfiguration": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneLabelSync": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneMonitoring": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileHostedControlPlaneUpgrades": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileKubeconfigAndPasswordSync_WhenKubeconfigFails_ItShouldStillSyncKubeadminPassword": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileNonBlockingBehavior": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileRBACAndPolicies_WhenPKIRBACFails_ItShouldStillCreatePrometheusRBAC": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileSREMetricsConfig_EffectiveMetricsSet": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileWhenPullSecretMissingAndNodePoolListFails": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconcileWhenPullSecretUnavailable": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go|TestReconciliationSuccessConditionSetting": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestKubevirtClusterServiceDefaulting": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestKubevirtNodePoolManagementDefaulting": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestValidateKVHostedClusterCreate": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestValidateKVHostedClusterUpdate": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestValidateKVNodePoolCreate": {}, + "hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go|TestValidateKVNodePoolUpdate": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go|TestDeleteOrphanedMachines": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go|TestDeleteOrphanedMachines_CapiProviderLookupError": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go|TestReconcileCredentials": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go|TestReconcileCredentialsPreservesWrappedErrors": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_conditions_test.go|TestNetworkConfigAccessSafety": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_conditions_test.go|TestWorkloadIdentityValidationScenarios": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestCAPIProviderDeploymentSpec": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestCAPIProviderDeploymentSpecNilGCPPlatform": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestCAPIProviderDeploymentSpecWithTLS": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestGCPPlatformInterface": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestReconcileCredentials": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestReconcileCredentialsNilGCPPlatform": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestReconcileGCPClusterPreservesServerDefaultedFields": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestReconcileGCPClusterResourceLabels": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go|TestValidateWorkloadIdentityConfiguration": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/openstack/openstack_test.go|TestCAPIProviderDeploymentSpec": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/openstack/openstack_test.go|TestCAPIProviderDeploymentSpecWithTLS": {}, + "hypershift-operator/controllers/hostedcluster/internal/platform/openstack/openstack_test.go|TestReconcileOpenStackCluster": {}, + "hypershift-operator/controllers/hostedcluster/metrics/metrics_test.go|TestReportTransitionDurationForAWSEndpointConditions": {}, + "hypershift-operator/controllers/hostedcluster/metrics/metrics_test.go|TestReportTransitionDurationForGCPEndpointConditions": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestGCPPrivateRouterNetworkPolicy_IngressOnly": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestKasEndpointsToCIDRs": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcileNetworkPolicies_GCP_PrivateRouter": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcileNetworkPolicies_HCPEgressBlockCIDRs": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcileNetworkPolicies_LoadBalancerOauth": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcileNetworkPolicies_OpenshiftIngressPolicy": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcilePrivateRouterNetworkPolicy_DualStack": {}, + "hypershift-operator/controllers/hostedcluster/network_policies_test.go|TestReconcileVirtLauncherNetworkPolicyEgressPeers": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_test.go|TestBlockingBehavior": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_test.go|TestExecuteOrBlock": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_test.go|TestReconcileReport": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_tracing_test.go|TestReconcileReportBlockedOperationMarksSpan": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_tracing_test.go|TestReconcileReportExecuteCreatesSpan": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_tracing_test.go|TestReconcileReportExecuteOrBlockCreatesSpan": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_tracing_test.go|TestReconcileReportExecuteRecordsError": {}, + "hypershift-operator/controllers/hostedcluster/reconcile_report_tracing_test.go|TestReconcileReportMultipleOperationsCreateMultipleSpans": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingDeletingCluster": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingNoClusterIDOmitsAttribute": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingNoSpanLinkWithoutTraceparent": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingNotFoundSkipsSpan": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingRecordError": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingSpanAttributes": {}, + "hypershift-operator/controllers/hostedcluster/tracing_test.go|TestReconcileTracingSpanLinkFromTraceparent": {}, + "hypershift-operator/controllers/hostedclustersizing/hostedclustersizing_controller_test.go|TestSizingController_Reconcile": {}, + "hypershift-operator/controllers/manifests/controlplanepkioperator/reconcile_test.go|TestReconcileCSRApproverClusterRole": {}, + "hypershift-operator/controllers/manifests/controlplanepkioperator/reconcile_test.go|TestReconcileCSRApproverClusterRoleBinding": {}, + "hypershift-operator/controllers/manifests/controlplanepkioperator/reconcile_test.go|TestReconcileCSRSignerClusterRole": {}, + "hypershift-operator/controllers/manifests/controlplanepkioperator/reconcile_test.go|TestReconcileCSRSignerClusterRoleBinding": {}, + "hypershift-operator/controllers/nodepool/apiserver-haproxy/haproxy_test.go|TestAPIServerHAProxyConfig": {}, + "hypershift-operator/controllers/nodepool/aws_test.go|TestAWSMachineTemplate": {}, + "hypershift-operator/controllers/nodepool/aws_test.go|TestAWSMachineTemplateSpec": {}, + "hypershift-operator/controllers/nodepool/aws_test.go|TestAWSMachineTemplateSpec_StreamSelection": {}, + "hypershift-operator/controllers/nodepool/aws_test.go|TestAWSTagConflicts": {}, + "hypershift-operator/controllers/nodepool/aws_test.go|TestApplyAWSMachineOptions": {}, + "hypershift-operator/controllers/nodepool/azure_test.go|TestAzureMachineTemplateSpec": {}, + "hypershift-operator/controllers/nodepool/azure_test.go|TestAzureMachineTemplateSpecIPForwardingDefaultsOff": {}, + "hypershift-operator/controllers/nodepool/azure_test.go|TestAzureMachineTemplateSpecIPForwardingSerialization": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestCAPIReconcile": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestCAPIReconcile_machineset": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestGlobalPSManagedLabelOnMachines": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestInPlaceUpgradeMaxUnavailable": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestListMachineTemplatesAWS": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestListMachineTemplatesIBMCloud": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestMHCRemediationAllowedBubbledUpToReady": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestMHCRemediationAllowedChangedPredicate": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestMachineTemplateBuilders": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestMachineTemplateBuildersPreexisting": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestPause": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestPauseUnpauseCycle": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestReconcileMachineHealthCheck": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestReconcileMachineHealthCheckAnnotation": {}, + "hypershift-operator/controllers/nodepool/capi_test.go|TestReconcileSelectorDropsStaleLabels": {}, + "hypershift-operator/controllers/nodepool/conditions_test.go|TestGenerateReconciliationPausedCondition": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestCloudConfigHash": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestGlobalConfigString": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestMissingCoreConfigError": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestRolloutGlobalConfigString": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestRolloutHash": {}, + "hypershift-operator/controllers/nodepool/config_test.go|TestRolloutHashAnnotationSeeding": {}, + "hypershift-operator/controllers/nodepool/gcp_test.go|TestGcpMachineTemplateSpec": {}, + "hypershift-operator/controllers/nodepool/gcp_test.go|TestGcpMachineTemplateSpecWithRHELStream": {}, + "hypershift-operator/controllers/nodepool/gcp_test.go|TestNodePoolReconciler_setGCPConditions": {}, + "hypershift-operator/controllers/nodepool/instancetype/aws/provider_test.go|TestTransformInstanceTypeInfoWhenMissingRequiredFieldsItShouldReturnError": {}, + "hypershift-operator/controllers/nodepool/instancetype/aws/provider_test.go|TestTransformInstanceTypeInfoWhenValidInputItShouldTransformCorrectly": {}, + "hypershift-operator/controllers/nodepool/instancetype/azure/provider_test.go|TestCacheTTLRefresh": {}, + "hypershift-operator/controllers/nodepool/instancetype/azure/provider_test.go|TestGetInstanceTypeInfo": {}, + "hypershift-operator/controllers/nodepool/instancetype/azure/provider_test.go|TestTransformSKU_WhenMissingRequiredFields_ItShouldReturnError": {}, + "hypershift-operator/controllers/nodepool/instancetype/azure/provider_test.go|TestTransformSKU_WhenValidInput_ItShouldTransformCorrectly": {}, + "hypershift-operator/controllers/nodepool/instancetype/azure/provider_test.go|TestVMSizeNotFoundError": {}, + "hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go|TestJsonPatch": {}, + "hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go|TestKubevirtMachineTemplate": {}, + "hypershift-operator/controllers/nodepool/metrics/metrics_test.go|TestCollect": {}, + "hypershift-operator/controllers/nodepool/metrics/metrics_test.go|TestCollectConcurrency": {}, + "hypershift-operator/controllers/nodepool/metrics/metrics_test.go|TestErrorCache": {}, + "hypershift-operator/controllers/nodepool/metrics/metrics_test.go|TestReportVCpusCountByHCluster": {}, + "hypershift-operator/controllers/nodepool/metrics/metrics_test.go|TestReportVCpusWithKarpenterAutoNode": {}, + "hypershift-operator/controllers/nodepool/nodepool_controller_test.go|TestGetName": {}, + "hypershift-operator/controllers/nodepool/nodepool_controller_test.go|TestNodePoolReconciler_reconcile": {}, + "hypershift-operator/controllers/nodepool/nodepool_controller_test.go|TestNodepoolDeletionDoesntRequireHCluster": {}, + "hypershift-operator/controllers/nodepool/nodepool_controller_test.go|TestSetCIDRConflictConditionDualStack": {}, + "hypershift-operator/controllers/nodepool/openstack/openstack_test.go|TestClusterImageName": {}, + "hypershift-operator/controllers/nodepool/openstack/openstack_test.go|TestOpenStackMachineTemplate": {}, + "hypershift-operator/controllers/nodepool/reconcile_test.go|TestReconcile": {}, + "hypershift-operator/controllers/nodepool/secret_janitor_test.go|TestSecretJanitor_Reconcile": {}, + "hypershift-operator/controllers/nodepool/token_test.go|TestOutdatedUserdataSecret": {}, + "hypershift-operator/controllers/nodepool/token_test.go|TestTokenReconcile": {}, + "hypershift-operator/controllers/nodepool/token_test.go|TestTokenReconcileAfterTokenRotation": {}, + "hypershift-operator/controllers/nodepool/token_test.go|TestTokenReconcileManagementSideDrift": {}, + "hypershift-operator/controllers/nodepool/token_test.go|TestTokenUserDataSecret": {}, + "hypershift-operator/controllers/nodepool/tracing_test.go|TestNodePoolReconcileTracingDeletingNodePool": {}, + "hypershift-operator/controllers/nodepool/tracing_test.go|TestNodePoolReconcileTracingNotFoundSkipsSpan": {}, + "hypershift-operator/controllers/nodepool/tracing_test.go|TestNodePoolReconcileTracingRecordsError": {}, + "hypershift-operator/controllers/nodepool/tracing_test.go|TestNodePoolReconcileTracingSpanAttributes": {}, + "hypershift-operator/controllers/nodepool/tracing_test.go|TestNodePoolReconcileTracingSpanLinkFromTraceparent": {}, + "hypershift-operator/controllers/platform/aws/controller_test.go|TestDeleteAWSEndpointService": {}, + "hypershift-operator/controllers/platform/aws/controller_test.go|TestReconcileAWSEndpointServiceStatus": {}, + "hypershift-operator/controllers/platform/aws/controller_test.go|TestReconcileAWSEndpointServiceStatusPreservesAWSAPIError": {}, + "hypershift-operator/controllers/platform/aws/controller_test.go|TestReconcileRequeuesWithJitter": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestDelete_WhenNoPEConnections_ItShouldProceedToDelete": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestDelete_WhenPEConnectionsExist_ItShouldRejectAndRequeue": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestDelete_WhenPLSAlreadyDeleted_ItShouldReturnCompleted": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenAdditionalAllowedSubscriptionsChange_ItShouldUpdatePLS": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenAdditionalAllowedSubscriptionsMatch_ItShouldNotUpdatePLS": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenCRDoesNotExist_ItShouldReturnEmptyResult": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenCRIsDeleted_AndDeleteSucceeds_ItShouldRemoveFinalizer": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenCRIsDeleted_ItShouldDeleteThePLS": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenHostedClusterIsPaused_ItShouldRequeueAfterPauseExpiry": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenLoadBalancerIPIsSet_ItShouldCreateAPLS": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenLoadBalancerIPNotSet_ItShouldRequeue": {}, + "hypershift-operator/controllers/platform/azure/controller_test.go|TestReconcile_WhenPLSAlreadyExists_ItShouldUpdateStatus": {}, + "hypershift-operator/controllers/platform/gcp/loadbalancer_labels_controller_test.go|TestGCPLoadBalancerLabelsReconciler": {}, + "hypershift-operator/controllers/platform/gcp/loadbalancer_labels_controller_test.go|TestGCPLoadBalancerLabelsReconcilerRecordsManagedLabelsAfterCompletedOperation": {}, + "hypershift-operator/controllers/platform/gcp/loadbalancer_labels_controller_test.go|TestGCPLoadBalancerLabelsReconcilerSetsPendingCondition": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestComputeServiceAdapterListServiceAttachmentsPagination": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestComputeServiceAdapterListSubnetworksPagination": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetAPIError": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetAllSubnetsInUse": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetNoSubnets": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetServiceAttachmentsError": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetSingleAPICall": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestDiscoverNATSubnetSubnetInUseSkipsToNext": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestLookupForwardingRuleAPIError": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestLookupForwardingRuleMultipleResultsUsesFirst": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestLookupForwardingRuleNoResults": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestLookupForwardingRuleSingleResult": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestNATSubnetFilterFormat": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileNotFound": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcilePausedUntil": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecBothFieldsSetEarlyReturn": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecForwardingRuleEmptyNetwork": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecForwardingRuleLookupError": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecForwardingRuleNotYetProvisioned": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecPartialWriteForwardingRuleNamePreservedNATSubnetDiscovered": {}, + "hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go|TestReconcileSpecSetsForwardingRuleNameAndNATSubnet": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/controller_test.go|TestReconcile": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/controller_test.go|TestReconcileHostedClusterNotFound": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/machine_sizes_cache_test.go|TestMachineSizesCache": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/machine_sizes_cache_test.go|TestMachineSizesCacheMemoryFraction": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/machine_sizes_cache_test.go|TestMachineSizesCacheValidation": {}, + "hypershift-operator/controllers/resourcebasedcpautoscaler/machine_sizes_cache_test.go|TestMachineSizesCacheWithSizingConfig": {}, + "hypershift-operator/controllers/scheduler/aws/dedicated_request_serving_nodes_test.go|TestHostedClusterScheduler": {}, + "hypershift-operator/controllers/scheduler/aws/dedicated_request_serving_nodes_test.go|TestHostedClusterSchedulerAndSizer": {}, + "hypershift-operator/controllers/scheduler/aws/dedicated_request_serving_nodes_test.go|TestNodeReaper": {}, + "hypershift-operator/controlplaneoperator-overrides/overrides_test.go|TestCPOImage": {}, + "hypershift-operator/controlplaneoperator-overrides/overrides_test.go|TestExistingOverride": {}, + "hypershift-operator/controlplaneoperator-overrides/overrides_test.go|TestLoadOverrides": {}, + "hypershift-operator/controlplaneoperator-overrides/overrides_test.go|TestLoadOverridesSample": {}, + "hypershift-operator/featuregate/feature_test.go|TestAllHypershiftOperatorFeatureGates": {}, + "hypershift-operator/featuregate/feature_test.go|TestGCPPlatformFeatureGate": {}, + "hypershift-operator/featuregate/feature_test.go|TestHCPEtcdBackupFeatureGate": {}, + "ignition-server/controllers/image_file_cache_test.go|TestDownloadImageErrorHashStripping": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestCopyMCOOutputToMCC": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestCopyMCOOutputToMCCMixedContent": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestGetOrGenerateMCSCert": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestGetOrGenerateMCSCertCacheReuse": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestInvokeFeatureGateRenderScript": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestInvokeFeatureGateRenderScriptEmbedsBinaryAndPaths": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestSyncBuffer": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestWriteInitialFiles": {}, + "ignition-server/controllers/local_ignitionprovider_test.go|TestWriteInitialFilesMultipleConfigEntries": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestCacheInvalidationOnCloudConfigHashChange": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestCloudConfigPendingReasonOnHashMismatch": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestGetTokenIDTimeLived": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestOldTokenFallbackWithCloudConfigHashMismatch": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestProcessedExpiredToken": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestReconcile": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestReconcileOSStreamPropagation": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestRotateTokenID": {}, + "ignition-server/controllers/tokensecret_controller_test.go|TestTokenIDNeedRotation": {}, + "karpenter-operator/controllers/karpenter/karpenter_controller_test.go|TestKarpenterDeletion": {}, + "karpenter-operator/controllers/karpenter/karpenter_controller_test.go|TestReconcileCRDsConcurrentAccess": {}, + "karpenter-operator/controllers/karpenter/karpenter_controller_test.go|TestReconcileDefaultOpenshiftEC2NodeClass": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestHostedClusterFromHCP": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestReconcile": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestReconcileKubeletConfigMap": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestReconcileKubeletConfigMapOrphanCleanup": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestReconcileVersionResolution": {}, + "karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go|TestResolveVersion": {}, + "karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go|TestReconcileEC2NodeClass": {}, + "karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go|TestReconcileEC2NodeClassUpgradePause": {}, + "karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go|TestReconcileStatus": {}, + "karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go|TestReconcileStatusIdempotency": {}, + "karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go|TestReconcileStatusTagConflictCondition": {}, + "pkg/featuregates/featuregates_test.go|TestConfiguringUnknownFeatureSetErrors": {}, + "pkg/featuregates/featuregates_test.go|TestCreatingFeatureGates": {}, + "product-cli/cmd/cluster/aws/create_test.go|TestNewCreateCommand": {}, + "product-cli/cmd/cluster/aws/create_test.go|TestNewCreateCommandClientProvider": {}, + "product-cli/cmd/kubeconfig/create_test.go|TestNewCreateCommand": {}, + "product-cli/cmd/kubeconfig/create_test.go|TestNewCreateCommandRunE": {}, + "product-cli/cmd/nodepool/agent/create_test.go|TestNewCreateCommand": {}, + "product-cli/cmd/nodepool/agent/create_test.go|TestUpdateNodePool": {}, + "sharedingress-config-generator/config_test.go|TestGenerateConfig": {}, + "sharedingress-config-generator/controller_test.go|TestReconcile": {}, + "sharedingress-config-generator/controller_test.go|TestReconcileCoalescing": {}, + "support/api/scheme_test.go|TestEncode": {}, + "support/api/scheme_test.go|TestEncodeJSON": {}, + "support/api/scheme_test.go|TestEncodeJSON_Consistency": {}, + "support/api/scheme_test.go|TestEncodeJSON_EdgeCase_EpochTimestampInAnnotations": {}, + "support/api/scheme_test.go|TestEncodeJSON_EdgeCase_EpochTimestampInData": {}, + "support/api/scheme_test.go|TestEncodeJSON_NonMetaObject": {}, + "support/api/scheme_test.go|TestEncode_Consistency": {}, + "support/api/scheme_test.go|TestEncode_EdgeCase_EpochTimestampInAnnotations": {}, + "support/api/scheme_test.go|TestEncode_EdgeCase_EpochTimestampInLabels": {}, + "support/api/scheme_test.go|TestEncode_NonMetaObject": {}, + "support/awsutil/sg_test.go|TestUpdateResourceTags_FiltersAWSReservedKeys": {}, + "support/azureutil/azureutil_test.go|TestGetNetworkSecurityGroupNameFromNetworkSecurityGroupID": {}, + "support/capabilities/hosted_control_plane_capabilities_test.go|TestCalculateEnabledCapabilities": {}, + "support/capabilities/hosted_control_plane_capabilities_test.go|TestCalculateEnabledCapabilitiesIncludesCompatibilityRequirements": {}, + "support/capabilities/management_cluster_capabilities_test.go|TestDetectManagementCapabilities": {}, + "support/capabilities/management_cluster_capabilities_test.go|TestDetectNativeSidecarCapability": {}, + "support/catalogs/images_test.go|TestImageExistsFnGuestCluster": {}, + "support/certs/tls_test.go|TestCertChainingAttributesPresent": {}, + "support/certs/tls_test.go|TestReconcileSignedCertIdempotentWithDualStackIPs": {}, + "support/certs/tls_test.go|TestReconcileSignedCertWithCustomCAKeys": {}, + "support/certs/tls_test.go|TestValidateKeyPairConsidersAllFields": {}, + "support/certs/tls_test.go|TestValidateKeyPairConsidersExpiration": {}, + "support/certs/tls_test.go|TestValidateKeyPairItempotency": {}, + "support/certs/tls_test.go|TestValidateKeyPairRejectsMalformedIP": {}, + "support/config/resources_test.go|TestApplyResourceRequestOverrides": {}, + "support/config/resources_test.go|TestSetResources": {}, + "support/controlplane-component/controlplane-component_test.go|TestReconcile": {}, + "support/controlplane-component/defaults_test.go|TestComputeResourceHashConsistency": {}, + "support/controlplane-component/defaults_test.go|TestSetControlPlaneIsolationNodeFailureTolerations": {}, + "support/controlplane-component/generic-adapter_test.go|TestGenericAdapterReconcile": {}, + "support/controlplane-component/gvk_cache_test.go|TestGVKAccessCache": {}, + "support/controlplane-component/konnectivity-container_test.go|TestBuildContainer": {}, + "support/controlplane-component/konnectivity-container_test.go|TestBuildContainerDualMode": {}, + "support/controlplane-component/konnectivity-container_test.go|TestBuildContainerPreferIPv4": {}, + "support/controlplane-component/precondition_test.go|TestReconcileFailsWhenGateErrors": {}, + "support/controlplane-component/precondition_test.go|TestReconcileSkipsWorkloadWhenPreconditionUnmet": {}, + "support/gcputil/gcputil_test.go|TestBuildWorkloadIdentityCredentials": {}, + "support/gcputil/gcputil_test.go|TestBuildWorkloadIdentityCredentialsValidation": {}, + "support/gcputil/lb_resource_labels_test.go|TestReconcileLBResourceLabelAnnotations": {}, + "support/gcputil/lb_resource_labels_test.go|TestReconcileLBResourceLabelAnnotationsFirstTimeCollisionAdoption": {}, + "support/globalconfig/imagecontentsource_test.go|TestReconcileMgmtImageRegistryOverrides": {}, + "support/imageregistry/imagemetadata_test.go|TestCacheCleanupOnExpiration": {}, + "support/imageregistry/imagemetadata_test.go|TestMirrorAvailabilityCache": {}, + "support/imageregistry/imagemetadata_test.go|TestSeekOverride": {}, + "support/imageregistry/imagemetadata_test.go|TestSeekOverrideFallsBackWhenMirrorUnavailable": {}, + "support/imageregistry/imagemetadata_test.go|TestSeekOverrideWithCache": {}, + "support/k8sutil/serializer_test.go|TestDeserializeResource": {}, + "support/k8sutil/serializer_test.go|TestSerializeDeserializeRoundTrip": {}, + "support/k8sutil/serializer_test.go|TestSerializeResource": {}, + "support/konnectivityproxy/dialer_test.go|TestKonnectivityHealth": {}, + "support/konnectivityproxy/dialer_test.go|TestKonnectivityHealthEndRetry": {}, + "support/konnectivityproxy/dialer_test.go|TestKonnectivityHealthEndRetryPreventsStubbornFlag": {}, + "support/konnectivityproxy/dialer_test.go|TestKonnectivityHealthRecovery": {}, + "support/konnectivityproxy/resolver_test.go|TestFallbackIPsContext": {}, + "support/konnectivityproxy/resolver_test.go|TestProxyResolverResolve": {}, + "support/konnectivityproxy/resolver_test.go|TestProxyResolverResolveGuestDNSSuccess": {}, + "support/konnectivityproxy/resolver_test.go|TestResolveAllIPs": {}, + "support/konnectivityproxy/resolver_test.go|TestResolveAllIPsOrdering": {}, + "support/labelenforcingclient/labelenforcer_test.go|TestLabelEnforcingClientEnforcesLabel": {}, + "support/labelenforcingclient/labelenforcer_test.go|TestLabelEnforcingUpsertProvider": {}, + "support/metrics/sets_test.go|TestLoadMetricsConfig": {}, + "support/netutil/networking_test.go|TestIsMultusDisabled": {}, + "support/netutil/route_test.go|TestShortenName": {}, + "support/netutil/route_test.go|TestShortenNameIsDifferent": {}, + "support/netutil/route_test.go|TestShortenNameReturnShortNames": {}, + "support/podspec/containers_test.go|TestEnforceRestrictedSecurityContextToContainers": {}, + "support/podspec/containers_test.go|TestEnforceRestrictedSecurityContextToContainers_InvalidCapabilities": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenCacheIsExpired_ItShouldReQueryAPI": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenCacheIsFresh_ItShouldReturnCachedWithoutHTTPCall": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenCustomChannel_ItShouldPassChannelToAPI": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenNon200Status_ItShouldReturnError": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenValidResponse_ItShouldReturnReleaseImage": {}, + "support/releaseinfo/cincinnati_version_resolver_test.go|TestCincinnatiVersionResolver_WhenVersionNotInGraph_ItShouldReturnError": {}, + "support/releaseinfo/deserialize_test.go|TestDeserializeImageMetadata": {}, + "support/releaseinfo/deserialize_test.go|TestDeserializeImageMetadataMultiStreamContent": {}, + "support/releaseinfo/deserialize_test.go|TestDeserializeImageMetadataStreamsOnly": {}, + "support/releaseinfo/registry_image_content_policies_test.go|TestProviderWithOpenShiftImageRegistryOverridesDecorator_Lookup": {}, + "support/releaseinfo/registry_image_content_policies_test.go|TestProviderWithOpenShiftImageRegistryOverridesDecorator_LookupWithNilRepoSetupFn": {}, + "support/releaseinfo/releaseinfo_test.go|TestReleaseInfoKubeVirt": {}, + "support/releaseinfo/releaseinfo_test.go|TestReleaseInfoPowerVS": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatus": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatusCondition": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatusCondition_GetFailure": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatusCondition_RetryOnConflict": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatus_GetFailure": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatus_MutateError": {}, + "support/statuspatching/statuspatching_test.go|TestPatchStatus_RetryOnConflict": {}, + "support/supportedversion/version_test.go|TestFindLatestSupportedVersionWithSorting": {}, + "support/supportedversion/version_test.go|TestLookupDefaultOCPVersion_ListFailure": {}, + "support/supportedversion/version_test.go|TestRetrieveSupportedOCPVersion": {}, + "support/supportedversion/version_test.go|TestRetrieveSupportedOCPVersionWithRCFiltering": {}, + "support/supportedversion/version_test.go|TestRetrieveSupportedOCPVersion_ListFailure": {}, + "support/supportedversion/version_test.go|TestSupportedVersions": {}, + "support/tracing/tracing_test.go|TestInitProvider_NoopWhenUnset": {}, + "support/tracing/tracing_test.go|TestInitProvider_SDKProviderWhenSet": {}, + "support/tracing/tracing_test.go|TestInitProvider_ShutdownIsIdempotent": {}, + "support/tracing/tracing_test.go|TestParseSampler_AlwaysOff": {}, + "support/tracing/tracing_test.go|TestParseSampler_DefaultIsParentBasedAlwaysOn": {}, + "support/tracing/tracing_test.go|TestParseSampler_InvalidArgDefaultsToOne": {}, + "support/tracing/tracing_test.go|TestParseSampler_ParentBasedTraceIDRatio": {}, + "support/tracing/tracing_test.go|TestParseSampler_TraceIDRatio": {}, + "support/tracing/tracing_test.go|TestSpanLinkFromAnnotations_InvalidTraceparent": {}, + "support/tracing/tracing_test.go|TestSpanLinkFromAnnotations_NilAnnotations": {}, + "support/tracing/tracing_test.go|TestSpanLinkFromAnnotations_NoAnnotation": {}, + "support/tracing/tracing_test.go|TestSpanLinkFromAnnotations_ValidTraceparent": {}, + "support/tracing/tracing_test.go|TestTracer_CreatesSpansWithCorrectName": {}, + "support/tracing/tracing_test.go|TestTracer_ReturnsNamedTracer": {}, + "support/tracing/tracing_test.go|TestTracer_SpanRecordsErrorStatus": {}, + "support/upsert/apply_test.go|TestApplyManifest": {}, + "support/upsert/apply_test.go|TestApplyManifestLabelRemoval": {}, + "support/upsert/apply_test.go|TestApplyManifestLabelRemovalOnCreate": {}, + "support/upsert/apply_test.go|TestApplyManifestLabelRemovalWithEmptyLabels": {}, + "support/upsert/apply_test.go|TestApplyManifest_DesiredStateHash": {}, + "support/upsert/apply_test.go|TestApplyManifest_DesiredStateHashEdgeCases": {}, + "support/util/loglevel_test.go|TestLogLevelToEtcdLevel": {}, + "support/util/loglevel_test.go|TestLogLevelToEtcdLevelPanicsOnUnsupported": {}, + "support/util/registryoverride/registryoverride_test.go|TestReplace": {}, + "support/util/registryoverride/registryoverride_test.go|TestReplace_DoesNotMutateOverrides": {}, + "support/util/util_test.go|TestCompressDecompress": {}, +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy_test.go new file mode 100644 index 000000000000..bd51a7f0672e --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/legacy_test.go @@ -0,0 +1,48 @@ +package testfuncstructure + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "path/filepath" + "runtime" + "strings" + "testing" + + . "github.com/onsi/gomega" + + "k8s.io/apimachinery/pkg/util/sets" +) + +func TestLegacyExceptions(t *testing.T) { + g := NewWithT(t) + _, source, _, ok := runtime.Caller(0) + g.Expect(ok).To(BeTrue()) + repoRoot := filepath.Clean(filepath.Join(filepath.Dir(source), "../../../../..")) + declarations := map[string][]string{} + + for _, exception := range sets.List(legacyExceptions) { + t.Run(fmt.Sprintf("When %s is baselined, it should name an existing test", exception), func(t *testing.T) { + g := NewWithT(t) + filename, name, ok := strings.Cut(exception, "|") + g.Expect(ok).To(BeTrue()) + g.Expect(filename).To(HaveSuffix("_test.go")) + g.Expect(name).To(HavePrefix("Test")) + + names, loaded := declarations[filename] + if !loaded { + file, err := parser.ParseFile(token.NewFileSet(), filepath.Join(repoRoot, filename), nil, parser.SkipObjectResolution) + g.Expect(err).ToNot(HaveOccurred()) + for _, declaration := range file.Decls { + if function, ok := declaration.(*ast.FuncDecl); ok && function.Recv == nil { + names = append(names, function.Name.Name) + } + } + declarations[filename] = names + } + + g.Expect(names).To(ContainElement(name)) + }) + } +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/metadata.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/metadata.go new file mode 100644 index 000000000000..075e66dbd6d2 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/metadata.go @@ -0,0 +1,95 @@ +package testfuncstructure + +import ( + "go/token" + "go/types" + "path/filepath" + + "golang.org/x/tools/go/analysis" +) + +// productionMetadata preserves declaration provenance when an external test +// package imports the package under test, including its internal test variant. +type productionMetadata struct { + SymbolIDs []string + Tests []testMetadata +} + +type testMetadata struct { + SymbolID string + Name string + Filename string + Kind resolutionKind + Priority int + Suppressed bool +} + +// AFact marks productionMetadata as a serializable analysis fact. +func (*productionMetadata) AFact() {} + +// String describes the fact in analysistest fixture expectations. +func (*productionMetadata) String() string { + return "testfuncstructure production symbols" +} + +func exportProductionMetadata(pass *analysis.Pass, symbols []*productionSymbol, groups map[string][]*testFunction) { + metadata := &productionMetadata{SymbolIDs: make([]string, 0, len(symbols))} + for _, symbol := range symbols { + metadata.SymbolIDs = append(metadata.SymbolIDs, symbol.id) + for _, test := range groups[symbol.id] { + metadata.Tests = append(metadata.Tests, testMetadata{ + SymbolID: symbol.id, + Name: test.name, + Filename: filepath.Base(test.filename), + Kind: test.resolution.kind, + Priority: test.resolution.priority, + Suppressed: test.suppressed, + }) + } + } + pass.ExportPackageFact(metadata) +} + +func collectImportedTests(pass *analysis.Pass, symbolsByID map[string]*productionSymbol) []*testFunction { + target := externalPackageUnderTest(pass) + if target == nil || len(pass.Files) == 0 { + return nil + } + var metadata productionMetadata + if !pass.ImportPackageFact(target, &metadata) { + return nil + } + + directory := filepath.Dir(pass.Fset.Position(pass.Files[0].Pos()).Filename) + tests := make([]*testFunction, 0, len(metadata.Tests)) + for _, recorded := range metadata.Tests { + symbol := symbolsByID[recorded.SymbolID] + if symbol == nil { + continue + } + + filename := filepath.Join(directory, recorded.Filename) + namePos, nameEnd := token.NoPos, token.NoPos + if function, ok := target.Scope().Lookup(recorded.Name).(*types.Func); ok { + if file := pass.Fset.File(function.Pos()); file != nil { + filename = file.Name() + namePos = function.Pos() + nameEnd = min(namePos+token.Pos(len(recorded.Name)), file.Pos(file.Size())) + } + } + tests = append(tests, &testFunction{ + filename: filename, + name: recorded.Name, + namePos: namePos, + nameEnd: nameEnd, + imported: true, + suppressed: recorded.Suppressed, + resolution: resolution{ + kind: recorded.Kind, + target: symbol, + priority: recorded.Priority, + }, + }) + } + return tests +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production.go new file mode 100644 index 000000000000..24f67a622e34 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production.go @@ -0,0 +1,9 @@ +package ambiguous // want package:"testfuncstructure production symbols" + +type First struct{} + +func (*First) Reconcile() {} + +type Second struct{} + +func (*Second) Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production_test.go new file mode 100644 index 000000000000..db6b0edc2c01 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/ambiguous/production_test.go @@ -0,0 +1,23 @@ +package ambiguous + +import "testing" + +func TestReconcile(t *testing.T) { // want `test function "TestReconcile" does not map unambiguously to one production function \(candidates: First.Reconcile, Second.Reconcile\); use the canonical Test or Test_ form, or add a documented //nolint:hypershiftlinter exception` +} + +func TestLifecycle(t *testing.T) { // want `test function "TestLifecycle" does not map unambiguously to one production function \(candidates: First.Reconcile, Second.Reconcile\); use the canonical Test or Test_ form, or add a documented //nolint:hypershiftlinter exception` + first := &First{} + first.Reconcile() + second := &Second{} + second.Reconcile() +} + +func TestFirst_Reconcile(t *testing.T) { + first := &First{} + first.Reconcile() +} + +func TestSecondReconcile(t *testing.T) { + second := &Second{} + second.Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/external_test.go new file mode 100644 index 000000000000..6e8568b4eada --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/external_test.go @@ -0,0 +1,11 @@ +package bothlegacy_test + +import ( + "testing" + + "a/bothlegacy" +) + +func TestValidateErrors(t *testing.T) { + bothlegacy.Validate() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/internal_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/internal_test.go new file mode 100644 index 000000000000..608bb5583f38 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/internal_test.go @@ -0,0 +1,7 @@ +package bothlegacy + +import "testing" + +func TestValidate(t *testing.T) { + Validate() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/production.go new file mode 100644 index 000000000000..6e254e4a2eb8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/bothlegacy/production.go @@ -0,0 +1,3 @@ +package bothlegacy // want package:"testfuncstructure production symbols" + +func Validate() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/external_test.go new file mode 100644 index 000000000000..052cd7365161 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/external_test.go @@ -0,0 +1,21 @@ +package crosspackage_test + +import ( + "testing" + + "a/crosspackage" +) + +func TestValidate(t *testing.T) { // want `test function "TestValidate" duplicates "TestValidate" for Validate; consolidate scenarios under "TestValidate" using table-driven cases or t.Run subtests` + crosspackage.Validate() +} + +func TestWorker_Run(t *testing.T) { // want `test function "TestWorker_Run" adds another top-level test for Worker.Run while internal test "TestRun" still exists; consolidate both under "TestWorker_Run" using table-driven cases or t.Run subtests` + worker := &crosspackage.Worker{} + worker.Run() +} + +func TestOther_Run(t *testing.T) { + other := &crosspackage.Other{} + other.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/internal_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/internal_test.go new file mode 100644 index 000000000000..526a732afe1c --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/internal_test.go @@ -0,0 +1,12 @@ +package crosspackage + +import "testing" + +func TestValidate(t *testing.T) { + Validate() +} + +func TestRun(t *testing.T) { + worker := &Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/production.go new file mode 100644 index 000000000000..0dc717738334 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/crosspackage/production.go @@ -0,0 +1,11 @@ +package crosspackage // want package:"testfuncstructure production symbols" + +func Validate() {} + +type Worker struct{} + +func (*Worker) Run() {} + +type Other struct{} + +func (*Other) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production.go new file mode 100644 index 000000000000..b9b4cc5ab42b --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production.go @@ -0,0 +1,7 @@ +package exception // want package:"testfuncstructure production symbols" + +type Worker struct{} + +func (*Worker) Run() {} + +func Validate() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production_test.go new file mode 100644 index 000000000000..a0f1468c2522 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/exception/production_test.go @@ -0,0 +1,24 @@ +package exception + +import "testing" + +//nolint:hypershiftlinter // testfuncstructure: validates compatibility independently of the standard method test +func TestWorker_Run(t *testing.T) { + worker := &Worker{} + worker.Run() +} + +func TestWorkerRun(t *testing.T) { + worker := &Worker{} + worker.Run() +} + +func TestRun(t *testing.T) { // want `test function "TestRun" duplicates "TestWorkerRun" for Worker.Run; consolidate scenarios under "TestWorkerRun" using table-driven cases or t.Run subtests` + worker := &Worker{} + worker.Run() +} + +//nolint:hypershiftlinter // testfuncstructure: validates compatibility without an ordinary unit test +func TestValidate(t *testing.T) { + Validate() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external.go new file mode 100644 index 000000000000..78cab85eab06 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external.go @@ -0,0 +1,17 @@ +package external // want package:"testfuncstructure production symbols" + +func Validate() {} + +type Worker struct{} + +func (*Worker) Run() {} + +func hidden() {} + +type hiddenWorker struct{} + +func NewHiddenWorker() *hiddenWorker { + return &hiddenWorker{} +} + +func (*hiddenWorker) Execute() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external_test.go new file mode 100644 index 000000000000..80fe4cec31d5 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/external/external_test.go @@ -0,0 +1,30 @@ +package external_test + +import ( + "testing" + + "a/external" +) + +func TestValidate(t *testing.T) { + external.Validate() +} + +func TestValidateErrors(t *testing.T) { // want `test function "TestValidateErrors" also tests Validate; consolidate it into "TestValidate" using table-driven cases or t.Run subtests` + external.Validate() +} + +func TestWorker_Run(t *testing.T) { + worker := &external.Worker{} + worker.Run() +} + +func TestWorker_RunError(t *testing.T) { // want `test function "TestWorker_RunError" also tests Worker.Run; consolidate it into "TestWorker_Run" using table-driven cases or t.Run subtests` + worker := &external.Worker{} + worker.Run() +} + +func TestHiddenWorker_Execute(t *testing.T) { + worker := external.NewHiddenWorker() + worker.Execute() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/external_test.go new file mode 100644 index 000000000000..44efef6155fb --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/external_test.go @@ -0,0 +1,21 @@ +package externalexception_test + +import ( + "testing" + + "a/externalexception" +) + +func TestValidate(t *testing.T) { + externalexception.Validate() +} + +func TestWorker_Run(t *testing.T) { + worker := &externalexception.Worker{} + worker.Run() +} + +func TestWorkerRun(t *testing.T) { // want `test function "TestWorkerRun" duplicates "TestWorker_Run" for Worker.Run; consolidate scenarios under "TestWorker_Run" using table-driven cases or t.Run subtests` + worker := &externalexception.Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/internal_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/internal_test.go new file mode 100644 index 000000000000..4ee9b641fc01 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/internal_test.go @@ -0,0 +1,14 @@ +package externalexception + +import "testing" + +//nolint:hypershiftlinter // testfuncstructure: validates compatibility across package boundaries +func TestValidateCompatibility(t *testing.T) { + Validate() +} + +//nolint:hypershiftlinter // testfuncstructure: validates internal compatibility independently of the external contract +func TestWorker_Run(t *testing.T) { + worker := &Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/production.go new file mode 100644 index 000000000000..a2373bdfc5b1 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externalexception/production.go @@ -0,0 +1,7 @@ +package externalexception // want package:"testfuncstructure production symbols" + +func Validate() {} + +type Worker struct{} + +func (*Worker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/external_test.go new file mode 100644 index 000000000000..67618372d29b --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/external_test.go @@ -0,0 +1,16 @@ +package externallegacy_test + +import ( + "testing" + + "a/externallegacy" +) + +func TestValidate(t *testing.T) { // want `test function "TestValidate" adds another top-level test for Validate while internal test "TestValidateErrors" still exists` + externallegacy.Validate() +} + +func TestWorker_Run(t *testing.T) { // want `test function "TestWorker_Run" duplicates "TestWorker_Run" for Worker.Run; consolidate scenarios under "TestWorker_Run" using table-driven cases or t.Run subtests` + worker := &externallegacy.Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/internal_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/internal_test.go new file mode 100644 index 000000000000..5984fd2c85b9 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/internal_test.go @@ -0,0 +1,12 @@ +package externallegacy + +import "testing" + +func TestValidateErrors(t *testing.T) { + Validate() +} + +func TestWorker_Run(t *testing.T) { + worker := &Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/production.go new file mode 100644 index 000000000000..5845bb642066 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/externallegacy/production.go @@ -0,0 +1,7 @@ +package externallegacy // want package:"testfuncstructure production symbols" + +func Validate() {} + +type Worker struct{} + +func (*Worker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated.go new file mode 100644 index 000000000000..39e9b1fca27b --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated.go @@ -0,0 +1,6 @@ +// want package:"testfuncstructure production symbols" +// Code generated by test fixture. DO NOT EDIT. + +package good + +func GeneratedUtility() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated_test.go new file mode 100644 index 000000000000..2ebab1eb2ffe --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated_test.go @@ -0,0 +1,9 @@ +// Code generated by test fixture. DO NOT EDIT. + +package good + +import "testing" + +func TestReconcileGenerated(t *testing.T) { + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production.go new file mode 100644 index 000000000000..440dd5b3058a --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production.go @@ -0,0 +1,25 @@ +package good + +func Reconcile() {} + +func buildThing() {} + +func ReconcileLonger() {} + +type Controller struct{} + +func (*Controller) Sync() {} + +func (*Controller) reconcile() {} + +type OtherController struct{} + +func (*OtherController) Sync() {} + +type Starter struct{} + +func (*Starter) Start() {} + +type worker struct{} + +func (*worker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production_test.go new file mode 100644 index 000000000000..22174ee7d5f7 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/production_test.go @@ -0,0 +1,99 @@ +package good + +import ( + "fmt" + "testing" +) + +func TestReconcile(t *testing.T) { + t.Run("When input is valid, it should reconcile", func(t *testing.T) { + Reconcile() + }) + t.Run("When input is invalid, it should return an error", func(t *testing.T) { + Reconcile() + }) +} + +func TestBuildThing(t *testing.T) { + tests := []struct { + name string + }{ + {name: "When the input is valid, it should build the thing"}, + {name: "When the input is empty, it should build the default thing"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buildThing() + }) + } +} + +func TestReconcileLonger(t *testing.T) { + ReconcileLonger() +} + +func TestController_Sync(t *testing.T) { + controller := &Controller{} + controller.Sync() +} + +func TestController_Reconcile(t *testing.T) { + controller := &Controller{} + controller.reconcile() +} + +// A bare method test is accepted when its call identifies one receiver. +func TestSync(t *testing.T) { + controller := &OtherController{} + controller.Sync() +} + +// A bare method test is accepted without call evidence when only one method has +// that name. +func TestStart(t *testing.T) {} + +func TestWorker_Run(t *testing.T) { + instance := &worker{} + instance.Run() +} + +// Package-level behavior with no production function call has no meaningful +// one-function mapping. +func TestConstants(t *testing.T) { + if fmt.Sprint(1) != "1" { + t.Fatal("unexpected formatting") + } +} + +func testHelper() {} + +func TestTestHelper(t *testing.T) { + testHelper() +} + +func TestGeneratedUtilityScenario(t *testing.T) { + GeneratedUtility() +} + +//nolint:hypershiftlinter // testfuncstructure: intentional compatibility scenario +func TestReconcileCompatibility(t *testing.T) { + Reconcile() +} + +func TestMain(m *testing.M) {} + +func BenchmarkReconcile(b *testing.B) {} + +func FuzzReconcile(f *testing.F) {} + +func ExampleReconcile() {} + +type suite struct{} + +func (*suite) TestReconcile(t *testing.T) {} + +func Test_Reconcile(t *testing.T) {} + +func Test(t *testing.T) { // want `test function "Test" must be named "TestReconcile" to map to Reconcile; use one top-level test with table-driven cases or t.Run subtests, or add a documented //nolint:hypershiftlinter exception` + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production.go new file mode 100644 index 000000000000..765dae8c8a9c --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production.go @@ -0,0 +1,7 @@ +package identity // want package:"testfuncstructure production symbols" + +func Run() {} + +type Box[T any] struct{} + +func (Box[T]) Execute() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production_test.go new file mode 100644 index 000000000000..f8396c5eec8c --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/identity/production_test.go @@ -0,0 +1,12 @@ +package identity + +import "testing" + +func TestAnonymousInterfaceCall(t *testing.T) { + var runner interface{ Run() } + runner.Run() +} + +func TestGenericMethodCall(t *testing.T) { // want `test function "TestGenericMethodCall" must be named "TestBox_Execute" to map to Box.Execute` + Box[int]{}.Execute() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production.go new file mode 100644 index 000000000000..94e7238c6dcd --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production.go @@ -0,0 +1,7 @@ +package legacy // want package:"testfuncstructure production symbols" + +func Reconcile() {} + +type Worker struct{} + +func (*Worker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production_test.go new file mode 100644 index 000000000000..3eaaf0ddf4e3 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacy/production_test.go @@ -0,0 +1,21 @@ +package legacy + +import "testing" + +func TestReconcile(t *testing.T) { // want `test function "TestReconcile" adds another top-level test for Reconcile while legacy test "TestReconcileErrors" still exists; consolidate both under "TestReconcile" using table-driven cases or t.Run subtests` + Reconcile() +} + +func TestReconcileErrors(t *testing.T) { + Reconcile() +} + +func TestWorker_Run(t *testing.T) { + worker := &Worker{} + worker.Run() +} + +func TestWorkerRun(t *testing.T) { // want `test function "TestWorkerRun" duplicates "TestWorker_Run" for Worker.Run; consolidate scenarios under "TestWorker_Run" using table-driven cases or t.Run subtests` + worker := &Worker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production.go new file mode 100644 index 000000000000..51ce4accaecb --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production.go @@ -0,0 +1,5 @@ +package legacyambiguous // want package:"testfuncstructure production symbols" + +func Alpha() {} + +func Beta() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production_test.go new file mode 100644 index 000000000000..7a1e050a2eb9 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/legacyambiguous/production_test.go @@ -0,0 +1,12 @@ +package legacyambiguous + +import "testing" + +func TestWorkflow(t *testing.T) { + Alpha() + Beta() +} + +func TestAlpha(t *testing.T) { // want `test function "TestAlpha" adds another top-level test for Alpha while legacy test "TestWorkflow" still exists; consolidate both under "TestAlpha" using table-driven cases or t.Run subtests` + Alpha() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production.go new file mode 100644 index 000000000000..4316b6407468 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production.go @@ -0,0 +1,7 @@ +package precedence // want package:"testfuncstructure production symbols" + +func Reconcile() {} + +type Controller struct{} + +func (*Controller) Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production_test.go new file mode 100644 index 000000000000..5ccf6afdab4f --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/precedence/production_test.go @@ -0,0 +1,6 @@ +package precedence + +import "testing" + +// The exact package-function name takes precedence over the bare method alias. +func TestReconcile(t *testing.T) {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/generated.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/generated.go new file mode 100644 index 000000000000..9e1b6ab49e15 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/generated.go @@ -0,0 +1,9 @@ +// want package:"testfuncstructure production symbols" +// Code generated by fixture. DO NOT EDIT. + +package provenance + +func (widget *Widget) DeepCopy() *Widget { + copy := *widget + return © +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/helper_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/helper_test.go new file mode 100644 index 000000000000..7d944b88e3bc --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/helper_test.go @@ -0,0 +1,9 @@ +package provenance + +import "testing" + +func ExportedTestHelper(t *testing.T) {} + +type TestOnlyWorker struct{} + +func (*TestOnlyWorker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production.go new file mode 100644 index 000000000000..1b7d92e347f3 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production.go @@ -0,0 +1,5 @@ +package provenance + +func Validate() {} + +type Widget struct{} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production_test.go new file mode 100644 index 000000000000..e024482dda69 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/production_test.go @@ -0,0 +1,25 @@ +package provenance_test + +import ( + "testing" + + "a/provenance" +) + +func TestValidate(t *testing.T) { + provenance.Validate() +} + +func TestGeneratedBehavior(t *testing.T) { + widget := &provenance.Widget{} + widget.DeepCopy() +} + +func TestHelperBehavior(t *testing.T) { + provenance.ExportedTestHelper(t) +} + +func TestHelperMethodBehavior(t *testing.T) { + worker := &provenance.TestOnlyWorker{} + worker.Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/external_test.go new file mode 100644 index 000000000000..b8859ecfb3a2 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/external_test.go @@ -0,0 +1,11 @@ +package reversecross_test + +import ( + "testing" + + "a/reversecross" +) + +func TestValidateErrors(t *testing.T) { + reversecross.Validate() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/internal_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/internal_test.go new file mode 100644 index 000000000000..e352f3abc615 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/internal_test.go @@ -0,0 +1,7 @@ +package reversecross + +import "testing" + +func TestValidate(t *testing.T) { + Validate() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/production.go new file mode 100644 index 000000000000..bcdf640f073a --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/reversecross/production.go @@ -0,0 +1,3 @@ +package reversecross + +func Validate() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production.go new file mode 100644 index 000000000000..267fd8c89a41 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production.go @@ -0,0 +1,3 @@ +package scenarioonly // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production_test.go new file mode 100644 index 000000000000..8763fcf9c847 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/scenarioonly/production_test.go @@ -0,0 +1,11 @@ +package scenarioonly + +import "testing" + +func TestReconcileHappy(t *testing.T) { // want `test function "TestReconcileHappy" must be named "TestReconcile" for Reconcile; keep scenarios in table-driven cases or t.Run subtests` + Reconcile() +} + +func TestReconcileError(t *testing.T) { // want `test function "TestReconcileError" must be named "TestReconcile" for Reconcile; keep scenarios in table-driven cases or t.Run subtests` + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production.go new file mode 100644 index 000000000000..8d13c1141470 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production.go @@ -0,0 +1,9 @@ +package split // want package:"testfuncstructure production symbols" + +func Reconcile() {} + +func ReconcileErrors() {} + +type Controller struct{} + +func (*Controller) Sync() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production_test.go new file mode 100644 index 000000000000..22d6f1ba0b29 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/split/production_test.go @@ -0,0 +1,25 @@ +package split + +import "testing" + +func TestReconcile(t *testing.T) { + Reconcile() +} + +func TestReconcileAggregatesErrors(t *testing.T) { // want `test function "TestReconcileAggregatesErrors" also tests Reconcile; consolidate it into "TestReconcile" using table-driven cases or t.Run subtests` + Reconcile() +} + +// The longer exact production name wins over the Reconcile prefix. +func TestReconcileErrors(t *testing.T) { + ReconcileErrors() +} + +func TestController_SyncFailure(t *testing.T) { // want `test function "TestController_SyncFailure" must be named "TestController_Sync" for Controller.Sync; keep scenarios in table-driven cases or t.Run subtests` + controller := &Controller{} + controller.Sync() +} + +func TestDisconnectedName(t *testing.T) { // want `test function "TestDisconnectedName" must be named "TestReconcile" to map to Reconcile; use one top-level test with table-driven cases or t.Run subtests, or add a documented //nolint:hypershiftlinter exception` + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/external_test.go new file mode 100644 index 000000000000..359f0b4d7473 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/external_test.go @@ -0,0 +1,18 @@ +package testingalias_test + +import ( + "testing" + + "a/testingalias" +) + +type testingT = testing.T +type T = testingT + +func TestReconcile(t *T) { // want `test function "TestReconcile" duplicates "TestReconcile" for Reconcile; consolidate scenarios under "TestReconcile" using table-driven cases or t.Run subtests` + testingalias.Reconcile() +} + +func TestReconcileErrors(t *T) { // want `test function "TestReconcileErrors" also tests Reconcile; consolidate it into "TestReconcile" using table-driven cases or t.Run subtests` + testingalias.Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production.go new file mode 100644 index 000000000000..4605a66a5e8c --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production.go @@ -0,0 +1,3 @@ +package testingalias // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production_test.go new file mode 100644 index 000000000000..558684ee9d3d --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/testingalias/production_test.go @@ -0,0 +1,13 @@ +package testingalias + +import "testing" + +type T = testing.T + +func TestReconcile(t *T) { + Reconcile() +} + +func TestReconcileErrors(t *T) { // want `test function "TestReconcileErrors" also tests Reconcile; consolidate it into "TestReconcile" using table-driven cases or t.Run subtests` + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/external_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/external_test.go new file mode 100644 index 000000000000..502fda9e3ff0 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/external_test.go @@ -0,0 +1,15 @@ +package unexportedreceiver_test + +import ( + "testing" + + "a/unexportedreceiver" +) + +func TestWorker_Run(t *testing.T) { + unexportedreceiver.NewWorker().Run() +} + +func TestWorkerRun(t *testing.T) { // want `test function "TestWorkerRun" duplicates "TestWorker_Run" for worker.Run; consolidate scenarios under "TestWorker_Run" using table-driven cases or t.Run subtests` + unexportedreceiver.NewWorker().Run() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/production.go new file mode 100644 index 000000000000..9f470035fd6b --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unexportedreceiver/production.go @@ -0,0 +1,9 @@ +package unexportedreceiver // want package:"testfuncstructure production symbols" + +type worker struct{} + +func NewWorker() *worker { + return &worker{} +} + +func (*worker) Run() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production.go new file mode 100644 index 000000000000..3d298a28a1b7 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production.go @@ -0,0 +1,3 @@ +package common // want package:"testfuncstructure production symbols" + +func Validate() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production_test.go new file mode 100644 index 000000000000..59fe50d3470e --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/common/production_test.go @@ -0,0 +1,11 @@ +package common_test + +import ( + "testing" + + "a/unrelated/inner/common" +) + +func TestDependencyBehavior(t *testing.T) { + common.Helper() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/inner/common/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/inner/common/production.go new file mode 100644 index 000000000000..0d17756f81cf --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/unrelated/inner/common/production.go @@ -0,0 +1,3 @@ +package common + +func Helper() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/cleanup_tracker_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/cleanup_tracker_test.go new file mode 100644 index 000000000000..d068f10479b8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/cleanup_tracker_test.go @@ -0,0 +1,7 @@ +package util + +import "testing" + +func TestConcurrentAccess(t *testing.T) { // want `test function "TestConcurrentAccess" must be named "TestRecordFailure" to map to RecordFailure` + RecordFailure() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/production.go new file mode 100644 index 000000000000..e239df83054b --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/support/util/production.go @@ -0,0 +1,3 @@ +package util // want package:"testfuncstructure production symbols" + +func RecordFailure() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production.go new file mode 100644 index 000000000000..976097af0b29 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production.go @@ -0,0 +1,3 @@ +package good // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production_test.go new file mode 100644 index 000000000000..e5c5893793a8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/e2e/good/production_test.go @@ -0,0 +1,7 @@ +package good + +import "testing" + +func TestReconcileErrors(t *testing.T) { + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production.go new file mode 100644 index 000000000000..976097af0b29 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production.go @@ -0,0 +1,3 @@ +package good // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production_test.go new file mode 100644 index 000000000000..e5c5893793a8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/envtest/good/production_test.go @@ -0,0 +1,7 @@ +package good + +import "testing" + +func TestReconcileErrors(t *testing.T) { + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production.go new file mode 100644 index 000000000000..976097af0b29 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production.go @@ -0,0 +1,3 @@ +package good // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production_test.go new file mode 100644 index 000000000000..e5c5893793a8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/integration/good/production_test.go @@ -0,0 +1,7 @@ +package good + +import "testing" + +func TestReconcileErrors(t *testing.T) { + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production.go new file mode 100644 index 000000000000..976097af0b29 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production.go @@ -0,0 +1,3 @@ +package good // want package:"testfuncstructure production symbols" + +func Reconcile() {} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production_test.go new file mode 100644 index 000000000000..e5c5893793a8 --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/test/reqserving-e2e/good/production_test.go @@ -0,0 +1,7 @@ +package good + +import "testing" + +func TestReconcileErrors(t *testing.T) { + Reconcile() +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure.go new file mode 100644 index 000000000000..ff933970bdae --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure.go @@ -0,0 +1,793 @@ +package testfuncstructure + +import ( + "fmt" + "go/ast" + "go/build/constraint" + "go/token" + "go/types" + "path/filepath" + "slices" + "strings" + "unicode" + "unicode/utf8" + + "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/pathutil" + + "k8s.io/apimachinery/pkg/util/sets" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "testfuncstructure", + Doc: "checks that unit tests map one-to-one to production functions and methods", + Run: run, + FactTypes: []analysis.Fact{new(productionMetadata)}, +} + +type productionSymbol struct { + id string + fn *types.Func + display string + canonicalTest string + variants []testNameVariant +} + +type testNameVariant struct { + name string + priority int +} + +type testFunction struct { + decl *ast.FuncDecl + filename string + name string + namePos token.Pos + nameEnd token.Pos + imported bool + suppressed bool + resolution resolution +} + +type resolution struct { + kind resolutionKind + target *productionSymbol + candidates []*productionSymbol + priority int +} + +type resolutionKind int + +const ( + resolutionIgnored resolutionKind = iota + resolutionExact + resolutionScenario + resolutionDisconnected + resolutionAmbiguous +) + +type symbolMatch struct { + symbol *productionSymbol + kind resolutionKind + prefixLen int + priority int +} + +var excludedBuildTags = sets.New("e2e", "e2ev2", "envtest", "integration", "reqserving") + +func run(pass *analysis.Pass) (any, error) { + symbols := collectProductionSymbols(pass) + symbolsByID := make(map[string]*productionSymbol, len(symbols)) + symbolsByFunc := make(map[*types.Func]*productionSymbol, len(symbols)) + for _, symbol := range symbols { + symbolsByID[symbol.id] = symbol + symbolsByFunc[symbol.fn] = symbol + } + + tests := collectTests(pass) + groups := map[string][]*testFunction{} + for _, test := range tests { + test.resolution = resolveTest(pass, test, symbols, symbolsByFunc) + switch test.resolution.kind { + case resolutionExact, resolutionScenario, resolutionDisconnected: + groups[test.resolution.target.id] = append(groups[test.resolution.target.id], test) + case resolutionAmbiguous: + reportAmbiguous(pass, test) + if isLegacyException(pass, test) { + for _, candidate := range test.resolution.candidates { + legacyCandidate := *test + legacyCandidate.resolution = resolution{kind: resolutionDisconnected, target: candidate} + groups[candidate.id] = append(groups[candidate.id], &legacyCandidate) + } + } + } + } + for _, test := range collectImportedTests(pass, symbolsByID) { + groups[test.resolution.target.id] = append(groups[test.resolution.target.id], test) + } + + groupIDs := make([]string, 0, len(groups)) + for id := range groups { + groupIDs = append(groupIDs, id) + } + slices.Sort(groupIDs) + for _, id := range groupIDs { + reportGroup(pass, groups[id]) + } + if !strings.HasSuffix(pass.Pkg.Name(), "_test") && len(symbols) > 0 { + exportProductionMetadata(pass, symbols, groups) + } + + return nil, nil +} + +func collectProductionSymbols(pass *analysis.Pass) []*productionSymbol { + seen := map[string]*productionSymbol{} + if target := externalPackageUnderTest(pass); target != nil { + var metadata productionMetadata + if pass.ImportPackageFact(target, &metadata) { + collectExportedSymbols(target, seen) + productionIDs := sets.New(metadata.SymbolIDs...) + for id := range seen { + if !productionIDs.Has(id) { + delete(seen, id) + } + } + } + } else { + for _, file := range pass.Files { + filename := pass.Fset.File(file.Pos()).Name() + if strings.HasSuffix(filename, "_test.go") || ast.IsGenerated(file) { + continue + } + for _, decl := range file.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + obj, ok := pass.TypesInfo.Defs[fn.Name].(*types.Func) + if ok { + addProductionSymbol(seen, obj) + } + } + } + } + + symbols := make([]*productionSymbol, 0, len(seen)) + for _, symbol := range seen { + symbols = append(symbols, symbol) + } + slices.SortFunc(symbols, func(a, b *productionSymbol) int { + return strings.Compare(a.id, b.id) + }) + return symbols +} + +func externalPackageUnderTest(pass *analysis.Pass) *types.Package { + if !strings.HasSuffix(pass.Pkg.Name(), "_test") { + return nil + } + + name := strings.TrimSuffix(pass.Pkg.Name(), "_test") + wantPath := strings.TrimSuffix(pass.Pkg.Path(), "_test") + for _, imported := range pass.Pkg.Imports() { + if imported.Name() == name && imported.Path() == wantPath { + return imported + } + } + return nil +} + +func collectExportedSymbols(pkg *types.Package, seen map[string]*productionSymbol) { + for _, name := range pkg.Scope().Names() { + obj := pkg.Scope().Lookup(name) + switch obj := obj.(type) { + case *types.Func: + if obj.Exported() { + addProductionSymbol(seen, obj) + } + case *types.TypeName: + named, ok := types.Unalias(obj.Type()).(*types.Named) + if !ok { + continue + } + collectExportedMethods(pkg, named, types.NewMethodSet(named), seen) + collectExportedMethods(pkg, named, types.NewMethodSet(types.NewPointer(named)), seen) + } + } +} + +func collectExportedMethods(pkg *types.Package, receiver *types.Named, methodSet *types.MethodSet, seen map[string]*productionSymbol) { + for method := range methodSet.Methods() { + fn, ok := method.Obj().(*types.Func) + if !ok || !fn.Exported() || fn.Pkg() != pkg || receiverName(fn) != receiver.Obj().Name() { + continue + } + addProductionSymbol(seen, fn) + } +} + +func addProductionSymbol(seen map[string]*productionSymbol, fn *types.Func) { + if fn.Pkg() == nil { + return + } + + receiver := receiverName(fn) + testReceiver := upperFirst(receiver) + name := upperFirst(fn.Name()) + display := fn.Name() + canonical := "Test" + name + variants := []testNameVariant{{name: canonical, priority: 3}} + if receiver != "" { + display = receiver + "." + fn.Name() + canonical = "Test" + testReceiver + "_" + name + variants = []testNameVariant{ + {name: canonical, priority: 3}, + {name: "Test" + testReceiver + name, priority: 2}, + {name: "Test" + name, priority: 1}, + } + } + + id := symbolID(fn) + seen[id] = &productionSymbol{ + id: id, + fn: functionOrigin(fn), + display: display, + canonicalTest: canonical, + variants: variants, + } +} + +func functionOrigin(fn *types.Func) *types.Func { + if fn == nil { + return nil + } + return fn.Origin() +} + +func receiverName(fn *types.Func) string { + signature, ok := fn.Type().(*types.Signature) + if !ok || signature.Recv() == nil { + return "" + } + typ := types.Unalias(signature.Recv().Type()) + if pointer, ok := typ.(*types.Pointer); ok { + typ = types.Unalias(pointer.Elem()) + } + named, ok := typ.(*types.Named) + if !ok { + return "" + } + return named.Obj().Name() +} + +func symbolID(fn *types.Func) string { + receiver := receiverName(fn) + if receiver == "" { + return fn.Pkg().Path() + "." + fn.Name() + } + return fn.Pkg().Path() + "." + receiver + "." + fn.Name() +} + +func upperFirst(value string) string { + r, size := utf8.DecodeRuneInString(value) + if r == utf8.RuneError && size == 0 { + return value + } + return string(unicode.ToUpper(r)) + value[size:] +} + +func collectTests(pass *analysis.Pass) []*testFunction { + var tests []*testFunction + for _, file := range pass.Files { + filename := pass.Fset.File(file.Pos()).Name() + if !isUnitTestFile(filename, file) { + continue + } + for _, decl := range file.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok || !isOrdinaryTest(pass, fn) { + continue + } + tests = append(tests, &testFunction{ + decl: fn, + filename: filename, + name: fn.Name.Name, + namePos: fn.Name.Pos(), + nameEnd: fn.Name.End(), + suppressed: hasNoLintDirective(fn), + }) + } + } + slices.SortFunc(tests, func(a, b *testFunction) int { + if result := strings.Compare(a.filename, b.filename); result != 0 { + return result + } + return cmpPos(a.namePos, b.namePos) + }) + return tests +} + +func isUnitTestFile(filename string, file *ast.File) bool { + if !pathutil.IsUnitTest(filename) || ast.IsGenerated(file) { + return false + } + + normalized := filepath.ToSlash(filename) + for _, excludedPath := range []string{"/test/envtest/", "/test/reqserving-e2e/"} { + if strings.Contains(normalized, excludedPath) { + return false + } + } + return !hasExcludedBuildTag(file) +} + +func hasExcludedBuildTag(file *ast.File) bool { + var plusBuildExpressions []constraint.Expr + for _, group := range file.Comments { + if group.Pos() > file.Package { + break + } + for _, comment := range group.List { + switch { + case constraint.IsGoBuild(comment.Text): + expression, err := constraint.Parse(comment.Text) + return err == nil && requiresExcludedBuildTag(expression) + case constraint.IsPlusBuild(comment.Text): + expression, err := constraint.Parse(comment.Text) + if err == nil { + plusBuildExpressions = append(plusBuildExpressions, expression) + } + } + } + } + if len(plusBuildExpressions) == 0 { + return false + } + expression := plusBuildExpressions[0] + for _, next := range plusBuildExpressions[1:] { + expression = &constraint.AndExpr{X: expression, Y: next} + } + return requiresExcludedBuildTag(expression) +} + +func requiresExcludedBuildTag(expression constraint.Expr) bool { + otherTags := sets.New[string]() + collectOtherBuildTags(expression, otherTags) + if len(otherTags) > 16 { + return false + } + + tags := sets.List(otherTags) + for assignment := range 1 << len(tags) { + values := make(map[string]bool, len(tags)) + for index, tag := range tags { + values[tag] = assignment&(1< 0 { + return resolveMatches(highestPriorityMatches(exactMatches), called) + } + if len(scenarioMatches) > 0 { + longest := 0 + for _, match := range scenarioMatches { + longest = max(longest, match.prefixLen) + } + scenarioMatches = slices.DeleteFunc(scenarioMatches, func(match symbolMatch) bool { + return match.prefixLen != longest + }) + return resolveMatches(highestPriorityMatches(scenarioMatches), called) + } + + calledSymbols := make([]*productionSymbol, 0, len(called)) + for symbol := range called { + calledSymbols = append(calledSymbols, symbol) + } + slices.SortFunc(calledSymbols, func(a, b *productionSymbol) int { + return strings.Compare(a.id, b.id) + }) + if len(calledSymbols) == 1 { + return resolution{kind: resolutionDisconnected, target: calledSymbols[0]} + } + if len(calledSymbols) > 1 { + return resolution{kind: resolutionAmbiguous, candidates: calledSymbols} + } + return resolution{kind: resolutionIgnored} +} + +func matchSymbol(testName string, symbol *productionSymbol) (symbolMatch, bool) { + var best symbolMatch + found := false + for _, variant := range symbol.variants { + kind, ok := matchTestName(testName, variant.name) + if !ok { + continue + } + candidate := symbolMatch{ + symbol: symbol, + kind: kind, + prefixLen: len(variant.name), + priority: variant.priority, + } + if !found || betterMatch(candidate, best) { + best = candidate + found = true + } + } + return best, found +} + +func matchTestName(testName, productionTestName string) (resolutionKind, bool) { + if testName == productionTestName { + return resolutionExact, true + } + if len(testName) <= len(productionTestName) || !strings.HasPrefix(testName, productionTestName) { + return resolutionIgnored, false + } + remainder := testName[len(productionTestName):] + first, _ := utf8.DecodeRuneInString(remainder) + if first != '_' && !unicode.IsUpper(first) && !unicode.IsDigit(first) { + return resolutionIgnored, false + } + return resolutionScenario, true +} + +func highestPriorityMatches(matches []symbolMatch) []symbolMatch { + highest := 0 + for _, match := range matches { + highest = max(highest, match.priority) + } + return slices.DeleteFunc(matches, func(match symbolMatch) bool { + return match.priority != highest + }) +} + +func betterMatch(candidate, current symbolMatch) bool { + if candidate.kind != current.kind { + return candidate.kind == resolutionExact + } + if candidate.prefixLen != current.prefixLen { + return candidate.prefixLen > current.prefixLen + } + return candidate.priority > current.priority +} + +func resolveMatches(matches []symbolMatch, called sets.Set[*productionSymbol]) resolution { + if len(matches) == 1 { + match := matches[0] + return resolution{kind: match.kind, target: match.symbol, priority: match.priority} + } + + calledMatches := slices.DeleteFunc(slices.Clone(matches), func(match symbolMatch) bool { + return !called.Has(match.symbol) + }) + if len(calledMatches) == 1 { + match := calledMatches[0] + return resolution{kind: match.kind, target: match.symbol, priority: match.priority} + } + + candidates := make([]*productionSymbol, 0, len(matches)) + for _, match := range matches { + candidates = append(candidates, match.symbol) + } + slices.SortFunc(candidates, func(a, b *productionSymbol) int { + return strings.Compare(a.id, b.id) + }) + return resolution{kind: resolutionAmbiguous, candidates: candidates} +} + +func calledProductionSymbols(pass *analysis.Pass, test *ast.FuncDecl, symbolsByFunc map[*types.Func]*productionSymbol) sets.Set[*productionSymbol] { + called := sets.New[*productionSymbol]() + ast.Inspect(test.Body, func(node ast.Node) bool { + call, ok := node.(*ast.CallExpr) + if !ok { + return true + } + fn := calledFunction(pass, call.Fun) + if fn == nil { + return true + } + if symbol := symbolsByFunc[functionOrigin(fn)]; symbol != nil { + called.Insert(symbol) + } + return true + }) + return called +} + +func calledFunction(pass *analysis.Pass, expression ast.Expr) *types.Func { + switch expression := expression.(type) { + case *ast.ParenExpr: + return calledFunction(pass, expression.X) + case *ast.IndexExpr: + return calledFunction(pass, expression.X) + case *ast.IndexListExpr: + return calledFunction(pass, expression.X) + case *ast.Ident: + fn, _ := pass.TypesInfo.Uses[expression].(*types.Func) + return fn + case *ast.SelectorExpr: + if selection := pass.TypesInfo.Selections[expression]; selection != nil { + fn, _ := selection.Obj().(*types.Func) + return fn + } + fn, _ := pass.TypesInfo.Uses[expression.Sel].(*types.Func) + return fn + default: + return nil + } +} + +func reportAmbiguous(pass *analysis.Pass, test *testFunction) { + if shouldSuppress(pass, test) { + return + } + names := make([]string, 0, len(test.resolution.candidates)) + for _, candidate := range test.resolution.candidates { + names = append(names, candidate.display) + } + pass.Report(analysis.Diagnostic{ + Pos: test.namePos, + End: test.nameEnd, + Message: fmt.Sprintf("test function %q does not map unambiguously to one production function (candidates: %s); use the canonical Test or Test_ form, or add a documented //nolint:hypershiftlinter exception", test.name, strings.Join(names, ", ")), + }) +} + +func reportGroup(pass *analysis.Pass, tests []*testFunction) { + // Explicit exceptions do not reserve a production target, but legacy baseline + // tests still count so new tests cannot expand existing violations. Clone the + // group to preserve suppression metadata for external test packages. + tests = slices.DeleteFunc(slices.Clone(tests), func(test *testFunction) bool { + return test.suppressed + }) + if len(tests) == 0 { + return + } + + slices.SortFunc(tests, func(a, b *testFunction) int { + if a.resolution.kind != b.resolution.kind { + if a.resolution.kind == resolutionExact { + return -1 + } + if b.resolution.kind == resolutionExact { + return 1 + } + } + if a.resolution.priority != b.resolution.priority { + return b.resolution.priority - a.resolution.priority + } + if a.imported != b.imported { + if a.imported { + return -1 + } + return 1 + } + if result := strings.Compare(a.filename, b.filename); result != 0 { + return result + } + return cmpPos(a.namePos, b.namePos) + }) + + symbol := tests[0].resolution.target + preferred := symbol.canonicalTest + var primary *testFunction + if tests[0].resolution.kind == resolutionExact { + primary = tests[0] + preferred = primary.name + } + // The external test package can see both the local external test and the + // internal test recorded in the production package fact. If the external + // test is baselined and the internal canonical test is new, the internal + // pass cannot see the conflict. Report it on the new internal declaration + // so changed-line filtering evaluates the declaration that introduced it. + if primary != nil && primary.imported && !primary.suppressed && !isLegacyException(pass, primary) { + for _, test := range tests { + if test.imported || !isLegacyException(pass, test) { + continue + } + pass.Report(analysis.Diagnostic{ + Pos: primary.namePos, + End: primary.nameEnd, + Message: fmt.Sprintf("test function %q adds another top-level test for %s while external test %q still exists; consolidate both under %q using table-driven cases or t.Run subtests", primary.name, symbol.display, test.name, preferred), + Related: relatedTest(test, fmt.Sprintf("external top-level test %q", test.name)), + }) + break + } + } + if primary != nil && !shouldSuppress(pass, primary) { + for _, test := range tests[1:] { + if !test.imported && !isLegacyException(pass, test) { + continue + } + kind := "legacy" + if test.imported { + kind = "internal" + } + pass.Report(analysis.Diagnostic{ + Pos: primary.namePos, + End: primary.nameEnd, + Message: fmt.Sprintf("test function %q adds another top-level test for %s while %s test %q still exists; consolidate both under %q using table-driven cases or t.Run subtests", primary.name, symbol.display, kind, test.name, preferred), + Related: relatedTest(test, fmt.Sprintf("%s top-level test %q", kind, test.name)), + }) + break + } + } + + for _, test := range tests { + if shouldSuppress(pass, test) { + continue + } + + var message string + switch test.resolution.kind { + case resolutionExact: + if test == primary { + continue + } + message = fmt.Sprintf("test function %q duplicates %q for %s; consolidate scenarios under %q using table-driven cases or t.Run subtests", test.name, primary.name, symbol.display, preferred) + case resolutionScenario: + if primary != nil { + message = fmt.Sprintf("test function %q also tests %s; consolidate it into %q using table-driven cases or t.Run subtests", test.name, symbol.display, preferred) + } else { + message = fmt.Sprintf("test function %q must be named %q for %s; keep scenarios in table-driven cases or t.Run subtests", test.name, preferred, symbol.display) + } + case resolutionDisconnected: + message = fmt.Sprintf("test function %q must be named %q to map to %s; use one top-level test with table-driven cases or t.Run subtests, or add a documented //nolint:hypershiftlinter exception", test.name, preferred, symbol.display) + default: + continue + } + + diagnostic := analysis.Diagnostic{ + Pos: test.namePos, + End: test.nameEnd, + Message: message, + } + if primary != nil && primary != test { + diagnostic.Related = relatedTest(primary, fmt.Sprintf("existing top-level test %q", primary.name)) + } + pass.Report(diagnostic) + } +} + +func shouldSuppress(pass *analysis.Pass, test *testFunction) bool { + if test.imported || test.suppressed { + return true + } + return isLegacyException(pass, test) +} + +func relatedTest(test *testFunction, message string) []analysis.RelatedInformation { + if !test.namePos.IsValid() { + return nil + } + return []analysis.RelatedInformation{{ + Pos: test.namePos, + End: test.nameEnd, + Message: message, + }} +} + +func isLegacyException(pass *analysis.Pass, test *testFunction) bool { + return legacyExceptions.Has(legacyKey(pass, test)) +} + +func legacyKey(pass *analysis.Pass, test *testFunction) string { + const modulePath = "github.com/openshift/hypershift" + + packagePath := strings.TrimSuffix(pass.Pkg.Path(), "_test") + if packagePath == modulePath { + return filepath.Base(test.filename) + "|" + test.name + } + if relativePackage, ok := strings.CutPrefix(packagePath, modulePath+"/"); ok { + return relativePackage + "/" + filepath.Base(test.filename) + "|" + test.name + } + return packagePath + "/" + filepath.Base(test.filename) + "|" + test.name +} + +func cmpPos(a, b token.Pos) int { + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} diff --git a/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure_test.go b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure_test.go new file mode 100644 index 000000000000..58085650a25e --- /dev/null +++ b/hack/tools/hypershiftlinter/analyzers/testfuncstructure/testfuncstructure_test.go @@ -0,0 +1,224 @@ +package testfuncstructure + +import ( + "go/ast" + "go/parser" + "go/token" + "path/filepath" + "testing" + + . "github.com/onsi/gomega" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + legacyFixtures := []string{ + "a/legacy/production_test.go|TestReconcileErrors", + "a/legacy/production_test.go|TestWorker_Run", + "a/legacyambiguous/production_test.go|TestWorkflow", + "a/externallegacy/internal_test.go|TestValidateErrors", + "a/externallegacy/internal_test.go|TestWorker_Run", + "a/reversecross/external_test.go|TestValidateErrors", + "a/bothlegacy/internal_test.go|TestValidate", + "a/bothlegacy/external_test.go|TestValidateErrors", + // Explicit exceptions must take precedence over baseline accounting. + "a/exception/production_test.go|TestWorker_Run", + "a/externalexception/internal_test.go|TestWorker_Run", + } + for _, fixture := range legacyFixtures { + legacyExceptions.Insert(fixture) + defer legacyExceptions.Delete(fixture) + } + + testdata := analysistest.TestData() + t.Run("When unit tests are analyzed, it should distinguish supported mappings and structure violations", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, + "a/good", + "a/split", + "a/ambiguous", + "a/external", + "a/unexportedreceiver", + "a/identity", + "a/legacy", + "a/legacyambiguous", + "a/precedence", + "a/scenarioonly", + "test/e2e/good", + "test/envtest/good", + "test/integration/good", + "test/reqserving-e2e/good", + ) + }) + t.Run("When external tests import only a namesake dependency, it should ignore that dependency", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/unrelated/common") + }) + t.Run("When external tests call helpers or generated code, it should not map those calls to production targets", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/provenance") + }) + t.Run("When internal and external tests share production targets, it should report each external duplicate with the internal location", func(t *testing.T) { + g := NewWithT(t) + results := analysistest.Run(t, testdata, Analyzer, "a/crosspackage") + foundExternalPackage := false + for _, result := range results { + if result.Pass.Pkg.Name() != "crosspackage_test" { + continue + } + foundExternalPackage = true + g.Expect(result.Diagnostics).To(HaveLen(2)) + for _, diagnostic := range result.Diagnostics { + g.Expect(diagnostic.Related).To(HaveLen(1)) + position := result.Pass.Fset.Position(diagnostic.Related[0].Pos) + g.Expect(position.Filename).To(HaveSuffix("internal_test.go")) + } + } + g.Expect(foundExternalPackage).To(BeTrue()) + }) + t.Run("When an external scenario is baselined and a new internal canonical test is added, it should report the new internal declaration", func(t *testing.T) { + g := NewWithT(t) + results := analysistest.Run(&discardTesting{}, testdata, Analyzer, "a/reversecross") + + var externalResult *analysistest.Result + diagnosticCount := 0 + for _, result := range results { + diagnosticCount += len(result.Diagnostics) + if result.Pass.Pkg.Name() == "reversecross_test" { + externalResult = result + } + } + g.Expect(externalResult).ToNot(BeNil()) + g.Expect(diagnosticCount).To(Equal(1)) + g.Expect(externalResult.Diagnostics).To(HaveLen(1)) + + diagnostic := externalResult.Diagnostics[0] + g.Expect(diagnostic.Message).To(ContainSubstring(`while external test "TestValidateErrors" still exists`)) + position := externalResult.Pass.Fset.Position(diagnostic.Pos) + internalTestFile := filepath.Join(testdata, "src", "a", "reversecross", "internal_test.go") + g.Expect(position.Filename).To(Equal(internalTestFile)) + g.Expect(position.Line).To(Equal(declarationLine(t, internalTestFile, "TestValidate"))) + + g.Expect(diagnostic.Related).To(HaveLen(1)) + relatedPosition := externalResult.Pass.Fset.Position(diagnostic.Related[0].Pos) + g.Expect(relatedPosition.Filename).To(Equal(filepath.Join(testdata, "src", "a", "reversecross", "external_test.go"))) + g.Expect(relatedPosition.Line).To(Equal(declarationLine(t, relatedPosition.Filename, "TestValidateErrors"))) + + changedInternal := changedLineDiagnostics(externalResult, map[string]map[int]bool{ + position.Filename: {position.Line: true}, + }) + g.Expect(changedInternal).To(HaveLen(1)) + changedExternalOnly := changedLineDiagnostics(externalResult, map[string]map[int]bool{ + relatedPosition.Filename: {relatedPosition.Line: true}, + }) + g.Expect(changedExternalOnly).To(BeEmpty()) + }) + t.Run("When a new test reuses a removed baseline path and name, it should still report the violation", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "support/util") + }) + t.Run("When internal and external split tests are both baselined, it should report no diagnostics", func(t *testing.T) { + g := NewWithT(t) + results := analysistest.Run(t, testdata, Analyzer, "a/bothlegacy") + for _, result := range results { + g.Expect(result.Diagnostics).To(BeEmpty()) + } + }) + t.Run("When internal tests have explicit exceptions, it should allow an external canonical test and report remaining duplicates", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/externalexception") + }) + t.Run("When canonical tests have explicit exceptions, it should exclude them from duplicate accounting", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/exception") + }) + t.Run("When internal tests are only baselined, it should still reject additional external tests", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/externallegacy") + }) + t.Run("When tests use direct and chained testing.T aliases, it should still enforce one top-level test per production function", func(t *testing.T) { + analysistest.Run(t, testdata, Analyzer, "a/testingalias") + }) +} + +// analysistest matches // want comments against the package being analyzed. The +// reverse cross-package diagnostic intentionally points into the imported +// internal test file, so discard expectation mismatches and assert the result. +type discardTesting struct{} + +func (*discardTesting) Errorf(string, ...any) {} + +func declarationLine(t *testing.T, filename, name string) int { + t.Helper() + fileset := token.NewFileSet() + file, err := parser.ParseFile(fileset, filename, nil, 0) + if err != nil { + t.Fatalf("parse %s: %v", filename, err) + } + for _, declaration := range file.Decls { + function, ok := declaration.(*ast.FuncDecl) + if ok && function.Name.Name == name { + return fileset.Position(function.Name.Pos()).Line + } + } + t.Fatalf("declaration %s not found in %s", name, filename) + return 0 +} + +// changedLineDiagnostics mirrors golangci-lint's --new-from-rev position filter. +func changedLineDiagnostics(result *analysistest.Result, changed map[string]map[int]bool) []analysis.Diagnostic { + var diagnostics []analysis.Diagnostic + for _, diagnostic := range result.Diagnostics { + position := result.Pass.Fset.Position(diagnostic.Pos) + if changed[position.Filename][position.Line] { + diagnostics = append(diagnostics, diagnostic) + } + } + return diagnostics +} + +func TestHasExcludedBuildTag(t *testing.T) { + tests := []struct { + name string + source string + excluded bool + }{ + { + name: "When an envtest build tag is present, it should exclude the file", + source: "//go:build envtest\n\npackage fixture\n", + excluded: true, + }, + { + name: "When an excluded tag is negated, it should retain the unit test file", + source: "//go:build linux && !envtest\n\npackage fixture\n", + excluded: false, + }, + { + name: "When an excluded tag is optional, it should retain the unit test file", + source: "//go:build linux || envtest\n\npackage fixture\n", + excluded: false, + }, + { + name: "When every build path requires an excluded tag, it should exclude the file", + source: "//go:build (linux && envtest) || integration\n\npackage fixture\n", + excluded: true, + }, + { + name: "When a legacy build constraint requires envtest, it should exclude the file", + source: "// +build envtest\n\npackage fixture\n", + excluded: true, + }, + { + name: "When no build tag is present, it should retain the unit test file", + source: "package fixture\n", + excluded: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + file, err := parser.ParseFile(token.NewFileSet(), "fixture_test.go", test.source, parser.ParseComments) + if err != nil { + t.Fatalf("parse fixture: %v", err) + } + if actual := hasExcludedBuildTag(file); actual != test.excluded { + t.Fatalf("expected excluded=%t, got %t", test.excluded, actual) + } + }) + } +} diff --git a/hack/tools/hypershiftlinter/plugin.go b/hack/tools/hypershiftlinter/plugin.go index f29f728fac4f..966c0a6a81ee 100644 --- a/hack/tools/hypershiftlinter/plugin.go +++ b/hack/tools/hypershiftlinter/plugin.go @@ -13,6 +13,7 @@ import ( "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/sippyannotation" "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/testcasename" "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/testfuncname" + "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/testfuncstructure" "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/vacuouspass" "golang.org/x/tools/go/analysis" @@ -62,6 +63,7 @@ func allAnalyzers() []*analysis.Analyzer { return []*analysis.Analyzer{ testcasename.Analyzer, testfuncname.Analyzer, + testfuncstructure.Analyzer, sippyannotation.Analyzer, guestcluster.Analyzer, contextbackground.Analyzer, diff --git a/hack/tools/hypershiftlinter/plugin_test.go b/hack/tools/hypershiftlinter/plugin_test.go index d5dde9061631..514e5090e240 100644 --- a/hack/tools/hypershiftlinter/plugin_test.go +++ b/hack/tools/hypershiftlinter/plugin_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/hcpstatuspatch" + "github.com/openshift/hypershift/hack/tools/hypershiftlinter/analyzers/testfuncstructure" "golang.org/x/tools/go/analysis" ) @@ -22,6 +23,19 @@ func TestBuildAnalyzers(t *testing.T) { t.Fatalf("expected only the HC and HCP status analyzer, got %v", analyzerNames(got)) } }) + + t.Run("When testfuncstructure is enabled, it should select the unit test structure analyzer", func(t *testing.T) { + raw := map[string]any{ + "analyzers": map[string]any{"enable": []any{"testfuncstructure"}}, + } + got, err := BuildAnalyzers(raw) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(got) != 1 || got[0] != testfuncstructure.Analyzer { + t.Fatalf("expected only the unit test structure analyzer, got %v", analyzerNames(got)) + } + }) } func TestBuildAnalyzersRejectsUnknownTopLevelField(t *testing.T) { diff --git a/skills/restructure-commits/ownership.json b/skills/restructure-commits/ownership.json index 297fde919fc8..fea45c333860 100644 --- a/skills/restructure-commits/ownership.json +++ b/skills/restructure-commits/ownership.json @@ -40,6 +40,9 @@ {"pattern": "cmd/install/assets/crds/external-dns/**", "group": "Vendor", "why": "marked as generated"}, {"pattern": "cmd/infra/aws/delegating_client.go", "group": "Vendor", "why": "marked as generated"}, {"pattern": "hack/tools/deadcode-report/testdata/reachability/generated.go", "group": "Vendor", "why": "marked as generated"}, + {"pattern": "hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated.go", "group": "Vendor", "why": "marked as generated"}, + {"pattern": "hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/good/generated_test.go", "group": "Vendor", "why": "marked as generated"}, + {"pattern": "hack/tools/hypershiftlinter/analyzers/testfuncstructure/testdata/src/a/provenance/generated.go", "group": "Vendor", "why": "marked as generated"}, {"pattern": "support/awsapi/ec2.go", "group": "Vendor", "why": "marked as generated"}, {"pattern": "support/awsapi/elasticloadbalancing.go", "group": "Vendor", "why": "marked as generated"}, {"pattern": "support/awsapi/elasticloadbalancingv2.go", "group": "Vendor", "why": "marked as generated"},