Skip to main content
Glama
mkXultra
by mkXultra
room-lifecycle.test.ts13.1 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { vol } from 'memfs'; import type { AgentProfile } from '../../../src/types/entities'; // Mock the getDataDirectory function to return our test directory vi.mock('../../../src/utils/dataDir', () => ({ getDataDirectory: () => process.env.AGENT_COMM_DATA_DIR || '/test-data', DEFAULT_HOME_DATA_DIR: '/home/.agent-communication-mcp', LEGACY_DATA_DIR: './data' })); // ファイルシステムをモック vi.mock('fs', () => ({ ...vol, promises: vol.promises, existsSync: vol.existsSync.bind(vol), accessSync: vol.accessSync.bind(vol), constants: { W_OK: 2 } })); vi.mock('fs/promises', () => ({ default: vol.promises, ...vol.promises })); describe('Room Lifecycle Integration Tests', () => { let RoomsAPI: any; const testDataDir = '/integration-test-data'; beforeEach(async () => { // Set environment variable to use test directory process.env.AGENT_COMM_DATA_DIR = testDataDir; // ファイルシステムをリセット vol.reset(); vol.fromJSON({ [`${testDataDir}/rooms.json`]: JSON.stringify({ rooms: {} }) }); // Import RoomsAPI dynamically after setting up mocks const roomsModule = await import('../../../src/features/rooms'); RoomsAPI = roomsModule.RoomsAPI; }); afterEach(() => { vol.reset(); // Clean up environment variable delete process.env.AGENT_COMM_DATA_DIR; }); describe('Complete room lifecycle', () => { it('should handle create → enter → leave → cleanup flow', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'lifecycle-room'; const description = 'Lifecycle test room'; // 1. ルーム作成 const createResult = await roomsAPI.createRoom(roomName, description); expect(createResult.success).toBe(true); expect(createResult.roomName).toBe(roomName); // 2. ルーム存在確認 const exists = await roomsAPI.roomExists(roomName); expect(exists).toBe(true); // 3. 初期状態のルーム情報確認 const roomData = await roomsAPI.getRoomData(roomName); expect(roomData).toMatchObject({ description, messageCount: 0, userCount: 0 }); // 4. エージェント1入室 const agent1Profile: AgentProfile = { role: 'coordinator', description: 'Test coordinator', capabilities: ['planning', 'coordination'] }; const enterResult1 = await roomsAPI.enterRoom('agent1', roomName, agent1Profile); expect(enterResult1.success).toBe(true); // 5. エージェント2入室 const agent2Profile: AgentProfile = { role: 'worker', capabilities: ['execution'] }; const enterResult2 = await roomsAPI.enterRoom('agent2', roomName, agent2Profile); expect(enterResult2.success).toBe(true); // 6. ルーム内ユーザー確認 const users = await roomsAPI.getRoomUsers(roomName); expect(users.roomName).toBe(roomName); expect(users.users).toHaveLength(2); expect(users.onlineCount).toBe(2); const agent1User = users.users.find(u => u.name === 'agent1'); expect(agent1User).toMatchObject({ name: 'agent1', status: 'online', messageCount: 0, profile: agent1Profile }); // 7. ルーム一覧確認(参加状況付き) const roomsWithAgent = await roomsAPI.listRooms('agent1'); expect(roomsWithAgent.rooms).toHaveLength(1); expect(roomsWithAgent.rooms[0]).toMatchObject({ name: roomName, description, userCount: 2, messageCount: 0, isJoined: true }); // 8. 統計情報確認 const onlineCount = await roomsAPI.getOnlineUsersCount(roomName); expect(onlineCount).toBe(2); const totalRooms = await roomsAPI.getRoomCount(); expect(totalRooms).toBe(1); // 9. エージェント1退室 const leaveResult1 = await roomsAPI.leaveRoom('agent1', roomName); expect(leaveResult1.success).toBe(true); // 10. 退室後の状態確認 const usersAfterLeave = await roomsAPI.getRoomUsers(roomName); expect(usersAfterLeave.onlineCount).toBe(1); const agent1AfterLeave = usersAfterLeave.users.find(u => u.name === 'agent1'); expect(agent1AfterLeave?.status).toBe('offline'); // 11. エージェント2も退室 await roomsAPI.leaveRoom('agent2', roomName); // 12. 最終状態確認 const finalUsers = await roomsAPI.getRoomUsers(roomName); expect(finalUsers.onlineCount).toBe(0); expect(finalUsers.users.every(u => u.status === 'offline')).toBe(true); }); it('should handle multiple rooms with overlapping users', async () => { const roomsAPI = new RoomsAPI(testDataDir); // 複数ルーム作成 await roomsAPI.createRoom('room-a', 'Room A'); await roomsAPI.createRoom('room-b', 'Room B'); await roomsAPI.createRoom('room-c', 'Room C'); // エージェントを異なるルームに配置 await roomsAPI.enterRoom('agent1', 'room-a', { role: 'coordinator' }); await roomsAPI.enterRoom('agent1', 'room-b', { role: 'coordinator' }); await roomsAPI.enterRoom('agent2', 'room-a', { role: 'worker' }); await roomsAPI.enterRoom('agent2', 'room-c', { role: 'worker' }); await roomsAPI.enterRoom('agent3', 'room-b', { role: 'observer' }); // 各ルームの状態確認 const roomAUsers = await roomsAPI.getRoomUsers('room-a'); expect(roomAUsers.onlineCount).toBe(2); // agent1, agent2 const roomBUsers = await roomsAPI.getRoomUsers('room-b'); expect(roomBUsers.onlineCount).toBe(2); // agent1, agent3 const roomCUsers = await roomsAPI.getRoomUsers('room-c'); expect(roomCUsers.onlineCount).toBe(1); // agent2 // agent1のルーム参加状況確認 const agent1Rooms = await roomsAPI.listRooms('agent1'); const joinedRooms = agent1Rooms.rooms.filter(r => r.isJoined); expect(joinedRooms.map(r => r.name).sort()).toEqual(['room-a', 'room-b']); // 全体統計 const totalRooms = await roomsAPI.getRoomCount(); expect(totalRooms).toBe(3); const allRoomNames = await roomsAPI.getAllRoomNames(); expect(allRoomNames.sort()).toEqual(['room-a', 'room-b', 'room-c']); }); it('should handle error scenarios gracefully', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'error-test-room'; // 存在しないルームへの入室 await expect( roomsAPI.enterRoom('agent1', 'non-existing-room') ).rejects.toThrow(); // ルーム作成 await roomsAPI.createRoom(roomName); // 重複ルーム作成 await expect( roomsAPI.createRoom(roomName) ).rejects.toThrow(); // 入室していないエージェントの退室 await expect( roomsAPI.leaveRoom('non-existing-agent', roomName) ).rejects.toThrow(); // 存在しないルームのユーザー一覧 await expect( roomsAPI.getRoomUsers('non-existing-room') ).rejects.toThrow(); // 不正な名前でのルーム作成 await expect( roomsAPI.createRoom('invalid room name') ).rejects.toThrow(); }); it('should maintain data consistency across operations', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'consistency-room'; // ルーム作成 await roomsAPI.createRoom(roomName, 'Consistency test'); // 複数エージェント入室 const agents = ['agent1', 'agent2', 'agent3', 'agent4', 'agent5']; for (const agent of agents) { await roomsAPI.enterRoom(agent, roomName, { role: 'tester', description: `Test agent ${agent}` }); } // 初期状態確認 let users = await roomsAPI.getRoomUsers(roomName); expect(users.onlineCount).toBe(5); // 一部エージェント退室 await roomsAPI.leaveRoom('agent2', roomName); await roomsAPI.leaveRoom('agent4', roomName); // 中間状態確認 users = await roomsAPI.getRoomUsers(roomName); expect(users.onlineCount).toBe(3); expect(users.users).toHaveLength(5); // オフラインユーザーも含む // ルーム一覧での数値一致確認 const roomsList = await roomsAPI.listRooms(); const testRoom = roomsList.rooms.find(r => r.name === roomName); expect(testRoom?.userCount).toBe(3); // オンラインユーザー数 // 残りエージェント退室 await roomsAPI.leaveRoom('agent1', roomName); await roomsAPI.leaveRoom('agent3', roomName); await roomsAPI.leaveRoom('agent5', roomName); // 最終状態確認 users = await roomsAPI.getRoomUsers(roomName); expect(users.onlineCount).toBe(0); const finalRoomsList = await roomsAPI.listRooms(); const finalTestRoom = finalRoomsList.rooms.find(r => r.name === roomName); expect(finalTestRoom?.userCount).toBe(0); }); it('should handle offline user cleanup', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'cleanup-room'; await roomsAPI.createRoom(roomName); // エージェント入室・退室 await roomsAPI.enterRoom('temp-agent1', roomName); await roomsAPI.enterRoom('temp-agent2', roomName); await roomsAPI.leaveRoom('temp-agent1', roomName); await roomsAPI.leaveRoom('temp-agent2', roomName); // 初期状態:オフラインユーザーが存在 let users = await roomsAPI.getRoomUsers(roomName); expect(users.users).toHaveLength(2); expect(users.onlineCount).toBe(0); // クリーンアップ実行(即座にクリーンアップ) const cleanedCount = await roomsAPI.cleanupOfflineUsers(roomName, 0); expect(cleanedCount).toBe(2); // クリーンアップ後:ユーザーが削除されている users = await roomsAPI.getRoomUsers(roomName); expect(users.users).toHaveLength(0); expect(users.onlineCount).toBe(0); }); }); describe('Complex scenarios', () => { it('should handle rapid enter/leave operations', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'rapid-ops-room'; await roomsAPI.createRoom(roomName); // 短時間で多数の操作を実行 const operations = []; // 入室操作 for (let i = 1; i <= 10; i++) { operations.push( roomsAPI.enterRoom(`agent${i}`, roomName, { role: 'rapid-tester' }) ); } // すべての入室操作を並行実行 const enterResults = await Promise.all(operations); expect(enterResults.every(r => r.success)).toBe(true); // 状態確認 let users = await roomsAPI.getRoomUsers(roomName); expect(users.onlineCount).toBe(10); // 退室操作 const leaveOperations = []; for (let i = 1; i <= 10; i++) { leaveOperations.push( roomsAPI.leaveRoom(`agent${i}`, roomName) ); } const leaveResults = await Promise.all(leaveOperations); expect(leaveResults.every(r => r.success)).toBe(true); // 最終状態確認 users = await roomsAPI.getRoomUsers(roomName); expect(users.onlineCount).toBe(0); }); it('should handle agent re-entering same room', async () => { const roomsAPI = new RoomsAPI(testDataDir); const roomName = 'reentry-room'; const agentName = 'reentry-agent'; await roomsAPI.createRoom(roomName); // 初回入室 let result = await roomsAPI.enterRoom(agentName, roomName, { role: 'initial', description: 'Initial entry' }); expect(result.success).toBe(true); // 状態確認 let users = await roomsAPI.getRoomUsers(roomName); let agent = users.users.find(u => u.name === agentName); expect(agent?.status).toBe('online'); expect(agent?.profile?.role).toBe('initial'); // 退室 await roomsAPI.leaveRoom(agentName, roomName); // オフライン確認 users = await roomsAPI.getRoomUsers(roomName); agent = users.users.find(u => u.name === agentName); expect(agent?.status).toBe('offline'); // 再入室(異なるプロフィール) result = await roomsAPI.enterRoom(agentName, roomName, { role: 'updated', description: 'Updated entry', capabilities: ['new-skill'] }); expect(result.success).toBe(true); // 再入室後の状態確認 users = await roomsAPI.getRoomUsers(roomName); agent = users.users.find(u => u.name === agentName); expect(agent?.status).toBe('online'); expect(agent?.profile?.role).toBe('updated'); expect(agent?.profile?.capabilities).toEqual(['new-skill']); }); }); });

Latest Blog Posts

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/mkXultra/agent-communication-mcp'

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