mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
63 lines
3.2 KiB
JavaScript
63 lines
3.2 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
|
|
const { safeSkipUnlessEnabled, gotoOnion, recordVideoOptions } = require("./personas");
|
|
const { roundcubeSsoLogin, roundcubeLogout } = require("./webmail");
|
|
const { webmailBaseUrl, biberUsername, biberPassword } = require("./env");
|
|
const { resolveTimeout, isSplitRealmOidc } = require("./timeouts");
|
|
|
|
// Outbound .onion delivery from the user's side: biber addresses a recipient on a
|
|
// foreign onion and the message leaves as queued mail instead of bouncing.
|
|
//
|
|
// The lab has no external onion mail peer, so this asserts the routing decision
|
|
// rather than a remote receipt: with MtaRoute + MtaOutboundStrategy in place the
|
|
// recipient is accepted and queued to the tor-smtp relay, whereas an unrouted
|
|
// .onion domain has no MX and returns a delivery failure to the sender. The
|
|
// recipient domain is deliberately not a local domain — is_local_domain wins over
|
|
// the .onion match in the strategy, so a self-addressed onion would route 'local'
|
|
// and never reach the gateway.
|
|
const FOREIGN_ONION = "recipient@nexusprobe7xk3mjqzvhbnd4rlyugc2pfe6sotai5w.onion";
|
|
|
|
test("biber: a .onion recipient is accepted and routed through the Tor gateway", async ({ browser }, testInfo) => {
|
|
test.skip(isSplitRealmOidc(), "clearnet app with an onion OIDC issuer: unreachable from one browser");
|
|
safeSkipUnlessEnabled("tor");
|
|
safeSkipUnlessEnabled("sso");
|
|
expect(webmailBaseUrl, "WEBMAIL_BASE_URL must be set").toBeTruthy();
|
|
expect(biberPassword, "BIBER_PASSWORD must be set").toBeTruthy();
|
|
|
|
const testSubject = `Playwright onion route ${Date.now()}`;
|
|
const proxyServer = process.env.PLAYWRIGHT_PROXY;
|
|
const context = await browser.newContext({
|
|
ignoreHTTPSErrors: true,
|
|
...(proxyServer ? { proxy: { server: proxyServer } } : {}),
|
|
...recordVideoOptions(testInfo),
|
|
});
|
|
|
|
try {
|
|
const page = await context.newPage();
|
|
await roundcubeSsoLogin(page, biberUsername, biberPassword);
|
|
await gotoOnion(page, `${webmailBaseUrl}/?_task=mail&_action=compose`);
|
|
await page.waitForLoadState("networkidle", { timeout: resolveTimeout(15_000) }).catch(() => {});
|
|
await page.locator("#_to, input[name='_to']").first().fill(FOREIGN_ONION);
|
|
await page.locator("#compose-subject, input[name='_subject']").first().fill(testSubject);
|
|
await page.locator("#composebody, textarea[name='_message'], [contenteditable='true']").first()
|
|
.fill("Automated Playwright probe for the .onion outbound route.");
|
|
await page.locator(".formbuttons .send, button.send, a.send").first().click();
|
|
|
|
const sendError = page.locator("#messagestack .error, .toast .error, .toast-error").first();
|
|
if (await sendError.isVisible().catch(() => false)) {
|
|
throw new Error(`Stalwart refused the .onion recipient: ${await sendError.textContent()}`);
|
|
}
|
|
|
|
await gotoOnion(page, `${webmailBaseUrl}/?_task=mail&_mbox=INBOX`);
|
|
await page.waitForTimeout(resolveTimeout(10_000));
|
|
await gotoOnion(page, `${webmailBaseUrl}/?_task=mail&_mbox=INBOX`);
|
|
await expect(
|
|
page.locator("#messagelist").getByText(/undelivered|delivery status|delivery failure|mailer-daemon/i).first()
|
|
).toBeHidden({ timeout: resolveTimeout(15_000) });
|
|
|
|
await roundcubeLogout(page);
|
|
} finally {
|
|
await context.close().catch(() => {});
|
|
}
|
|
});
|