WellNuo/services/ble/__tests__/BLEManager.cleanup.test.ts
Sergei 2b2bd88726 Add BLE cleanup on user logout
Implement comprehensive BLE cleanup functionality that properly
disconnects all devices and releases resources when user logs out.

Changes:
- Add cleanup() method to BLEManager and MockBLEManager
- Update IBLEManager interface to include cleanup
- Add cleanupBLE() to BLEContext to disconnect all devices
- Implement callback mechanism in api.ts for BLE cleanup on logout
- Wire up BLE cleanup in app layout to trigger on logout
- Add unit tests for BLE cleanup functionality

This ensures no BLE connections remain active after logout,
preventing resource leaks and potential connection issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 10:57:43 -08:00

128 lines
4.1 KiB
TypeScript

/**
* BLE Manager Cleanup Tests
* Tests for BLE cleanup functionality on logout
*/
import { RealBLEManager } from '../BLEManager';
import { MockBLEManager } from '../MockBLEManager';
describe('BLEManager cleanup', () => {
describe('RealBLEManager', () => {
let manager: RealBLEManager;
beforeEach(() => {
manager = new RealBLEManager();
});
it('should have cleanup method', () => {
expect(manager.cleanup).toBeDefined();
expect(typeof manager.cleanup).toBe('function');
});
it('should not throw when cleaning up with no connections', async () => {
await expect(manager.cleanup()).resolves.not.toThrow();
});
it('should stop scanning when cleanup is called during scan', async () => {
// Mock the scanning state
(manager as any).scanning = true;
const stopScanSpy = jest.spyOn(manager, 'stopScan');
await manager.cleanup();
expect(stopScanSpy).toHaveBeenCalled();
});
it('should clear connected devices map after cleanup', async () => {
// Simulate connected devices
const mockDevice = { id: 'device-1' } as any;
(manager as any).connectedDevices.set('device-1', mockDevice);
await manager.cleanup();
expect((manager as any).connectedDevices.size).toBe(0);
});
it('should attempt to disconnect all connected devices', async () => {
// Mock connected devices
const device1 = { id: 'device-1', cancelConnection: jest.fn().mockResolvedValue(undefined) } as any;
const device2 = { id: 'device-2', cancelConnection: jest.fn().mockResolvedValue(undefined) } as any;
(manager as any).connectedDevices.set('device-1', device1);
(manager as any).connectedDevices.set('device-2', device2);
await manager.cleanup();
expect(device1.cancelConnection).toHaveBeenCalled();
expect(device2.cancelConnection).toHaveBeenCalled();
expect((manager as any).connectedDevices.size).toBe(0);
});
it('should continue cleanup even if one device disconnection fails', async () => {
const device1 = {
id: 'device-1',
cancelConnection: jest.fn().mockRejectedValue(new Error('Connection lost'))
} as any;
const device2 = {
id: 'device-2',
cancelConnection: jest.fn().mockResolvedValue(undefined)
} as any;
(manager as any).connectedDevices.set('device-1', device1);
(manager as any).connectedDevices.set('device-2', device2);
// Should not throw even if one device fails
await expect(manager.cleanup()).resolves.not.toThrow();
// Both should have been attempted
expect(device1.cancelConnection).toHaveBeenCalled();
expect(device2.cancelConnection).toHaveBeenCalled();
// Map should still be cleared
expect((manager as any).connectedDevices.size).toBe(0);
});
});
describe('MockBLEManager', () => {
let manager: MockBLEManager;
beforeEach(() => {
manager = new MockBLEManager();
});
it('should have cleanup method', () => {
expect(manager.cleanup).toBeDefined();
expect(typeof manager.cleanup).toBe('function');
});
it('should not throw when cleaning up with no connections', async () => {
await expect(manager.cleanup()).resolves.not.toThrow();
});
it('should clear connected devices set after cleanup', async () => {
// Simulate connected devices
await manager.connectDevice('mock-743');
await manager.connectDevice('mock-769');
expect((manager as any).connectedDevices.size).toBe(2);
await manager.cleanup();
expect((manager as any).connectedDevices.size).toBe(0);
});
it('should call disconnectDevice for each connected device', async () => {
await manager.connectDevice('mock-743');
await manager.connectDevice('mock-769');
const disconnectSpy = jest.spyOn(manager, 'disconnectDevice');
await manager.cleanup();
expect(disconnectSpy).toHaveBeenCalledTimes(2);
expect(disconnectSpy).toHaveBeenCalledWith('mock-743');
expect(disconnectSpy).toHaveBeenCalledWith('mock-769');
});
});
});