Files
gemini-cli/packages/a2a-server/src/http/endpoints.test.ts
2025-09-10 16:08:58 +00:00

163 lines
4.9 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
import request from 'supertest';
import type express from 'express';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import type { Server } from 'node:http';
import type { AddressInfo } from 'node:net';
import { createApp, updateCoderAgentCardUrl } from './app.js';
import type { TaskMetadata } from '../types.js';
import { createMockConfig } from '../utils/testing_utils.js';
import type { Config } from '@google/gemini-cli-core';
// Mock the logger to avoid polluting test output
// Comment out to help debug
vi.mock('../utils/logger.js', () => ({
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
// Mock Task.create to avoid its complex setup
vi.mock('../agent/task.js', () => {
class MockTask {
id: string;
contextId: string;
taskState = 'submitted';
config = {
getContentGeneratorConfig: vi
.fn()
.mockReturnValue({ model: 'gemini-pro' }),
};
geminiClient = {
initialize: vi.fn().mockResolvedValue(undefined),
};
constructor(id: string, contextId: string) {
this.id = id;
this.contextId = contextId;
}
static create = vi
.fn()
.mockImplementation((id, contextId) =>
Promise.resolve(new MockTask(id, contextId)),
);
getMetadata = vi.fn().mockImplementation(async () => ({
id: this.id,
contextId: this.contextId,
taskState: this.taskState,
model: 'gemini-pro',
mcpServers: [],
availableTools: [],
}));
}
return { Task: MockTask };
});
vi.mock('../config/config.js', async () => {
const actual = await vi.importActual('../config/config.js');
return {
...actual,
loadConfig: vi
.fn()
.mockImplementation(async () => createMockConfig({}) as Config),
};
});
describe('Agent Server Endpoints', () => {
let app: express.Express;
let server: Server;
let testWorkspace: string;
const createTask = (contextId: string) =>
request(app)
.post('/tasks')
.send({
contextId,
agentSettings: {
kind: 'agent-settings',
workspacePath: testWorkspace,
},
})
.set('Content-Type', 'application/json');
beforeAll(async () => {
// Create a unique temporary directory for the workspace to avoid conflicts
testWorkspace = fs.mkdtempSync(
path.join(os.tmpdir(), 'gemini-agent-test-'),
);
app = await createApp();
await new Promise<void>((resolve) => {
server = app.listen(0, () => {
const port = (server.address() as AddressInfo).port;
updateCoderAgentCardUrl(port);
resolve();
});
});
});
afterAll(async () => {
if (server) {
await new Promise<void>((resolve, reject) => {
server.close((err) => {
if (err) return reject(err);
resolve();
});
});
}
if (testWorkspace) {
try {
fs.rmSync(testWorkspace, { recursive: true, force: true });
} catch (e) {
console.warn(`Could not remove temp dir '${testWorkspace}':`, e);
}
}
});
it('should create a new task via POST /tasks', async () => {
const response = await createTask('test-context');
expect(response.status).toBe(201);
expect(response.body).toBeTypeOf('string'); // Should return the task ID
}, 7000);
it('should get metadata for a specific task via GET /tasks/:taskId/metadata', async () => {
const createResponse = await createTask('test-context-2');
const taskId = createResponse.body;
const response = await request(app).get(`/tasks/${taskId}/metadata`);
expect(response.status).toBe(200);
expect(response.body.metadata.id).toBe(taskId);
}, 6000);
it('should get metadata for all tasks via GET /tasks/metadata', async () => {
const createResponse = await createTask('test-context-3');
const taskId = createResponse.body;
const response = await request(app).get('/tasks/metadata');
expect(response.status).toBe(200);
expect(Array.isArray(response.body)).toBe(true);
expect(response.body.length).toBeGreaterThan(0);
const taskMetadata = response.body.find(
(m: TaskMetadata) => m.id === taskId,
);
expect(taskMetadata).toBeDefined();
});
it('should return 404 for a non-existent task', async () => {
const response = await request(app).get('/tasks/fake-task/metadata');
expect(response.status).toBe(404);
});
it('should return agent metadata via GET /.well-known/agent-card.json', async () => {
const response = await request(app).get('/.well-known/agent-card.json');
const port = (server.address() as AddressInfo).port;
expect(response.status).toBe(200);
expect(response.body.name).toBe('Gemini SDLC Agent');
expect(response.body.url).toBe(`http://localhost:${port}/`);
});
});