UNPKG

matterbridge-roborock-vacuum-plugin

Version:
368 lines 15.8 kB
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { PlatformConfigManager as PlatformConfigManagerStatic } from '../../../platform/platformConfigManager.js'; import { RoborockService } from '../../../services/roborockService.js'; import { localStorageMock } from '../../testData/localStorageMock.js'; import { asPartial, asType, createMockLocalStorage, makeLogger } from '../../testUtils.js'; describe('RoborockService - listDevices', () => { let roborockService; let mockLogger; beforeEach(async () => { mockLogger = makeLogger(); const persist = createMockLocalStorage(); const PlatformConfigManager = PlatformConfigManagerStatic; const config = { name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false, authentication: { username: 'test', region: 'US', forceAuthentication: false, authenticationMethod: 'Password', password: '', }, pluginConfiguration: { whiteList: [], enableServerMode: false, enableMultipleMap: false, sanitizeSensitiveLogs: false, refreshInterval: 10, debug: false, unregisterOnShutdown: false, }, advancedFeature: { enableAdvancedFeature: false, settings: { clearStorageOnStartup: false, showRoutinesAsRoom: false, includeDockStationStatus: false, includeVacuumErrorStatus: false, forceRunAtDefault: false, useVacationModeToSendVacuumToDock: false, enableCleanModeMapping: false, cleanModeSettings: { vacuuming: { fanMode: 'Balanced', mopRouteMode: 'Standard' }, mopping: { waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, vacmop: { fanMode: 'Balanced', waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, }, overrideMatterConfiguration: false, matterOverrideSettings: { matterVendorName: 'xxx', matterVendorId: 123, matterProductName: 'yy', matterProductId: 456, }, enableEmailNotification: false, emailNotificationSettings: {}, }, }, }; Object.assign(config, { name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false }); const configManager = PlatformConfigManager.create(Object.assign({ name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false }, config), mockLogger); roborockService = new RoborockService({ refreshInterval: 10, baseUrl: 'https://api.roborock.com', persist: persist, configManager: configManager, toastMessage: vi.fn(), }, mockLogger, configManager); }); it('should throw if not authenticated', async () => { await expect(roborockService.listDevices()).rejects.toThrow(); }); it('should throw if homeDetails is missing', async () => { const mockDeviceService = { iotApi: {}, userdata: {}, listDevices: vi.fn().mockRejectedValue(new Error('No home found for user')), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); await expect(roborockService.listDevices()).rejects.toThrow('No home found for user'); }); it('should return empty array if homeData is missing', async () => { const mockDeviceService = { listDevices: vi.fn().mockResolvedValue([]), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result).toEqual([]); }); it('should return devices with correct mapping', async () => { const mockDevices = [ { duid: '1', rrHomeId: 123, rooms: [], localKey: 'lk', pv: 'pv', sn: 'sn', scenes: [], data: {}, store: {} }, ]; const mockDeviceService = { listDevices: vi.fn().mockResolvedValue(mockDevices), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result).toEqual(mockDevices); }); it('should throw if getHomev3 fails when v3 API is needed', async () => { const mockDeviceService = { listDevices: vi.fn().mockRejectedValue(new Error('getHomev3 failed')), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); await expect(roborockService.listDevices()).rejects.toThrow('getHomev3 failed'); }); it('should merge v3 devices and receivedDevices if v3 API is needed', async () => { const mergedDevices = [ { duid: '1', rrHomeId: 123 }, { duid: '2', rrHomeId: 123 }, ]; const mockDeviceService = { listDevices: vi.fn().mockResolvedValue(mergedDevices), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result).toEqual(mergedDevices); }); it('should fallback batteryLevel to 100 if not present', async () => { const device = { duid: '1', specs: {}, store: {}, rrHomeId: 123, rooms: [], localKey: '', pv: '', sn: '', scenes: [], batteryLevel: undefined, }; const mockDeviceService = { listDevices: vi.fn().mockResolvedValue([{ ...device, specs: { batteryLevel: undefined } }]), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result[0].specs.batteryLevel ?? 100).toBe(100); }); it('should filter scenes correctly for devices', async () => { const scenes = [{ param: '{"action":{"items":[{"entityId":"1"}]}}' }]; const device = { duid: '1', specs: {}, store: {}, rrHomeId: 123, rooms: [], localKey: '', pv: '', sn: '', scenes }; const mockDeviceService = { listDevices: vi.fn().mockResolvedValue([device]), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result[0].scenes).toEqual(scenes); }); it('should handle rooms fallback from v2 and v3 APIs', async () => { const device = asPartial({ duid: '1', specs: asPartial({}), store: asPartial({ homeData: { id: 123, name: 'Test Home', products: [], devices: [], receivedDevices: [], rooms: [{ id: 1, name: 'Living Room' }], }, }), rrHomeId: 123, localKey: '', pv: '', sn: '', scenes: [], }); const mockDeviceService = { listDevices: vi.fn().mockResolvedValue([device]), }; roborockService = Object.create(roborockService, { deviceService: { value: mockDeviceService }, }); const result = await roborockService.listDevices(); expect(result[0].store.homeData.rooms).toEqual([{ id: 1, name: 'Living Room' }]); }); }); describe('getHomeDataForUpdating', () => { let service; let mockLogger; const homeid = 123; beforeEach(async () => { mockLogger = makeLogger(); const persist = createMockLocalStorage(); const PlatformConfigManager = PlatformConfigManagerStatic; const config = { name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false, authentication: { username: 'test', region: 'US', forceAuthentication: false, authenticationMethod: 'Password', password: '', }, pluginConfiguration: { whiteList: [], enableServerMode: false, enableMultipleMap: false, sanitizeSensitiveLogs: false, refreshInterval: 10, debug: false, unregisterOnShutdown: false, }, advancedFeature: { enableAdvancedFeature: false, settings: { clearStorageOnStartup: false, showRoutinesAsRoom: false, includeDockStationStatus: false, includeVacuumErrorStatus: false, forceRunAtDefault: false, useVacationModeToSendVacuumToDock: false, enableCleanModeMapping: false, cleanModeSettings: { vacuuming: { fanMode: 'Balanced', mopRouteMode: 'Standard' }, mopping: { waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, vacmop: { fanMode: 'Balanced', waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, }, overrideMatterConfiguration: false, matterOverrideSettings: { matterVendorName: 'xxx', matterVendorId: 123, matterProductName: 'yy', matterProductId: 456, }, enableEmailNotification: false, emailNotificationSettings: {}, }, }, }; const configManager = PlatformConfigManager.create(Object.assign({ name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false }, config), mockLogger); service = new RoborockService({ authenticateApiFactory: () => asType(undefined), iotApiFactory: () => asType(undefined), refreshInterval: 10, baseUrl: 'https://api.roborock.com', persist: persist, configManager: configManager, toastMessage: vi.fn(), }, mockLogger, configManager); }); it('should throw if not authenticated', async () => { const result = await service.getHomeDataForUpdating(homeid); expect(result).toBeUndefined(); }); }); describe('Device Management Methods', () => { let service; let mockLogger; let mockContainer; const homeid = 123; beforeEach(async () => { mockLogger = makeLogger(); mockContainer = asPartial({ setUserData: vi.fn(), getIotApi: vi.fn(), getAuthenticationCoordinator: vi.fn().mockReturnValue({}), getDeviceManagementService: vi .fn() .mockReturnValue({ getHomeDataForUpdating: vi.fn().mockResolvedValue(undefined) }), getAreaManagementService: vi.fn().mockReturnValue({}), getMessageRoutingService: vi.fn().mockReturnValue({}), getPollingService: vi.fn().mockReturnValue({}), getConnectionService: vi.fn().mockReturnValue({ initializeMessageClient: vi.fn(), initializeMessageClientForLocal: vi.fn().mockResolvedValue(false), setDeviceNotify: vi.fn(), }), }); const persist = createMockLocalStorage({ getItem: localStorageMock.getItem, setItem: localStorageMock.setItem, clear: localStorageMock.clear, }); const PlatformConfigManager = PlatformConfigManagerStatic; const config = { name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false, authentication: { username: 'test', region: 'US', forceAuthentication: false, authenticationMethod: 'Password', password: '', }, pluginConfiguration: { whiteList: [], enableServerMode: false, enableMultipleMap: false, sanitizeSensitiveLogs: false, refreshInterval: 10, debug: false, unregisterOnShutdown: false, }, advancedFeature: { enableAdvancedFeature: false, settings: { clearStorageOnStartup: false, showRoutinesAsRoom: false, includeDockStationStatus: false, includeVacuumErrorStatus: false, forceRunAtDefault: false, useVacationModeToSendVacuumToDock: false, enableCleanModeMapping: false, cleanModeSettings: { vacuuming: { fanMode: 'Balanced', mopRouteMode: 'Standard' }, mopping: { waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, vacmop: { fanMode: 'Balanced', waterFlowMode: 'Medium', mopRouteMode: 'Standard', distanceOff: 25 }, }, overrideMatterConfiguration: false, matterOverrideSettings: { matterVendorName: 'xxx', matterVendorId: 123, matterProductName: 'yy', matterProductId: 456, }, enableEmailNotification: false, emailNotificationSettings: {}, }, }, }; const configManager = PlatformConfigManager.create(Object.assign({ name: 'test', type: 'test', version: '1.0', debug: false, unregisterOnShutdown: false }, config), mockLogger); service = new RoborockService({ authenticateApiFactory: () => asPartial({}), iotApiFactory: () => asPartial({}), refreshInterval: 10, baseUrl: 'https://api.roborock.com', persist: persist, configManager: configManager, container: mockContainer, toastMessage: vi.fn(), }, mockLogger, configManager); }); it('should throw error when getting custom API without IoT API', async () => { mockContainer.getIotApi.mockReturnValue(undefined); await expect(service.getCustomAPI('/test')).rejects.toThrow('IoT API not initialized. Please login first.'); }); it('should throw if not authenticated', async () => { const result = await service.getHomeDataForUpdating(homeid); expect(result).toBeUndefined(); }); }); //# sourceMappingURL=roborockService.devicemanagement.test.js.map