/** * Tests for PrivacySettings contract. * @module tests/unit/contracts/privacy-settings.test */ import { PRIVACY_SETTINGS_DEFAULT, PRIVACY_SETTINGS_VERSION, PRIVACY_SETTING_KEYS, FEATURE_NAME_MAP, createPrivacySettings, isValidPrivacySettings, validateSettingKey, validateSettingValue, validateFeatureName, } from "../../../src/contracts/privacy-settings.js"; import { describe, it, expect } from "vitest"; describe("privacy-settings contract", () => { describe("constants", () => { it("should export PRIVACY_SETTINGS_VERSION as 1", () => { expect(PRIVACY_SETTINGS_VERSION).toBe(1); }); it("should export PRIVACY_SETTINGS_DEFAULT with all false", () => { expect(PRIVACY_SETTINGS_DEFAULT).toEqual({ reactionCamEnabled: false, hpReactiveCamStylingEnabled: false, }); }); it("should export PRIVACY_SETTING_KEYS as frozen array", () => { expect(PRIVACY_SETTING_KEYS).toEqual([ "reactionCamEnabled", "hpReactiveCamStylingEnabled", ]); expect(Object.isFrozen(PRIVACY_SETTING_KEYS)).toBe(true); }); it("should export FEATURE_NAME_MAP as frozen object", () => { expect(FEATURE_NAME_MAP).toEqual({ reactionCam: "reactionCamEnabled", hpReactiveCamStyling: "hpReactiveCamStylingEnabled", }); expect(Object.isFrozen(FEATURE_NAME_MAP)).toBe(true); }); }); describe("createPrivacySettings", () => { it("should return default settings when no overrides provided", () => { const result = createPrivacySettings(); expect(result).toEqual(PRIVACY_SETTINGS_DEFAULT); }); it("should merge overrides with defaults", () => { const result = createPrivacySettings({ reactionCamEnabled: true }); expect(result).toEqual({ reactionCamEnabled: true, hpReactiveCamStylingEnabled: false, }); }); it("should allow both settings to be overridden", () => { const result = createPrivacySettings({ reactionCamEnabled: true, hpReactiveCamStylingEnabled: true, }); expect(result).toEqual({ reactionCamEnabled: true, hpReactiveCamStylingEnabled: true, }); }); it("should not modify the defaults object", () => { const originalDefaults = { ...PRIVACY_SETTINGS_DEFAULT }; createPrivacySettings({ reactionCamEnabled: true }); expect(PRIVACY_SETTINGS_DEFAULT).toEqual(originalDefaults); }); it("should ignore extra properties in overrides", () => { const result = createPrivacySettings({ reactionCamEnabled: true, extraProp: "should be ignored", }); expect(result).toEqual({ reactionCamEnabled: true, hpReactiveCamStylingEnabled: false, }); }); }); describe("isValidPrivacySettings", () => { it("should accept valid settings with all false", () => { const valid = { reactionCamEnabled: false, hpReactiveCamStylingEnabled: false, }; expect(isValidPrivacySettings(valid)).toEqual(valid); }); it("should accept valid settings with all true", () => { const valid = { reactionCamEnabled: true, hpReactiveCamStylingEnabled: true, }; expect(isValidPrivacySettings(valid)).toEqual(valid); }); it("should accept valid settings with mixed values", () => { const valid = { reactionCamEnabled: true, hpReactiveCamStylingEnabled: false, }; expect(isValidPrivacySettings(valid)).toEqual(valid); }); it("should throw TypeError for null", () => { expect(() => isValidPrivacySettings(null)).toThrow(TypeError); expect(() => isValidPrivacySettings(null)).toThrow( "PrivacySettings: must be an object" ); }); it("should throw TypeError for undefined", () => { expect(() => isValidPrivacySettings(undefined)).toThrow(TypeError); expect(() => isValidPrivacySettings(undefined)).toThrow( "PrivacySettings: must be an object" ); }); it("should throw TypeError for string", () => { expect(() => isValidPrivacySettings("invalid")).toThrow(TypeError); expect(() => isValidPrivacySettings("invalid")).toThrow( "PrivacySettings: must be an object" ); }); it("should throw TypeError for array", () => { expect(() => isValidPrivacySettings([])).toThrow(TypeError); expect(() => isValidPrivacySettings([])).toThrow( "PrivacySettings: must be an object" ); }); it("should throw TypeError for unknown keys", () => { const invalid = { reactionCamEnabled: false, hpReactiveCamStylingEnabled: false, extraKey: "invalid", }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow("unknown keys"); }); it("should throw TypeError when reactionCamEnabled is not boolean", () => { const invalid = { reactionCamEnabled: "not a boolean", hpReactiveCamStylingEnabled: false, }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "reactionCamEnabled must be a boolean" ); }); it("should throw TypeError when reactionCamEnabled is a number", () => { const invalid = { reactionCamEnabled: 1, hpReactiveCamStylingEnabled: false, }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "reactionCamEnabled must be a boolean" ); }); it("should throw TypeError when hpReactiveCamStylingEnabled is not boolean", () => { const invalid = { reactionCamEnabled: false, hpReactiveCamStylingEnabled: "not a boolean", }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "hpReactiveCamStylingEnabled must be a boolean" ); }); it("should throw TypeError when hpReactiveCamStylingEnabled is null", () => { const invalid = { reactionCamEnabled: false, hpReactiveCamStylingEnabled: null, }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "hpReactiveCamStylingEnabled must be a boolean" ); }); it("should throw TypeError for missing reactionCamEnabled", () => { const invalid = { hpReactiveCamStylingEnabled: false, }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "reactionCamEnabled must be a boolean" ); }); it("should throw TypeError for missing hpReactiveCamStylingEnabled", () => { const invalid = { reactionCamEnabled: false, }; expect(() => isValidPrivacySettings(invalid)).toThrow(TypeError); expect(() => isValidPrivacySettings(invalid)).toThrow( "hpReactiveCamStylingEnabled must be a boolean" ); }); }); describe("validateSettingKey", () => { it("should accept valid key: reactionCamEnabled", () => { expect(validateSettingKey("reactionCamEnabled")).toBe("reactionCamEnabled"); }); it("should accept valid key: hpReactiveCamStylingEnabled", () => { expect(validateSettingKey("hpReactiveCamStylingEnabled")).toBe( "hpReactiveCamStylingEnabled" ); }); it("should throw TypeError for empty string", () => { expect(() => validateSettingKey("")).toThrow(TypeError); expect(() => validateSettingKey("")).toThrow( "Setting key must be a non-empty string" ); }); it("should throw TypeError for non-string", () => { expect(() => validateSettingKey(123)).toThrow(TypeError); expect(() => validateSettingKey(123)).toThrow( "Setting key must be a non-empty string" ); }); it("should throw TypeError for unknown key", () => { expect(() => validateSettingKey("unknownKey")).toThrow(TypeError); expect(() => validateSettingKey("unknownKey")).toThrow("unknown key"); }); it("should throw TypeError for null", () => { expect(() => validateSettingKey(null)).toThrow(TypeError); expect(() => validateSettingKey(null)).toThrow( "Setting key must be a non-empty string" ); }); it("should throw TypeError for undefined", () => { expect(() => validateSettingKey(undefined)).toThrow(TypeError); expect(() => validateSettingKey(undefined)).toThrow( "Setting key must be a non-empty string" ); }); }); describe("validateSettingValue", () => { it("should accept true", () => { expect(validateSettingValue(true)).toBe(true); }); it("should accept false", () => { expect(validateSettingValue(false)).toBe(false); }); it("should throw TypeError for string", () => { expect(() => validateSettingValue("true")).toThrow(TypeError); expect(() => validateSettingValue("true")).toThrow( "value must be a boolean" ); }); it("should throw TypeError for number", () => { expect(() => validateSettingValue(1)).toThrow(TypeError); expect(() => validateSettingValue(1)).toThrow( "value must be a boolean" ); }); it("should throw TypeError for null", () => { expect(() => validateSettingValue(null)).toThrow(TypeError); expect(() => validateSettingValue(null)).toThrow( "value must be a boolean" ); }); it("should throw TypeError for undefined", () => { expect(() => validateSettingValue(undefined)).toThrow(TypeError); expect(() => validateSettingValue(undefined)).toThrow( "value must be a boolean" ); }); it("should throw TypeError for object", () => { expect(() => validateSettingValue({})).toThrow(TypeError); expect(() => validateSettingValue({})).toThrow( "value must be a boolean" ); }); }); describe("validateFeatureName", () => { it("should accept valid feature: reactionCam", () => { expect(validateFeatureName("reactionCam")).toBe("reactionCam"); }); it("should accept valid feature: hpReactiveCamStyling", () => { expect(validateFeatureName("hpReactiveCamStyling")).toBe( "hpReactiveCamStyling" ); }); it("should throw TypeError for empty string", () => { expect(() => validateFeatureName("")).toThrow(TypeError); expect(() => validateFeatureName("")).toThrow( "Feature name must be a non-empty string" ); }); it("should throw TypeError for non-string", () => { expect(() => validateFeatureName(123)).toThrow(TypeError); expect(() => validateFeatureName(123)).toThrow( "Feature name must be a non-empty string" ); }); it("should throw TypeError for unknown feature", () => { expect(() => validateFeatureName("unknownFeature")).toThrow(TypeError); expect(() => validateFeatureName("unknownFeature")).toThrow( "unknown feature" ); }); it("should throw TypeError for null", () => { expect(() => validateFeatureName(null)).toThrow(TypeError); expect(() => validateFeatureName(null)).toThrow( "Feature name must be a non-empty string" ); }); it("should throw TypeError for undefined", () => { expect(() => validateFeatureName(undefined)).toThrow(TypeError); expect(() => validateFeatureName(undefined)).toThrow( "Feature name must be a non-empty string" ); }); }); });