/** * API Logout Tests * Tests for logout functionality including BLE cleanup and WiFi password cleanup */ import { api, setOnLogoutBLECleanupCallback } from '../api'; import * as SecureStore from 'expo-secure-store'; import AsyncStorage from '@react-native-async-storage/async-storage'; import * as wifiPasswordStore from '../wifiPasswordStore'; // Mock dependencies jest.mock('expo-secure-store'); jest.mock('@react-native-async-storage/async-storage'); jest.mock('../wifiPasswordStore'); 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 WiFi passwords are cleared expect(wifiPasswordStore.clearAllWiFiPasswords).toHaveBeenCalledTimes(1); // 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(); }); it('should continue logout even if WiFi password cleanup fails', async () => { // Make WiFi password cleanup fail (wifiPasswordStore.clearAllWiFiPasswords as jest.Mock).mockRejectedValue( new Error('WiFi cleanup failed') ); // Logout should still complete await expect(api.logout()).resolves.not.toThrow(); // Verify WiFi cleanup was attempted expect(wifiPasswordStore.clearAllWiFiPasswords).toHaveBeenCalled(); // Verify auth data was still cleared despite WiFi cleanup failure expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('accessToken'); expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith('userId'); }); }); 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(); }); }); });