diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-15 09:11:16 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-15 09:11:16 -0600 |
| commit | 3d01a69471fc4f0ae9f2f4145620b6aea50f2216 (patch) | |
| tree | f85607ebfb2575bce94b5618250ebd957f965f6e /test | |
| parent | 7c75fac3360b8bc3df630b5f8e12b2ff927a2d23 (diff) | |
| parent | 564e140de454c78d7e6d34044bb78f53bd0b2bf3 (diff) | |
Merge branch 'envoy-start' into 'main'
Enable Envoy to run consistently locally and in Docker
See merge request gitlab-org/software-supply-chain-security/authorization/sparkled!6
Diffstat (limited to 'test')
| -rw-r--r-- | test/integration/ci.go | 12 | ||||
| -rw-r--r-- | test/integration/container.go | 41 | ||||
| -rw-r--r-- | test/integration/container_test.go | 107 | ||||
| -rw-r--r-- | test/integration/http.go | 43 | ||||
| -rw-r--r-- | test/integration/logger.go | 15 | ||||
| -rw-r--r-- | test/integration/ui.go | 33 |
6 files changed, 188 insertions, 63 deletions
diff --git a/test/integration/ci.go b/test/integration/ci.go new file mode 100644 index 0000000..7b5280f --- /dev/null +++ b/test/integration/ci.go @@ -0,0 +1,12 @@ +package test + +import ( + "testing" + + "github.com/xlgmokha/x/pkg/env" +) + +func isGitlabCI(t *testing.T) bool { + t.Logf("CI? %v\n", env.Fetch("CI", "")) + return env.Fetch("CI", "") == "true" +} diff --git a/test/integration/container.go b/test/integration/container.go new file mode 100644 index 0000000..0a210dd --- /dev/null +++ b/test/integration/container.go @@ -0,0 +1,41 @@ +package test + +import ( + "context" + "net/http" + "testing" + + xcontainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/go-connections/nat" + "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/log" + "github.com/testcontainers/testcontainers-go/wait" + "github.com/xlgmokha/x/pkg/env" + "github.com/xlgmokha/x/pkg/x" +) + +func NewContainer(t *testing.T, ctx context.Context, envVars map[string]string) *testcontainers.DockerContainer { + require.Equal(t, http.StatusOK, HttpGet(t, ctx, envVars["OIDC_ISSUER"]+"/.well-known/openid-configuration").StatusCode) + container, err := testcontainers.Run( + ctx, + env.Fetch("IMAGE_TAG", "sparkled:invalid"), + testcontainers.WithEnv(envVars), + testcontainers.WithLogConsumers(&Logger{TB: t}), + testcontainers.WithLogger(log.TestLogger(t)), + testcontainers.WithWaitStrategy( + wait.ForLog("Listening on"), + wait.ForListeningPort(x.Must(nat.NewPort("tcp", "10000"))), + wait.ForListeningPort(x.Must(nat.NewPort("tcp", "8080"))), + wait.ForListeningPort(x.Must(nat.NewPort("tcp", "9901"))), + ), + testcontainers.WithHostConfigModifier(func(cfg *xcontainer.HostConfig) { + cfg.NetworkMode = xcontainer.NetworkMode(network.NetworkHost) + }), + // testcontainers.WithExposedPorts("8080/tcp", "9901/tcp", "10000/tcp"), + // testcontainers.WithHostPortAccess(port), + ) + require.NoError(t, err) + return container +} diff --git a/test/integration/container_test.go b/test/integration/container_test.go index 40bbe74..b956250 100644 --- a/test/integration/container_test.go +++ b/test/integration/container_test.go @@ -6,72 +6,53 @@ package test import ( "context" "net/http" - "net/url" "testing" "time" - "github.com/oauth2-proxy/mockoidc" - "github.com/playwright-community/playwright-go" + playwright "github.com/playwright-community/playwright-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" "github.com/xlgmokha/x/pkg/env" - "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc" ) -type TestLogConsumer struct { - t *testing.T -} - -func (lc *TestLogConsumer) Accept(l testcontainers.Log) { - lc.t.Logf("%s", l.Content) +func environmentVariables(srv *oidc.TestServer) map[string]string { + return map[string]string{ + "APP_ENV": "test", + "BIND_ADDR": ":8080", + "DEBUG": env.Fetch("DEBUG", ""), + "HMAC_SESSION_SECRET": "secret", + "OAUTH_CLIENT_ID": srv.MockOIDC.ClientID, + "OAUTH_CLIENT_SECRET": srv.MockOIDC.ClientSecret, + "OAUTH_REDIRECT_URL": "", + "OIDC_ISSUER": srv.Issuer(), + } } func TestContainer(t *testing.T) { - srv := oidc.NewTestServer(t) - defer srv.Close() - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - address, err := url.Parse(srv.MockOIDC.Addr()) - require.NoError(t, err) - - issuer := srv.Issuer() - t.Logf("mockoidc: %v %v %v\n", address.String(), issuer, testcontainers.HostInternal) - container, err := testcontainers.Run( - ctx, - env.Fetch("IMAGE_TAG", "sparkled:invalid"), - testcontainers.WithEnv(map[string]string{ - "APP_ENV": "test", - "BIND_ADDR": ":8080", - "DEBUG": env.Fetch("DEBUG", ""), - "HMAC_SESSION_SECRET": "secret", - "OAUTH_CLIENT_ID": srv.MockOIDC.Config().ClientID, - "OAUTH_CLIENT_SECRET": srv.MockOIDC.Config().ClientSecret, - "OAUTH_REDIRECT_URL": "", - "OIDC_ISSUER": issuer, - }), - testcontainers.WithExposedPorts("8080/tcp", "9901/tcp", "10000/tcp"), - testcontainers.WithLogConsumers(&TestLogConsumer{t: t}), - testcontainers.WithWaitStrategy(wait.ForLog("Listening on").WithStartupTimeout(time.Second*5)), - ) - require.NoError(t, err) + srv := oidc.NewTestServer(t) + defer srv.Close() + container := NewContainer(t, ctx, environmentVariables(srv)) defer testcontainers.TerminateContainer(container) - oidcProviderEndpoint := address.String() + require.True(t, container.IsRunning()) + sparkleEndpoint, err := container.PortEndpoint(ctx, "8080", "http") require.NoError(t, err) envoyEndpoint, err := container.PortEndpoint(ctx, "10000", "http") require.NoError(t, err) - client := &http.Client{Timeout: 5 * time.Second} + envoyAdminEndpoint, err := container.PortEndpoint(ctx, "9901", "http") + require.NoError(t, err) - publicPaths := []string{ + for _, publicPath := range []string{ + envoyAdminEndpoint + "/", envoyEndpoint + "/", envoyEndpoint + "/application.js", envoyEndpoint + "/favicon.ico", @@ -79,43 +60,43 @@ func TestContainer(t *testing.T) { envoyEndpoint + "/health", envoyEndpoint + "/index.html", envoyEndpoint + "/logo.png", - oidcProviderEndpoint + mockoidc.DiscoveryEndpoint, sparkleEndpoint + "/", sparkleEndpoint + "/favicon.ico", sparkleEndpoint + "/health", - } - - for _, path := range publicPaths { - t.Run(path, func(t *testing.T) { - request, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil) - response, err := client.Do(request) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, response.StatusCode) + srv.DiscoveryEndpoint(), + } { + t.Run(publicPath, func(t *testing.T) { + assert.Equal(t, http.StatusOK, HttpGet(t, ctx, publicPath).StatusCode) }) } - t.Run("UI", func(t *testing.T) { - if env.Fetch("SKIP_E2E", "") != "" { - t.Skip() - } + t.Run("envoy.yaml", func(t *testing.T) { + response := HttpGet(t, ctx, envoyAdminEndpoint+"/config_dump") - _ = playwright.Install() + require.Equal(t, http.StatusOK, response.StatusCode) + body := JSONBody[map[string]interface{}](t, response) - pw := x.Must(playwright.Run()) - browser := x.Must(pw.Firefox.Launch(playwright.BrowserTypeLaunchOptions{ - Headless: playwright.Bool(env.Fetch("HEADLESS", "true") == "true"), - SlowMo: playwright.Float(1000), - })) - page := x.Must(browser.NewPage()) + assert.NotEmpty(t, "listener_0", body["configs"]) + }) - defer browser.Close() - defer pw.Stop() + WithUI(t, func(browser playwright.Browser) { + page, err := browser.NewPage() + require.NoError(t, err) t.Run("initiates an OIDC login", func(t *testing.T) { require.NoError(t, page.Context().ClearCookies()) - response, err := page.Goto(sparkleEndpoint + "/") + response, err := page.Goto(envoyEndpoint + "/") require.NoError(t, err) assert.True(t, response.Ok()) + + t.Run("redirects to the OpenID Connect Provider", func(t *testing.T) { + t.Skip() + + require.NoError(t, page.GetByText("Login").Click()) + // The envoy.yaml configuration has a hardcoded path that doesn't match the one provided by mockoidc + // because the oauth2 envoy filter doesn't support the OIDC discovery endpoint. + assert.Contains(t, page.URL(), srv.AuthorizationEndpoint()+"?client_id="+srv.MockOIDC.ClientID) + }) }) }) } diff --git a/test/integration/http.go b/test/integration/http.go new file mode 100644 index 0000000..20991a2 --- /dev/null +++ b/test/integration/http.go @@ -0,0 +1,43 @@ +package test + +import ( + "context" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/xlgmokha/x/pkg/serde" +) + +type testTransport struct { + t *testing.T +} + +func (r *testTransport) RoundTrip(request *http.Request) (*http.Response, error) { + response, err := http.DefaultTransport.RoundTrip(request) + require.NoError(r.t, err) + r.t.Logf("%v %v %v\n", response.StatusCode, request.Method, request.URL) + return response, err +} + +func HttpGet(t *testing.T, ctx context.Context, path string) *http.Response { + client := &http.Client{ + Timeout: 5 * time.Second, + Transport: &testTransport{t: t}, + } + + request, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil) + require.NoError(t, err) + + response, err := client.Do(request) + require.NoError(t, err) + + return response +} + +func JSONBody[T any](t *testing.T, r *http.Response) T { + item, err := serde.FromJSON[T](r.Body) + require.NoError(t, err) + return item +} diff --git a/test/integration/logger.go b/test/integration/logger.go new file mode 100644 index 0000000..86660bc --- /dev/null +++ b/test/integration/logger.go @@ -0,0 +1,15 @@ +package test + +import ( + "testing" + + "github.com/testcontainers/testcontainers-go" +) + +type Logger struct { + testing.TB +} + +func (t *Logger) Accept(l testcontainers.Log) { + t.Logf("%s", l.Content) +} diff --git a/test/integration/ui.go b/test/integration/ui.go new file mode 100644 index 0000000..1af0744 --- /dev/null +++ b/test/integration/ui.go @@ -0,0 +1,33 @@ +package test + +import ( + "testing" + + playwright "github.com/playwright-community/playwright-go" + "github.com/stretchr/testify/require" + "github.com/xlgmokha/x/pkg/env" + "github.com/xlgmokha/x/pkg/x" +) + +func WithUI(t *testing.T, callback x.Visitor[playwright.Browser]) { + t.Run("UI", func(t *testing.T) { + if isGitlabCI(t) { + t.Skip() + } + _ = playwright.Install() + + driver, err := playwright.Run() + require.NoError(t, err) + + browser, err := driver.Firefox.Launch(playwright.BrowserTypeLaunchOptions{ + Headless: playwright.Bool(env.Fetch("HEADLESS", "true") == "true"), + SlowMo: playwright.Float(1000), + }) + require.NoError(t, err) + + defer browser.Close() + defer driver.Stop() + + callback(browser) + }) +} |
