Skip to main content
Glama

WhaTap MXQL CLI

by devload
SessionStore.test.ts8.37 kB
/** * SessionStore Unit Tests * * Tests encryption, decryption, and session management functionality */ import * as fs from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; import { SessionStore } from '../../../src/core/auth/SessionStore'; import type { Session } from '../../../src/core/types'; import { SessionError } from '../../../src/core/types'; describe('SessionStore', () => { let testDir: string; let sessionStore: SessionStore; let mockSession: Session; beforeEach(async () => { // Create temporary test directory testDir = path.join(os.tmpdir(), `whatap-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); sessionStore = new SessionStore(testDir); // Mock session data mockSession = { email: 'test@whatap.io', accountId: 12345, cookies: { wa: 'test-wa-cookie', jsessionid: 'test-jsessionid', }, apiToken: 'test-api-token', serviceUrl: 'https://service.whatap.io', createdAt: new Date(), expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours }; }); afterEach(async () => { // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe('save and load', () => { test('should save and load session successfully', async () => { // Save session await sessionStore.save(mockSession); // Verify file exists const exists = await sessionStore.exists(); expect(exists).toBe(true); // Load session const loaded = await sessionStore.load(); expect(loaded).not.toBeNull(); expect(loaded?.email).toBe(mockSession.email); expect(loaded?.accountId).toBe(mockSession.accountId); expect(loaded?.cookies.wa).toBe(mockSession.cookies.wa); expect(loaded?.cookies.jsessionid).toBe(mockSession.cookies.jsessionid); expect(loaded?.apiToken).toBe(mockSession.apiToken); expect(loaded?.serviceUrl).toBe(mockSession.serviceUrl); // Check dates are correctly parsed expect(loaded?.createdAt).toBeInstanceOf(Date); expect(loaded?.expiresAt).toBeInstanceOf(Date); }); test('should return null when session file does not exist', async () => { const loaded = await sessionStore.load(); expect(loaded).toBeNull(); }); test('should return null and clear expired session', async () => { // Create expired session const expiredSession: Session = { ...mockSession, expiresAt: new Date(Date.now() - 1000), // Expired 1 second ago }; await sessionStore.save(expiredSession); // Try to load - should return null const loaded = await sessionStore.load(); expect(loaded).toBeNull(); // Verify file was deleted const exists = await sessionStore.exists(); expect(exists).toBe(false); }); test('should overwrite existing session', async () => { // Save first session await sessionStore.save(mockSession); // Save second session with different data const updatedSession: Session = { ...mockSession, accountId: 67890, apiToken: 'updated-token', }; await sessionStore.save(updatedSession); // Load and verify updated data const loaded = await sessionStore.load(); expect(loaded?.accountId).toBe(67890); expect(loaded?.apiToken).toBe('updated-token'); }); }); describe('encryption', () => { test('should encrypt session data (not plain text)', async () => { await sessionStore.save(mockSession); // Read raw file content const sessionFile = sessionStore.getSessionFilePath(); const encrypted = await fs.readFile(sessionFile, 'utf-8'); // Verify it's not plain text expect(encrypted).not.toContain('test@whatap.io'); expect(encrypted).not.toContain('test-wa-cookie'); expect(encrypted).not.toContain('test-api-token'); // Verify it contains encryption metadata const encryptedData = JSON.parse(encrypted); expect(encryptedData).toHaveProperty('iv'); expect(encryptedData).toHaveProperty('encrypted'); expect(encryptedData).toHaveProperty('authTag'); }); test('should use consistent encryption key', async () => { // Save and load first time await sessionStore.save(mockSession); const loaded1 = await sessionStore.load(); // Create new SessionStore instance (same directory) const sessionStore2 = new SessionStore(testDir); const loaded2 = await sessionStore2.load(); // Should load same session data expect(loaded1).toEqual(loaded2); }); test('should generate new key if key file is deleted', async () => { await sessionStore.save(mockSession); // Delete key file const keyFile = path.join(testDir, '.key'); await fs.unlink(keyFile); // Create new instance - should generate new key const sessionStore2 = new SessionStore(testDir); // Should fail to load (wrong key) await expect(sessionStore2.load()).rejects.toThrow(SessionError); }); }); describe('clear', () => { test('should clear session successfully', async () => { await sessionStore.save(mockSession); expect(await sessionStore.exists()).toBe(true); await sessionStore.clear(); expect(await sessionStore.exists()).toBe(false); const loaded = await sessionStore.load(); expect(loaded).toBeNull(); }); test('should not throw error when clearing non-existent session', async () => { await expect(sessionStore.clear()).resolves.not.toThrow(); }); }); describe('file permissions', () => { test('should create session file with restricted permissions (0600)', async () => { await sessionStore.save(mockSession); const sessionFile = sessionStore.getSessionFilePath(); const stats = await fs.stat(sessionFile); // Check permissions (Unix only) if (process.platform !== 'win32') { const mode = stats.mode & 0o777; expect(mode).toBe(0o600); } }); test('should create key file with restricted permissions (0600)', async () => { await sessionStore.save(mockSession); const keyFile = path.join(testDir, '.key'); const stats = await fs.stat(keyFile); // Check permissions (Unix only) if (process.platform !== 'win32') { const mode = stats.mode & 0o777; expect(mode).toBe(0o600); } }); }); describe('error handling', () => { test('should throw SessionError on encryption failure', async () => { // This is hard to test without mocking crypto, but we can test the wrapper const invalidSession = { ...mockSession } as any; delete invalidSession.email; // Should still save (JSON.stringify handles it) await expect(sessionStore.save(invalidSession)).resolves.not.toThrow(); }); test('should throw SessionError on corrupted session file', async () => { await sessionStore.save(mockSession); // Corrupt the session file const sessionFile = sessionStore.getSessionFilePath(); await fs.writeFile(sessionFile, 'corrupted-data', 'utf-8'); // Should throw error on load await expect(sessionStore.load()).rejects.toThrow(SessionError); }); test('should throw SessionError on invalid encrypted data', async () => { await sessionStore.save(mockSession); // Write invalid JSON const sessionFile = sessionStore.getSessionFilePath(); await fs.writeFile( sessionFile, JSON.stringify({ iv: 'invalid', encrypted: 'invalid', authTag: 'invalid', }), 'utf-8' ); // Should throw error on load await expect(sessionStore.load()).rejects.toThrow(SessionError); }); }); describe('exists', () => { test('should return false when session does not exist', async () => { const exists = await sessionStore.exists(); expect(exists).toBe(false); }); test('should return true when session exists', async () => { await sessionStore.save(mockSession); const exists = await sessionStore.exists(); expect(exists).toBe(true); }); }); });

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/devload/whatap-mxql-cli'

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