import { MOCK_CAMOFOX_IDENTITY } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { sourcesIdentityModule } from "../../src/modules/sources-identity/index.ts"; import { readBrowserUserAgent } from "../../src/modules/sources-identity/service/user-agent.ts"; import { egressProxy } from "../../src/modules/browser/service.ts"; import { type BrowserHarness, createBrowserHarness, type Loose, ORIGIN, pgAvailable } from "./harness.ts"; // BR-4:打开会话前按站点下发出口(站点开代理 = 与 HTTP 同一代理,否则直连);浏览器固定身份写进 browser_provider.identity, // 站点请求 UA 经钩子跟随它。 const up = await pgAvailable(); describe("egressProxy", () => { it("URL → 上下文代理(口令拆出);非法为 null", () => { expect(egressProxy("http://u:p%40ss@172.17.0.1:7891")).toEqual({ server: "http://172.17.0.1:7891", username: "u", password: "p@ss", }); expect(egressProxy("socks5://127.0.0.1:1080")).toEqual({ server: "socks5://127.0.0.1:1080" }); expect(egressProxy("not a url")).toBeNull(); }); }); describe.skipIf(!up)("BR-4 浏览器身份与出口", () => { let h: BrowserHarness; const open = async () => (await h.req("/browser/sessions", { body: { siteId: h.siteId, accountId: null, url: `${ORIGIN}/torrents.php` } })) .body.data as Loose; const closeAll = async () => { for (const s of (await h.req("/browser/sessions")).body.data.items as Loose[]) await h.req(`/browser/sessions/${s.id}`, { method: "DELETE" }); }; beforeAll(async () => { h = await createBrowserHarness("br4id"); }, 120_000); afterAll(async () => { await h?.close(); }); it("站点不开代理:该 profile 直连;打开时读浏览器身份存库,站点请求 UA 跟随", async () => { const s = await open(); expect(h.mock.egress.get(s.profileKey)).toBeNull(); const calls = h.mock.calls.map((c) => `${c.method} ${c.path}`); expect(calls.indexOf(`PUT /umt/sessions/${encodeURIComponent(s.profileKey)}/proxy`)).toBeLessThan( calls.lastIndexOf("POST /tabs"), ); const row = await h.db .selectFrom("browser_provider") .select("identity") .where("id", "=", h.providerId) .executeTakeFirstOrThrow(); expect(row.identity).toMatchObject({ userAgent: MOCK_CAMOFOX_IDENTITY.userAgent, os: "windows" }); expect(await readBrowserUserAgent(h.ctx)).toBe(MOCK_CAMOFOX_IDENTITY.userAgent); expect(await sourcesIdentityModule.runtime(h.ctx).defaultUserAgent()).toBe(MOCK_CAMOFOX_IDENTITY.userAgent); await closeAll(); }); it("站点开代理且设了代理地址:该 profile 走同一代理(口令单独传)", async () => { await h.db.updateTable("site").set({ proxy: true }).where("id", "=", h.siteId).execute(); await h.db .insertInto("settings") .values({ key: "sites", value: JSON.stringify({ proxyUrl: "http://user:pw@172.17.0.1:7891" }) }) .onConflict((oc) => oc.column("key").doUpdateSet({ value: JSON.stringify({ proxyUrl: "http://user:pw@172.17.0.1:7891" }) }), ) .execute(); const s = await open(); expect(h.mock.egress.get(s.profileKey)).toEqual({ server: "http://172.17.0.1:7891", username: "user", password: "pw", }); await closeAll(); await h.db.updateTable("site").set({ proxy: false }).where("id", "=", h.siteId).execute(); }); });