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>
128 lines
4.1 KiB
TypeScript
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');
|
|
});
|
|
});
|
|
});
|