Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,88 @@
|
||||
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<ConnectorCandidate[]> => {
|
||||
candidateAttempts += 1;
|
||||
if (candidateAttempts === 1) throw new Error("temporary busy");
|
||||
return [candidate("plex")];
|
||||
},
|
||||
getBindings: async (): Promise<ConnectorBinding[]> => [],
|
||||
});
|
||||
|
||||
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<ConnectorCandidate[]> => {
|
||||
throw new Error("still down");
|
||||
},
|
||||
getBindings: async (): Promise<ConnectorBinding[]> => [],
|
||||
});
|
||||
|
||||
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 <T,>(value: T): Promise<T> => {
|
||||
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<ConnectorCandidate[]> => observe([candidate(connectorId)]),
|
||||
getBindings: async (): Promise<ConnectorBinding[]> => observe([]),
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(result.partial, false);
|
||||
assert.equal(Object.keys(result.candidates).length, 5);
|
||||
assert.equal(maxActive, 4);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user