UNPKG

matterbridge-roborock-vacuum-plugin

Version:
541 lines 24.1 kB
import { beforeEach, describe, expect, it } from 'vitest'; import { DeviceRegistry } from '../../platform/deviceRegistry.js'; import { ProtocolVersion } from '../../roborockCommunication/enums/index.js'; import { DeviceCategory } from '../../roborockCommunication/models/deviceCategory.js'; import { DeviceModel } from '../../roborockCommunication/models/index.js'; import { asType } from '../testUtils.js'; function createMockDevice(serialNumber) { return { duid: `duid-${serialNumber}`, name: `Device ${serialNumber}`, sn: serialNumber, serialNumber, activeTime: Date.now(), createTime: Date.now(), localKey: 'test-key', pv: '1.0', online: true, productId: 'test-product', rrHomeId: 123, fv: '1.0.0', deviceStatus: {}, schema: [], specs: { id: `id-${serialNumber}`, firmwareVersion: '1.0.0', serialNumber, model: DeviceModel.Q7_MAX, protocol: ProtocolVersion.V1, category: DeviceCategory.VacuumCleaner, batteryLevel: 100, hasRealTimeConnection: true, }, store: { userData: { username: 'test', uid: 'uid', tokentype: '', token: '', rruid: '', region: 'US', countrycode: '', country: '', nickname: 'nick', rriot: { u: '', s: '', h: '', k: '', r: { r: '', a: '', m: '', l: '' } }, }, localKey: 'test-key', pv: '1.0', model: DeviceModel.Q7_MAX, homeData: { id: 123, name: 'Test Home', products: [], devices: [], receivedDevices: [], rooms: [], }, }, scenes: [], mapInfos: undefined, }; } function createMockRobot(serialNumber) { return { serialNumber, device: createMockDevice(serialNumber), }; } let registry; beforeEach(() => { registry = new DeviceRegistry(); }); describe('DeviceRegistry', () => { describe('register', () => { it('should register both device and robot', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN001'); registry.register(device, robot); expect(registry.getDevice('duid-SN001')).toBe(device); expect(registry.getRobot('duid-SN001')).toBe(robot); }); it('should not register when device is null', () => { const robot = createMockRobot('SN001'); registry.register(asType(undefined), robot); expect(registry.size).toBe(0); }); it('should not register when device is undefined', () => { const robot = createMockRobot('SN001'); registry.register(asType(undefined), robot); expect(registry.size).toBe(0); }); it('should overwrite existing registration for same serialNumber', () => { const device1 = createMockDevice('SN001'); const robot1 = createMockRobot('SN001'); const device2 = createMockDevice('SN001'); const robot2 = createMockRobot('SN001'); registry.register(device1, robot1); registry.register(device2, robot2); expect(registry.getDevice('duid-SN001')).toBe(device2); expect(registry.getRobot('duid-SN001')).toBe(robot2); expect(registry.size).toBe(1); }); }); describe('registerRobot', () => { it('should register only robot', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); expect(registry.getRobot('duid-SN001')).toBe(robot); expect(registry.getDevice('duid-SN001')).toBeUndefined(); }); it('should not register when robot is null', () => { registry.registerRobot(asType(undefined)); expect(registry.getAllRobots()).toHaveLength(0); }); it('should not register when robot is undefined', () => { registry.registerRobot(asType(undefined)); expect(registry.getAllRobots()).toHaveLength(0); }); it('should overwrite existing robot for same serialNumber', () => { const robot1 = createMockRobot('SN001'); const robot2 = createMockRobot('SN001'); registry.registerRobot(robot1); registry.registerRobot(robot2); expect(registry.getRobot('duid-SN001')).toBe(robot2); expect(registry.getAllRobots()).toHaveLength(1); }); }); describe('registerDevice', () => { it('should register only device', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); expect(registry.getDevice('duid-SN001')).toBe(device); expect(registry.getRobot('duid-SN001')).toBeUndefined(); }); it('should not register when device has no serialNumber', () => { const device = createMockDevice('SN001'); device.serialNumber = ''; registry.registerDevice(device); expect(registry.getDevice('SN001')).toBeUndefined(); }); it('should not register when device is null', () => { registry.registerDevice(asType(undefined)); expect(registry.size).toBe(0); }); it('should not register when device is undefined', () => { registry.registerDevice(asType(undefined)); expect(registry.size).toBe(0); }); it('should overwrite existing device for same serialNumber', () => { const device1 = createMockDevice('SN001'); const device2 = createMockDevice('SN001'); registry.registerDevice(device1); registry.registerDevice(device2); expect(registry.getDevice('duid-SN001')).toBe(device2); expect(registry.size).toBe(1); }); }); describe('unregister', () => { it('should unregister both device and robot', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN001'); registry.register(device, robot); registry.unregister('duid-SN001'); expect(registry.getDevice('duid-SN001')).toBeUndefined(); expect(registry.getRobot('duid-SN001')).toBeUndefined(); expect(registry.size).toBe(0); }); it('should handle unregistering non-existent serialNumber', () => { registry.unregister('NON_EXISTENT'); expect(registry.size).toBe(0); }); it('should only affect specified serialNumber', () => { const device1 = createMockDevice('SN001'); const robot1 = createMockRobot('SN001'); const device2 = createMockDevice('SN002'); const robot2 = createMockRobot('SN002'); registry.register(device1, robot1); registry.register(device2, robot2); registry.unregister('duid-SN001'); expect(registry.getDevice('duid-SN001')).toBeUndefined(); expect(registry.getRobot('duid-SN001')).toBeUndefined(); expect(registry.getDevice('duid-SN002')).toBe(device2); expect(registry.getRobot('duid-SN002')).toBe(robot2); expect(registry.size).toBe(1); }); }); describe('getRobot', () => { it('should return robot by serialNumber', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); const result = registry.getRobot('duid-SN001'); expect(result).toBe(robot); }); it('should return undefined for non-existent serialNumber', () => { const result = registry.getRobot('NON_EXISTENT'); expect(result).toBeUndefined(); }); it('should return undefined for empty registry', () => { const result = registry.getRobot('duid-SN001'); expect(result).toBeUndefined(); }); }); describe('getDevice', () => { it('should return device by serialNumber', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); const result = registry.getDevice('duid-SN001'); expect(result).toBe(device); }); it('should return undefined for non-existent serialNumber', () => { const result = registry.getDevice('NON_EXISTENT'); expect(result).toBeUndefined(); }); it('should return undefined for empty registry', () => { const result = registry.getDevice('duid-SN001'); expect(result).toBeUndefined(); }); }); describe('getAllRobots', () => { it('should return all registered robots', () => { const robot1 = createMockRobot('SN001'); const robot2 = createMockRobot('SN002'); const robot3 = createMockRobot('SN003'); registry.registerRobot(robot1); registry.registerRobot(robot2); registry.registerRobot(robot3); const result = registry.getAllRobots(); expect(result).toHaveLength(3); expect(result).toContain(robot1); expect(result).toContain(robot2); expect(result).toContain(robot3); }); it('should return empty array for empty registry', () => { const result = registry.getAllRobots(); expect(result).toEqual([]); }); it('should return new array instance each time', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); const result1 = registry.getAllRobots(); const result2 = registry.getAllRobots(); expect(result1).not.toBe(result2); expect(result1).toEqual(result2); }); }); describe('getAllDevices', () => { it('should return all registered devices', () => { const device1 = createMockDevice('SN001'); const device2 = createMockDevice('SN002'); const device3 = createMockDevice('SN003'); registry.registerDevice(device1); registry.registerDevice(device2); registry.registerDevice(device3); const result = registry.getAllDevices(); expect(result).toHaveLength(3); expect(result).toContain(device1); expect(result).toContain(device2); expect(result).toContain(device3); }); it('should return empty array for empty registry', () => { const result = registry.getAllDevices(); expect(result).toEqual([]); }); it('should return new array instance each time', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); const result1 = registry.getAllDevices(); const result2 = registry.getAllDevices(); expect(result1).not.toBe(result2); expect(result1).toEqual(result2); }); }); describe('size', () => { it('should return 0 for empty registry', () => { expect(registry.size).toBe(0); }); it('should return number of registered devices', () => { const device1 = createMockDevice('SN001'); const device2 = createMockDevice('SN002'); registry.registerDevice(device1); expect(registry.size).toBe(1); registry.registerDevice(device2); expect(registry.size).toBe(2); }); it('should count only devices not robots', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); expect(registry.size).toBe(0); }); it('should count correctly when using register method', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN001'); registry.register(device, robot); expect(registry.size).toBe(1); }); it('should decrease after unregister', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); expect(registry.size).toBe(1); registry.unregister('duid-SN001'); expect(registry.size).toBe(0); }); }); describe('hasDevices', () => { it('should return false for empty registry', () => { expect(registry.hasDevices()).toBe(false); }); it('should return true when devices registered', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); expect(registry.hasDevices()).toBe(true); }); it('should return false when only robots registered', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); expect(registry.hasDevices()).toBe(false); }); it('should return false after clearing', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); registry.clear(); expect(registry.hasDevices()).toBe(false); }); }); describe('clear', () => { it('should clear all devices and robots', () => { const device1 = createMockDevice('SN001'); const robot1 = createMockRobot('SN001'); const device2 = createMockDevice('SN002'); const robot2 = createMockRobot('SN002'); registry.register(device1, robot1); registry.register(device2, robot2); registry.clear(); expect(registry.size).toBe(0); expect(registry.getAllRobots()).toHaveLength(0); expect(registry.getAllDevices()).toHaveLength(0); expect(registry.getDevice('duid-SN001')).toBeUndefined(); expect(registry.getRobot('duid-SN001')).toBeUndefined(); }); it('should handle clearing empty registry', () => { registry.clear(); expect(registry.size).toBe(0); }); it('should allow registering after clearing', () => { const device1 = createMockDevice('SN001'); registry.registerDevice(device1); registry.clear(); const device2 = createMockDevice('SN002'); registry.registerDevice(device2); expect(registry.size).toBe(1); expect(registry.getDevice('duid-SN002')).toBe(device2); }); }); describe('robotEntries', () => { it('should iterate over all robot entries', () => { const robot1 = createMockRobot('SN001'); const robot2 = createMockRobot('SN002'); registry.registerRobot(robot1); registry.registerRobot(robot2); const entries = Array.from(registry.robotEntries()); expect(entries).toHaveLength(2); expect(entries).toContainEqual(['duid-SN001', robot1]); expect(entries).toContainEqual(['duid-SN002', robot2]); }); it('should return empty iterator for empty registry', () => { const entries = Array.from(registry.robotEntries()); expect(entries).toEqual([]); }); it('should support for...of loop', () => { const robot1 = createMockRobot('SN001'); const robot2 = createMockRobot('SN002'); registry.registerRobot(robot1); registry.registerRobot(robot2); const serialNumbers = []; for (const [sn, _robot] of registry.robotEntries()) { serialNumbers.push(sn); } expect(serialNumbers).toContain('duid-SN001'); expect(serialNumbers).toContain('duid-SN002'); }); }); describe('deviceEntries', () => { it('should iterate over all device entries', () => { const device1 = createMockDevice('SN001'); const device2 = createMockDevice('SN002'); registry.registerDevice(device1); registry.registerDevice(device2); const entries = Array.from(registry.deviceEntries()); expect(entries).toHaveLength(2); expect(entries).toContainEqual(['duid-SN001', device1]); expect(entries).toContainEqual(['duid-SN002', device2]); }); it('should return empty iterator for empty registry', () => { const entries = Array.from(registry.deviceEntries()); expect(entries).toEqual([]); }); it('should support for...of loop', () => { const device1 = createMockDevice('SN001'); const device2 = createMockDevice('SN002'); registry.registerDevice(device1); registry.registerDevice(device2); const serialNumbers = []; for (const [sn, _device] of registry.deviceEntries()) { serialNumbers.push(sn); } expect(serialNumbers).toContain('duid-SN001'); expect(serialNumbers).toContain('duid-SN002'); }); }); describe('robotsMap', () => { it('should return the internal robots map', () => { const robot = createMockRobot('SN001'); registry.registerRobot(robot); const map = registry.robotsMap; expect(map).toBeInstanceOf(Map); expect(map.get('duid-SN001')).toBe(robot); }); it('should return same map instance', () => { const map1 = registry.robotsMap; const map2 = registry.robotsMap; expect(map1).toBe(map2); }); it('should reflect changes made through registry', () => { const map = registry.robotsMap; const robot = createMockRobot('SN001'); registry.registerRobot(robot); expect(map.get('duid-SN001')).toBe(robot); }); }); describe('devicesMap', () => { it('should return the internal devices map', () => { const device = createMockDevice('SN001'); registry.registerDevice(device); const map = registry.devicesMap; expect(map).toBeInstanceOf(Map); expect(map.get('duid-SN001')).toBe(device); }); it('should return same map instance', () => { const map1 = registry.devicesMap; const map2 = registry.devicesMap; expect(map1).toBe(map2); }); it('should reflect changes made through registry', () => { const map = registry.devicesMap; const device = createMockDevice('SN001'); registry.registerDevice(device); expect(map.get('duid-SN001')).toBe(device); }); }); describe('integration scenarios', () => { it('should handle mixed operations correctly', () => { const device1 = createMockDevice('SN001'); const robot1 = createMockRobot('SN001'); const device2 = createMockDevice('SN002'); const robot2 = createMockRobot('SN002'); registry.register(device1, robot1); registry.registerDevice(device2); registry.registerRobot(robot2); expect(registry.size).toBe(2); expect(registry.getAllRobots()).toHaveLength(2); expect(registry.getDevice('duid-SN001')).toBe(device1); expect(registry.getDevice('duid-SN002')).toBe(device2); expect(registry.getRobot('duid-SN001')).toBe(robot1); expect(registry.getRobot('duid-SN002')).toBe(robot2); }); describe('edge cases and error handling', () => { it('should not throw when unregistering empty string', () => { expect(() => registry.unregister('')).not.toThrow(); expect(registry.size).toBe(0); }); it('should not throw when unregistering undefined', () => { // @ts-expect-error: purposely passing undefined expect(() => registry.unregister(undefined)).not.toThrow(); expect(registry.size).toBe(0); }); it('should not throw when clearing already empty registry', () => { expect(() => registry.clear()).not.toThrow(); expect(registry.size).toBe(0); }); it('should allow direct map manipulation and reflect in registry', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN001'); registry.devicesMap.set('SN001', device); registry.robotsMap.set('SN001', robot); expect(registry.getDevice('SN001')).toBe(device); expect(registry.getRobot('SN001')).toBe(robot); registry.devicesMap.delete('SN001'); registry.robotsMap.delete('SN001'); expect(registry.getDevice('SN001')).toBeUndefined(); expect(registry.getRobot('SN001')).toBeUndefined(); }); it('should handle registering device and robot with mismatched serialNumbers', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN002'); registry.register(device, robot); // Both device and robot are registered under device.serialNumber (SN001) expect(registry.getDevice('duid-SN001')).toBe(device); expect(registry.getRobot('duid-SN001')).toBe(robot); expect(registry.getRobot('duid-SN002')).toBeUndefined(); }); it('should not register device if serialNumber is null or undefined', () => { const device = createMockDevice('SN001'); // @ts-expect-error: purposely setting serialNumber to null device.duid = null; registry.registerDevice(device); expect(registry.size).toBe(0); // @ts-expect-error: purposely setting serialNumber to undefined device.duid = undefined; registry.registerDevice(device); expect(registry.size).toBe(0); }); }); it('should handle partial registration and unregistration', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN002'); registry.registerDevice(device); registry.registerRobot(robot); expect(registry.size).toBe(1); expect(registry.getAllRobots()).toHaveLength(1); registry.unregister('duid-SN001'); expect(registry.size).toBe(0); expect(registry.getAllRobots()).toHaveLength(1); }); it('should support complete workflow: register -> get -> unregister -> verify', () => { const device = createMockDevice('SN001'); const robot = createMockRobot('SN001'); registry.register(device, robot); expect(registry.size).toBe(1); expect(registry.hasDevices()).toBe(true); const retrievedDevice = registry.getDevice('duid-SN001'); const retrievedRobot = registry.getRobot('duid-SN001'); expect(retrievedDevice).toBe(device); expect(retrievedRobot).toBe(robot); registry.unregister('duid-SN001'); expect(registry.size).toBe(0); expect(registry.hasDevices()).toBe(false); expect(registry.getDevice('duid-SN001')).toBeUndefined(); expect(registry.getRobot('duid-SN001')).toBeUndefined(); }); }); }); //# sourceMappingURL=deviceRegistry.test.js.map