import { MockBLEManager } from '@/services/ble/MockBLEManager'; import type { BulkOperationResult, BulkWiFiResult } from '@/services/ble/types'; // Increase timeout for bulk operations (they take time due to mock delays) jest.setTimeout(15000); describe('Bulk BLE Operations', () => { let bleManager: MockBLEManager; beforeEach(() => { bleManager = new MockBLEManager(); }); afterEach(async () => { await bleManager.cleanup(); }); describe('bulkDisconnect', () => { it('should disconnect multiple devices successfully', async () => { // Setup: Connect devices first const device1 = 'mock-743'; const device2 = 'mock-769'; await bleManager.connectDevice(device1); await bleManager.connectDevice(device2); // Verify connected expect(bleManager.isDeviceConnected(device1)).toBe(true); expect(bleManager.isDeviceConnected(device2)).toBe(true); // Test: Bulk disconnect const results = await bleManager.bulkDisconnect([device1, device2]); // Verify results expect(results).toHaveLength(2); expect(results[0]).toMatchObject({ deviceId: device1, success: true, }); expect(results[1]).toMatchObject({ deviceId: device2, success: true, }); // Verify disconnected expect(bleManager.isDeviceConnected(device1)).toBe(false); expect(bleManager.isDeviceConnected(device2)).toBe(false); }); it('should handle partial failures gracefully', async () => { const validDevice = 'mock-743'; const invalidDevice = 'non-existent-device'; await bleManager.connectDevice(validDevice); const results = await bleManager.bulkDisconnect([validDevice, invalidDevice]); expect(results).toHaveLength(2); // Valid device should succeed expect(results[0].success).toBe(true); expect(results[0].deviceId).toBe(validDevice); // Invalid device may succeed (disconnect is idempotent) or report no error // This is okay - disconnecting already-disconnected device is not an error expect(results[1].deviceId).toBe(invalidDevice); }); it('should return empty array for empty input', async () => { const results = await bleManager.bulkDisconnect([]); expect(results).toEqual([]); }); }); describe('bulkReboot', () => { it('should reboot multiple devices successfully', async () => { const device1 = 'mock-743'; const device2 = 'mock-769'; const results = await bleManager.bulkReboot([device1, device2]); expect(results).toHaveLength(2); expect(results[0]).toMatchObject({ deviceId: device1, success: true, }); expect(results[1]).toMatchObject({ deviceId: device2, success: true, }); // After reboot, devices should be disconnected expect(bleManager.isDeviceConnected(device1)).toBe(false); expect(bleManager.isDeviceConnected(device2)).toBe(false); }); it('should connect before rebooting if device is not connected', async () => { const deviceId = 'mock-743'; // Device not connected initially expect(bleManager.isDeviceConnected(deviceId)).toBe(false); const results = await bleManager.bulkReboot([deviceId]); expect(results).toHaveLength(1); expect(results[0].success).toBe(true); }); it('should return empty array for empty input', async () => { const results = await bleManager.bulkReboot([]); expect(results).toEqual([]); }); }); describe('bulkSetWiFi', () => { it('should configure WiFi for multiple devices successfully', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; const ssid = 'TestNetwork'; const password = 'testPassword123'; const results = await bleManager.bulkSetWiFi(devices, ssid, password); expect(results).toHaveLength(2); expect(results[0]).toMatchObject({ deviceId: devices[0].id, deviceName: devices[0].name, success: true, }); expect(results[1]).toMatchObject({ deviceId: devices[1].id, deviceName: devices[1].name, success: true, }); // Devices should be disconnected after reboot expect(bleManager.isDeviceConnected(devices[0].id)).toBe(false); expect(bleManager.isDeviceConnected(devices[1].id)).toBe(false); }); it('should call progress callback for each device', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; const ssid = 'TestNetwork'; const password = 'testPassword123'; const progressEvents: Array<{ deviceId: string; status: string; }> = []; const onProgress = ( deviceId: string, status: 'connecting' | 'configuring' | 'rebooting' | 'success' | 'error' ) => { progressEvents.push({ deviceId, status }); }; await bleManager.bulkSetWiFi(devices, ssid, password, onProgress); // Should have progress events for both devices const device1Events = progressEvents.filter(e => e.deviceId === devices[0].id); const device2Events = progressEvents.filter(e => e.deviceId === devices[1].id); // Each device should go through: connecting -> configuring -> rebooting -> success expect(device1Events.length).toBeGreaterThanOrEqual(4); expect(device2Events.length).toBeGreaterThanOrEqual(4); expect(device1Events.map(e => e.status)).toContain('connecting'); expect(device1Events.map(e => e.status)).toContain('configuring'); expect(device1Events.map(e => e.status)).toContain('rebooting'); expect(device1Events.map(e => e.status)).toContain('success'); }); it('should handle errors and continue with remaining devices', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'invalid-device', name: 'InvalidDevice' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; const ssid = 'TestNetwork'; const password = 'testPassword123'; const results = await bleManager.bulkSetWiFi(devices, ssid, password); expect(results).toHaveLength(3); // First device should succeed expect(results[0].success).toBe(true); // Invalid device should fail // (Note: MockBLEManager might still succeed, but real implementation would fail) // We're just checking the structure is correct expect(results[1].deviceId).toBe('invalid-device'); // Third device should still be processed expect(results[2].deviceId).toBe('mock-769'); }, 20000); // 20 second timeout for 3 devices it('should return empty array for empty input', async () => { const results = await bleManager.bulkSetWiFi([], 'TestSSID', 'password'); expect(results).toEqual([]); }); it('should handle sequential processing correctly', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; const ssid = 'TestNetwork'; const password = 'testPassword123'; const startTime = Date.now(); await bleManager.bulkSetWiFi(devices, ssid, password); const endTime = Date.now(); // Mock delays: connect (800ms) + config (1200ms) + reboot (600ms) = 2600ms per device // For 2 devices sequentially: ~5200ms minimum const duration = endTime - startTime; // Should take at least 4000ms (allowing some margin for test environment) expect(duration).toBeGreaterThanOrEqual(4000); }); }); describe('Bulk operation error handling', () => { it('should include error messages in failed results', async () => { // This test is more meaningful with RealBLEManager, but we can verify the structure const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, ]; const results = await bleManager.bulkSetWiFi(devices, '', ''); // Empty credentials expect(results).toHaveLength(1); if (!results[0].success) { expect(results[0].error).toBeDefined(); expect(typeof results[0].error).toBe('string'); } }); it('should maintain device name in all result objects', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; const results = await bleManager.bulkDisconnect(devices.map(d => d.id)); results.forEach((result, index) => { expect(result.deviceName).toBeDefined(); expect(typeof result.deviceName).toBe('string'); }); }); }); describe('Integration: Multiple bulk operations in sequence', () => { it('should handle multiple bulk operations on the same devices', async () => { const devices = [ { id: 'mock-743', name: 'WP_497_81a14c' }, { id: 'mock-769', name: 'WP_523_81aad4' }, ]; // Operation 1: Bulk WiFi config const wifiResults = await bleManager.bulkSetWiFi( devices, 'Network1', 'password1' ); expect(wifiResults.every(r => r.success)).toBe(true); // Operation 2: Connect again and reconfigure const reconfigResults = await bleManager.bulkSetWiFi( devices, 'Network2', 'password2' ); expect(reconfigResults.every(r => r.success)).toBe(true); // Operation 3: Bulk reboot const rebootResults = await bleManager.bulkReboot(devices.map(d => d.id)); expect(rebootResults.every(r => r.success)).toBe(true); }, 30000); // 30 second timeout for multiple sequential operations }); });