test(tiers): cover normalizeProxy for string and object inputs

This commit is contained in:
germondai
2026-07-06 13:55:25 +02:00
parent 4adf1e0347
commit 1e5acb7975
+133
View File
@@ -0,0 +1,133 @@
import { describe, expect, test } from "bun:test"
import { normalizeProxy } from "../src/proxyRotator"
// Covers issue #12: Prowlarr's Cardigann flow serializes `proxy` as an object
// {url, username, password}; other callers send a plain URL string. The API
// boundary must normalize both into a single URL string before the orchestrator
// hands the value to Playwright/Camoufox `newContext({proxy})` (which requires
// `server` to be a string).
describe("normalizeProxy", () => {
describe("nullish / empty inputs", () => {
test("undefined → undefined", () => {
expect(normalizeProxy(undefined)).toBeUndefined()
})
test("null → undefined", () => {
expect(normalizeProxy(null)).toBeUndefined()
})
test("empty string → undefined", () => {
expect(normalizeProxy("")).toBeUndefined()
})
test("whitespace-only string → undefined", () => {
expect(normalizeProxy(" ")).toBeUndefined()
})
test("empty object → undefined", () => {
expect(normalizeProxy({})).toBeUndefined()
})
test("object with neither url nor server → undefined", () => {
expect(normalizeProxy({ username: "u", password: "p" })).toBeUndefined()
})
})
describe("plain URL string (legacy / non-Prowlarr callers)", () => {
test("passes a simple URL through unchanged", () => {
expect(normalizeProxy("http://proxy.example.com:8080")).toBe("http://proxy.example.com:8080")
})
test("trims surrounding whitespace", () => {
expect(normalizeProxy(" http://proxy.example.com:8080 ")).toBe("http://proxy.example.com:8080")
})
test("preserves embedded credentials in URL string", () => {
expect(normalizeProxy("http://user:pass@proxy.example.com:8080")).toBe("http://user:pass@proxy.example.com:8080")
})
})
describe("Prowlarr object form {url, username, password}", () => {
test("object with only url → returns url as-is", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080" })).toBe("http://proxy.example.com:8080")
})
test("object with url + username + password → embeds credentials in URL", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "alice", password: "secret" })).toBe(
"http://alice:secret@proxy.example.com:8080",
)
})
test("object with url + username (no password) → embeds username only", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "alice" })).toBe(
"http://alice:@proxy.example.com:8080",
)
})
test("object with url + password (no username) → embeds password only", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", password: "secret" })).toBe(
"http://:secret@proxy.example.com:8080",
)
})
test("object with empty-string username/password → returns url as-is", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "", password: "" })).toBe(
"http://proxy.example.com:8080",
)
})
})
describe("alternate object form {server, ...}", () => {
test("accepts `server` field in place of `url`", () => {
expect(normalizeProxy({ server: "http://proxy.example.com:8080" })).toBe("http://proxy.example.com:8080")
})
test("prefers `url` over `server` when both are present", () => {
expect(
normalizeProxy({ url: "http://primary.example.com:8080", server: "http://fallback.example.com:8080" }),
).toBe("http://primary.example.com:8080")
})
})
describe("credential encoding", () => {
test("URL-encodes '@' in password", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "alice", password: "p@ss" })).toBe(
"http://alice:p%40ss@proxy.example.com:8080",
)
})
test("URL-encodes ':' in password", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "alice", password: "pa:ss" })).toBe(
"http://alice:pa%3Ass@proxy.example.com:8080",
)
})
test("URL-encodes special characters in username", () => {
expect(normalizeProxy({ url: "http://proxy.example.com:8080", username: "user@org", password: "secret" })).toBe(
"http://user%40org:secret@proxy.example.com:8080",
)
})
test("preserves https scheme", () => {
expect(normalizeProxy({ url: "https://proxy.example.com:443", username: "u", password: "p" })).toBe(
"https://u:p@proxy.example.com:443",
)
})
test("preserves socks5 scheme", () => {
expect(normalizeProxy({ url: "socks5://proxy.example.com:1080", username: "u", password: "p" })).toBe(
"socks5://u:p@proxy.example.com:1080",
)
})
})
describe("malformed inputs (defensive)", () => {
test("server-only object with no recognised fields → undefined", () => {
expect(normalizeProxy({ username: "u", password: "p" })).toBeUndefined()
})
test("non-string url field → undefined", () => {
expect(normalizeProxy({ url: 42 as unknown as string })).toBeUndefined()
})
})
})