mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Add the global TIMEOUT_FACTOR and a shared timeouts.js helper: resolveTimeout(base) scales by TIMEOUT_FACTOR and, over a .onion target, an extra multiplier, so Tor's per-request latency no longer trips hard-coded timeouts. Migrate every raw timeout in the specs (timeout:/timeout=/setTimeout/waitForTimeout) to resolveTimeout, stage the helper in all three staging paths, and add test_no_raw_timeouts to forbid raw numeric timeouts. Clearnet behaviour is unchanged (factor 1: resolveTimeout(N) === N). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
112 lines
4.2 KiB
JavaScript
112 lines
4.2 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("../timeouts");
|
|
|
|
const { skipUnlessAddonEnabled } = require("../addon-gating");
|
|
const { skipUnlessServiceEnabled } = require("../service-gating");
|
|
const {
|
|
decodeDotenvQuotedValue,
|
|
normalizeBaseUrl,
|
|
runAdminFlow,
|
|
} = require("../personas");
|
|
|
|
test.use({ ignoreHTTPSErrors: true });
|
|
|
|
const appBaseUrl = normalizeBaseUrl(process.env.APP_BASE_URL || "");
|
|
const canonicalDomain = decodeDotenvQuotedValue(process.env.CANONICAL_DOMAIN || "");
|
|
|
|
test("OpenIDConnect: wiki login hands off to the Keycloak OIDC authorize endpoint", async ({
|
|
page,
|
|
}) => {
|
|
skipUnlessAddonEnabled("OpenIDConnect");
|
|
skipUnlessServiceEnabled("sso");
|
|
|
|
expect(appBaseUrl, "APP_BASE_URL must be set").toBeTruthy();
|
|
expect(canonicalDomain, "CANONICAL_DOMAIN must be set").toBeTruthy();
|
|
|
|
await page.context().clearCookies();
|
|
|
|
// Drive MediaWiki's PluggableAuth login entry directly. With OpenIDConnect
|
|
// loaded and its provider wired to Keycloak (oidc.php -> $wgPluggableAuth_Config
|
|
// providerURL/clientID), PluggableAuth must redirect the browser to the
|
|
// Keycloak OIDC authorization endpoint (.../protocol/openid-connect/auth?...).
|
|
// A wiki where OpenIDConnect is NOT actually wired would instead stay on a
|
|
// local MediaWiki login form and never reach the IdP authorize endpoint, so
|
|
// this assertion fails the integration loudly.
|
|
const oidcAuthorizeUrl = await new Promise((resolve) => {
|
|
let settled = false;
|
|
const done = (value) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
resolve(value);
|
|
};
|
|
|
|
page.on("request", (request) => {
|
|
if (/\/protocol\/openid-connect\/auth(\?|$)/.test(request.url())) {
|
|
done(request.url());
|
|
}
|
|
});
|
|
|
|
page
|
|
.goto(`${appBaseUrl}/index.php?title=Special:UserLogin&returnto=Main+Page`, {
|
|
waitUntil: "domcontentloaded",
|
|
timeout: resolveTimeout(60_000),
|
|
})
|
|
.then(() => page.waitForLoadState("networkidle", { timeout: resolveTimeout(30_000) }).catch(() => {}))
|
|
.then(() => {
|
|
if (/\/protocol\/openid-connect\/auth/.test(page.url())) {
|
|
done(page.url());
|
|
return;
|
|
}
|
|
done(null);
|
|
})
|
|
.catch(() => done(null));
|
|
});
|
|
|
|
expect(
|
|
oidcAuthorizeUrl,
|
|
"MediaWiki login did NOT redirect to the Keycloak OIDC authorize endpoint " +
|
|
"(/protocol/openid-connect/auth) — OpenIDConnect is not wired to the Keycloak provider. " +
|
|
`Current URL: ${page.url()}.`,
|
|
).toBeTruthy();
|
|
|
|
// The authorize endpoint must be an absolute Keycloak URL on a DIFFERENT host
|
|
// than the wiki itself — proves the provider URL points at the real partner
|
|
// IdP (a separate Keycloak service), not a wiki-local stub or relative path.
|
|
const authorizeHost = new URL(oidcAuthorizeUrl).host;
|
|
const wikiHost = new URL(appBaseUrl).host;
|
|
expect(
|
|
authorizeHost && authorizeHost !== wikiHost,
|
|
"OIDC authorize endpoint is not served from a distinct Keycloak IdP host " +
|
|
`(wiki host ${wikiHost}, authorize URL ${oidcAuthorizeUrl}).`,
|
|
).toBe(true);
|
|
|
|
// Full coupling: complete the OIDC round-trip through Keycloak and assert we
|
|
// land on an authenticated MediaWiki surface (personal/logout tools that only
|
|
// render once OpenIDConnect has authenticated the user).
|
|
await page.context().clearCookies();
|
|
await runAdminFlow(page, {
|
|
adminInteraction: async (interactivePage) => {
|
|
const wikiSurface = interactivePage
|
|
.locator("#mw-content-text")
|
|
.or(interactivePage.locator("#firstHeading"))
|
|
.or(interactivePage.locator("#mw-panel"))
|
|
.or(interactivePage.locator("body.mediawiki"))
|
|
.first();
|
|
await expect(wikiSurface).toBeVisible({ timeout: resolveTimeout(60_000) });
|
|
|
|
const authenticatedMarker = interactivePage
|
|
.locator("#pt-logout")
|
|
.or(interactivePage.locator("#pt-userpage"))
|
|
.or(interactivePage.locator("#p-personal a[href*='Logout' i]"))
|
|
.or(interactivePage.locator("#p-personal a[href*='Special:UserLogout' i]"))
|
|
.or(
|
|
interactivePage.getByRole("link", {
|
|
name: /log\s*out|sign\s*out|abmelden/i,
|
|
}),
|
|
)
|
|
.first();
|
|
await expect(authenticatedMarker).toBeVisible({ timeout: resolveTimeout(60_000) });
|
|
},
|
|
});
|
|
});
|