diff --git a/analyzer/analyzer_test.go b/analyzer/analyzer_test.go index f6f9171a..2552e5e7 100644 --- a/analyzer/analyzer_test.go +++ b/analyzer/analyzer_test.go @@ -38,6 +38,10 @@ func TestTestifyLint(t *testing.T) { }, }, {dir: "ginkgo"}, + { + dir: "go-require-issue67", + flags: map[string]string{"disable-all": "true", "enable": checkers.NewGoRequire().Name()}, + }, { dir: "not-std-funcs", flags: map[string]string{"enable-all": "true"}, @@ -79,7 +83,7 @@ func TestTestifyLint(t *testing.T) { t.Fatal(err) } } - analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), anlzr, tt.dir) + analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), anlzr, filepath.Join(tt.dir, "...")) }) } } diff --git a/analyzer/testdata/src/go-require-issue67/conformance_test.go b/analyzer/testdata/src/go-require-issue67/conformance_test.go new file mode 100644 index 00000000..e24df2bb --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/conformance_test.go @@ -0,0 +1,13 @@ +package conformance_test + +import ( + "testing" + + "go-require-issue67/suite" + "go-require-issue67/tests" +) + +func TestConformance(t *testing.T) { + cSuite := new(suite.ConformanceTestSuite) + cSuite.Run(t, tests.ConformanceTests) +} diff --git a/analyzer/testdata/src/go-require-issue67/suite/features.go b/analyzer/testdata/src/go-require-issue67/suite/features.go new file mode 100644 index 00000000..de8ca2fe --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/suite/features.go @@ -0,0 +1,26 @@ +package suite + +// SupportedFeature allows opting in to additional conformance tests at an +// individual feature granularity. +type SupportedFeature string + +const ( + // This option indicates support for Gateway. + // Opting out of this is allowed only for GAMMA-only implementations + SupportGateway SupportedFeature = "Gateway" +) + +const ( + // This option indicates that the Gateway can also use port 8080 + SupportGatewayPort8080 SupportedFeature = "GatewayPort8080" + + // SupportGatewayStaticAddresses option indicates that the Gateway is capable + // of allocating pre-determined addresses, rather than dynamically having + // addresses allocated for it. + SupportGatewayStaticAddresses SupportedFeature = "GatewayStaticAddresses" +) + +const ( + // This option indicates support for ReferenceGrant. + SupportReferenceGrant SupportedFeature = "ReferenceGrant" +) diff --git a/analyzer/testdata/src/go-require-issue67/suite/suite.go b/analyzer/testdata/src/go-require-issue67/suite/suite.go new file mode 100644 index 00000000..2b4a239f --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/suite/suite.go @@ -0,0 +1,33 @@ +package suite + +import "testing" + +type ConformanceTestSuite struct{} + +// Run runs the provided set of conformance tests. +func (suite *ConformanceTestSuite) Run(t *testing.T, tests []ConformanceTest) { + for _, test := range tests { + t.Run(test.ShortName, func(t *testing.T) { + test.Run(t, suite) + }) + } +} + +type ConformanceTest struct { + ShortName string + Description string + Features []SupportedFeature + Manifests []string + Parallel bool + Test func(*testing.T, *ConformanceTestSuite) +} + +// Run runs an individual tests, applying and cleaning up the required manifests +// before calling the Test function. +func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) { + if test.Parallel { + t.Parallel() + } + + test.Test(t, suite) +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-route-kind.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-route-kind.go new file mode 100644 index 00000000..e0a0dac8 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-route-kind.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayInvalidRouteKind) +} + +var GatewayInvalidRouteKind = suite.ConformanceTest{ + ShortName: "GatewayInvalidRouteKind", + Description: "A Gateway in the gateway-conformance-infra namespace should fail to become ready an invalid Route kind is specified.", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + t.Run("Gateway listener should have a false ResolvedRefs condition with reason InvalidRouteKinds and no supportedKinds", func(t *testing.T) { + // ... + }) + + t.Run("Gateway listener should have a false ResolvedRefs condition with reason InvalidRouteKinds and HTTPRoute must be put in the supportedKinds", func(t *testing.T) { + // ... + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-tls-configuration.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-tls-configuration.go new file mode 100644 index 00000000..1d030364 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-invalid-tls-configuration.go @@ -0,0 +1,55 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayInvalidTLSConfiguration) +} + +var GatewayInvalidTLSConfiguration = suite.ConformanceTest{ + ShortName: "GatewayInvalidTLSConfiguration", + Description: "A Gateway should fail to become ready if the Gateway has an invalid TLS configuration", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + testCases := []struct { + name string + gatewayNamespacedName NamespacedName + }{ + { + name: "Nonexistent secret referenced as CertificateRef in a Gateway listener", + gatewayNamespacedName: NamespacedName{Name: "gateway-certificate-nonexistent-secret", Namespace: "gateway-conformance-infra"}, + }, + { + name: "Unsupported group resource referenced as CertificateRef in a Gateway listener", + gatewayNamespacedName: NamespacedName{Name: "gateway-certificate-unsupported-group", Namespace: "gateway-conformance-infra"}, + }, + { + name: "Unsupported kind resource referenced as CertificateRef in a Gateway listener", + gatewayNamespacedName: NamespacedName{Name: "gateway-certificate-unsupported-kind", Namespace: "gateway-conformance-infra"}, + }, + { + name: "Malformed secret referenced as CertificateRef in a Gateway listener", + gatewayNamespacedName: NamespacedName{Name: "gateway-certificate-malformed-secret", Namespace: "gateway-conformance-infra"}, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // ... + }) + } + }, +} + +type NamespacedName struct { + Namespace string + Name string +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-modify-listeners.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-modify-listeners.go new file mode 100644 index 00000000..38f2fbd7 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-modify-listeners.go @@ -0,0 +1,57 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayModifyListeners) +} + +var GatewayModifyListeners = suite.ConformanceTest{ + ShortName: "GatewayModifyListeners", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + t.Run("should be able to add a listener that then becomes available for routing traffic", func(t *testing.T) { + _, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + var err error + require.NoErrorf(t, err, "error getting Gateway: %v", err) + + var err2 error + require.NoErrorf(t, err2, "error patching the Gateway: %v", err) + + var err3 error + require.NoErrorf(t, err3, "error getting Gateway: %v", err) + require.NotEqual(t, "original.Generation", "updated.Generation", + "generation should change after an update") + }) + + t.Run("should be able to remove listeners, which would then stop routing the relevant traffic", func(t *testing.T) { + _, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + var err error + require.NoErrorf(t, err, "error getting Gateway: %v", err) + + require.Equalf(t, 2, "len(mutate.Spec.Listeners", "the gateway must have 2 listeners") + + var err2 error + require.NoErrorf(t, err2, "error patching the Gateway: %v", err) + + var err3 error + require.NoErrorf(t, err3, "error getting Gateway: %v", err) + + require.NotEqual(t, "original.Generation", "updated.Generation", + "generation should change after an update") + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-observed-generation-bump.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-observed-generation-bump.go new file mode 100644 index 00000000..15a9f719 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-observed-generation-bump.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayObservedGenerationBump) +} + +var GatewayObservedGenerationBump = suite.ConformanceTest{ + ShortName: "GatewayObservedGenerationBump", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportGatewayPort8080, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + t.Run("observedGeneration should increment", func(t *testing.T) { + var err error + require.NoErrorf(t, err, "error getting Gateway: %v", err) + require.NotEqual(t, "original.Generation", "updated.Generation", + "generation should change after an update") + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-invalid-reference-grant.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-invalid-reference-grant.go new file mode 100644 index 00000000..7dee26bc --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-invalid-reference-grant.go @@ -0,0 +1,27 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewaySecretInvalidReferenceGrant) +} + +var GatewaySecretInvalidReferenceGrant = suite.ConformanceTest{ + ShortName: "GatewaySecretInvalidReferenceGrant", + Description: "A Gateway in the gateway-conformance-infra namespace should fail to become ready if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant exists but does not grant permission to that specific Secret", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportReferenceGrant, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + _ = NamespacedName{Name: "gateway-secret-invalid-reference-grant", Namespace: "gateway-conformance-infra"} + + t.Run("Gateway listener should have a false ResolvedRefs condition with reason RefNotPermitted", func(t *testing.T) { + // ... + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-missing-reference-grant.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-missing-reference-grant.go new file mode 100644 index 00000000..3f612332 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-missing-reference-grant.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewaySecretMissingReferenceGrant) +} + +var GatewaySecretMissingReferenceGrant = suite.ConformanceTest{ + ShortName: "GatewaySecretMissingReferenceGrant", + Description: "A Gateway in the gateway-conformance-infra namespace should fail to become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to the Secret does not exist", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportReferenceGrant, + }, + Manifests: []string{"tests/gateway-secret-missing-reference-grant.yaml"}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + _ = NamespacedName{Name: "gateway-secret-missing-reference-grant", Namespace: "gateway-conformance-infra"} + + t.Run("Gateway listener should have a false ResolvedRefs condition with reason RefNotPermitted", func(t *testing.T) { + // ... + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-all-in-namespace.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-all-in-namespace.go new file mode 100644 index 00000000..69e8e182 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-all-in-namespace.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewaySecretReferenceGrantAllInNamespace) +} + +var GatewaySecretReferenceGrantAllInNamespace = suite.ConformanceTest{ + ShortName: "GatewaySecretReferenceGrantAllInNamespace", + Description: "A Gateway in the gateway-conformance-infra namespace should become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to all Secrets in the namespace exists", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportReferenceGrant, + }, + Manifests: []string{"tests/gateway-secret-reference-grant-all-in-namespace.yaml"}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + _ = NamespacedName{Name: "gateway-secret-reference-grant-all-in-namespace", Namespace: "gateway-conformance-infra"} + + t.Run("Gateway listener should have a true ResolvedRefs condition and a true Programmed condition", func(t *testing.T) { + // .... + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-specific.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-specific.go new file mode 100644 index 00000000..8e5b5708 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-secret-reference-grant-specific.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewaySecretReferenceGrantSpecific) +} + +var GatewaySecretReferenceGrantSpecific = suite.ConformanceTest{ + ShortName: "GatewaySecretReferenceGrantSpecific", + Description: "A Gateway in the gateway-conformance-infra namespace should become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to the specific Secret exists", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportReferenceGrant, + }, + Manifests: []string{"tests/gateway-secret-reference-grant-specific.yaml"}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + _ = NamespacedName{Name: "gateway-secret-reference-grant-specific", Namespace: "gateway-conformance-infra"} + + t.Run("Gateway listener should have a true ResolvedRefs condition and a true Programmed condition", func(t *testing.T) { + // ... + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/gateway-static-addresses.go b/analyzer/testdata/src/go-require-issue67/tests/gateway-static-addresses.go new file mode 100644 index 00000000..35ef3944 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/gateway-static-addresses.go @@ -0,0 +1,66 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayStaticAddresses) +} + +// GatewayStaticAddresses tests the implementation's support of deploying +// Gateway resources with static addresses, or in other words addresses +// provided via the specification rather than relying on the underlying +// implementation/network to dynamically assign the Gateway an address. +// +// Running this test against your own implementation is currently a little bit +// messy, as at the time of writing we didn't have great ways to provide the +// test suite with things like known good, or known bad addresses to run the +// test with (as we obviously can't determine that for the implementation). +// +// As such, if you're trying to enable this test for yourself and you're getting +// confused about how to provide addresses, you'll actually do that in the +// conformance test suite BEFORE you even set up and run your tests. Make sure +// you populate the following test suite fields: +// +// - suite.UsableNetworkAddresses +// - suite.UnusableNetworkAddresses +// +// With appropriate network addresses for your network environment. +var GatewayStaticAddresses = suite.ConformanceTest{ + ShortName: "GatewayStaticAddresses", + Description: "A Gateway in the gateway-conformance-infra namespace should be able to use previously determined addresses.", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportGatewayStaticAddresses, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + _, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + var err error + require.NoError(t, err, "error getting Gateway: %v", err) + + var err2 error + require.NoError(t, err2, "failed to patch Gateway: %v", err) + + t.Logf("verifying that the Gateway %s/%s is now accepted, but is not programmed due to an address that can't be used") + var err3 error + require.NoError(t, err3, "error getting Gateway: %v", err) + + t.Logf("patching Gateway %s/%s to remove the unusable address %s") + var err4 error + require.NoError(t, err4, "failed to patch Gateway: %v", err) + + t.Logf("verifying that the Gateway %s/%s is accepted and programmed with the usable static address %s assigned") + require.Equal(t, "usableAddress.Type", "currentGW.Status.Addresses[0].Type", + "expected address type to match the usable address") + require.Equal(t, "usableAddress.Value", "currentGW.Status.Addresses[0].Value", + "expected usable address to be assigned") + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/httproute-reference-grant.go b/analyzer/testdata/src/go-require-issue67/tests/httproute-reference-grant.go new file mode 100644 index 00000000..7f60d360 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/httproute-reference-grant.go @@ -0,0 +1,39 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, HTTPRouteReferenceGrant) +} + +var HTTPRouteReferenceGrant = suite.ConformanceTest{ + ShortName: "HTTPRouteReferenceGrant", + Description: "A single HTTPRoute in the gateway-conformance-infra namespace, with a backendRef in the gateway-conformance-web-backend namespace, should attach to Gateway in the gateway-conformance-infra namespace", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + }, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("Simple HTTP request should reach web-backend", func(t *testing.T) { + var err error + require.NoError(t, err) + }) + + _, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + var err error + require.NoError(t, err) + + t.Run("Simple HTTP request should return 500 after deleting the relevant reference grant", func(t *testing.T) { + var err error + require.NoError(t, err) + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/main.go b/analyzer/testdata/src/go-require-issue67/tests/main.go new file mode 100644 index 00000000..57434b61 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/main.go @@ -0,0 +1,5 @@ +package tests + +import "go-require-issue67/suite" + +var ConformanceTests []suite.ConformanceTest diff --git a/analyzer/testdata/src/go-require-issue67/tests/mesh-split.go b/analyzer/testdata/src/go-require-issue67/tests/mesh-split.go new file mode 100644 index 00000000..026fff34 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/mesh-split.go @@ -0,0 +1,128 @@ +package tests + +import ( + "fmt" + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, MeshTrafficSplit) +} + +var MeshTrafficSplit = suite.ConformanceTest{ + ShortName: "MeshTrafficSplit", + Description: "A mesh client can send traffic to a Service which is split between two versions", + Features: []suite.SupportedFeature{}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + cases := []ExpectedResponse{ + { + Request: Request{ + Host: "echo", + Method: "GET", + Path: "/v1", + }, + Response: Response{ + StatusCode: 200, + }, + Backend: "echo-v1", + }, + { + Request: Request{ + Host: "echo", + Method: "GET", + Path: "/v2", + }, + Response: Response{ + StatusCode: 200, + }, + Backend: "echo-v2", + }, + } + for i := range cases { + // Declare tc here to avoid loop variable + // reuse issues across parallel tests. + tc := cases[i] + t.Run(tc.GetTestCaseName(i), func(t *testing.T) { + // ... + }) + } + }, +} + +// ExpectedResponse defines the response expected for a given request. +type ExpectedResponse struct { + // Request defines the request to make. + Request Request + + // ExpectedRequest defines the request that + // is expected to arrive at the backend. If + // not specified, the backend request will be + // expected to match Request. + ExpectedRequest *ExpectedRequest + + // BackendSetResponseHeaders is a set of headers + // the echoserver should set in its response. + BackendSetResponseHeaders map[string]string + + // Response defines what response the test case + // should receive. + Response Response + + Backend string + Namespace string + + // User Given TestCase name + TestCaseName string +} + +// GetTestCaseName gets the user-defined test case name or generates one from expected response to a given request. +func (er *ExpectedResponse) GetTestCaseName(i int) string { + // If TestCase name is provided then use that or else generate one. + if er.TestCaseName != "" { + return er.TestCaseName + } + + headerStr := "" + reqStr := "" + + if er.Request.Headers != nil { + headerStr = " with headers" + } + + reqStr = fmt.Sprintf("%d request to '%s%s'%s", i, er.Request.Host, er.Request.Path, headerStr) + + if er.Backend != "" { + return fmt.Sprintf("%s should go to %s", reqStr, er.Backend) + } + return fmt.Sprintf("%s should receive a %d", reqStr, er.Response.StatusCode) +} + +// Request can be used as both the request to make and a means to verify +// that echoserver received the expected request. Note that multiple header +// values can be provided, as a comma-separated value. +type Request struct { + Host string + Method string + Path string + Headers map[string]string + UnfollowRedirect bool + Protocol string +} + +// ExpectedRequest defines expected properties of a request that reaches a backend. +type ExpectedRequest struct { + Request + + // AbsentHeaders are names of headers that are expected + // *not* to be present on the request. + AbsentHeaders []string +} + +// Response defines expected properties of a response from a backend. +type Response struct { + StatusCode int + Headers map[string]string + AbsentHeaders []string +} diff --git a/analyzer/testdata/src/go-require-issue67/tests/tlsroute-invalid-reference-grant.go b/analyzer/testdata/src/go-require-issue67/tests/tlsroute-invalid-reference-grant.go new file mode 100644 index 00000000..c5abbf63 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/tests/tlsroute-invalid-reference-grant.go @@ -0,0 +1,25 @@ +package tests + +import ( + "testing" + + "go-require-issue67/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, TLSRouteInvalidReferenceGrant) +} + +var TLSRouteInvalidReferenceGrant = suite.ConformanceTest{ + ShortName: "TLSRouteInvalidReferenceGrant", + Description: "A single TLSRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace" + + " without valid ReferenceGrant, should have the ResolvedRefs condition set to False", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + }, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("TLSRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a "+ + "ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) { + }) + }, +} diff --git a/analyzer/testdata/src/go-require-issue67/utils/http/mirror.go b/analyzer/testdata/src/go-require-issue67/utils/http/mirror.go new file mode 100644 index 00000000..8c410e21 --- /dev/null +++ b/analyzer/testdata/src/go-require-issue67/utils/http/mirror.go @@ -0,0 +1,49 @@ +package http + +import ( + "fmt" + "regexp" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func ExpectMirroredRequest(t *testing.T, client interface{}, clientset interface{}, mirrorPods []BackendRef, path string) { + for i, mirrorPod := range mirrorPods { + if mirrorPod.Name == "" { + t.Fatalf("Mirrored BackendRef[%d].Name wasn't provided in the testcase, this test should only check http request mirror.", i) + } + } + + var wg sync.WaitGroup + wg.Add(len(mirrorPods)) + + for _, mirrorPod := range mirrorPods { + go func(mirrorPod BackendRef) { + defer wg.Done() + + require.Eventually(t, func() bool { // want "go-require: require must only be used in the goroutine running the test function" + mirrorLogRegexp := regexp.MustCompile(fmt.Sprintf("Echoing back request made to \\%s to client", path)) + // ... + + for _, log := range [][]byte{} { + if mirrorLogRegexp.MatchString(string(log)) { + return true + } + } + return false + }, 60*time.Second, time.Millisecond*100, fmt.Sprintf(`Couldn't find mirrored request in "%s/%s" logs`)) + }(mirrorPod) + } + + wg.Wait() + + t.Log("Found mirrored request log in all desired backends") +} + +type BackendRef struct { + Name string + Namespace string +} diff --git a/internal/checkers/go_require.go b/internal/checkers/go_require.go index 4c0c9d1f..0844f15a 100644 --- a/internal/checkers/go_require.go +++ b/internal/checkers/go_require.go @@ -66,6 +66,7 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect nodesFilter := []ast.Node{ (*ast.FuncDecl)(nil), + (*ast.FuncType)(nil), (*ast.GoStmt)(nil), (*ast.CallExpr)(nil), } @@ -83,6 +84,19 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect return true } + if ft, ok := node.(*ast.FuncType); ok { + if !isTestingAnonymousFunc(pass, ft) { + return false + } + + if push { + inGoroutineRunningTestFunc.Push(true) + } else { + inGoroutineRunningTestFunc.Pop() + } + return true + } + if _, ok := node.(*ast.GoStmt); ok { if push { inGoroutineRunningTestFunc.Push(false) @@ -107,6 +121,10 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect if !push { return false } + if inGoroutineRunningTestFunc.Len() == 0 { + // Insufficient info. + return true + } if inGoroutineRunningTestFunc.Last() { // We are in testing goroutine and can skip any assertion checks. return true @@ -252,6 +270,10 @@ func (fd funcDeclarations) Get(pass *analysis.Pass, ce *ast.CallExpr) *ast.FuncD type boolStack []bool +func (s boolStack) Len() int { + return len(s) +} + func (s *boolStack) Push(v bool) { *s = append(*s, v) } @@ -284,15 +306,19 @@ func isSubTestRun(pass *analysis.Pass, ce *ast.CallExpr) bool { } func isTestingFuncOrMethod(pass *analysis.Pass, fd *ast.FuncDecl) bool { - return hasTestingTParam(pass, fd) || isTestifySuiteMethod(pass, fd) + return hasTestingTParam(pass, fd.Type) || isTestifySuiteMethod(pass, fd) +} + +func isTestingAnonymousFunc(pass *analysis.Pass, ft *ast.FuncType) bool { + return hasTestingTParam(pass, ft) } -func hasTestingTParam(pass *analysis.Pass, fd *ast.FuncDecl) bool { - if fd.Type == nil || fd.Type.Params == nil { +func hasTestingTParam(pass *analysis.Pass, ft *ast.FuncType) bool { + if ft == nil || ft.Params == nil { return false } - for _, param := range fd.Type.Params.List { + for _, param := range ft.Params.List { if isTestingTPtr(pass, param.Type) { return true }