import assert from "node:assert/strict"; import { describe, it } from "node:test"; import type { Connector, ConnectorBinding, ConnectorCandidate } from "@/types/appops"; import { loadSetupConnectorContext } from "./setupConnectorContext.ts"; const connector = (id: string): Connector => ({ id, name: id, status: "connected", lastTest: new Date(0).toISOString(), capabilities: [], type: "Media", }); const candidate = (connectorId: string): ConnectorCandidate => ({ connectorId, discoveredContainerId: connectorId, containerId: connectorId, containerName: connectorId, displayName: connectorId, appType: connectorId, lifecycleState: "running", suggestedAppId: connectorId, connectorStatus: "connected", missingSecrets: [], requiredSecrets: [], confidence: 0.99, reasons: ["matched"], ports: [], }); describe("loadSetupConnectorContext", () => { it("retries transient candidate failures before marking setup context partial", async () => { let candidateAttempts = 0; const result = await loadSetupConnectorContext([connector("plex")], { getCandidates: async (): Promise => { candidateAttempts += 1; if (candidateAttempts === 1) throw new Error("temporary busy"); return [candidate("plex")]; }, getBindings: async (): Promise => [], }); assert.equal(candidateAttempts, 2); assert.equal(result.partial, false); assert.equal(result.candidates.plex.length, 1); assert.deepEqual(result.bindings.plex, []); }); it("keeps partial true when retry still fails", async () => { const result = await loadSetupConnectorContext([connector("tautulli")], { getCandidates: async (): Promise => { throw new Error("still down"); }, getBindings: async (): Promise => [], }); assert.equal(result.partial, true); assert.deepEqual(result.candidates.tautulli, []); assert.deepEqual(result.bindings.tautulli, []); }); it("limits connector context concurrency to avoid backend pool exhaustion", async () => { let active = 0; let maxActive = 0; const observe = async (value: T): Promise => { active += 1; maxActive = Math.max(maxActive, active); await new Promise(resolve => setTimeout(resolve, 1)); active -= 1; return value; }; const result = await loadSetupConnectorContext( ["authentik", "deluge", "plex", "tautulli", "sonarr"].map(connector), { getCandidates: async (connectorId): Promise => observe([candidate(connectorId)]), getBindings: async (): Promise => observe([]), }, ); assert.equal(result.partial, false); assert.equal(Object.keys(result.candidates).length, 5); assert.equal(maxActive, 4); }); });