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 }, ); });