UNPKG

@oliverlessa/gemini-agent-lib

Version:

Uma biblioteca NodeJS para criar agentes de IA com Gemini LLM

157 lines (133 loc) 7.46 kB
const assert = require('assert'); const SemanticMemoryTool = require('../lib/tools/semantic-memory-tool'); const SemanticMemory = require('../lib/memory/semantic-memory'); const FunctionDeclarationSchemaType = require('../lib/function-declaration-schema-type'); // Mock da classe SemanticMemory para testes class MockSemanticMemory extends SemanticMemory { constructor() { super(); // Chama construtor da classe base abstrata (necessário para instanceof) this.initCalled = false; this.searchCalledWith = null; this.shouldThrowOnInit = false; this.shouldThrowOnSearch = false; this.searchResults = []; } async init() { this.initCalled = true; if (this.shouldThrowOnInit) { throw new Error("Mock init error"); } } async search(query, k, filter) { this.searchCalledWith = { query, k, filter }; if (this.shouldThrowOnSearch) { throw new Error("Mock search error"); } // Retorna cópia para evitar modificação externa return JSON.parse(JSON.stringify(this.searchResults)); } // Métodos não usados no teste, mas necessários para estender a classe abstrata async add(documents) { throw new Error("Not implemented in mock"); } async delete(ids) { throw new Error("Not implemented in mock"); } } describe('SemanticMemoryTool', () => { let mockMemory; let tool; beforeEach(() => { // Cria uma nova instância mock antes de cada teste mockMemory = new MockSemanticMemory(); tool = new SemanticMemoryTool({ semanticMemory: mockMemory }); }); describe('constructor', () => { it('deve instanciar corretamente com uma SemanticMemory válida', () => { assert.ok(tool instanceof SemanticMemoryTool); }); it('deve lançar erro se semanticMemory não for fornecida', () => { assert.throws(() => { new SemanticMemoryTool({}); }, /A configuração 'semanticMemory' é obrigatória/); }); it('deve lançar erro se semanticMemory não for instância de SemanticMemory', () => { assert.throws(() => { new SemanticMemoryTool({ semanticMemory: {} }); // Passando objeto vazio }, /deve ser uma instância de SemanticMemory/); }); }); describe('getToolDeclaration', () => { it('deve retornar a declaração da ferramenta no formato correto', () => { const declaration = tool.getToolDeclaration(); assert.strictEqual(declaration.name, 'semantic_memory_search'); assert.ok(declaration.description); assert.deepStrictEqual(declaration.parameters.type, FunctionDeclarationSchemaType.OBJECT); assert.ok(declaration.parameters.properties.query); assert.strictEqual(declaration.parameters.properties.query.type, FunctionDeclarationSchemaType.STRING); assert.ok(declaration.parameters.properties.k); assert.strictEqual(declaration.parameters.properties.k.type, FunctionDeclarationSchemaType.NUMBER); assert.ok(declaration.parameters.properties.filter); assert.strictEqual(declaration.parameters.properties.filter.type, FunctionDeclarationSchemaType.OBJECT); assert.deepStrictEqual(declaration.parameters.required, ['query']); }); }); describe('execute', () => { it('deve retornar erro se query não for fornecida', async () => { const result = await tool.execute({}); // Sem query assert.match(result, /Erro: O parâmetro 'query' é obrigatório/); }); it('deve chamar search na memória com os parâmetros corretos e formatar resultados', async () => { mockMemory.searchResults = [ { id: 'doc1', content: 'Conteúdo do doc 1', metadata: { source: 'file1.txt' }, score: 0.9 }, { id: 'doc2', content: 'Conteúdo do doc 2', metadata: {}, score: 0.85 } // Sem metadados ]; const query = "teste de busca"; const k = 2; const filter = { type: 'test' }; const result = await tool.execute({ query, k, filter }); assert.deepStrictEqual(mockMemory.searchCalledWith, { query, k, filter }); assert.match(result, /Resultados da busca na memória semântica para "teste de busca":/); assert.match(result, /Resultado 1: \[Score: 0\.9000\]\nConteúdo: Conteúdo do doc 1 \(Metadados: {"source":"file1\.txt"}\)/); assert.match(result, /---\n\nResultado 2: \[Score: 0\.8500\]\nConteúdo: Conteúdo do doc 2/); // Verifica que não adiciona metadados vazios }); it('deve usar k padrão = 3 se não for fornecido', async () => { const query = "teste k padrão"; await tool.execute({ query }); assert.strictEqual(mockMemory.searchCalledWith.k, 3); }); it('deve retornar mensagem específica se nenhum resultado for encontrado', async () => { mockMemory.searchResults = []; // Nenhum resultado const result = await tool.execute({ query: "busca sem resultado" }); assert.match(result, /Nenhuma informação relevante encontrada/); }); it('deve retornar mensagem de erro se search falhar', async () => { mockMemory.shouldThrowOnSearch = true; const result = await tool.execute({ query: "busca com erro" }); assert.match(result, /Erro ao realizar a busca na memória semântica/); // Poderíamos também verificar se console.error foi chamado, se tivéssemos um spy/stub }); it('deve chamar init da memória apenas na primeira execução (lazy initialization)', async () => { assert.strictEqual(mockMemory.initCalled, false); // Não chamado ainda // Primeira execução await tool.execute({ query: "primeira busca" }); assert.strictEqual(mockMemory.initCalled, true); // Deve ter chamado init assert.ok(mockMemory.searchCalledWith); // E search // Resetar para verificar a segunda chamada mockMemory.initCalled = false; // Resetar flag do mock manualmente para o teste mockMemory.searchCalledWith = null; // Segunda execução await tool.execute({ query: "segunda busca" }); assert.strictEqual(mockMemory.initCalled, false); // NÃO deve ter chamado init novamente assert.ok(mockMemory.searchCalledWith); // Mas deve ter chamado search }); it('deve retornar erro se init falhar', async () => { mockMemory.shouldThrowOnInit = true; const result = await tool.execute({ query: "busca com erro no init" }); assert.match(result, /Erro ao realizar a busca na memória semântica/); // A mensagem de erro genérica é retornada assert.strictEqual(mockMemory.initCalled, true); // Tentou chamar init assert.strictEqual(mockMemory.searchCalledWith, null); // Não chegou a chamar search }); it('deve passar filtro undefined se não for fornecido', async () => { const query = "teste sem filtro"; await tool.execute({ query }); assert.strictEqual(mockMemory.searchCalledWith.filter, undefined); }); }); });