Download packages/a2a-server/src/http/endpoints.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 7.3 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/a2a-server/src/http/endpoints.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/a2a-server/src/http/endpoints.test.ts
-
curl -L -o endpoints.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/a2a-server/src/http/endpoints.test.ts
7.3 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { | |
| describe, | |
| it, | |
| expect, | |
| beforeAll, | |
| afterAll, | |
| beforeEach, | |
| afterEach, | |
| 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 { debugLogger, type Config } from '@google/gemini-cli-core'; | |
| import { logger } from '../utils/logger.js'; | |
| // 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() }, | |
| })); | |
| vi.mock('../persistence/gcs.js', () => { | |
| class MockGCSTaskStore { | |
| load = vi.fn(); | |
| save = vi.fn(); | |
| } | |
| class MockNoOpTaskStore { | |
| constructor(public store: unknown) {} | |
| } | |
| return { | |
| GCSTaskStore: MockGCSTaskStore, | |
| NoOpTaskStore: MockNoOpTaskStore, | |
| }; | |
| }); | |
| // 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) { | |
| debugLogger.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}/`); | |
| }); | |
| describe('GET /tasks/metadata with non-InMemoryTaskStore', () => { | |
| let nonInMemoryApp: express.Express; | |
| let nonInMemoryServer: Server; | |
| let nonInMemoryWorkspace: string; | |
| let originalCwd: string; | |
| beforeEach(async () => { | |
| originalCwd = process.cwd(); | |
| nonInMemoryWorkspace = fs.mkdtempSync( | |
| path.join(os.tmpdir(), 'gemini-agent-gcs-test-'), | |
| ); | |
| vi.stubEnv('CODER_AGENT_WORKSPACE_PATH', nonInMemoryWorkspace); | |
| vi.stubEnv('GCS_BUCKET_NAME', 'test-bucket'); | |
| nonInMemoryApp = await createApp(); | |
| await new Promise<void>((resolve) => { | |
| nonInMemoryServer = nonInMemoryApp.listen(0, () => { | |
| resolve(); | |
| }); | |
| }); | |
| }); | |
| afterEach(async () => { | |
| vi.unstubAllEnvs(); | |
| if (nonInMemoryServer) { | |
| await new Promise<void>((resolve, reject) => { | |
| nonInMemoryServer.close((err) => { | |
| if (err) return reject(err); | |
| resolve(); | |
| }); | |
| }); | |
| } | |
| if (originalCwd) { | |
| process.chdir(originalCwd); | |
| } | |
| if (nonInMemoryWorkspace) { | |
| try { | |
| fs.rmSync(nonInMemoryWorkspace, { recursive: true, force: true }); | |
| } catch (e) { | |
| debugLogger.warn( | |
| `Could not remove temp dir '${nonInMemoryWorkspace}':`, | |
| e, | |
| ); | |
| } | |
| } | |
| }); | |
| it('should return 501 and not fall through to send a second response', async () => { | |
| vi.mocked(logger.error).mockClear(); | |
| const response = await request(nonInMemoryApp).get('/tasks/metadata'); | |
| expect(response.status).toBe(501); | |
| expect(response.body).toEqual({ | |
| error: | |
| 'Listing all task metadata is only supported when using InMemoryTaskStore.', | |
| }); | |
| // Without return, the handler falls through and triggers ERR_HTTP_HEADERS_SENT | |
| expect(logger.error).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| }); | |