mcp-memory-libsql

by spences10
Verified
import { AccountManager } from '../../../modules/accounts/manager.js'; import { mockAccounts, mockTokens } from '../../../__fixtures__/accounts.js'; // Simple mocks for token and oauth jest.mock('../../../modules/accounts/token.js', () => ({ TokenManager: jest.fn().mockImplementation(() => ({ validateToken: jest.fn().mockResolvedValue({ valid: true, status: 'VALID', token: { access_token: 'test-token' } }), saveToken: jest.fn().mockResolvedValue(undefined), deleteToken: jest.fn().mockResolvedValue(undefined), autoRenewToken: jest.fn().mockResolvedValue({ success: true, status: 'VALID', token: { access_token: 'test-token' } }) })) })); jest.mock('../../../modules/accounts/oauth.js', () => ({ GoogleOAuthClient: jest.fn().mockImplementation(() => ({ ensureInitialized: jest.fn().mockResolvedValue(undefined), getTokenFromCode: jest.fn().mockResolvedValue(mockTokens.valid), refreshToken: jest.fn().mockResolvedValue(mockTokens.valid), generateAuthUrl: jest.fn().mockReturnValue('https://mock-auth-url'), getAuthClient: jest.fn().mockReturnValue({ setCredentials: jest.fn(), getAccessToken: jest.fn().mockResolvedValue({ token: 'test-token' }) }) })) })); // Simple file system mock jest.mock('fs/promises', () => ({ readFile: jest.fn(), writeFile: jest.fn().mockResolvedValue(undefined), mkdir: jest.fn().mockResolvedValue(undefined) })); jest.mock('path', () => ({ resolve: jest.fn().mockReturnValue('/mock/accounts.json'), dirname: jest.fn().mockReturnValue('/mock') })); describe('AccountManager', () => { let accountManager: AccountManager; const fs = require('fs/promises'); beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); process.env.ACCOUNTS_FILE = '/mock/accounts.json'; // Default successful file read fs.readFile.mockResolvedValue(JSON.stringify(mockAccounts)); // Reset TokenManager mock to default implementation const TokenManager = require('../../../modules/accounts/token.js').TokenManager; TokenManager.mockImplementation(() => ({ validateToken: jest.fn().mockResolvedValue({ valid: true }), saveToken: jest.fn().mockResolvedValue(undefined), deleteToken: jest.fn().mockResolvedValue(undefined), autoRenewToken: jest.fn().mockResolvedValue({ success: true, status: 'VALID', token: { access_token: 'test-token' } }) })); accountManager = new AccountManager(); }); // Basic account operations describe('account operations', () => { it('should load accounts from file', async () => { await accountManager.initialize(); const accounts = await accountManager.listAccounts(); expect(accounts).toHaveLength(mockAccounts.accounts.length); expect(accounts[0].email).toBe(mockAccounts.accounts[0].email); }); it('should add new account', async () => { await accountManager.initialize(); const newAccount = await accountManager.addAccount( 'new@example.com', 'work', 'New Test Account' ); expect(newAccount.email).toBe('new@example.com'); expect(fs.writeFile).toHaveBeenCalled(); }); it('should not add duplicate account', async () => { await accountManager.initialize(); await expect(accountManager.addAccount( mockAccounts.accounts[0].email, 'work', 'Duplicate' )).rejects.toThrow('Account already exists'); }); }); // File system operations describe('file operations', () => { it('should handle missing accounts file', async () => { // Reset all mocks and modules jest.clearAllMocks(); jest.resetModules(); // Re-require fs after reset const fs = require('fs/promises'); // Setup error for first read attempt const error = new Error('File not found'); (error as any).code = 'ENOENT'; fs.readFile.mockRejectedValueOnce(error); // Mock path module jest.doMock('path', () => ({ resolve: jest.fn().mockReturnValue('/mock/accounts.json'), dirname: jest.fn().mockReturnValue('/mock') })); // Re-require AccountManager to use fresh mocks const { AccountManager } = require('../../../modules/accounts/manager.js'); accountManager = new AccountManager(); // Initialize with empty file system await accountManager.initialize(); // Mock empty response for listAccounts call fs.readFile.mockResolvedValueOnce('{"accounts":[]}'); const accounts = await accountManager.listAccounts(); // Verify results expect(accounts).toHaveLength(0); // Verify write was called with correct data expect(fs.writeFile).toHaveBeenCalledTimes(1); const [path, content] = fs.writeFile.mock.calls[0]; expect(path).toBe('/mock/accounts.json'); // Parse and re-stringify to normalize formatting const parsedContent = JSON.parse(content); expect(parsedContent).toEqual({ accounts: [] }); }); it('should handle invalid JSON', async () => { fs.readFile.mockResolvedValueOnce('invalid json'); await expect(accountManager.initialize()) .rejects .toThrow('Failed to parse accounts configuration'); }); }); // Token validation (simplified) describe('token validation', () => { const testEmail = mockAccounts.accounts[0].email; it('should validate token successfully', async () => { await accountManager.initialize(); const account = await accountManager.validateAccount(testEmail); expect(account.email).toBe(testEmail); expect(account.auth_status).toEqual({ valid: true, status: 'VALID' }); }); it('should handle token validation failure', async () => { // Reset all mocks and modules jest.clearAllMocks(); jest.resetModules(); // Re-require fs and setup fresh state const fs = require('fs/promises'); fs.readFile.mockResolvedValue(JSON.stringify(mockAccounts)); // Create simple mock implementation const mockValidateToken = jest.fn().mockResolvedValue({ valid: false, status: 'EXPIRED', reason: 'Token expired' }); // Setup TokenManager with tracked mock jest.doMock('../../../modules/accounts/token.js', () => ({ TokenManager: jest.fn().mockImplementation(() => ({ validateToken: mockValidateToken, saveToken: jest.fn(), deleteToken: jest.fn() })) })); // Re-require AccountManager to use new mocks const { AccountManager } = require('../../../modules/accounts/manager.js'); accountManager = new AccountManager(); await accountManager.initialize(); const account = await accountManager.validateAccount(testEmail); expect(mockValidateToken).toHaveBeenCalledWith(testEmail, false); expect(account.auth_status).toMatchObject({ valid: false, status: 'EXPIRED', reason: 'Token expired' }); expect(account.auth_status).toHaveProperty('authUrl'); }); }); // OAuth operations (simplified) describe('oauth operations', () => { it('should handle token from auth code', async () => { await accountManager.initialize(); const token = await accountManager.getTokenFromCode('test-code'); expect(token).toEqual(mockTokens.valid); }); it('should save token for account', async () => { // Reset all mocks and modules jest.clearAllMocks(); jest.resetModules(); // Re-require fs and setup fresh state const fs = require('fs/promises'); fs.readFile.mockResolvedValue(JSON.stringify(mockAccounts)); // Create mock implementation const mockSaveToken = jest.fn().mockResolvedValue(undefined); // Setup TokenManager with tracked mock jest.doMock('../../../modules/accounts/token.js', () => ({ TokenManager: jest.fn().mockImplementation(() => ({ validateToken: jest.fn().mockResolvedValue({ valid: true }), saveToken: mockSaveToken, deleteToken: jest.fn() })) })); // Re-require AccountManager to use new mocks const { AccountManager } = require('../../../modules/accounts/manager.js'); accountManager = new AccountManager(); await accountManager.initialize(); const testEmail = 'test@example.com'; await accountManager.saveToken(testEmail, mockTokens.valid); expect(mockSaveToken).toHaveBeenCalledWith(testEmail, mockTokens.valid); expect(mockSaveToken).toHaveBeenCalledTimes(1); }); }); });