import { describe, expect, it, vi } from "vitest"; import * as computeDeviceNameModule from "../../../src/common/entity/compute_device_name"; import { computeEntityEntryName, computeEntityName, } from "../../../src/common/entity/compute_entity_name"; import * as computeStateNameModule from "../../../src/common/entity/compute_state_name"; import * as stripPrefixModule from "../../../src/common/entity/strip_prefix_from_entity_name"; import type { HomeAssistant } from "../../../src/types"; import { mockEntity, mockEntityEntry, mockStateObj, } from "./context/context-mock"; describe("computeEntityName", () => { it("returns state name if entity not in registry", () => { vi.spyOn(computeStateNameModule, "computeStateName").mockReturnValue( "Kitchen Light" ); const stateObj = mockStateObj({ entity_id: "light.kitchen", attributes: { friendly_name: "Kitchen Light" }, state: "on", }); const hass = { entities: {}, devices: {}, } as unknown as HomeAssistant; expect(computeEntityName(stateObj, hass.entities, hass.devices)).toBe( "Kitchen Light" ); vi.restoreAllMocks(); }); it("returns entity entry name if present", () => { const stateObj = mockStateObj({ entity_id: "light.kitchen", attributes: {}, state: "on", }); const hass = { entities: { "light.kitchen": { entity_id: "light.kitchen", name: "Ceiling Light", labels: [], }, }, devices: {}, states: { "light.kitchen": stateObj, }, } as unknown as HomeAssistant; expect(computeEntityName(stateObj, hass.entities, hass.devices)).toBe( "Ceiling Light" ); }); }); describe("computeEntityEntryName", () => { it("returns entry.name if no device", () => { const entry = mockEntity({ entity_id: "light.kitchen", name: "Ceiling Light", }); const hass = { devices: {}, states: {} }; expect(computeEntityEntryName(entry, hass.devices)).toBe("Ceiling Light"); }); it("returns device-stripped name if device present", () => { vi.spyOn(computeDeviceNameModule, "computeDeviceName").mockReturnValue( "Kitchen" ); vi.spyOn(stripPrefixModule, "stripPrefixFromEntityName").mockImplementation( (name, prefix) => name.replace(prefix + " ", "") ); const entry = mockEntity({ entity_id: "light.kitchen", name: "Kitchen Light", device_id: "dev1", }); const hass = { devices: { dev1: {} }, states: {}, } as unknown as HomeAssistant; expect(computeEntityEntryName(entry, hass.devices)).toBe("Light"); vi.restoreAllMocks(); }); it("returns undefined if device name equals entity name", () => { vi.spyOn(computeDeviceNameModule, "computeDeviceName").mockReturnValue( "Kitchen Light" ); const entry = mockEntity({ entity_id: "light.kitchen", name: "Kitchen Light", device_id: "dev1", }); const hass = { devices: { dev1: {} }, states: {}, } as unknown as HomeAssistant; expect(computeEntityEntryName(entry, hass.devices)).toBeUndefined(); vi.restoreAllMocks(); }); it("falls back to state name if no name and no device", () => { vi.spyOn(computeStateNameModule, "computeStateName").mockReturnValue( "Fallback Name" ); const entry = mockEntity({ entity_id: "light.kitchen" }); const hass = { devices: {}, } as unknown as HomeAssistant; const stateObj = mockStateObj({ entity_id: "light.kitchen" }); expect(computeEntityEntryName(entry, hass.devices, stateObj)).toBe( "Fallback Name" ); vi.restoreAllMocks(); }); it("returns original_name if present", () => { const entry = mockEntityEntry({ entity_id: "light.kitchen", original_name: "Old Name", }); const hass = { devices: {}, states: {}, } as unknown as HomeAssistant; expect(computeEntityEntryName(entry, hass.devices)).toBe("Old Name"); }); it("returns undefined if no name, original_name, or device", () => { const entry = mockEntity({ entity_id: "light.kitchen" }); const hass = { devices: {}, states: {}, } as unknown as HomeAssistant; expect(computeEntityEntryName(entry, hass.devices)).toBeUndefined(); }); it("handles entities with numeric original_name (real bug from issue #25363)", () => { vi.spyOn(computeDeviceNameModule, "computeDeviceName").mockReturnValue( "Texas Instruments CC2652" ); const entry = { entity_id: "sensor.texas_instruments_cc2652_2", name: null, // null name original_name: 2, // Number instead of string! This caused the original crash device_id: "dev1", has_entity_name: true, }; const hass = { devices: { dev1: {} }, states: {}, }; // Should not throw an error and should return the stringified number expect(() => computeEntityEntryName(entry as any, hass as any) ).not.toThrow(); expect(computeEntityEntryName(entry as any, hass as any)).toBe("2"); vi.restoreAllMocks(); }); it("returns undefined when entity has device but no name or original_name", () => { vi.spyOn(computeDeviceNameModule, "computeDeviceName").mockReturnValue( "Kitchen Device" ); const entry = { entity_id: "sensor.kitchen_sensor", // No name property // No original_name property device_id: "dev1", }; const hass = { devices: { dev1: {} }, states: {}, }; // Should return undefined to maintain function contract expect(computeEntityEntryName(entry as any, hass as any)).toBeUndefined(); vi.restoreAllMocks(); }); });