import { AgentDefinitionService } from '@services/agentDefinition'; import { AgentDefinition } from '@services/agentDefinition/interface'; import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json'; import type { IAgentInstanceService } from '@services/agentInstance/interface'; import { container } from '@services/container'; import type { IDatabaseService } from '@services/database/interface'; import { AgentDefinitionEntity } from '@services/database/schema/agent'; import serviceIdentifier from '@services/serviceIdentifier'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; describe('AgentDefinitionService getAgentDefs integration', () => { let agentDefinitionService: AgentDefinitionService; beforeEach(async () => { vi.clearAllMocks(); // Ensure DatabaseService is initialized with all schemas const databaseService = container.get(serviceIdentifier.Database); await databaseService.initializeForApp(); // Use globally bound AgentDefinitionService (configured in src/__tests__/setup-vitest.ts) agentDefinitionService = container.get(serviceIdentifier.AgentDefinition); const serviceWithPrivate = agentDefinitionService as unknown as { agentInstanceService: IAgentInstanceService; wikiService: { wikiOperationInServer: (...args: unknown[]) => Promise }; workspaceService: { getWorkspacesAsList: () => Promise }; }; // Manually inject agentInstanceService to avoid lazyInject issues serviceWithPrivate.agentInstanceService = container.get(serviceIdentifier.AgentInstance); // Mock wiki and workspace services for template testing serviceWithPrivate.wikiService = { wikiOperationInServer: vi.fn().mockResolvedValue([]), }; serviceWithPrivate.workspaceService = { getWorkspacesAsList: vi.fn().mockResolvedValue([]), }; // Initialize the service to set up dataSource and repositories using real database service await agentDefinitionService.initialize(); // Clean up any existing test data from the real database try { const realDatabaseService = container.get(serviceIdentifier.Database); const realDataSource = await realDatabaseService.getDatabase('agent'); const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity); await agentDefRepo.clear(); } catch { // Ignore errors during cleanup } }); afterEach(async () => { // Clean up is handled automatically by beforeEach for each test }); it('should initialize default agents on first run when database is empty', async () => { // Get the real database repository that the service uses const realDatabaseService = container.get(serviceIdentifier.Database); const realDataSource = await realDatabaseService.getDatabase('agent'); const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity); // Ensure database is empty before initialization await agentDefRepo.clear(); expect(await agentDefRepo.count()).toBe(0); // Create a fresh service instance to test initialization behavior const freshService = new AgentDefinitionService(); const freshServiceWithPrivate = freshService as unknown as { databaseService: IDatabaseService; agentInstanceService: IAgentInstanceService; agentBrowserService: { initialize: () => Promise }; }; freshServiceWithPrivate.databaseService = container.get(serviceIdentifier.Database); freshServiceWithPrivate.agentInstanceService = container.get(serviceIdentifier.AgentInstance); // Mock agentBrowserService for this test freshServiceWithPrivate.agentBrowserService = { initialize: async () => {}, }; // Initialize the fresh service - this should create default agents await freshService.initialize(); // Check that default agents were created in database const count = await agentDefRepo.count(); expect(count).toBeGreaterThan(0); // Verify that the agents have complete data const defs = await freshService.getAgentDefs(); expect(defs.length).toBeGreaterThan(0); const exampleAgent = defs.find(d => d.id === (defaultAgents as AgentDefinition[])[0].id); expect(exampleAgent).toBeDefined(); expect(exampleAgent!.name).toBeDefined(); expect(exampleAgent!.handlerID).toBeDefined(); expect(exampleAgent!.handlerConfig).toBeDefined(); expect(typeof exampleAgent!.handlerConfig).toBe('object'); }); it('should return only database data without fallback to defaultAgents', async () => { // Get the real database repository that the service uses const realDatabaseService = container.get(serviceIdentifier.Database); const realDataSource = await realDatabaseService.getDatabase('agent'); const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity); // Save only minimal record (id only) to test new behavior const example = (defaultAgents as AgentDefinition[])[0]; await agentDefRepo.save({ id: example.id, }); const defs = await agentDefinitionService.getAgentDefs(); const found = defs.find(d => d.id === example.id); expect(found).toBeDefined(); // With new behavior, only id should be present, other fields should be undefined or empty expect(found!.id).toBe(example.id); expect(found!.handlerID).toBeUndefined(); expect(found!.name).toBeUndefined(); expect(found!.description).toBeUndefined(); expect(found!.avatarUrl).toBeUndefined(); expect(found!.handlerConfig).toEqual({}); expect(found!.aiApiConfig).toBeUndefined(); expect(found!.agentTools).toBeUndefined(); }); it('should have only id field populated when directly querying database entity for build-in agent', async () => { // Get the real database repository that the service uses const realDatabaseService = container.get(serviceIdentifier.Database); const realDataSource = await realDatabaseService.getDatabase('agent'); const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity); // Save only minimal record (id only) as per new behavior const example = (defaultAgents as AgentDefinition[])[0]; await agentDefRepo.save({ id: example.id, }); // Directly query the database entity const entity = await agentDefRepo.findOne({ where: { id: example.id }, }); expect(entity).toBeDefined(); expect(entity!.id).toBe(example.id); // Other fields should be null/undefined since we only saved id expect(entity!.name).toBeNull(); expect(entity!.description).toBeNull(); expect(entity!.avatarUrl).toBeNull(); expect(entity!.handlerID).toBeNull(); expect(entity!.handlerConfig).toBeNull(); expect(entity!.aiApiConfig).toBeNull(); expect(entity!.agentTools).toBeNull(); }); it('should return default agents as templates from getAgentTemplates', async () => { const templates = await agentDefinitionService.getAgentTemplates(); // Should include all default agents expect(templates.length).toBe((defaultAgents as AgentDefinition[]).length); // Check that template has complete data from defaultAgents.json const exampleTemplate = templates.find(t => t.id === (defaultAgents as AgentDefinition[])[0].id); expect(exampleTemplate).toBeDefined(); expect(exampleTemplate!.name).toBeDefined(); expect(exampleTemplate!.handlerID).toBeDefined(); expect(exampleTemplate!.handlerConfig).toBeDefined(); expect(typeof exampleTemplate!.handlerConfig).toBe('object'); }); it('should not throw when searchName filtering is requested (client-side filtering expected)', async () => { // getAgentTemplates no longer accepts searchName; client is expected to filter results. const templates = await agentDefinitionService.getAgentTemplates(); expect(Array.isArray(templates)).toBe(true); }); it('should handle wiki service errors gracefully in getAgentTemplates', async () => { const serviceWithPrivate = agentDefinitionService as unknown as { wikiService: { wikiOperationInServer: (...args: unknown[]) => Promise }; }; // Mock wiki service to throw error serviceWithPrivate.wikiService.wikiOperationInServer = vi.fn().mockRejectedValue(new Error('Wiki error')); // Should still return default agents and not throw const templates = await agentDefinitionService.getAgentTemplates(); expect(templates.length).toBe((defaultAgents as AgentDefinition[]).length); }); });