From 57dbcc54597b0254f1967a62bedb85b573738ab6 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 12 Sep 2026 13:31:00 -0700 Subject: [PATCH] e2e: add Marreta homepage-link regression test 16-marreta.spec.ts (standard project) asserts the user homepage's Marreta open link actually reaches the Marreta app through the full guard chain (Anubis PoW -> Authentik SSO -> app), and explicitly NOT the authentik login flow. Guards the 2026-09-12 incident where marreta-proxy was created without a linked Application, so the embedded outpost never loaded it and the homepage link parked on a login wall. Wires spec 16 into the standard project testMatch. Verified passing live. --- tests/e2e/playwright.config.ts | 2 +- tests/e2e/specs/16-marreta.spec.ts | 65 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/specs/16-marreta.spec.ts diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index d892b3f..aa42895 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ }, { name: "standard", - testMatch: /(10|12|13)-.*\.spec\.ts/, + testMatch: /(10|12|13|16)-.*\.spec\.ts/, use: { storageState: ".auth/qa-e2e.json" }, dependencies: ["setup"], }, diff --git a/tests/e2e/specs/16-marreta.spec.ts b/tests/e2e/specs/16-marreta.spec.ts new file mode 100644 index 0000000..10129b1 --- /dev/null +++ b/tests/e2e/specs/16-marreta.spec.ts @@ -0,0 +1,65 @@ +import { expect, test } from "@playwright/test"; + +// Regression guard for the 2026-09-12 incident. The homepage's Marreta tile +// resolved to the authentik login flow instead of the app: the `marreta-proxy` +// ProxyProvider had been created without a linked authentik Application, so the +// embedded outpost's config API +// (`ProxyProvider.objects.filter(application__isnull=False)`) never returned it, +// the Rust outpost never loaded the provider, and the request fell through to +// raw flow serving on the marreta host — where the IdP session cookie (scoped +// to auth.franksdank.cloud) is invisible, so login was demanded. +// +// This test proves the homepage link actually reaches the Marreta app through +// the full guard chain (Anubis PoW -> Authentik SSO -> app), and that it does +// NOT park on a login wall. + +const CHALLENGE = "Making sure you're not a bot"; +const MARRETA_HOST = "marreta.franksdank.cloud"; + +test("Marreta homepage tile reaches the Marreta app, not a login wall", async ({ page }) => { + test.setTimeout(150_000); + + // 1. The user homepage must expose exactly one Marreta open link, and it must + // target the marreta vhost (not a docs stub or a wrong host). + await page.goto("/"); + const marretaPanel = page + .locator("#grid .panel") + .filter({ has: page.locator(".name", { hasText: "Marreta" }) }); + await expect(marretaPanel, "exactly one Marreta panel on the user grid").toHaveCount(1); + + const openLink = marretaPanel.locator("a.open"); + await expect(openLink, "Marreta panel must have an open link").toHaveCount(1); + const href = await openLink.getAttribute("href"); + expect(href, "Marreta open link must target the marreta vhost").toMatch( + new RegExp(`marreta\\.franksdank\\.cloud`), + ); + + // 2. Follow the link through the guard chain and wait out the Anubis PoW. + await page.goto(href!); + await expect + .poll(async () => !(await page.locator("body").innerText()).includes(CHALLENGE), { + timeout: 90_000, + }) + .toBe(true); + + // 3. Must land on the Marreta app — NOT the authentik login flow. + const finalUrl = new URL(page.url()); + expect(finalUrl.hostname, "must stay on the marreta host").toBe(MARRETA_HOST); + expect( + finalUrl.pathname, + "must not be parked on an authentik flow / outpost start page", + ).not.toMatch(/\/(if|flows)\/|\/outpost\.goauthentik\.io\/start/); + expect( + await page.locator('input[name="uidField"]').count(), + "must not show the authentik login form", + ).toBe(0); + + // 4. Positive identity: the page really is Marreta. + await expect(page, "destination title must be Marreta").toHaveTitle(/marreta/i, { + timeout: 30_000, + }); + await expect(page.locator("body"), "Marreta app marker").toContainText( + /paywall-free reading/i, + { timeout: 30_000 }, + ); +});