import { describe, test, expect } from "bun:test"; import { getAutomationStateSnapshot, getAutomationStateEventPayload, automationStatesEqual, } from "../services/automationState"; import type { AutomationStateResponse } from "../types/api"; // ============================================================================= // normalizeAutomationState (via getAutomationStateSnapshot) // ============================================================================= describe("normalizeAutomationState", () => { test("returns undefined when metadata has no automation_state key", () => { expect(getAutomationStateSnapshot({})).toBeUndefined(); expect(getAutomationStateSnapshot({ other: true })).toBeUndefined(); expect(getAutomationStateSnapshot(null)).toBeUndefined(); expect(getAutomationStateSnapshot(undefined)).toBeUndefined(); }); test("returns disabled state for null automation_state", () => { const result = getAutomationStateSnapshot({ automation_state: null }); expect(result).toEqual({ enabled: false, phase: null, next_tick_at: null, sleep_until: null, }); }); test("returns disabled state for non-object automation_state", () => { for (const val of ["string", 123, true, []]) { const result = getAutomationStateSnapshot({ automation_state: val }); expect(result?.enabled).toBe(false); } }); test("normalizes enabled: true correctly", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true } }); expect(result?.enabled).toBe(true); }); test("normalizes enabled to false for non-true values", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: "yes" } }); expect(result?.enabled).toBe(false); }); test("accepts phase: standby", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, phase: "standby" } }); expect(result?.phase).toBe("standby"); }); test("accepts phase: sleeping", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, phase: "sleeping" } }); expect(result?.phase).toBe("sleeping"); }); test("rejects invalid phase values", () => { for (const phase of ["running", "idle", "active", "", null]) { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, phase } }); expect(result?.phase).toBeNull(); } }); test("normalizes next_tick_at as number", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, next_tick_at: 12345 } }); expect(result?.next_tick_at).toBe(12345); }); test("normalizes next_tick_at as null for non-number", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, next_tick_at: "soon" } }); expect(result?.next_tick_at).toBeNull(); }); test("normalizes sleep_until as number", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, sleep_until: 99999 } }); expect(result?.sleep_until).toBe(99999); }); test("normalizes sleep_until as null for non-number", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, sleep_until: false } }); expect(result?.sleep_until).toBeNull(); }); test("fully normalizes a complete valid state", () => { const result = getAutomationStateSnapshot({ automation_state: { enabled: true, phase: "sleeping", next_tick_at: 100, sleep_until: 200 }, }); expect(result).toEqual({ enabled: true, phase: "sleeping", next_tick_at: 100, sleep_until: 200, }); }); }); // ============================================================================= // getAutomationStateEventPayload // ============================================================================= describe("getAutomationStateEventPayload", () => { test("returns disabled default when no automation_state in metadata", () => { const result = getAutomationStateEventPayload({}); expect(result).toEqual({ enabled: false, phase: null, next_tick_at: null, sleep_until: null, }); }); test("returns disabled default for null metadata", () => { const result = getAutomationStateEventPayload(null); expect(result).toEqual({ enabled: false, phase: null, next_tick_at: null, sleep_until: null, }); }); test("returns normalized state when automation_state present", () => { const result = getAutomationStateEventPayload({ automation_state: { enabled: true, phase: "standby", next_tick_at: 50, sleep_until: 60 }, }); expect(result).toEqual({ enabled: true, phase: "standby", next_tick_at: 50, sleep_until: 60, }); }); test("returns a new object each call (not frozen reference)", () => { const a = getAutomationStateEventPayload({}); const b = getAutomationStateEventPayload({}); expect(a).toEqual(b); expect(a).not.toBe(b); }); }); // ============================================================================= // automationStatesEqual // ============================================================================= describe("automationStatesEqual", () => { const base: AutomationStateResponse = { enabled: true, phase: "standby", next_tick_at: 100, sleep_until: 200, }; test("returns true for identical states", () => { expect(automationStatesEqual(base, { ...base })).toBe(true); }); test("returns false when enabled differs", () => { expect(automationStatesEqual(base, { ...base, enabled: false })).toBe(false); }); test("returns false when phase differs", () => { expect(automationStatesEqual(base, { ...base, phase: "sleeping" })).toBe(false); expect(automationStatesEqual(base, { ...base, phase: null })).toBe(false); }); test("returns false when next_tick_at differs", () => { expect(automationStatesEqual(base, { ...base, next_tick_at: 999 })).toBe(false); expect(automationStatesEqual(base, { ...base, next_tick_at: null })).toBe(false); }); test("returns false when sleep_until differs", () => { expect(automationStatesEqual(base, { ...base, sleep_until: 999 })).toBe(false); expect(automationStatesEqual(base, { ...base, sleep_until: null })).toBe(false); }); test("returns true when both are disabled defaults", () => { const disabled: AutomationStateResponse = { enabled: false, phase: null, next_tick_at: null, sleep_until: null }; expect(automationStatesEqual(disabled, { ...disabled })).toBe(true); }); });