mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-25 16:42:16 +03:00
test(a2a): call the agent-card route handlers with a NextRequest
PR #11418 (S2 topology sanitisation) removed the hardcoded localhost:20128 from both well-known agent-card routes and made them derive the base URL from `request.nextUrl.origin` via `getBaseUrl(request)` (src/lib/wellKnown.ts). That changed the handler contract: `GET` now requires the request Next.js always passes it. Three sibling test files were never aligned and still invoked the handler as a bare `GET()`, so every case blew up with `TypeError: Cannot read properties of undefined (reading nextUrl)` before reaching a single assertion — 8 base-reds from one moved contract, not from a skill-count drift. Align the callers to the shipped contract with a local `makeCardRequest()` helper mirroring tests/unit/security-s1-s2-s4.test.ts (a Request with a defined `nextUrl`). No assertion was removed, loosened or skipped; the assert counts are unchanged and the cases now actually execute. Refs #11418
This commit is contained in:
@@ -14,6 +14,19 @@ const settingsDb = await import("../../src/lib/db/settings.ts");
|
||||
const a2aRoute = await import("../../src/app/a2a/route.ts");
|
||||
const agentCardRoute = await import("../../src/app/.well-known/agent-card.json/route.ts");
|
||||
|
||||
/**
|
||||
* The agent-card routes derive their base URL from `request.nextUrl.origin`
|
||||
* (S2 topology sanitisation, #11418), so the handler must be invoked with a
|
||||
* request the way Next.js does — a bare `GET()` throws on `nextUrl`.
|
||||
*/
|
||||
function makeCardRequest(
|
||||
url = "https://gateway.example.com/.well-known/agent-card.json"
|
||||
): NextRequest {
|
||||
const request = new Request(url) as unknown as NextRequest;
|
||||
Object.defineProperty(request, "nextUrl", { value: new URL(url), configurable: true });
|
||||
return request;
|
||||
}
|
||||
|
||||
function makeJsonRpcRequest(body: unknown): NextRequest {
|
||||
return new Request("http://localhost/a2a", {
|
||||
method: "POST",
|
||||
@@ -48,7 +61,13 @@ test("#10839: v1.0 SendMessage is aliased to message/send and reshapes the respo
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as {
|
||||
result?: { task?: { id: string; status?: { message?: { parts?: { text?: string }[] } }; artifacts?: unknown } };
|
||||
result?: {
|
||||
task?: {
|
||||
id: string;
|
||||
status?: { message?: { parts?: { text?: string }[] } };
|
||||
artifacts?: unknown;
|
||||
};
|
||||
};
|
||||
error?: unknown;
|
||||
};
|
||||
assert.equal(body.error, undefined, JSON.stringify(body));
|
||||
@@ -102,7 +121,7 @@ test("#10839: SendStreamingMessage no longer 404s (aliased to message/stream)",
|
||||
});
|
||||
|
||||
test("#10839: GET /.well-known/agent-card.json serves a v1.0 card declaring both interfaces", async () => {
|
||||
const res = await agentCardRoute.GET();
|
||||
const res = await agentCardRoute.GET(makeCardRequest());
|
||||
assert.equal(res.status, 200);
|
||||
const card = (await res.json()) as { supportedInterfaces?: { protocolVersion?: string }[] };
|
||||
assert.ok(Array.isArray(card.supportedInterfaces));
|
||||
|
||||
@@ -8,9 +8,21 @@
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
const { GET } = await import("../../src/app/.well-known/agent.json/route.js");
|
||||
|
||||
/**
|
||||
* The agent-card routes derive their base URL from `request.nextUrl.origin`
|
||||
* (S2 topology sanitisation, #11418), so the handler must be invoked with a
|
||||
* request the way Next.js does — a bare `GET()` throws on `nextUrl`.
|
||||
*/
|
||||
function makeCardRequest(url = "https://gateway.example.com/.well-known/agent.json"): NextRequest {
|
||||
const request = new Request(url) as unknown as NextRequest;
|
||||
Object.defineProperty(request, "nextUrl", { value: new URL(url), configurable: true });
|
||||
return request;
|
||||
}
|
||||
|
||||
interface AgentSkillEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -26,7 +38,7 @@ interface AgentCard {
|
||||
}
|
||||
|
||||
test("GET /.well-known/agent.json returns 6 skills", async () => {
|
||||
const response = await GET();
|
||||
const response = await GET(makeCardRequest());
|
||||
assert.equal(response.status, 200, "Expected HTTP 200");
|
||||
|
||||
const body = (await response.json()) as AgentCard;
|
||||
@@ -35,7 +47,7 @@ test("GET /.well-known/agent.json returns 6 skills", async () => {
|
||||
});
|
||||
|
||||
test("Agent Card includes list-capabilities skill entry", async () => {
|
||||
const response = await GET();
|
||||
const response = await GET(makeCardRequest());
|
||||
const body = (await response.json()) as AgentCard;
|
||||
|
||||
const skill = body.skills.find((s) => s.id === "list-capabilities");
|
||||
@@ -43,7 +55,7 @@ test("Agent Card includes list-capabilities skill entry", async () => {
|
||||
});
|
||||
|
||||
test("list-capabilities entry has required tags [discovery, capabilities]", async () => {
|
||||
const response = await GET();
|
||||
const response = await GET(makeCardRequest());
|
||||
const body = (await response.json()) as AgentCard;
|
||||
|
||||
const skill = body.skills.find((s) => s.id === "list-capabilities");
|
||||
@@ -54,7 +66,7 @@ test("list-capabilities entry has required tags [discovery, capabilities]", asyn
|
||||
});
|
||||
|
||||
test("list-capabilities entry has at least one example question", async () => {
|
||||
const response = await GET();
|
||||
const response = await GET(makeCardRequest());
|
||||
const body = (await response.json()) as AgentCard;
|
||||
|
||||
const skill = body.skills.find((s) => s.id === "list-capabilities");
|
||||
@@ -64,7 +76,7 @@ test("list-capabilities entry has at least one example question", async () => {
|
||||
});
|
||||
|
||||
test("Agent Card includes all 5 original skills", async () => {
|
||||
const response = await GET();
|
||||
const response = await GET(makeCardRequest());
|
||||
const body = (await response.json()) as AgentCard;
|
||||
|
||||
const originalIds = [
|
||||
@@ -78,7 +90,7 @@ test("Agent Card includes all 5 original skills", async () => {
|
||||
for (const id of originalIds) {
|
||||
assert.ok(
|
||||
body.skills.some((s) => s.id === id),
|
||||
`Original skill '${id}' must be present in Agent Card`,
|
||||
`Original skill '${id}' must be present in Agent Card`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,9 +2,22 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createServer, type Server } from "node:http";
|
||||
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
import { GET } from "../../src/app/.well-known/agent.json/route.ts";
|
||||
import { clearFleetSkillsCache } from "../../src/lib/conductor/fleetSkills.ts";
|
||||
|
||||
/**
|
||||
* The agent-card routes derive their base URL from `request.nextUrl.origin`
|
||||
* (S2 topology sanitisation, #11418), so the handler must be invoked with a
|
||||
* request the way Next.js does — a bare `GET()` throws on `nextUrl`.
|
||||
*/
|
||||
function makeCardRequest(url = "https://gateway.example.com/.well-known/agent.json"): NextRequest {
|
||||
const request = new Request(url) as unknown as NextRequest;
|
||||
Object.defineProperty(request, "nextUrl", { value: new URL(url), configurable: true });
|
||||
return request;
|
||||
}
|
||||
|
||||
const servers: Server[] = [];
|
||||
|
||||
test.beforeEach(() => {
|
||||
@@ -22,7 +35,7 @@ test.after(async () => {
|
||||
});
|
||||
|
||||
test("sem CONDUCTOR_HUB_URL o card continua válido, com as skills estáticas e zero conductor-*", async () => {
|
||||
const res = await GET();
|
||||
const res = await GET(makeCardRequest());
|
||||
const card = await res.json();
|
||||
assert.equal(typeof card.name, "string");
|
||||
assert.ok(Array.isArray(card.skills) && card.skills.length >= 6, "skills estáticas presentes");
|
||||
@@ -34,7 +47,11 @@ test("com hub de pé o card anuncia as skills da frota SEM perder as estáticas"
|
||||
res.writeHead(200, { "content-type": "application/json" });
|
||||
res.end(
|
||||
JSON.stringify([
|
||||
{ id: "r_1", online: true, capabilities: { name: "devbox", clis: [{ profile: "claude" }], skills: [] } },
|
||||
{
|
||||
id: "r_1",
|
||||
online: true,
|
||||
capabilities: { name: "devbox", clis: [{ profile: "claude" }], skills: [] },
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
@@ -44,7 +61,7 @@ test("com hub de pé o card anuncia as skills da frota SEM perder as estáticas"
|
||||
process.env.CONDUCTOR_HUB_URL = `http://127.0.0.1:${typeof addr === "object" && addr ? addr.port : 0}`;
|
||||
process.env.CONDUCTOR_HUB_TOKEN = "tok";
|
||||
|
||||
const res = await GET();
|
||||
const res = await GET(makeCardRequest());
|
||||
const card = await res.json();
|
||||
const ids = card.skills.map((s: { id: string }) => s.id);
|
||||
assert.ok(ids.includes("conductor-cli-claude"), `frota anunciada (ids: ${ids.join(",")})`);
|
||||
|
||||
Reference in New Issue
Block a user