Files
proxy-bun/src/lib/ipv6-pool.test.ts
T
MythEclipse 17338a7dca feat: IPv6 outbound rotation via curl --interface
- Add IPv6SourcePool for round-robin IP rotation
- fetchViaCurl: Bun.spawn + curl -6 --interface for source binding
- Auto-fallback to regular fetch when IPv6 connection fails
- Response decompression via --compressed flag
- Docker + GHCR build/deploy workflows
- 8 routable IPv6 addresses on VPS
2026-06-19 19:07:46 +07:00

119 lines
3.4 KiB
TypeScript

import { test, expect, describe } from "bun:test";
import { IPv6SourcePool } from "./ipv6-pool";
describe("IPv6SourcePool", () => {
test("should load addresses from comma-separated string", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2,2001:db8::3");
expect(pool.size).toBe(3);
expect(pool.configured).toBe(true);
});
test("should return null when empty", () => {
const pool = new IPv6SourcePool();
expect(pool.size).toBe(0);
expect(pool.configured).toBe(false);
expect(pool.getNext()).toBeNull();
});
test("should rotate through addresses round-robin", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2,2001:db8::3");
expect(pool.getNext()).toBe("2001:db8::1");
expect(pool.getNext()).toBe("2001:db8::2");
expect(pool.getNext()).toBe("2001:db8::3");
expect(pool.getNext()).toBe("2001:db8::1"); // wraps around
});
test("should skip failed sources", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2,2001:db8::3");
pool.setFailureThreshold(1);
pool.markFailed("2001:db8::2");
const results = [pool.getNext(), pool.getNext(), pool.getNext(), pool.getNext()];
// Should skip 2001:db8::2 (disabled after 1 failure with threshold=1)
expect(results).not.toContain("2001:db8::2");
});
test("should reset after all sources fail", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2");
// Mark all as failed
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::2");
pool.markFailed("2001:db8::2");
pool.markFailed("2001:db8::2");
// Should auto-reset and return first
const addr = pool.getNext();
expect(addr).toBe("2001:db8::1");
});
test("should reset failure count on success", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2");
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::1");
pool.markSuccess("2001:db8::1");
// Should not be disabled after success
const addr = pool.getNext();
expect(addr).toBe("2001:db8::1");
});
test("should get address by index", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2");
expect(pool.getAtIndex(0)).toBe("2001:db8::1");
expect(pool.getAtIndex(1)).toBe("2001:db8::2");
expect(pool.getAtIndex(2)).toBeNull();
});
test("should handle whitespace in input", () => {
const pool = new IPv6SourcePool();
pool.loadFromString(" 2001:db8::1 , 2001:db8::2 ");
expect(pool.size).toBe(2);
expect(pool.getNext()).toBe("2001:db8::1");
});
test("should handle empty input", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("");
expect(pool.size).toBe(0);
});
test("should reset all sources", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1,2001:db8::2");
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::1");
pool.markFailed("2001:db8::1");
pool.reset();
expect(pool.getNext()).toBe("2001:db8::1");
});
test("should change failure threshold", () => {
const pool = new IPv6SourcePool();
pool.loadFromString("2001:db8::1");
pool.setFailureThreshold(1);
pool.markFailed("2001:db8::1");
// With threshold 1, should be disabled after 1 failure
// getNext should auto-reset since all failed
const addr = pool.getNext();
expect(addr).toBe("2001:db8::1");
});
});