Skip to main content
Glama
logout.test.ts9.37 kB
import { beforeEach, describe, expect, it, vi } from 'vitest'; import keytar from 'keytar'; import { ALL_KEYCHAIN_ITEMS, keychain, KEYCHAIN_SERVICE_NAME, KeychainItem, KeychainOperationResult, } from '../../src/utils/keychain'; // Mock dependencies vi.mock('keytar', () => ({ default: { setPassword: vi.fn(), getPassword: vi.fn(), deletePassword: vi.fn(), }, })); vi.mock('../../src/utils/logger', () => ({ log: vi.fn(), logError: vi.fn(), })); describe('Keychain Service', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('token management', () => { it('should store and retrieve access token', async () => { // Arrange const mockToken = 'test-access-token'; vi.mocked(keytar.setPassword).mockResolvedValue(); vi.mocked(keytar.getPassword).mockResolvedValue(mockToken); // Act & Assert - Store token const setResult = await keychain.setToken(mockToken); expect(setResult).toBe(true); expect(keytar.setPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.TOKEN, mockToken ); // Act & Assert - Retrieve token const getResult = await keychain.getToken(); expect(getResult).toBe(mockToken); expect(keytar.getPassword).toHaveBeenCalledWith(KEYCHAIN_SERVICE_NAME, KeychainItem.TOKEN); }); it('should handle missing token', async () => { // Arrange vi.mocked(keytar.getPassword).mockResolvedValue(null); // Act const result = await keychain.getToken(); // Assert expect(result).toBeNull(); }); }); describe('domain management', () => { it('should store and retrieve domain', async () => { // Arrange const mockDomain = 'test-domain.auth0.com'; vi.mocked(keytar.setPassword).mockResolvedValue(); vi.mocked(keytar.getPassword).mockResolvedValue(mockDomain); // Act - Store domain const setResult = await keychain.setDomain(mockDomain); // Assert expect(setResult).toBe(true); expect(keytar.setPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.DOMAIN, mockDomain ); // Act - Retrieve domain const result = await keychain.getDomain(); // Assert expect(result).toBe(mockDomain); expect(keytar.getPassword).toHaveBeenCalledWith(KEYCHAIN_SERVICE_NAME, KeychainItem.DOMAIN); }); it('should handle missing domain', async () => { // Arrange vi.mocked(keytar.getPassword).mockResolvedValue(null); // Act const result = await keychain.getDomain(); // Assert expect(result).toBeNull(); }); }); describe('refresh token management', () => { it('should store and retrieve refresh token', async () => { // Arrange const mockRefreshToken = 'test-refresh-token'; vi.mocked(keytar.setPassword).mockResolvedValue(); vi.mocked(keytar.getPassword).mockResolvedValue(mockRefreshToken); // Act - Store refresh token const setResult = await keychain.setRefreshToken(mockRefreshToken); // Assert expect(setResult).toBe(true); expect(keytar.setPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.REFRESH_TOKEN, mockRefreshToken ); // Act - Retrieve refresh token const getResult = await keychain.getRefreshToken(); // Assert expect(getResult).toBe(mockRefreshToken); expect(keytar.getPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.REFRESH_TOKEN ); }); it('should handle missing refresh token', async () => { // Arrange vi.mocked(keytar.getPassword).mockResolvedValue(null); // Act const result = await keychain.getRefreshToken(); // Assert expect(result).toBeNull(); }); }); describe('token expiration management', () => { it('should store and retrieve token expiration timestamp', async () => { // Arrange const timestamp = Date.now() + 3600 * 1000; vi.mocked(keytar.setPassword).mockResolvedValue(); vi.mocked(keytar.getPassword).mockResolvedValue(timestamp.toString()); // Act - Store expiration const setResult = await keychain.setTokenExpiresAt(timestamp); // Assert expect(setResult).toBe(true); expect(keytar.setPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.TOKEN_EXPIRES_AT, timestamp.toString() ); // Act - Retrieve expiration const getResult = await keychain.getTokenExpiresAt(); // Assert expect(getResult).toBe(timestamp); expect(keytar.getPassword).toHaveBeenCalledWith( KEYCHAIN_SERVICE_NAME, KeychainItem.TOKEN_EXPIRES_AT ); }); it('should return null for missing expiration time', async () => { // Arrange vi.mocked(keytar.getPassword).mockResolvedValue(null); // Act const result = await keychain.getTokenExpiresAt(); // Assert expect(result).toBeNull(); }); }); describe('keychain clearing', () => { it('should successfully delete all items from keychain', async () => { // Arrange vi.mocked(keytar.deletePassword).mockResolvedValue(true); // Act const results = await keychain.clearAll(); // Assert expect(keytar.deletePassword).toHaveBeenCalledTimes(ALL_KEYCHAIN_ITEMS.length); ALL_KEYCHAIN_ITEMS.forEach((item) => { expect(keytar.deletePassword).toHaveBeenCalledWith(KEYCHAIN_SERVICE_NAME, item); }); const expectedResults = ALL_KEYCHAIN_ITEMS.map((item) => ({ item, success: true })); expect(results).toEqual(expectedResults); }); it('should handle mixed success and failure when clearing keychain', async () => { // Arrange - Set up different responses for different items vi.mocked(keytar.deletePassword).mockImplementation(async (service, key) => { switch (key) { case KeychainItem.TOKEN: case KeychainItem.DOMAIN: return true; case KeychainItem.REFRESH_TOKEN: throw new Error('Access denied'); case KeychainItem.TOKEN_EXPIRES_AT: default: return false; } }); // Act const results = await keychain.clearAll(); // Assert expect(keytar.deletePassword).toHaveBeenCalledTimes(ALL_KEYCHAIN_ITEMS.length); // Verify results for each item const findResult = (item: string): KeychainOperationResult => { const result = results.find((r) => r.item === item); if (!result) { throw new Error(`Result for ${item} not found`); } return result; }; expect(findResult(KeychainItem.TOKEN).success).toBe(true); expect(findResult(KeychainItem.DOMAIN).success).toBe(true); const refreshTokenResult = findResult(KeychainItem.REFRESH_TOKEN); expect(refreshTokenResult.success).toBe(false); expect(refreshTokenResult.error?.message).toBe('Access denied'); expect(findResult(KeychainItem.TOKEN_EXPIRES_AT).success).toBe(false); }); it('should handle errors in bulk deletion operations', async () => { // Arrange - Force a global error const testError = new Error('Keychain unavailable'); vi.mocked(keytar.deletePassword).mockRejectedValue(testError); // Act const results = await keychain.clearAll(); // Assert expect(keytar.deletePassword).toHaveBeenCalledTimes(ALL_KEYCHAIN_ITEMS.length); // All operations should have failed with the same error expect(results.every((result) => !result.success)).toBe(true); expect(results.every((result) => result.error?.message === 'Keychain unavailable')).toBe( true ); }); }); describe('delete', () => { it('should delete a specific keychain item', async () => { // Only run if delete method exists if (typeof keychain.delete !== 'function') { console.log('Skipping test for delete method - not implemented'); return; } // Arrange vi.mocked(keytar.deletePassword).mockResolvedValue(true); // Act const result = await keychain.delete(KeychainItem.TOKEN); // Assert expect(result).toBe(true); expect(keytar.deletePassword).toHaveBeenCalledWith(KEYCHAIN_SERVICE_NAME, KeychainItem.TOKEN); }); it('should handle failure when deleting item', async () => { // Only run if delete method exists if (typeof keychain.delete !== 'function') { return; } // Arrange vi.mocked(keytar.deletePassword).mockResolvedValue(false); // Act const result = await keychain.delete(KeychainItem.TOKEN); // Assert expect(result).toBe(false); }); it('should handle errors when deleting item', async () => { // Only run if delete method exists if (typeof keychain.delete !== 'function') { return; } // Arrange const testError = new Error('Delete failed'); vi.mocked(keytar.deletePassword).mockRejectedValue(testError); // Act const result = await keychain.delete(KeychainItem.TOKEN); // Assert expect(result).toBe(false); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/auth0/auth0-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server