Skip to main content
Glama

1MCP Server

fileStorageService.test.ts30.7 kB
import fs from 'fs'; import { tmpdir } from 'os'; import path from 'path'; import { ExpirableData } from '@src/auth/sessionTypes.js'; import { FILE_PREFIX_MAPPING, STORAGE_SUBDIRS } from '@src/constants.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { FileStorageService } from './fileStorageService.js'; // Mock logger to avoid console output during tests vi.mock('@src/logger/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), }, })); interface TestData extends ExpirableData { id: string; value: string; expires: number; createdAt: number; } describe('FileStorageService', () => { let service: FileStorageService; let tempDir: string; beforeEach(() => { // Create a temporary directory for testing tempDir = path.join(tmpdir(), `file-storage-test-${Date.now()}`); service = new FileStorageService(tempDir); }); afterEach(() => { service.shutdown(); // Clean up temp directory if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true, force: true }); } }); describe('Constructor and Directory Management', () => { it('should create storage directory if it does not exist', () => { expect(fs.existsSync(tempDir)).toBe(true); }); it('should use provided storage directory', () => { const customDir = path.join(tmpdir(), `custom-test-${Date.now()}`); const customService = new FileStorageService(customDir); const expectedPath = path.join(customDir, 'sessions'); expect(fs.existsSync(expectedPath)).toBe(true); expect(customService.getStorageDir()).toBe(expectedPath); customService.shutdown(); fs.rmSync(customDir, { recursive: true, force: true }); }); it('should handle directory creation errors', () => { const invalidDir = '/invalid/path/that/cannot/be/created'; expect(() => new FileStorageService(invalidDir)).toThrow(); }); }); describe('CRUD Operations', () => { const testPrefix = 'test_'; const testId = 'sess-12345678-1234-4abc-89de-123456789012'; const testData: TestData = { id: testId, value: 'test value', expires: Date.now() + 60000, // 1 minute from now createdAt: Date.now(), }; it('should write and read data correctly', () => { service.writeData(testPrefix, testId, testData); const retrieved = service.readData<TestData>(testPrefix, testId); expect(retrieved).toEqual(testData); }); it('should return null for non-existent data', () => { const result = service.readData<TestData>(testPrefix, 'nonexistent'); expect(result).toBeNull(); }); it('should delete data successfully', () => { service.writeData(testPrefix, testId, testData); expect(service.readData<TestData>(testPrefix, testId)).toEqual(testData); const deleted = service.deleteData(testPrefix, testId); expect(deleted).toBe(true); expect(service.readData<TestData>(testPrefix, testId)).toBeNull(); }); it('should return false when deleting non-existent data', () => { const deleted = service.deleteData(testPrefix, 'nonexistent'); expect(deleted).toBe(false); }); it('should handle file path generation correctly', () => { const filePath = service.getFilePath(testPrefix, testId); const expectedPath = path.join(tempDir, 'sessions', `${testPrefix}${testId}.json`); expect(filePath).toBe(expectedPath); }); }); describe('Path Security', () => { it('should prevent path traversal attacks', () => { const maliciousId = '../../../etc/passwd'; expect(() => service.writeData('test_', maliciousId, {} as TestData)).toThrow('Invalid ID format'); }); it('should reject IDs with invalid characters', () => { const invalidChars = ['/', '\\', '..', '\0', '<', '>', ':', '"', '|', '?', '*']; for (const char of invalidChars) { const maliciousId = `test${char}id`; expect(() => service.writeData('test_', maliciousId, {} as TestData)).toThrow('Invalid ID format'); } }); it('should accept valid IDs with proper prefixes', () => { const validIds = ['sess-12345678-1234-4abc-89de-123456789012', 'code-87654321-4321-4def-89ab-210987654321']; for (const id of validIds) { const data: TestData = { id, value: 'test', expires: Date.now() + 60000, createdAt: Date.now(), }; expect(() => service.writeData('test_', id, data)).not.toThrow(); expect(service.readData<TestData>('test_', id)).toEqual(data); } }); }); describe('Expiration and Cleanup', () => { it('should identify expired data correctly', () => { const expiredId = 'sess-11111111-1234-4abc-89de-123456789012'; const validId = 'sess-22222222-1234-4def-89ab-123456789012'; const expiredData: TestData = { id: expiredId, value: 'expired', expires: Date.now() - 1000, // 1 second ago createdAt: Date.now() - 60000, }; const validData: TestData = { id: validId, value: 'valid', expires: Date.now() + 60000, // 1 minute from now createdAt: Date.now(), }; service.writeData('test_', expiredId, expiredData); service.writeData('test_', validId, validData); // Manually trigger cleanup service.cleanupExpiredData(); // Expired data should be removed expect(service.readData<TestData>('test_', expiredId)).toBeNull(); // Valid data should remain expect(service.readData<TestData>('test_', validId)).toEqual(validData); }); it('should handle corrupted JSON files during cleanup', () => { const storageDir = service.getStorageDir(); const corruptedFilePath = path.join(storageDir, 'test_corrupted.json'); fs.writeFileSync(corruptedFilePath, 'invalid json {'); // Should not throw and should remove corrupted file expect(() => service.cleanupExpiredData()).not.toThrow(); expect(fs.existsSync(corruptedFilePath)).toBe(false); }); it('should handle files without expires field during cleanup', () => { const invalidData = { id: 'test', value: 'no expires field' }; const storageDir = service.getStorageDir(); const filePath = path.join(storageDir, 'test_invalid.json'); fs.writeFileSync(filePath, JSON.stringify(invalidData)); // Should not throw and should skip files without expires expect(() => service.cleanupExpiredData()).not.toThrow(); expect(fs.existsSync(filePath)).toBe(true); // Should not be removed }); it('should count cleaned up items correctly', () => { const expiredId1 = 'sess-33333333-1234-4abc-89de-123456789012'; const expiredId2 = 'sess-44444444-1234-4def-89ab-123456789012'; const expiredData1: TestData = { id: expiredId1, value: 'expired1', expires: Date.now() - 1000, createdAt: Date.now() - 60000, }; const expiredData2: TestData = { id: expiredId2, value: 'expired2', expires: Date.now() - 2000, createdAt: Date.now() - 60000, }; service.writeData('test_', expiredId1, expiredData1); service.writeData('test_', expiredId2, expiredData2); const cleanedCount = service.cleanupExpiredData(); expect(cleanedCount).toBe(2); }); }); describe('Periodic Cleanup', () => { it('should start periodic cleanup by default', () => { // Verify cleanup interval is set (private field test via behavior) expect(service).toBeDefined(); // The interval should be running, but we can't easily test it without waiting // This is tested indirectly through the shutdown test }); it('should stop periodic cleanup on shutdown', () => { service.shutdown(); // After shutdown, no errors should occur and service should be clean expect(() => service.shutdown()).not.toThrow(); // Should be idempotent }); it('should be idempotent when calling shutdown multiple times', () => { service.shutdown(); service.shutdown(); service.shutdown(); // Should not throw errors expect(true).toBe(true); }); }); describe('Error Handling', () => { it('should handle read errors gracefully', () => { const testId = 'sess-55555555-1234-4abc-89de-123456789012'; // Create a file and then make directory non-readable service.writeData('test_', testId, { id: testId, value: 'test', expires: Date.now() + 60000, createdAt: Date.now(), } as TestData); // Change permissions to make file unreadable (on Unix systems) const filePath = service.getFilePath('test_', testId); try { fs.chmodSync(filePath, 0o000); const result = service.readData<TestData>('test_', testId); expect(result).toBeNull(); } catch (_error) { // On some systems, chmod might not work as expected // In that case, we just verify the method doesn't crash expect(true).toBe(true); } finally { // Restore permissions for cleanup try { fs.chmodSync(filePath, 0o644); } catch { // Ignore errors during cleanup } } }); it('should handle write errors gracefully', () => { // Try to write to a read-only directory const readOnlyDir = path.join(tempDir, 'readonly'); fs.mkdirSync(readOnlyDir); try { fs.chmodSync(readOnlyDir, 0o444); // Read-only const readOnlyService = new FileStorageService(readOnlyDir); const writeTestId = 'sess-66666666-1234-4abc-89de-123456789012'; expect(() => readOnlyService.writeData('test_', writeTestId, { id: writeTestId, value: 'test', expires: Date.now() + 60000, createdAt: Date.now(), } as TestData), ).toThrow(); readOnlyService.shutdown(); } catch (_error) { // On some systems, chmod might not work as expected expect(true).toBe(true); } finally { // Restore permissions for cleanup try { fs.chmodSync(readOnlyDir, 0o755); } catch { // Ignore errors during cleanup } } }); it('should handle JSON parsing errors', () => { const storageDir = service.getStorageDir(); const filePath = path.join(storageDir, 'test_corrupted.json'); fs.writeFileSync(filePath, 'invalid json content'); const result = service.readData<TestData>('test_', 'corrupted'); expect(result).toBeNull(); }); }); describe('Utility Methods', () => { it('should return correct storage directory', () => { const expectedPath = path.join(tempDir, 'sessions'); expect(service.getStorageDir()).toBe(expectedPath); }); it('should validate file IDs correctly', () => { // Test ID validation through public interface behavior const validIds = ['sess-12345678-1234-4abc-89de-123456789012', 'code-87654321-4321-4def-89ab-210987654321']; const invalidIds = ['../test', 'test/path', 'test\\path', 'shortid']; for (const id of validIds) { expect(() => service.getFilePath('test_', id)).not.toThrow(); } for (const id of invalidIds) { expect(() => service.getFilePath('test_', id)).toThrow(); } }); }); describe('Subdirectory Support', () => { it('should create storage in subdirectory when provided', () => { const baseDir = path.join(tmpdir(), `base-dir-test-${Date.now()}`); const subdirService = new FileStorageService(baseDir, 'server'); const expectedPath = path.join(baseDir, 'sessions', 'server'); expect(subdirService.getStorageDir()).toBe(expectedPath); expect(fs.existsSync(expectedPath)).toBe(true); subdirService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should create storage without subdirectory when not provided', () => { const baseDir = path.join(tmpdir(), `base-dir-test-${Date.now()}`); const noSubdirService = new FileStorageService(baseDir); const expectedPath = path.join(baseDir, 'sessions'); expect(noSubdirService.getStorageDir()).toBe(expectedPath); expect(fs.existsSync(expectedPath)).toBe(true); noSubdirService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); }); describe('Migration Logic', () => { it('should migrate old server session files from sessions/ to server subdirectory', () => { // Arrange: Create parent directory with old flat structure const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create old session files in flat structure const oldFiles = [ 'session_sess-12345678-1234-4abc-89de-123456789012.json', 'auth_code_code-87654321-4321-4def-89ab-210987654321.json', 'auth_request_code-11111111-1111-4111-8111-111111111111.json', ]; for (const file of oldFiles) { fs.writeFileSync(path.join(sessionsDir, file), JSON.stringify({ test: 'data' })); } // Act: Create service with 'server' subdirectory const serverService = new FileStorageService(baseDir, 'server'); // Assert: Files should be migrated to server subdirectory const serverSubdir = path.join(sessionsDir, 'server'); for (const file of oldFiles) { expect(fs.existsSync(path.join(serverSubdir, file))).toBe(true); expect(fs.existsSync(path.join(sessionsDir, file))).toBe(false); } // Assert: Migration flag should be created in sessions directory const migrationFlagPath = path.join(sessionsDir, '.migrated-to-server'); expect(fs.existsSync(migrationFlagPath)).toBe(true); serverService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should migrate old client session files from clientSessions/ to client subdirectory', () => { // Arrange: Create parent directory with old clientSessions structure const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const clientSessionsDir = path.join(baseDir, 'clientSessions'); fs.mkdirSync(clientSessionsDir, { recursive: true }); // Create old client files in clientSessions directory const oldFiles = [ 'oauth_test-server.json', 'cli_client-123.json', 'tok_token-456.json', 'ver_verifier-789.json', 'sta_state-abc.json', ]; for (const file of oldFiles) { fs.writeFileSync(path.join(clientSessionsDir, file), JSON.stringify({ test: 'data' })); } // Act: Create service with 'client' subdirectory const clientService = new FileStorageService(baseDir, 'client'); // Assert: Files should be migrated to client subdirectory const clientSubdir = path.join(baseDir, 'sessions', 'client'); for (const file of oldFiles) { expect(fs.existsSync(path.join(clientSubdir, file))).toBe(true); expect(fs.existsSync(path.join(clientSessionsDir, file))).toBe(false); } // Assert: Migration flag should be created in clientSessions directory const migrationFlagPath = path.join(clientSessionsDir, '.migrated-to-client'); expect(fs.existsSync(migrationFlagPath)).toBe(true); clientService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should not migrate transport files (new feature)', () => { // Arrange: Create parent directory structure const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create some files that would normally be migrated const oldFiles = ['streamable_session_stream-12345678-1234-4abc-89de-123456789012.json']; for (const file of oldFiles) { fs.writeFileSync(path.join(sessionsDir, file), JSON.stringify({ test: 'data' })); } // Act: Create service with 'transport' subdirectory const transportService = new FileStorageService(baseDir, 'transport'); // Assert: Files should NOT be migrated (transport is new feature) const transportSubdir = path.join(sessionsDir, 'transport'); for (const file of oldFiles) { expect(fs.existsSync(path.join(sessionsDir, file))).toBe(true); // Still in original location expect(fs.existsSync(path.join(transportSubdir, file))).toBe(false); // Not migrated } // Assert: No migration flag should be created const migrationFlagPath = path.join(sessionsDir, '.migrated-to-transport'); expect(fs.existsSync(migrationFlagPath)).toBe(false); transportService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should not migrate files when not in subdirectory mode', () => { // Arrange: Create parent directory with old flat structure const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); const oldFile = 'session_sess-12345678-1234-4abc-89de-123456789012.json'; fs.writeFileSync(path.join(sessionsDir, oldFile), JSON.stringify({ test: 'data' })); // Act: Create service without subdirectory const flatService = new FileStorageService(baseDir); // Assert: Files should remain in flat structure expect(fs.existsSync(path.join(sessionsDir, oldFile))).toBe(true); flatService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should handle migration errors gracefully', () => { // Arrange: Create parent directory const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create a file that will cause migration issues const problemFile = 'session_test.json'; const problemPath = path.join(sessionsDir, problemFile); fs.writeFileSync(problemPath, JSON.stringify({ test: 'data' })); // Make the file read-only to cause rename error try { fs.chmodSync(problemPath, 0o444); // Act: Create service - should not throw expect(() => new FileStorageService(baseDir, 'server')).not.toThrow(); // Cleanup fs.chmodSync(problemPath, 0o644); } catch (_error) { // On some systems, chmod might not work as expected expect(true).toBe(true); } fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should handle independent migrations for server and client', () => { // Arrange: Create both legacy directory structures const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); const clientSessionsDir = path.join(baseDir, 'clientSessions'); fs.mkdirSync(sessionsDir, { recursive: true }); fs.mkdirSync(clientSessionsDir, { recursive: true }); // Create server files in sessions/ const serverFiles = [ 'session_sess-12345678-1234-4abc-89de-123456789012.json', 'auth_code_code-87654321-4321-4def-89ab-210987654321.json', ]; // Create client files in clientSessions/ const clientFiles = ['oauth_test-server.json', 'cli_client-123.json']; for (const file of serverFiles) { fs.writeFileSync(path.join(sessionsDir, file), JSON.stringify({ test: 'server-data' })); } for (const file of clientFiles) { fs.writeFileSync(path.join(clientSessionsDir, file), JSON.stringify({ test: 'client-data' })); } // Act: Create server service first const serverService = new FileStorageService(baseDir, 'server'); // Assert: Server files migrated, client files untouched const serverSubdir = path.join(sessionsDir, 'server'); for (const file of serverFiles) { expect(fs.existsSync(path.join(serverSubdir, file))).toBe(true); expect(fs.existsSync(path.join(sessionsDir, file))).toBe(false); } for (const file of clientFiles) { expect(fs.existsSync(path.join(clientSessionsDir, file))).toBe(true); // Still in clientSessions/ } // Assert: Server migration flag created const serverFlagPath = path.join(sessionsDir, '.migrated-to-server'); expect(fs.existsSync(serverFlagPath)).toBe(true); // Act: Create client service const clientService = new FileStorageService(baseDir, 'client'); // Assert: Client files migrated independently const clientSubdir = path.join(sessionsDir, 'client'); for (const file of clientFiles) { expect(fs.existsSync(path.join(clientSubdir, file))).toBe(true); expect(fs.existsSync(path.join(clientSessionsDir, file))).toBe(false); } // Assert: Client migration flag created const clientFlagPath = path.join(clientSessionsDir, '.migrated-to-client'); expect(fs.existsSync(clientFlagPath)).toBe(true); serverService.shutdown(); clientService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should skip migration when no old files exist', () => { // Arrange: Create empty parent directory const baseDir = path.join(tmpdir(), `migration-test-${Date.now()}`); // Act & Assert: Should not throw expect(() => new FileStorageService(baseDir, 'server')).not.toThrow(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should create migration flag after successful migration', () => { // Arrange: Create parent directory with old flat structure const baseDir = path.join(tmpdir(), `migration-flag-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create old session files const oldFiles = ['session_sess-12345678-1234-4abc-89de-123456789012.json']; for (const file of oldFiles) { fs.writeFileSync(path.join(sessionsDir, file), JSON.stringify({ test: 'data' })); } // Act: Create service with 'server' subdirectory const serverService = new FileStorageService(baseDir, 'server'); // Assert: Migration flag should be created const migrationFlagPath = path.join(sessionsDir, '.migrated-to-server'); expect(fs.existsSync(migrationFlagPath)).toBe(true); // Verify flag content const flagContent = JSON.parse(fs.readFileSync(migrationFlagPath, 'utf8')); expect(flagContent.migrated).toBe(true); expect(flagContent.targetSubDir).toBe('server'); expect(typeof flagContent.timestamp).toBe('number'); serverService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should skip migration when flag already exists', () => { // Arrange: Create parent directory with migration flag const baseDir = path.join(tmpdir(), `migration-flag-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create migration flag const migrationFlagPath = path.join(sessionsDir, '.migrated-to-server'); fs.writeFileSync( migrationFlagPath, JSON.stringify({ migrated: true, targetSubDir: 'server', timestamp: Date.now() }), ); // Create old files that should NOT be migrated const oldFile = 'session_sess-12345678-1234-4abc-89de-123456789012.json'; fs.writeFileSync(path.join(sessionsDir, oldFile), JSON.stringify({ test: 'data' })); // Act: Create service with 'server' subdirectory const serverService = new FileStorageService(baseDir, 'server'); // Assert: File should remain in parent directory (not migrated) expect(fs.existsSync(path.join(sessionsDir, oldFile))).toBe(true); expect(fs.existsSync(path.join(sessionsDir, 'server', oldFile))).toBe(false); serverService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should create migration flag even when no files to migrate', () => { // Arrange: Create empty parent directory const baseDir = path.join(tmpdir(), `migration-flag-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Act: Create service with 'server' subdirectory const serverService = new FileStorageService(baseDir, 'server'); // Assert: Migration flag should still be created const migrationFlagPath = path.join(sessionsDir, '.migrated-to-server'); expect(fs.existsSync(migrationFlagPath)).toBe(true); serverService.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should handle migration flag creation errors gracefully', () => { // Arrange: Create parent directory and make it read-only const baseDir = path.join(tmpdir(), `migration-flag-test-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); fs.mkdirSync(sessionsDir, { recursive: true }); // Create old files const oldFile = 'session_sess-12345678-1234-4abc-89de-123456789012.json'; fs.writeFileSync(path.join(sessionsDir, oldFile), JSON.stringify({ test: 'data' })); try { // Make directory read-only to cause flag creation error fs.chmodSync(sessionsDir, 0o444); // Act: Create service - should not throw despite flag creation failure expect(() => new FileStorageService(baseDir, 'server')).not.toThrow(); // Assert: Files should still be migrated even if flag creation fails expect(fs.existsSync(path.join(sessionsDir, 'server', oldFile))).toBe(true); } catch (_error) { // On some systems, chmod might not work as expected expect(true).toBe(true); } finally { // Restore permissions for cleanup try { fs.chmodSync(sessionsDir, 0o755); } catch { // Ignore errors during cleanup } fs.rmSync(baseDir, { recursive: true, force: true }); } }); }); describe('Configuration Constants Integration', () => { it('should use STORAGE_SUBDIRS constants for subdirectory detection', () => { // Test that all subdirectories from STORAGE_SUBDIRS are recognized const baseDir = path.join(tmpdir(), `config-test-${Date.now()}`); for (const subdir of Object.values(STORAGE_SUBDIRS)) { const service = new FileStorageService(baseDir, subdir as string); const expectedPath = path.join(baseDir, 'sessions', subdir as string); expect(service.getStorageDir()).toBe(expectedPath); service.shutdown(); } fs.rmSync(baseDir, { recursive: true, force: true }); }); it('should use FILE_PREFIX_MAPPING for migration logic', () => { // Test that all prefixes from FILE_PREFIX_MAPPING are handled correctly // Test migration for each subdirectory type with separate base directories for (const [category, subdir] of Object.entries(STORAGE_SUBDIRS)) { const baseDir = path.join(tmpdir(), `config-test-${category}-${Date.now()}`); const sessionsDir = path.join(baseDir, 'sessions'); const clientSessionsDir = path.join(baseDir, 'clientSessions'); // Create source directories fs.mkdirSync(sessionsDir, { recursive: true }); fs.mkdirSync(clientSessionsDir, { recursive: true }); // Create test files for current category in appropriate source directory const testFiles: string[] = []; const prefixes = FILE_PREFIX_MAPPING[category as keyof typeof FILE_PREFIX_MAPPING] as readonly string[]; for (const prefix of prefixes) { const fileName = `${prefix}test-${Date.now()}.json`; testFiles.push(fileName); // Place files in appropriate source directory based on category if (category === 'CLIENT') { fs.writeFileSync(path.join(clientSessionsDir, fileName), JSON.stringify({ test: 'data' })); } else { // SERVER and TRANSPORT files go in sessions/ directory fs.writeFileSync(path.join(sessionsDir, fileName), JSON.stringify({ test: 'data' })); } } const service = new FileStorageService(baseDir, subdir as string); // Check that files with matching prefixes were migrated for (const file of testFiles) { if (category === 'TRANSPORT') { // TRANSPORT files are not migrated (new feature) // They should remain in the source directory expect(fs.existsSync(path.join(sessionsDir, file))).toBe(true); expect(fs.existsSync(path.join(sessionsDir, subdir as string, file))).toBe(false); } else { // SERVER and CLIENT files should be migrated expect(fs.existsSync(path.join(sessionsDir, subdir as string, file))).toBe(true); // Check that files were removed from source directory if (category === 'CLIENT') { expect(fs.existsSync(path.join(clientSessionsDir, file))).toBe(false); } else { expect(fs.existsSync(path.join(sessionsDir, file))).toBe(false); } } } service.shutdown(); fs.rmSync(baseDir, { recursive: true, force: true }); } }); }); describe('Helper Method Integration', () => { it('should validate IDs correctly with refactored helper method', () => { // Test that the refactored isValidId method works with extractUuidPart helper const validIds = [ 'sess-12345678-1234-4abc-89de-123456789012', 'code-87654321-4321-4def-89ab-210987654321', 'stream-11111111-1111-4111-8111-111111111111', ]; const invalidIds = ['sess-invalid-uuid', 'code-short', 'unknown-prefix-12345678-1234-4abc-89de-123456789012']; for (const id of validIds) { expect(() => service.getFilePath('test_', id)).not.toThrow(); } for (const id of invalidIds) { expect(() => service.getFilePath('test_', id)).toThrow(); } }); }); });

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/1mcp-app/agent'

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