diff --git a/tests/integration/a2a/input_required/multi_hop_remote_agent_test.ts b/tests/integration/a2a/input_required/multi_hop_remote_agent_test.ts new file mode 100644 index 000000000..24286b43f --- /dev/null +++ b/tests/integration/a2a/input_required/multi_hop_remote_agent_test.ts @@ -0,0 +1,84 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {RemoteA2AAgentConfig} from '@google/adk'; +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; + +const PORT_ENV_VAR = 'TEST_API_SERVER_PORT'; + +const {agentConfigs} = vi.hoisted(() => ({ + agentConfigs: [] as RemoteA2AAgentConfig[], +})); + +// The agent card URL is not publicly readable off a constructed +// RemoteA2AAgent, so record the config the agent module builds it with. +vi.mock('@google/adk', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + RemoteA2AAgent: class extends actual.RemoteA2AAgent { + constructor(config: RemoteA2AAgentConfig) { + super(config); + agentConfigs.push(config); + } + }, + }; +}); + +/** + * Re-evaluates the agent module against the currently stubbed environment. + * + * The port is read once at module evaluation time, so the module registry has + * to be reset before every import or later cases reuse the first evaluation. + */ +function importAgentModule() { + vi.resetModules(); + return import('./test_agents/multi_hop_remote_agent.js'); +} + +describe('multi_hop_remote_agent', () => { + beforeEach(() => { + agentConfigs.length = 0; + }); + + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it('points the agent card at the port the harness supplied', async () => { + vi.stubEnv(PORT_ENV_VAR, '41234'); + + const {rootAgent} = await importAgentModule(); + + expect(rootAgent.name).toBe('multi_hop'); + expect(agentConfigs).toHaveLength(1); + expect(agentConfigs[0].agentCard).toBe( + 'http://localhost:41234/a2a/multi_hop/', + ); + }); + + it.each([undefined, ''])( + 'throws a "not set" error when the port is %j', + async (raw) => { + vi.stubEnv(PORT_ENV_VAR, raw); + + await expect(importAgentModule()).rejects.toThrow( + `${PORT_ENV_VAR} is not set.`, + ); + }, + ); + + it.each(['abc', '41234abc', '0', '-1', '1.5', ' '])( + 'throws a "positive integer" error when the port is %j', + async (raw) => { + vi.stubEnv(PORT_ENV_VAR, raw); + + await expect(importAgentModule()).rejects.toThrow( + `${PORT_ENV_VAR} must be a positive integer, got "${raw}".`, + ); + }, + ); +}); diff --git a/tests/integration/a2a/input_required/test_agents/multi_hop_remote_agent.ts b/tests/integration/a2a/input_required/test_agents/multi_hop_remote_agent.ts index 652644f08..acd1b484b 100644 --- a/tests/integration/a2a/input_required/test_agents/multi_hop_remote_agent.ts +++ b/tests/integration/a2a/input_required/test_agents/multi_hop_remote_agent.ts @@ -6,7 +6,37 @@ import {RemoteA2AAgent} from '@google/adk'; -const port = process.env.TEST_API_SERVER_PORT || '40000'; +const PORT_ENV_VAR = 'TEST_API_SERVER_PORT'; + +/** + * Returns the port of the test ADK API server this agent is loaded into. + * + * The harness picks that port at random and this agent points back at its own + * server, so no default could ever be right. + * + * @throws If the environment variable is unset or not a positive integer. + */ +function requireServerPort(): number { + const raw = process.env[PORT_ENV_VAR]; + if (!raw) { + throw new Error( + `${PORT_ENV_VAR} is not set. This agent points back at the test ADK ` + + `API server it runs inside, so it has no default port to fall back ` + + `to; AdkTsApiServer must propagate ${PORT_ENV_VAR}.`, + ); + } + + const port = Number(raw); + if (!Number.isInteger(port) || port <= 0) { + throw new Error( + `${PORT_ENV_VAR} must be a positive integer, got "${raw}".`, + ); + } + + return port; +} + +const port = requireServerPort(); export const rootAgent = new RemoteA2AAgent({ name: 'multi_hop',