import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { getClientsConfigCollection, getActiveScanId, updateActiveScanId, clearActiveScanId, updateProductConfig, getActiveScanIds, addActiveScanId, removeActiveScanId, getPrimaryScanId, setPrimaryScanId, clearPrimaryScanId, } from "../clientsConfig.getters"; import { MAX_ACTIVE_SCANS } from "../clientsConfig.constants"; import { createClientConfig } from "../../../test-utils/factories/talkpilot/clientsConfig"; describe("updateProductConfig", () => { beforeEach(async () => { await getClientsConfigCollection().deleteMany({}); }); afterEach(async () => { await getClientsConfigCollection().deleteMany({}); }); it("should update partial fields using correct dot notation", async () => { const clientId = "client-123"; const collection = getClientsConfigCollection(); await collection.insertOne({ clientId, products: { websiteTalk: { language: "English", companyName: "OldCorp", timezone: "UTC", }, }, } as any); const updates = { language: "Hebrew", companyName: "TestCorp" }; await updateProductConfig(clientId, "websiteTalk", updates); const updatedDoc = await collection.findOne({ clientId }); expect(updatedDoc).toBeDefined(); expect(updatedDoc?.products.websiteTalk?.language).toBe("Hebrew"); expect(updatedDoc?.products.websiteTalk?.companyName).toBe("TestCorp"); expect(updatedDoc?.products.websiteTalk?.timezone).toBe("UTC"); }); it("should throw an error if no client config is found", async () => { await expect( updateProductConfig("invalid-client", "websiteTalk", { language: "English", }), ).rejects.toThrow("No client config found for clientId: invalid-client"); }); }); describe("activeScanId", () => { beforeEach(async () => { await getClientsConfigCollection().deleteMany({}); }); afterEach(async () => { await getClientsConfigCollection().deleteMany({}); }); it("should return undefined when activeScanId is not set", async () => { const clientId = "client-active"; await getClientsConfigCollection().insertOne({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he" }, }, } as any); expect(await getActiveScanId(clientId)).toBeUndefined(); }); it("should persist activeScanId and return it from getActiveScanId", async () => { const clientId = "client-active-2"; await getClientsConfigCollection().insertOne({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he" }, }, } as any); await updateActiveScanId(clientId, "scan-abc"); expect(await getActiveScanId(clientId)).toBe("scan-abc"); }); it("should clear activeScanId and return undefined from getActiveScanId", async () => { const clientId = "client-active-3"; await getClientsConfigCollection().insertOne({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanId: "scan-old", }, }, } as any); await clearActiveScanId(clientId); expect(await getActiveScanId(clientId)).toBeUndefined(); }); }); describe("activeScanIds (CP-1628)", () => { beforeEach(async () => { await getClientsConfigCollection().deleteMany({}); }); afterEach(async () => { await getClientsConfigCollection().deleteMany({}); }); it("returns an empty list when nothing is set", async () => { const clientId = "client-list-1"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he" }, }, }), ); expect(await getActiveScanIds(clientId)).toEqual([]); }); it("falls back to wrapping the legacy activeScanId when activeScanIds is absent", async () => { const clientId = "client-list-2"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanId: "scan-legacy", }, }, }), ); expect(await getActiveScanIds(clientId)).toEqual(["scan-legacy"]); }); it("adds a scan id and returns it from getActiveScanIds", async () => { const clientId = "client-list-3"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he" }, }, }), ); await addActiveScanId(clientId, "scan-a"); await addActiveScanId(clientId, "scan-b"); expect(await getActiveScanIds(clientId)).toEqual(["scan-a", "scan-b"]); }); it("does not duplicate an already-present scan id, and does not count it against the cap", async () => { const clientId = "client-list-4"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanIds: ["scan-a", "scan-b", "scan-c"], }, }, }), ); await expect(addActiveScanId(clientId, "scan-a")).resolves.toBeUndefined(); expect(await getActiveScanIds(clientId)).toEqual([ "scan-a", "scan-b", "scan-c", ]); }); it(`throws when adding a new scan id beyond MAX_ACTIVE_SCANS (${MAX_ACTIVE_SCANS})`, async () => { const clientId = "client-list-5"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanIds: ["scan-a", "scan-b", "scan-c"], }, }, }), ); await expect(addActiveScanId(clientId, "scan-d")).rejects.toThrow( `Client ${clientId} already has the maximum of ${MAX_ACTIVE_SCANS} active scans`, ); expect(await getActiveScanIds(clientId)).toEqual([ "scan-a", "scan-b", "scan-c", ]); }); it("removes a scan id without affecting the others", async () => { const clientId = "client-list-6"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanIds: ["scan-a", "scan-b"], }, }, }), ); await removeActiveScanId(clientId, "scan-a"); expect(await getActiveScanIds(clientId)).toEqual(["scan-b"]); }); it("removing a scan id that isn't present is a harmless no-op", async () => { const clientId = "client-list-7"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanIds: ["scan-a"], }, }, }), ); await expect( removeActiveScanId(clientId, "scan-x"), ).resolves.toBeUndefined(); expect(await getActiveScanIds(clientId)).toEqual(["scan-a"]); }); it("throws when removing from a client that doesn't exist", async () => { await expect( removeActiveScanId("no-such-client", "scan-a"), ).rejects.toThrow("No client config found for clientId: no-such-client"); }); it("migrates a legacy-only activeScanId into activeScanIds instead of dropping it when adding a new one", async () => { const clientId = "client-list-8"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanId: "scan-legacy", }, }, }), ); await addActiveScanId(clientId, "scan-new"); expect(await getActiveScanIds(clientId)).toEqual(["scan-legacy", "scan-new"]); const doc = await getClientsConfigCollection().findOne({ clientId }); expect(doc?.products?.websiteTalk?.activeScanId).toBeUndefined(); }); it("throws when adding to a client that doesn't exist", async () => { await expect( addActiveScanId("no-such-client", "scan-a"), ).rejects.toThrow("No client config found for clientId: no-such-client"); }); it("unsets a legacy-only activeScanId when it is the one being removed", async () => { const clientId = "client-list-9"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanId: "scan-legacy", }, }, }), ); await removeActiveScanId(clientId, "scan-legacy"); expect(await getActiveScanIds(clientId)).toEqual([]); const doc = await getClientsConfigCollection().findOne({ clientId }); expect(doc?.products?.websiteTalk?.activeScanId).toBeUndefined(); }); }); describe("primaryScanId (CP-1628 follow-up)", () => { beforeEach(async () => { await getClientsConfigCollection().deleteMany({}); }); afterEach(async () => { await getClientsConfigCollection().deleteMany({}); }); it("returns undefined when nothing is set", async () => { const clientId = "client-primary-1"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he" }, }, }), ); expect(await getPrimaryScanId(clientId)).toBeUndefined(); }); it("sets and returns the primary scan id, independent of activeScanIds", async () => { const clientId = "client-primary-2"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", activeScanIds: ["scan-a", "scan-b"], }, }, }), ); await setPrimaryScanId(clientId, "scan-c"); expect(await getPrimaryScanId(clientId)).toBe("scan-c"); expect(await getActiveScanIds(clientId)).toEqual(["scan-a", "scan-b"]); }); it("replaces the previous primary scan id when set again", async () => { const clientId = "client-primary-3"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", primaryScanId: "scan-a", }, }, }), ); await setPrimaryScanId(clientId, "scan-b"); expect(await getPrimaryScanId(clientId)).toBe("scan-b"); }); it("clears the primary scan id", async () => { const clientId = "client-primary-4"; await getClientsConfigCollection().insertOne( createClientConfig({ clientId, products: { websiteTalk: { companyName: "X", timezone: "UTC", language: "he", primaryScanId: "scan-a", }, }, }), ); await clearPrimaryScanId(clientId); expect(await getPrimaryScanId(clientId)).toBeUndefined(); }); it("throws when setting the primary scan for a client that doesn't exist", async () => { await expect( setPrimaryScanId("no-such-client", "scan-a"), ).rejects.toThrow("No client config found for clientId: no-such-client"); }); it("throws when clearing the primary scan for a client that doesn't exist", async () => { await expect( clearPrimaryScanId("no-such-client"), ).rejects.toThrow("No client config found for clientId: no-such-client"); }); });