Files
sase.tr/apps/api/src/integrations/emex/emex.service.spec.ts
Semih Yesilyurt b992c0075b
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
feat(proxy): otomatik Floxy→DataImpulse failover (decode regresyonu)
Floxy residential proxy bitince/çökünce (402 bakiye veya tünel reddi —
ERR_TUNNEL_CONNECTION_FAILED), EMEX + pcat decode istekleri ölü proxy'ye
çarpıp decode başarı oranını çökertiyordu (2026-06-13: ~17k tünel hatası/24s,
decode %78→%45). Önceki "DataImpulse last-ditch fallback" tarayıcı yolunda hiç
yoktu ve HTTP yolunda her istekte 2 ölü Floxy denemesi ziyan ediyordu.

Paylaşılan ProxyHealthService kapısı (in-memory cooldown): herhangi bir tüketici
bir Floxy bağlantı hatası görünce kapıyı tetikler; cooldown boyunca TÜM tüketiciler
DataImpulse'a düşer. Floxy'den ilk başarılı yanıt veya cooldown bitişi kapıyı
temizler (kendi kendini iyileştirir, periyodik yeniden-deneme). FLOXY_FAILOVER_COOLDOWN_MS
ile ayarlanır (varsayılan 180s).

Bağlanan tüketiciler:
- EMEX HTTP (fetchEmexHtml): kapı açıkken DataImpulse-öncelikli zamanlama
- EMEX tarayıcı (Playwright): launch'ta dinamik sağlayıcı seçimi + ensureSession
  health-gate'i (Floxy ölünce DataImpulse'a relaunch, kapı temizlenince Floxy'i
  yeniden dene); scrape-içi tünel ölümünde tripFloxyFailover
- pcat (buildProxy): hem call hem capture (Playwright JWT) bacakları DataImpulse'a düşer

isProxyConnectFailure(): yalnız gerçek bağlantı hatalarında tetikler — yavaş-ama-
canlı exit'in nav timeout'u failover'ı tetiklemez.

Test: ProxyHealthService + isProxyConnectFailure birim testleri; tüm api suite (303) yeşil.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 21:03:23 +03:00

71 lines
2.5 KiB
TypeScript

import type { ConfigService } from "@nestjs/config";
import { describe, expect, it, vi } from "vitest";
import { EmexService } from "./emex.service";
// ConfigService.get(key, default) returns the raw ENV STRING (NestJS does not
// coerce <number> generics at runtime). These tests guard the regression where a
// string port range made the proxy-port arithmetic do string concat
// (45 + "10001" = "4510001"), producing an out-of-range port that crashed the
// whole app with undici "Invalid URL" at construction time.
function makeConfig(overrides: Record<string, string>): ConfigService {
return {
get: vi.fn((key: string, def?: unknown) =>
Object.hasOwn(overrides, key) ? overrides[key] : def,
),
} as unknown as ConfigService;
}
const browser = {} as never;
const redis = { set: vi.fn() } as never;
const posthog = {} as never;
const proxyTelemetry = { record: vi.fn() } as never;
const proxyHealth = {
isFloxyDown: vi.fn(() => false),
reportFloxyFailure: vi.fn(),
reportFloxySuccess: vi.fn(),
} as never;
describe("EmexService proxy-port coercion (regression)", () => {
it("does not throw 'Invalid URL' when ports arrive as strings over a real range", () => {
const config = makeConfig({
EMEX_USE_PROXY: "true",
EMEX_PROXY_HOST: "74.81.81.81",
EMEX_PROXY_PORT_START: "10001",
EMEX_PROXY_PORT_END: "10099",
});
// Pre-fix: threw "Invalid URL" here (port "4510001" > 65535).
expect(
() => new EmexService(config, browser, redis, posthog, proxyTelemetry, proxyHealth),
).not.toThrow();
});
it("constructs with a single-port range (string env)", () => {
const config = makeConfig({
EMEX_USE_PROXY: "true",
EMEX_PROXY_PORT_START: "823",
EMEX_PROXY_PORT_END: "823",
});
expect(
() => new EmexService(config, browser, redis, posthog, proxyTelemetry, proxyHealth),
).not.toThrow();
});
it("falls back to a valid default when the port env is garbage", () => {
const config = makeConfig({
EMEX_USE_PROXY: "true",
EMEX_PROXY_PORT_START: "not-a-number",
EMEX_PROXY_PORT_END: "999999",
});
expect(
() => new EmexService(config, browser, redis, posthog, proxyTelemetry, proxyHealth),
).not.toThrow();
});
it("constructs cleanly with the proxy disabled", () => {
const config = makeConfig({ EMEX_USE_PROXY: "false" });
expect(
() => new EmexService(config, browser, redis, posthog, proxyTelemetry, proxyHealth),
).not.toThrow();
});
});