import { describe, it, expect } from "vitest"; import { createVisibilityMatrix, isValidVisibilityMatrix, VISIBILITY_STATES, VISIBILITY_MATRIX_VERSION, } from "../../../src/contracts/visibility-matrix.js"; describe("visibility-matrix contract", () => { describe("createVisibilityMatrix()", () => { it("creates a matrix with default empty state", () => { const m = createVisibilityMatrix(); expect(m._version).toBe(VISIBILITY_MATRIX_VERSION); expect(m.matrix).toEqual({}); }); it("creates a matrix with provided entries", () => { const m = createVisibilityMatrix({ "user-1": "active", "user-2": "hidden" }); expect(m.matrix["user-1"]).toBe("active"); expect(m.matrix["user-2"]).toBe("hidden"); }); it("returns a shallow copy of the provided matrix", () => { const input = /** @type {{ "user-1": import("../../../src/contracts/visibility-matrix.js").VisibilityState }} */ ({ "user-1": "active" }); const m = createVisibilityMatrix(input); input["user-1"] = "hidden"; expect(m.matrix["user-1"]).toBe("active"); // not mutated }); }); describe("isValidVisibilityMatrix()", () => { it("accepts a valid matrix with all known states", () => { for (const state of VISIBILITY_STATES) { const data = { _version: 1, matrix: { "user-1": state } }; expect(() => isValidVisibilityMatrix(data)).not.toThrow(); } }); it("accepts an empty matrix", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: {} })).not.toThrow(); }); it("throws if not an object", () => { expect(() => isValidVisibilityMatrix(null)).toThrow(TypeError); expect(() => isValidVisibilityMatrix("string")).toThrow(TypeError); }); it("throws on unknown top-level keys", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: {}, extra: true }) ).toThrow(TypeError); }); it("throws on wrong _version", () => { expect(() => isValidVisibilityMatrix({ _version: 2, matrix: {} }) ).toThrow(TypeError); }); it("throws if matrix is not a plain object", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: null }) ).toThrow(TypeError); expect(() => isValidVisibilityMatrix({ _version: 1, matrix: ["active"] }) ).toThrow(TypeError); }); it("throws on empty userId key", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: { "": "active" } }) ).toThrow(TypeError); }); it("throws on invalid state value", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: { "user-1": "invisible" } }) ).toThrow(TypeError); }); it("throws on null state value", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: { "user-1": null } }) ).toThrow(TypeError); }); it("throws on non-string state value (Symbol)", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: { "user-1": Symbol("test") } }) ).toThrow(TypeError); }); it("throws on non-string state value (object)", () => { expect(() => isValidVisibilityMatrix({ _version: 1, matrix: { "user-1": { value: "active" } } }) ).toThrow(TypeError); }); }); describe("VISIBILITY_STATES", () => { it("contains exactly 8 states", () => { expect(VISIBILITY_STATES).toHaveLength(8); }); it("is frozen", () => { expect(Object.isFrozen(VISIBILITY_STATES)).toBe(true); }); it("contains the 8 expected state values", () => { const expected = [ "active", "hidden", "self-muted", "offline", "cam-lost", "reconnecting", "never-connected", "ghost", ]; expect([...VISIBILITY_STATES].sort()).toEqual(expected.sort()); }); }); });