mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
* test(infra): retry recursive temp-dir removal on main (main twin of #11968)
`main` has been red since b342c1a361 on the vitest and integration gates:
✖ tests/unit/autoCombo/provider-family-combos.test.ts > auto/<family>
✖ chat pipeline applies Codex OAuth fingerprint and priority tier inside combos
Both call resetStorage() from beforeEach, which does an fs.rmSync(TEST_DATA_DIR,
{recursive: true, force: true}) with no retry, and intermittently loses the race
with a not-yet-released SQLite handle (ENOTEMPTY).
release/v3.8.51 fixed this in #11968 with a mechanical codemod adding
maxRetries/retryDelay to every recursive rm/rmSync/rmdirSync under tests/, but
that PR landed only on the release branch. Because main only receives work at
the release squash, it stayed broken for the whole cycle — and repo-wide gates
then turn every open PR into main red on checks unrelated to their diff.
This is the --base main twin: re-runs the same codemod that already shipped on
the release branch (scripts/ad-hoc/codemod-rm-maxretries.mjs), so the two
branches converge on identical test-teardown semantics. Test-only; no product
logic is touched.
The remaining three failures reported on #12133 (unit full suite exceeding its
4800s ceiling, package-artifact exceeding 1200s, and the boot-smoke that is
skipped as a consequence) are runner-contention timeouts, not code defects —
validate-release-green.mjs runs those heavy gates concurrently on one shared
hosted runner. There is no fix to port for those.
* chore(scripts): carry the rm-maxretries codemod onto main alongside its output
The codemod that generated the previous commit lives in the repo on
release/v3.8.51 (added by #11968) but was never on main. Bringing it over keeps
the tool next to the change it produced, so the transformation stays
reproducible and auditable from either branch.
368 lines
13 KiB
TypeScript
368 lines
13 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-webshare-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
|
|
// Providers read process.env at call-time, so we can set flags before import.
|
|
// Keep the other free-proxy sources disabled so this file only exercises Webshare.
|
|
process.env.FREE_PROXY_1PROXY_ENABLED = "false";
|
|
process.env.FREE_PROXY_PROXIFLY_ENABLED = "false";
|
|
process.env.FREE_PROXY_IPLOCATE_ENABLED = "false";
|
|
|
|
const FAKE_API_KEY = "wsk_test_super_secret_token_1234567890";
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
const { getProvider } = await import("../../src/lib/freeProxyProviders/index.ts");
|
|
const freeProxiesDb = await import("../../src/lib/db/freeProxies.ts");
|
|
|
|
async function reset() {
|
|
core.resetDbInstance();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
}
|
|
|
|
test.after(() => {
|
|
core.resetDbInstance();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
});
|
|
|
|
function webshareResponse(results: unknown[], next: string | null = null) {
|
|
return new Response(JSON.stringify({ count: results.length, next, previous: null, results }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
|
|
// ── isEnabled ────────────────────────────────────────────────────────────────
|
|
|
|
test("WebshareProvider.isEnabled is false without an API key", () => {
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
|
|
const p = getProvider("webshare")!;
|
|
assert.equal(p.isEnabled(), false);
|
|
|
|
if (originalKey !== undefined) process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey;
|
|
});
|
|
|
|
test("WebshareProvider.isEnabled is true once an API key is configured", () => {
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
const p = getProvider("webshare")!;
|
|
assert.equal(p.isEnabled(), true);
|
|
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
});
|
|
|
|
test("WebshareProvider.isEnabled is false when explicitly disabled, even with a key", () => {
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalEnabled = process.env.FREE_PROXY_WEBSHARE_ENABLED;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
process.env.FREE_PROXY_WEBSHARE_ENABLED = "false";
|
|
|
|
const p = getProvider("webshare")!;
|
|
assert.equal(p.isEnabled(), false);
|
|
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
process.env.FREE_PROXY_WEBSHARE_ENABLED = originalEnabled ?? "";
|
|
if (originalEnabled === undefined) delete process.env.FREE_PROXY_WEBSHARE_ENABLED;
|
|
});
|
|
|
|
// ── sync — disabled path ────────────────────────────────────────────────────
|
|
|
|
test("WebshareProvider.sync returns a disabled error (no key leak) when no API key is set", async () => {
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
await reset();
|
|
|
|
const p = getProvider("webshare")!;
|
|
const result = await p.sync();
|
|
|
|
assert.equal(result.fetched, 0);
|
|
assert.ok(result.errors.length > 0);
|
|
assert.ok(result.errors[0].includes("disabled"));
|
|
|
|
if (originalKey !== undefined) process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey;
|
|
});
|
|
|
|
// ── sync — pagination + upsert ──────────────────────────────────────────────
|
|
|
|
test("WebshareProvider.sync paginates via `next` and upserts proxies", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
const seenAuthHeaders: string[] = [];
|
|
const seenPages: string[] = [];
|
|
|
|
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const url = new URL(String(input));
|
|
seenPages.push(url.searchParams.get("page") || "");
|
|
const headers = new Headers(init?.headers);
|
|
seenAuthHeaders.push(headers.get("authorization") || "");
|
|
|
|
const page = url.searchParams.get("page");
|
|
if (page === "1") {
|
|
return webshareResponse(
|
|
[
|
|
{
|
|
proxy_address: "45.10.20.1",
|
|
port: 6001,
|
|
valid: true,
|
|
country_code: "US",
|
|
last_verification: "2026-01-01T00:00:00.000Z",
|
|
},
|
|
{
|
|
proxy_address: "45.10.20.2",
|
|
port: 6002,
|
|
valid: true,
|
|
country_code: "DE",
|
|
last_verification: "2026-01-01T00:00:00.000Z",
|
|
},
|
|
],
|
|
"https://proxy.webshare.io/api/v2/proxy/list/?page=2"
|
|
);
|
|
}
|
|
return webshareResponse(
|
|
[
|
|
{
|
|
proxy_address: "45.10.20.3",
|
|
port: 6003,
|
|
valid: true,
|
|
country_code: "FR",
|
|
last_verification: "2026-01-01T00:00:00.000Z",
|
|
},
|
|
],
|
|
null
|
|
);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
const result = await p.sync();
|
|
|
|
assert.deepEqual(seenPages, ["1", "2"]);
|
|
assert.ok(
|
|
seenAuthHeaders.every((h) => h === `Token ${FAKE_API_KEY}`),
|
|
"every request must carry the Webshare Authorization token"
|
|
);
|
|
assert.equal(result.fetched, 3);
|
|
assert.equal(result.added, 3);
|
|
assert.equal(result.updated, 0);
|
|
assert.deepEqual(result.errors, []);
|
|
|
|
const items = await p.list({ limit: 10 });
|
|
assert.equal(items.length, 3);
|
|
assert.ok(items.every((item) => item.source === "webshare"));
|
|
assert.ok(items.some((item) => item.host === "45.10.20.1" && item.port === 6001));
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
test("WebshareProvider.sync updates existing rows instead of duplicating them", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
let call = 0;
|
|
globalThis.fetch = (async () => {
|
|
call++;
|
|
return webshareResponse([
|
|
{
|
|
proxy_address: "45.10.20.1",
|
|
port: 6001,
|
|
valid: true,
|
|
country_code: "US",
|
|
last_verification: new Date().toISOString(),
|
|
},
|
|
]);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
const first = await p.sync();
|
|
const second = await p.sync();
|
|
|
|
assert.equal(first.added, 1);
|
|
assert.equal(second.added, 0);
|
|
assert.equal(second.updated, 1);
|
|
assert.equal(call, 2);
|
|
|
|
const items = await p.list({ limit: 10 });
|
|
assert.equal(items.length, 1);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
// ── sync — tombstone ─────────────────────────────────────────────────────────
|
|
|
|
test("WebshareProvider.sync tombstones proxies no longer returned by the account list", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
let responses: unknown[][] = [
|
|
[
|
|
{ proxy_address: "45.10.20.1", port: 6001, valid: true, country_code: "US" },
|
|
{ proxy_address: "45.10.20.2", port: 6002, valid: true, country_code: "DE" },
|
|
],
|
|
];
|
|
let callIndex = 0;
|
|
globalThis.fetch = (async () => {
|
|
const batch = responses[callIndex] ?? [];
|
|
callIndex++;
|
|
return webshareResponse(batch);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
await p.sync();
|
|
let items = await p.list({ limit: 10 });
|
|
assert.equal(items.length, 2);
|
|
|
|
// Second sync: the account list now only has 45.10.20.1 — .2 was retired.
|
|
responses = [[{ proxy_address: "45.10.20.1", port: 6001, valid: true, country_code: "US" }]];
|
|
callIndex = 0;
|
|
await p.sync();
|
|
|
|
items = await p.list({ limit: 10 });
|
|
assert.equal(items.length, 1);
|
|
assert.equal(items[0].host, "45.10.20.1");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
test("WebshareProvider.sync never tombstones proxies already promoted to the pool", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
globalThis.fetch = (async () =>
|
|
webshareResponse([
|
|
{ proxy_address: "45.10.20.1", port: 6001, valid: true, country_code: "US" },
|
|
])) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
await p.sync();
|
|
const [item] = await freeProxiesDb.listFreeProxies({ sources: ["webshare"] });
|
|
assert.ok(item);
|
|
await freeProxiesDb.markFreeProxyInPool(item.id, "some-registry-id");
|
|
|
|
// Next sync returns an empty list — if pruning ignored `in_pool`, this row
|
|
// would be deleted even though it's actively in use by the proxy pool.
|
|
globalThis.fetch = (async () => webshareResponse([])) as typeof fetch;
|
|
await p.sync();
|
|
|
|
const stillThere = await freeProxiesDb.getFreeProxyById(item.id);
|
|
assert.ok(stillThere, "in-pool proxy must survive a sync where it is no longer listed");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
// ── sync — error sanitization / key masking ─────────────────────────────────
|
|
|
|
test("WebshareProvider.sync never leaks the API key in error messages on an HTTP failure", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
globalThis.fetch = (async () =>
|
|
new Response("Unauthorized", { status: 401 })) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
const result = await p.sync();
|
|
|
|
assert.equal(result.fetched, 0);
|
|
assert.ok(result.errors.length > 0);
|
|
for (const err of result.errors) {
|
|
assert.ok(!err.includes(FAKE_API_KEY), `error must not leak the API key: ${err}`);
|
|
assert.ok(!err.toLowerCase().includes("authorization"), `error must not leak the auth header: ${err}`);
|
|
}
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
test("WebshareProvider.sync never leaks the API key in error messages on a network exception", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
globalThis.fetch = (async () => {
|
|
throw new Error(`connect ECONNREFUSED (key was ${FAKE_API_KEY})`);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
const result = await p.sync();
|
|
|
|
// The provider itself must not additionally embed the key; it only
|
|
// forwards err.message. This guards the call site never re-injects the
|
|
// key into a wrapper string.
|
|
assert.equal(result.fetched, 0);
|
|
assert.ok(result.errors.length > 0);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|
|
|
|
test("WebshareProvider.sync skips private/loopback hosts", async () => {
|
|
await reset();
|
|
const originalKey = process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
const originalFetch = globalThis.fetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = FAKE_API_KEY;
|
|
|
|
globalThis.fetch = (async () =>
|
|
webshareResponse([
|
|
{ proxy_address: "127.0.0.1", port: 6001, valid: true, country_code: "US" },
|
|
{ proxy_address: "45.10.20.9", port: 6009, valid: true, country_code: "US" },
|
|
])) as typeof fetch;
|
|
|
|
try {
|
|
const p = getProvider("webshare")!;
|
|
const result = await p.sync();
|
|
|
|
assert.equal(result.fetched, 1);
|
|
assert.ok(result.errors.some((e) => e.includes("private/loopback")));
|
|
|
|
const items = await p.list({ limit: 10 });
|
|
assert.equal(items.length, 1);
|
|
assert.equal(items[0].host, "45.10.20.9");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
process.env.FREE_PROXY_WEBSHARE_API_KEY = originalKey ?? "";
|
|
if (originalKey === undefined) delete process.env.FREE_PROXY_WEBSHARE_API_KEY;
|
|
}
|
|
});
|