import { describe, it, expect, vi, beforeEach } from "vitest"; import { get } from "svelte/store"; import type { Session } from "$lib/api/types"; const mockInvoke = vi.fn(); vi.mock("$lib/api/bindings", () => ({ commands: { lmsGetSyncGroups: () => mockInvoke("lms_get_sync_groups"), lmsCreateSyncGroup: (masterMac: string, slaveMacs: string[]) => mockInvoke("lms_create_sync_group", { masterMac, slaveMacs }), lmsUnsyncPlayer: (mac: string) => mockInvoke("lms_unsync_player", { mac }), lmsDissolveSyncGroup: (masterMac: string) => mockInvoke("lms_dissolve_sync_group", { masterMac }), }, })); import { lmsSync, isLmsSession, macForSession } from "./lmsSync"; function session(deviceId: string | null): Session { return { id: "s", deviceId } as unknown as Session; } describe("LMS session detection", () => { it("detects LMS zones by the lms- device-id prefix", () => { expect(isLmsSession(session("lms-aa:bb:cc:dd:ee:ff"))).toBe(true); expect(isLmsSession(session("chromecast-123"))).toBe(false); expect(isLmsSession(session(null))).toBe(false); expect(isLmsSession(null)).toBe(false); }); it("recovers the MAC from an LMS session, null otherwise", () => { expect(macForSession(session("lms-aa:bb:cc:dd:ee:ff"))).toBe("aa:bb:cc:dd:ee:ff"); expect(macForSession(session("web-xyz"))).toBeNull(); expect(macForSession(null)).toBeNull(); }); }); describe("lmsSync store", () => { beforeEach(() => { mockInvoke.mockReset(); lmsSync.reset(); }); it("fusing preserves existing slaves and adds the new zone", async () => { // Initial group: master M with slave A. mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]); await lmsSync.refresh(); // create returns void; refresh after returns the updated group. mockInvoke.mockResolvedValueOnce(undefined); mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A", "B"], slaveNames: [] }]); await lmsSync.fuseZone("M", "B"); expect(mockInvoke).toHaveBeenCalledWith("lms_create_sync_group", { masterMac: "M", slaveMacs: ["A", "B"], }); expect(get(lmsSync).groups[0].slaveMacs).toEqual(["A", "B"]); }); it("decoupling a slave unsyncs just that player", async () => { mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]); await lmsSync.refresh(); mockInvoke.mockResolvedValueOnce(undefined); // unsync mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: [], slaveNames: [] }]); await lmsSync.decoupleZone("A"); expect(mockInvoke).toHaveBeenCalledWith("lms_unsync_player", { mac: "A" }); }); it("decoupling the master dissolves the whole group", async () => { mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]); await lmsSync.refresh(); mockInvoke.mockResolvedValueOnce(undefined); // dissolve mockInvoke.mockResolvedValueOnce([]); await lmsSync.decoupleZone("M"); expect(mockInvoke).toHaveBeenCalledWith("lms_dissolve_sync_group", { masterMac: "M" }); }); it("treats a missing plugin (refresh error) as no groups, not fatal", async () => { mockInvoke.mockRejectedValueOnce(new Error("404")); await lmsSync.refresh(); expect(get(lmsSync).groups).toEqual([]); }); });