mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
fix(e2e): retry onion API requests past the 30s socks connect cap
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
78a0807039
commit
0a52433dcb
2 files changed
+33
-2
No files matched your search
@@ -72,6 +72,36 @@ async function gotoOnion(page, url, opts = {}) {
|
||||
throw lastErr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tor-resilient `request.get` for the standalone APIRequestContext fixture.
|
||||
* Its SOCKS CONNECT goes through the bundled `socks` client whose 30s connect
|
||||
* cap is not configurable, so a cold onion circuit fails the request no matter
|
||||
* how large the request timeout is. Retries only transient proxy/socket
|
||||
* errors; clearnet URLs get a single attempt and real HTTP failures re-throw.
|
||||
*/
|
||||
const _API_TRANSIENT_RE =
|
||||
/Proxy connection timed out|Socket closed|socket hang up|ECONNRESET|ETIMEDOUT|ECONNREFUSED|ENOTFOUND/i;
|
||||
|
||||
async function apiGetOnion(request, url, opts = {}) {
|
||||
const isRelative = /^\/(?!\/)/.test(url);
|
||||
const isOnion =
|
||||
/\.onion(?::\d+)?(?:\/|$|\?)/i.test(url) || (isRelative && isOnionCanonical());
|
||||
const attempts = isOnion ? Number(process.env.PLAYWRIGHT_ONION_GOTO_RETRIES) || 4 : 1;
|
||||
let lastErr;
|
||||
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
||||
try {
|
||||
return await request.get(url, opts);
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
if (attempt >= attempts || !_API_TRANSIENT_RE.test(String(err && err.message))) {
|
||||
throw err;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, resolveTimeout(2_000 * attempt)));
|
||||
}
|
||||
}
|
||||
throw lastErr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tolerant variant of `skipUnlessServiceEnabled`: treats an unknown
|
||||
* service (i.e. one whose `<NAME>_SERVICE_ENABLED` flag is not declared
|
||||
@@ -106,6 +136,7 @@ module.exports = {
|
||||
readEnv,
|
||||
isOnionCanonical,
|
||||
gotoOnion,
|
||||
apiGetOnion,
|
||||
safeSkipUnlessEnabled,
|
||||
safeIsEnabled,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
const { test, expect } = require("@playwright/test");
|
||||
const { resolveTimeout } = require("./timeouts");
|
||||
|
||||
const { decodeDotenvQuotedValue, gotoOnion, inAppLogout, performKeycloakLoginForm, runAdminFlow, runBiberFlow, runGuestFlow, safeSkipUnlessEnabled } = require("./personas");
|
||||
const { apiGetOnion, decodeDotenvQuotedValue, gotoOnion, inAppLogout, performKeycloakLoginForm, runAdminFlow, runBiberFlow, runGuestFlow, safeSkipUnlessEnabled } = require("./personas");
|
||||
test.use({
|
||||
ignoreHTTPSErrors: true
|
||||
});
|
||||
@@ -40,7 +40,7 @@ test.beforeEach(() => {
|
||||
// used to live in each consumer role's own playwright.spec.js.
|
||||
test("metricz endpoint is accessible and returns prometheus text format", async ({ request }) => {
|
||||
const metriczUrl = `${prometheusBaseUrl.replace(/\/$/, "")}/metricz`;
|
||||
const response = await request.get(metriczUrl, { timeout: resolveTimeout(30_000) });
|
||||
const response = await apiGetOnion(request, metriczUrl, { timeout: resolveTimeout(30_000) });
|
||||
|
||||
expect(
|
||||
response.status(),
|
||||
|
||||
Reference in new issue
Block a user