UNPKG

matterbridge-roborock-vacuum-plugin

Version:
375 lines 18.2 kB
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { RoomIndexMap } from '../../core/application/models/index.js'; import { DeviceError } from '../../errors/index.js'; import { AreaManagementService } from '../../services/areaManagementService.js'; describe('AreaManagementService', () => { let areaService; let mockLogger; let mockIotApi; let mockMessageClient; let mockMessageRoutingService; const mockDeviceId = 'test-device-1'; const mockAreas = [ { areaId: 0, mapId: 1 }, { areaId: 1, mapId: 1 }, { areaId: 2, mapId: 1 }, ]; const mockRoutines = [ { areaId: 100, mapId: 1 }, { areaId: 101, mapId: 1 }, ]; beforeEach(() => { mockLogger = createMockLogger(); mockIotApi = createMockIotApi(); mockMessageClient = createMockMessageClient(); mockMessageRoutingService = { getRoomMap: vi.fn(), getMapInfo: vi.fn(), }; areaService = new AreaManagementService(mockLogger, mockMessageRoutingService); areaService.setIotApi(mockIotApi); areaService.setMessageClient(mockMessageClient); }); function createMockLogger() { return { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), notice: vi.fn(), }; } function createMockIotApi() { return { getScenes: vi.fn(), startScene: vi.fn(), }; } function createMockMessageClient() { return { get: vi.fn(), send: vi.fn(), }; } afterEach(() => { vi.clearAllMocks(); }); describe('Initialization', () => { it('should initialize with all dependencies', () => { expect(areaService).toBeDefined(); }); it('should initialize with logger only', () => { const serviceWithoutDeps = new AreaManagementService(mockLogger, undefined); expect(serviceWithoutDeps).toBeDefined(); }); it('should initialize with logger and message routing service', () => { const service = new AreaManagementService(mockLogger, mockMessageRoutingService); expect(service).toBeDefined(); }); it('should set IoT API after initialization', () => { const service = new AreaManagementService(mockLogger, undefined); const newIotApi = {}; expect(() => { service.setIotApi(newIotApi); }).not.toThrow(); }); it('should set message client after initialization', () => { const service = new AreaManagementService(mockLogger, undefined); const newMessageClient = {}; expect(() => { service.setMessageClient(newMessageClient); }).not.toThrow(); }); }); describe('Supported Areas Management', () => { it('should set and get supported areas', () => { areaService.setSupportedAreas(mockDeviceId, mockAreas); const areas = areaService.getSupportedAreas(mockDeviceId); expect(areas).toBeDefined(); expect(areas).toHaveLength(3); expect(areas).toEqual(mockAreas); }); it('should return undefined for device without areas', () => { const areas = areaService.getSupportedAreas('unknown-device'); expect(areas).toEqual([]); }); it('should handle empty areas array', () => { areaService.setSupportedAreas(mockDeviceId, []); const areas = areaService.getSupportedAreas(mockDeviceId); expect(areas).toBeDefined(); expect(areas).toHaveLength(0); }); it('should support multiple devices', () => { const device2 = 'test-device-2'; const areas2 = [{ areaId: 10, mapId: 2 }]; areaService.setSupportedAreas(mockDeviceId, mockAreas); areaService.setSupportedAreas(device2, areas2); expect(areaService.getSupportedAreas(mockDeviceId)).toEqual(mockAreas); expect(areaService.getSupportedAreas(device2)).toEqual(areas2); }); it('should update existing areas', () => { areaService.setSupportedAreas(mockDeviceId, mockAreas); const newAreas = [{ areaId: 99, mapId: 1 }]; areaService.setSupportedAreas(mockDeviceId, newAreas); const areas = areaService.getSupportedAreas(mockDeviceId); expect(areas).toEqual(newAreas); }); }); describe('Area Index Map Management', () => { it('should set and get area index map', () => { const roomMapData = new Map([ [0, { roomId: 16, mapId: 1, roomName: 'rn-1' }], [1, { roomId: 17, mapId: 1, roomName: 'rn-2' }], ]); const roomInfo = new Map([]); const indexMap = new RoomIndexMap(roomMapData, roomInfo); areaService.setSupportedAreaIndexMap(mockDeviceId, indexMap); const retrievedMap = areaService.getSupportedAreasIndexMap(mockDeviceId); expect(retrievedMap).toBeDefined(); expect(retrievedMap).toBe(indexMap); }); it('should return undefined for device without index map', () => { const indexMap = areaService.getSupportedAreasIndexMap('unknown-device'); expect(indexMap).toBeUndefined(); }); }); describe('Selected Areas Management', () => { it('should return empty array when no areas selected', () => { const selectedAreas = areaService.getSelectedAreas(mockDeviceId); expect(selectedAreas).toEqual([]); }); it('should handle empty selection', () => { const roomMapData = new Map([[0, { roomId: 16, mapId: 1, roomName: 'rn-1' }]]); const roomInfo = new Map([]); const indexMap = new RoomIndexMap(roomMapData, roomInfo); areaService.setSupportedAreaIndexMap(mockDeviceId, indexMap); areaService.setSelectedAreas(mockDeviceId, []); const selectedAreas = areaService.getSelectedAreas(mockDeviceId); expect(selectedAreas).toEqual([]); }); it('should return empty array for unknown device', () => { const selectedAreas = areaService.getSelectedAreas('unknown-device'); expect(selectedAreas).toEqual([]); }); }); describe('Supported Routines Management', () => { it('should set and get supported routines', () => { areaService.setSupportedRoutines(mockDeviceId, mockRoutines); const routines = areaService.getSupportedRoutines(mockDeviceId); expect(routines).toBeDefined(); expect(routines).toHaveLength(2); expect(routines).toEqual(mockRoutines); }); it('should return undefined for device without routines', () => { const routines = areaService.getSupportedRoutines('unknown-device'); expect(routines).toBeUndefined(); }); it('should handle empty routines array', () => { areaService.setSupportedRoutines(mockDeviceId, []); const routines = areaService.getSupportedRoutines(mockDeviceId); expect(routines).toBeDefined(); expect(routines).toHaveLength(0); }); }); describe('getMapInformation', () => { const mockMultipleMaps = [ { max_multi_map: 4, max_bak_map: 1, multi_map_count: 2, map_info: [ { mapFlag: 1, name: 'First Floor', rooms: [] }, { mapFlag: 2, name: 'Second Floor', rooms: [] }, ], }, ]; it('should retrieve map information successfully', async () => { mockMessageRoutingService.getMapInfo.mockResolvedValue(mockMultipleMaps); const mapInfo = await areaService.getMapInfo(mockDeviceId); expect(mapInfo).toBeDefined(); expect(mockMessageRoutingService.getMapInfo).toHaveBeenCalled(); expect(mockLogger.debug).toHaveBeenCalledWith('AreaManagementService - getMapInfo', mockDeviceId); }); it('should return undefined when no maps available', async () => { mockMessageRoutingService.getMapInfo.mockResolvedValue(undefined); const mapInfo = await areaService.getMapInfo(mockDeviceId); expect(mapInfo).toBeUndefined(); }); it('should throw DeviceError when service routing not initialized', async () => { const serviceWithoutClient = new AreaManagementService(mockLogger, undefined); await expect(serviceWithoutClient.getMapInfo(mockDeviceId)).rejects.toThrow(DeviceError); await expect(serviceWithoutClient.getMapInfo(mockDeviceId)).rejects.toThrow('Service routing not initialized'); }); }); describe('getRoomMappings', () => { const mockRoomMappings = [ [16, 1], [17, 2], [18, 3], ]; it('should retrieve room mappings with non-secure request', async () => { mockMessageRoutingService.getRoomMap.mockImplementation((duid, activeMap, rooms) => { mockMessageClient.get(duid, { method: 'get_room_mapping', secure: false }); return Promise.resolve(mockRoomMappings); }); const mappings = await areaService.getRoomMap(mockDeviceId, 1); expect(mappings).toEqual(mockRoomMappings); expect(mockMessageRoutingService.getRoomMap).toHaveBeenCalledWith(mockDeviceId, 1); }); it('should return undefined when message client not initialized', async () => { const serviceWithoutClient = new AreaManagementService(mockLogger, undefined); await expect(serviceWithoutClient.getRoomMap(mockDeviceId, 1)).rejects.toThrow(DeviceError); }); it('should handle empty room mappings', async () => { mockMessageRoutingService.getRoomMap.mockResolvedValue([]); const mappings = await areaService.getRoomMap(mockDeviceId, 1); expect(mappings).toBeInstanceOf(Object); expect(mappings.length).toEqual(0); }); }); describe('getScenes', () => { const mockHomeId = 12345; const mockScenes = [{ id: 1, name: 'Morning Clean' }, { id: 2, name: 'Evening Clean' }]; it('should retrieve scenes successfully', async () => { mockIotApi.getScenes.mockResolvedValue(mockScenes); const scenes = await areaService.getScenes(mockHomeId); expect(scenes).toEqual(mockScenes); expect(mockIotApi.getScenes).toHaveBeenCalledWith(mockHomeId); }); it('should throw DeviceError when IoT API not initialized', async () => { const serviceWithoutApi = new AreaManagementService(mockLogger, undefined); await expect(serviceWithoutApi.getScenes(mockHomeId)).rejects.toThrow(DeviceError); await expect(serviceWithoutApi.getScenes(mockHomeId)).rejects.toThrow('IoT API not initialized'); }); it('should handle empty scenes array', async () => { mockIotApi.getScenes.mockResolvedValue([]); const scenes = await areaService.getScenes(mockHomeId); expect(scenes).toEqual([]); }); it('should handle undefined scenes', async () => { mockIotApi.getScenes.mockResolvedValue(undefined); const scenes = await areaService.getScenes(mockHomeId); expect(scenes).toBeUndefined(); }); }); describe('startScene', () => { const mockSceneId = 42; it('should start scene successfully', async () => { const mockResult = { success: true }; mockIotApi.startScene.mockResolvedValue(mockResult); const result = await areaService.startScene(mockSceneId); expect(result).toEqual(mockResult); expect(mockIotApi.startScene).toHaveBeenCalledWith(mockSceneId); }); it('should throw DeviceError when IoT API not initialized', async () => { const serviceWithoutApi = new AreaManagementService(mockLogger, undefined); await expect(serviceWithoutApi.startScene(mockSceneId)).rejects.toThrow(DeviceError); await expect(serviceWithoutApi.startScene(mockSceneId)).rejects.toThrow('IoT API not initialized'); }); it('should propagate API errors', async () => { const error = new Error('Scene execution failed'); mockIotApi.startScene.mockRejectedValue(error); await expect(areaService.startScene(mockSceneId)).rejects.toThrow('Scene execution failed'); }); }); describe('clearAll', () => { it('should clear all data', () => { // Set up some data areaService.setSupportedAreas(mockDeviceId, mockAreas); areaService.setSupportedRoutines(mockDeviceId, mockRoutines); const roomMapData = new Map([[0, { roomId: 16, mapId: 1, roomName: 'rn-1' }]]); const roomInfo = new Map([]); areaService.setSupportedAreaIndexMap(mockDeviceId, new RoomIndexMap(roomMapData, roomInfo)); areaService.setSelectedAreas(mockDeviceId, [0]); // Clear all areaService.clearAll(); // Verify everything is cleared expect(areaService.getSupportedAreas(mockDeviceId)).toEqual([]); expect(areaService.getSupportedRoutines(mockDeviceId)).toBeUndefined(); expect(areaService.getSupportedAreasIndexMap(mockDeviceId)).toBeUndefined(); expect(areaService.getSelectedAreas(mockDeviceId)).toEqual([]); expect(mockLogger.debug).toHaveBeenCalledWith('AreaManagementService - All data cleared'); }); it('should be safe to call multiple times', () => { areaService.clearAll(); areaService.clearAll(); expect(() => { areaService.clearAll(); }).not.toThrow(); }); it('should clear empty service without errors', () => { const newService = new AreaManagementService(mockLogger, undefined); expect(() => { newService.clearAll(); }).not.toThrow(); }); }); describe('Integration Scenarios', () => { it('should handle complete area setup workflow', () => { // Setup areas areaService.setSupportedAreas(mockDeviceId, mockAreas); // Setup index map const roomMapData = new Map([ [0, { roomId: 16, mapId: 1, roomName: 'rn-1' }], [1, { roomId: 17, mapId: 1, roomName: 'rn-2' }], [2, { roomId: 18, mapId: 1, roomName: 'rn-3' }], ]); const roomInfo = new Map([]); areaService.setSupportedAreaIndexMap(mockDeviceId, new RoomIndexMap(roomMapData, roomInfo)); // Setup routines areaService.setSupportedRoutines(mockDeviceId, mockRoutines); // Select areas areaService.setSelectedAreas(mockDeviceId, [0, 2]); // Verify complete setup expect(areaService.getSupportedAreas(mockDeviceId)).toEqual(mockAreas); expect(areaService.getSupportedRoutines(mockDeviceId)).toEqual(mockRoutines); expect(areaService.getSelectedAreas(mockDeviceId)).toEqual([0, 2]); }); it('should handle multiple devices independently', () => { const device1 = 'device-1'; const device2 = 'device-2'; const areas1 = [{ areaId: 1, mapId: 1 }]; const areas2 = [{ areaId: 2, mapId: 2 }]; areaService.setSupportedAreas(device1, areas1); areaService.setSupportedAreas(device2, areas2); expect(areaService.getSupportedAreas(device1)).toEqual(areas1); expect(areaService.getSupportedAreas(device2)).toEqual(areas2); }); it('should handle scene workflow with IoT API', async () => { const homeId = 123; const mockScenes = [{ id: 1, name: 'Test' }]; mockIotApi.getScenes.mockResolvedValue(mockScenes); mockIotApi.startScene.mockResolvedValue({ success: true }); const scenes = await areaService.getScenes(homeId); expect(scenes).toEqual(mockScenes); const result = await areaService.startScene(1); expect(result).toEqual({ success: true }); }); }); describe('Error Handling', () => { it('should handle unknown device IDs gracefully', () => { expect(areaService.getSupportedAreas('unknown-device')).toEqual([]); expect(areaService.getSelectedAreas('unknown-device')).toEqual([]); expect(areaService.getSupportedRoutines('unknown-device')).toBeUndefined(); }); it('should handle malformed area data', () => { const malformedAreas = [{ areaId: 1 }, { areaId: 2 }]; expect(() => { areaService.setSupportedAreas(mockDeviceId, malformedAreas); }).not.toThrow(); const areas = areaService.getSupportedAreas(mockDeviceId); expect(areas).toEqual(malformedAreas); }); it('should propagate message client errors', async () => { const error = new Error('Network error'); mockMessageRoutingService.getMapInfo.mockRejectedValue(error); await expect(areaService.getMapInfo(mockDeviceId)).rejects.toThrow('Network error'); }); it('should propagate IoT API errors', async () => { const error = new Error('API error'); mockIotApi.getScenes.mockRejectedValue(error); await expect(areaService.getScenes(123)).rejects.toThrow('API error'); }); }); }); //# sourceMappingURL=areaManagementService.test.js.map