From f15f57f9ed989bfb2a5044e8f1a33fe6fb31963c Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 3 Jun 2026 21:51:21 +0200 Subject: [PATCH] test(web): cover the /welcome landing page and its routing Add a configurable fakeAuth (StubAuth can't express the logged-out case) and assert: /welcome renders the logged-out CTA with no session and the logged-in controls with one, unauthenticated / redirects to /welcome, unauthenticated deep links redirect to /auth/login, and an authenticated root still renders the list. Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/web/welcome_test.go | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 internal/web/welcome_test.go diff --git a/internal/web/welcome_test.go b/internal/web/welcome_test.go new file mode 100644 index 0000000..270f2bf --- /dev/null +++ b/internal/web/welcome_test.go @@ -0,0 +1,92 @@ +package web_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "gitea.d-ma.be/mathias/tapir/internal/web" +) + +// fakeAuth is a configurable web.Auth for the landing-page tests: it reports a +// fixed (user, ok) from CurrentUser and, when logged out, replicates DexAuth's +// redirect split in Middleware — bare root → /welcome, deeper paths → login. +// StubAuth can't express the logged-out case (it allows everything), so the +// welcome routing needs this. +type fakeAuth struct { + user web.User + ok bool +} + +func (f fakeAuth) CurrentUser(*http.Request) (web.User, bool) { return f.user, f.ok } +func (f fakeAuth) Routes() http.Handler { return http.NewServeMux() } + +func (f fakeAuth) Middleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if f.ok { + h.ServeHTTP(w, r) + return + } + if r.URL.Path == "/" { + http.Redirect(w, r, "/welcome", http.StatusFound) + return + } + http.Redirect(w, r, "/auth/login", http.StatusFound) + }) +} + +// appWithAuth builds an App with a given Auth but no store wiring — enough for +// the /welcome page (which never touches the store) and the unauthenticated +// redirect paths (which never reach a handler). +func appWithAuth(auth web.Auth) *web.App { + return &web.App{Auth: auth} +} + +func TestWelcomeLoggedOut(t *testing.T) { + app := appWithAuth(fakeAuth{ok: false}) + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/welcome", nil)) + + require.Equal(t, http.StatusOK, rec.Code) + html := body(t, rec) + require.Contains(t, html, "Get Started", "logged-out CTA present") + require.Contains(t, html, `href="/auth/login"`, "CTA links into the Dex flow") + require.NotContains(t, html, "Go to my Tapir", "no logged-in controls") +} + +func TestWelcomeLoggedIn(t *testing.T) { + app := appWithAuth(fakeAuth{user: web.User{Subject: "s", Email: "me@d-ma.be"}, ok: true}) + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/welcome", nil)) + + require.Equal(t, http.StatusOK, rec.Code) + html := body(t, rec) + require.Contains(t, html, "Go to my Tapir", "logged-in CTA present") + require.Contains(t, html, `href="/"`, "links back into the app") + require.Contains(t, html, "me@d-ma.be", "greets by email") + require.NotContains(t, html, "Get Started", "no logged-out CTA") +} + +func TestUnauthenticatedRootRedirectsToWelcome(t *testing.T) { + app := appWithAuth(fakeAuth{ok: false}) + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil)) + + require.Equal(t, http.StatusFound, rec.Code) + require.Equal(t, "/welcome", rec.Header().Get("Location")) +} + +func TestUnauthenticatedDeepLinkRedirectsToLogin(t *testing.T) { + app := appWithAuth(fakeAuth{ok: false}) + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/v/some-id", nil)) + + require.Equal(t, http.StatusFound, rec.Code) + require.Equal(t, "/auth/login", rec.Header().Get("Location")) +} + +func TestAuthenticatedRootRendersList(t *testing.T) { + app := newApp(t) + resetDB(t, rawPool(t)) + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil)) + require.Equal(t, http.StatusOK, rec.Code) + require.Contains(t, body(t, rec), "