SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
3 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
3.4 KB · 50 lines typescript
Raw Blame History
1import { describe, expect, it } from "vitest";2import { intervalMs, parseConnectorConfig, validateAgainstRegistry } from "./config.js";3import { registerParser } from "./registry.js";45const BASE = `id: t\nname: T\ndomain: x.com\nkind: operator\n`;67describe("connector config cross-field validation", () => {8  it("rejects level > maxLevel", () => {9    expect(() => parseConnectorConfig(`${BASE}fetch: { level: 3, maxLevel: 2 }`)).toThrow(/fetch.level 3 exceeds fetch.maxLevel 2/);10  });11  it("rejects a per-run credit budget above the daily one on premium connectors", () => {12    expect(() => parseConnectorConfig(`${BASE}fetch: { maxLevel: 4, maxCreditsPerRun: 200, maxCreditsPerDay: 60 }`)).toThrow(/maxCreditsPerRun 200 exceeds fetch.maxCreditsPerDay 60/);13    expect(() => parseConnectorConfig(`${BASE}fetch: { maxLevel: 2, maxCreditsPerRun: 200, maxCreditsPerDay: 0 }`)).not.toThrow();14  });15  it("compiles every discovery / extractor regex at parse time", () => {16    expect(() => parseConnectorConfig(`${BASE}discovery: { include: ["/ok/", "(unclosed"] }`)).toThrow(/discovery.include.1: invalid regex/);17    expect(() => parseConnectorConfig(`${BASE}discovery: { classify: [{ pattern: "[a-", group: g }] }`)).toThrow(/classify.0.pattern/);18    expect(() => parseConnectorConfig(`${BASE}extractors: { f: { parser: p, match: ["*bad"] } }`)).toThrow(/extractors.f.match.0/);19    expect(() => parseConnectorConfig(`${BASE}extractors: { f: { fields: { name: { selector: h1, regex: "(" } } } }`)).toThrow(/fields.name.regex/);20  });21  it("rejects seeds / classify rules whose minLevel exceeds maxLevel and extractors without parser or fields", () => {22    expect(() => parseConnectorConfig(`${BASE}fetch: { maxLevel: 2 }\ndiscovery: { seeds: [{ url: /a, minLevel: 4 }] }`)).toThrow(/minLevel 4 exceeds/);23    expect(() => parseConnectorConfig(`${BASE}extractors: { f: { kind: facility } }`)).toThrow(/needs either/);24  });25  it("rejects bad schedule intervals and accepts the documented forms", () => {26    expect(() => parseConnectorConfig(`${BASE}schedule: { newsroom: soonish }`)).toThrow(/bad interval "soonish"/);27    const cfg = parseConnectorConfig(`${BASE}schedule: { a: daily, b: 6h, c: 30m, d: never, e: quarterly }`);28    expect(cfg.schedule.d).toBe("never");29    expect(intervalMs("6h")).toBe(6 * 3_600_000);30    expect(intervalMs("never")).toBe(Number.POSITIVE_INFINITY);31    expect(() => intervalMs("2y")).toThrow(/bad interval/);32  });33  it("applies defaults", () => {34    const cfg = parseConnectorConfig(BASE);35    expect(cfg.fetch).toMatchObject({ level: 1, maxLevel: 2, rpm: 30, respectRobots: true, maxCreditsPerRun: 200 });36    expect(cfg.schedule).toEqual({ seed: "weekly" });37  });38});3940describe("validateAgainstRegistry", () => {41  it("is a no-op while nothing is registered, then reports unknown parsers / implementations", () => {42    const cfg = parseConnectorConfig(`${BASE}extractors: { f: { parser: nope_v1 } }`);43    expect(validateAgainstRegistry(cfg)).toEqual([]);44    registerParser({ name: "known_v1", version: "1", parse: () => [] });45    expect(validateAgainstRegistry(cfg)).toEqual(['extractors.f.parser "nope_v1" is not registered']);46    expect(validateAgainstRegistry(parseConnectorConfig(`${BASE}extractors: { f: { parser: known_v1 } }`))).toEqual([]);47    expect(validateAgainstRegistry(parseConnectorConfig(`${BASE}implementation: ghost`))).toEqual(['implementation "ghost" is not registered']);48  });49});50