test: add integrations, cache, cron, and telemetry tests

- New integrations.test.ts: 13 tests covering CRUD, token generation,
  token lookup, regeneration, and multi-user isolation
- New cache.test.ts: 3 tests for purgeMetadataCache (shell title
  deletion, fully-fetched title preservation, empty case)
- New cron.test.ts: 5 tests for cron run lifecycle (start, complete,
  fail with Error and string)
- New telemetry.test.ts: 7 tests for isTelemetryEnabled toggle and
  performTelemetryReport (disabled skip, enabled send, 24h interval
  throttle, interval expiry, fetch failure resilience)

345 → 386 tests passing across 27 files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 12:39:04 -04:00
co-authored by Claude Opus 4.6
parent 554271a888
commit 373a5d3caf
16 changed files with 402 additions and 61 deletions
+122
View File
@@ -0,0 +1,122 @@
import { beforeEach, describe, expect, test } from "vitest";
import { clearAllTables, insertUser } from "@sofa/test/db";
import {
createOrUpdateIntegration,
deleteIntegration,
findIntegrationByToken,
listUserIntegrations,
regenerateToken,
} from "../src/integrations";
beforeEach(() => {
clearAllTables();
insertUser("user-1");
});
describe("createOrUpdateIntegration", () => {
test("creates a new webhook integration", () => {
const result = createOrUpdateIntegration("user-1", "plex");
expect(result.provider).toBe("plex");
expect(result.type).toBe("webhook");
expect(result.enabled).toBe(true);
expect(result.token).toHaveLength(64); // 32 bytes hex
});
test("creates a list integration for sonarr", () => {
const result = createOrUpdateIntegration("user-1", "sonarr");
expect(result.type).toBe("list");
});
test("creates a list integration for radarr", () => {
const result = createOrUpdateIntegration("user-1", "radarr");
expect(result.type).toBe("list");
});
test("returns existing integration without duplicating", () => {
const first = createOrUpdateIntegration("user-1", "plex");
const second = createOrUpdateIntegration("user-1", "plex");
expect(second.id).toBe(first.id);
expect(second.token).toBe(first.token);
});
test("updates enabled state on existing integration", () => {
createOrUpdateIntegration("user-1", "plex", true);
const updated = createOrUpdateIntegration("user-1", "plex", false);
expect(updated.enabled).toBe(false);
});
test("creates with enabled=false when specified", () => {
const result = createOrUpdateIntegration("user-1", "jellyfin", false);
expect(result.enabled).toBe(false);
});
});
describe("listUserIntegrations", () => {
test("returns empty list for user with no integrations", () => {
const result = listUserIntegrations("user-1");
expect(result.integrations).toHaveLength(0);
});
test("returns all integrations for user", () => {
createOrUpdateIntegration("user-1", "plex");
createOrUpdateIntegration("user-1", "jellyfin");
const result = listUserIntegrations("user-1");
expect(result.integrations).toHaveLength(2);
const providers = result.integrations.map((i) => i.provider).sort();
expect(providers).toEqual(["jellyfin", "plex"]);
});
test("does not return other users integrations", () => {
insertUser("user-2");
createOrUpdateIntegration("user-1", "plex");
createOrUpdateIntegration("user-2", "jellyfin");
const result = listUserIntegrations("user-1");
expect(result.integrations).toHaveLength(1);
expect(result.integrations[0].provider).toBe("plex");
});
test("serializes dates as ISO strings", () => {
createOrUpdateIntegration("user-1", "plex");
const result = listUserIntegrations("user-1");
expect(result.integrations[0].createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
});
});
describe("deleteIntegration", () => {
test("removes an integration", () => {
createOrUpdateIntegration("user-1", "plex");
deleteIntegration("user-1", "plex");
const result = listUserIntegrations("user-1");
expect(result.integrations).toHaveLength(0);
});
test("does not throw for nonexistent integration", () => {
expect(() => deleteIntegration("user-1", "nonexistent")).not.toThrow();
});
});
describe("findIntegrationByToken", () => {
test("finds integration by token", () => {
const created = createOrUpdateIntegration("user-1", "plex");
const found = findIntegrationByToken(created.token);
expect(found?.id).toBe(created.id);
});
test("returns undefined for invalid token", () => {
expect(findIntegrationByToken("nonexistent")).toBeUndefined();
});
});
describe("regenerateToken", () => {
test("generates a new token for existing integration", () => {
const created = createOrUpdateIntegration("user-1", "plex");
const result = regenerateToken("user-1", "plex");
expect(result?.token).not.toBe(created.token);
expect(result?.token).toHaveLength(64);
});
});