WellNuo/services/__tests__/api.logout.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

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();
});
});
});