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>
113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
/**
|
|
* API Logout Tests
|
|
* Tests for logout functionality including BLE cleanup
|
|
*/
|
|
|
|
import { api, setOnLogoutBLECleanupCallback } from '../api';
|
|
import * as SecureStore from 'expo-secure-store';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
|
|
// Mock dependencies
|
|
jest.mock('expo-secure-store');
|
|
jest.mock('@react-native-async-storage/async-storage');
|
|
|
|
describe('API logout with BLE cleanup', () => {
|
|
let bleCleanupCallback: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
// Reset mocks
|
|
jest.clearAllMocks();
|
|
|
|
// Create mock BLE cleanup callback
|
|
bleCleanupCallback = jest.fn().mockResolvedValue(undefined);
|
|
});
|
|
|
|
describe('logout without BLE cleanup callback', () => {
|
|
it('should clear all auth tokens and data', async () => {
|
|
await api.logout();
|
|
|
|
// Verify SecureStore items are deleted
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('accessToken');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('userId');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('userEmail');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('onboardingCompleted');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('legacyAccessToken');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('privileges');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('maxRole');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('userAvatar');
|
|
|
|
// Verify AsyncStorage items are removed
|
|
expect(AsyncStorage.removeItem).toHaveBeenCalledWith('wellnuo_local_beneficiaries');
|
|
});
|
|
|
|
it('should complete logout even if no BLE callback is set', async () => {
|
|
// Should not throw
|
|
await expect(api.logout()).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('logout with BLE cleanup callback', () => {
|
|
beforeEach(() => {
|
|
// Set the BLE cleanup callback
|
|
setOnLogoutBLECleanupCallback(bleCleanupCallback);
|
|
});
|
|
|
|
it('should call BLE cleanup callback before clearing data', async () => {
|
|
await api.logout();
|
|
|
|
// Verify BLE cleanup was called
|
|
expect(bleCleanupCallback).toHaveBeenCalledTimes(1);
|
|
|
|
// Verify auth data was still cleared
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('accessToken');
|
|
});
|
|
|
|
it('should continue logout even if BLE cleanup fails', async () => {
|
|
// Make BLE cleanup fail
|
|
bleCleanupCallback.mockRejectedValue(new Error('BLE cleanup failed'));
|
|
|
|
// Logout should still complete
|
|
await expect(api.logout()).resolves.not.toThrow();
|
|
|
|
// Verify BLE cleanup was attempted
|
|
expect(bleCleanupCallback).toHaveBeenCalled();
|
|
|
|
// Verify auth data was still cleared despite BLE failure
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('accessToken');
|
|
expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('userId');
|
|
});
|
|
|
|
it('should log error but not throw if BLE cleanup fails', async () => {
|
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
bleCleanupCallback.mockRejectedValue(new Error('BLE error'));
|
|
|
|
await api.logout();
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
'[API] BLE cleanup failed during logout:',
|
|
expect.any(Error)
|
|
);
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('BLE cleanup callback registration', () => {
|
|
it('should allow setting BLE cleanup callback', () => {
|
|
const callback = jest.fn();
|
|
expect(() => setOnLogoutBLECleanupCallback(callback)).not.toThrow();
|
|
});
|
|
|
|
it('should allow replacing BLE cleanup callback', () => {
|
|
const callback1 = jest.fn().mockResolvedValue(undefined);
|
|
const callback2 = jest.fn().mockResolvedValue(undefined);
|
|
|
|
setOnLogoutBLECleanupCallback(callback1);
|
|
setOnLogoutBLECleanupCallback(callback2);
|
|
|
|
// Only callback2 should be called
|
|
expect(callback1).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|