inspector/clients/cli/__tests__/metadata.test.ts
Bob Dickinson 33e88eac9f
CLI: catalog listing, auth flags, and OAuth browser UX (#1782)
* feat(cli): catalog listing, auth flags, and OAuth browser UX

Add servers/list and servers/show, --relogin / --stored-auth-only, browser
OAuth navigation with OSC 8 links, and extract method handlers under
clients/cli/src/handlers. Tracks #1781.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): address #1782 review — relogin clear, stored-auth-only, docs

Clear both raw and normalized OAuth keys on --relogin; check the shared
store before bailing under --stored-auth-only mid-session; restore the
tests README link target; document servers/show redaction limits; keep
mcpi groundwork with TODO(#1432) markers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): gate OAuth browser open and harden #1782 review follow-ups

Arm auto-open only for CLI-owned interactive OAuth, honor MCP_AUTO_OPEN_ENABLED,
move --relogin clear after validation, skip keychain on servers/list, and cover
secret redaction after rehydrate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): silence disarmed OAuth URLs and round-3 review nits

Suppress authorize URL print when navigation is disarmed or
--stored-auth-only; document MCP_AUTO_OPEN_ENABLED; redact Cookie/auth
headers; keep servers/show unit tests off the OS keychain.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(cli): cover withArmedAutoOpen for the ≥90 branch gate

CI failed on cliOAuth.ts branch coverage after the auto-open arming
helper landed; exercise the armed path through runCliInteractiveOAuth.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): mid-RPC unauthorized recovery and clear round-4 nits

Mirror connect's UnauthorizedError path in withCliAuthRecoveryRetry, let
MCP_AUTO_OPEN_ENABLED=true force open off-TTY, mock open-url in the
in-process runner, reject --relogin on catalog methods, restore the
__tests__ README inventory, and narrow OAuth helpers to structural client types.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): reconnect after mid-RPC OAuth and finish round-5 nits

Reconnect before retrying after unauthorized recovery, guard non-OAuth
configs, decouple forceAutoOpen, move open-url mock to vitest setupFiles,
and reject --relogin with stored-auth short-circuits.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): fail fast on non-TTY interactive OAuth and round-6 nits

Skip interactive OAuth without a TTY unless MCP_AUTO_OPEN_ENABLED=true;
document --stored-auth-only for CI; redact settings.metadata; reject
--relogin for stdio; clarify AuthRecoveryRequired reconnect comments.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): admit interactive OAuth when stdin is a TTY (round-7)

Gate on stdin||stderr so `2>&1 | tee` still works; fold confirm/auto-open/
TTY into CliOAuthConnectOptions; rename authRequiredFailure; document
MCP_AUTO_OPEN_ENABLED=true as non-TTY admit.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): fail fast on step-up without stdin (round-8)

Decline [y/N] on readline EOF/close, require a stdin TTY for the default
step-up confirmer (force-open admit is not enough), document stderr-only
browser auto-open vs stdin||stderr flow admit, and drop the alias wrapper.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): allow piped step-up answers; bound silent pipes (round-9)

Drop the hard stdin-TTY gate that broke `echo y | …`. Keep EOF decline and
add a short non-TTY timeout so an open pipe that never writes cannot hang.
Clarify isTTY as admit-only; extract the readline mock factory.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): keep partial piped step-up answers (round-10)

Capture readline `line` before close so `printf y` without a newline is
not treated as decline; timeout is auth_required with a distinct message;
document the 5s bound and post-confirm callback wait; pin the TTY no-timeout arm.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): tighten step-up timeout messaging and TTY test (round-11)

Make the TTY no-timeout assertion use a settled flag (the Promise.race
check was vacuous), say "timed out" instead of "declined" on the pipe
bound, and document that piped answers need a newline or stdin close.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 16:55:25 -04:00

971 lines
26 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { runCli } from "./helpers/cli-runner.js";
import {
expectCliSuccess,
expectCliFailure,
expectValidJson,
} from "./helpers/assertions.js";
import {
createTestServerHttp,
createEchoTool,
createAddTool,
createTestServerInfo,
} from "@modelcontextprotocol/inspector-test-server";
import { NO_SERVER_SENTINEL } from "./helpers/fixtures.js";
describe("Metadata Tests", () => {
describe("General Metadata", () => {
it("should work with tools/list", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("tools");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
it("should work with resources/list", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
resources: [
{
uri: "test://resource",
name: "test-resource",
text: "test content",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"resources/list",
"--metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("resources");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const resourcesListRequest = recordedRequests.find(
(r) => r.method === "resources/list",
);
expect(resourcesListRequest).toBeDefined();
expect(resourcesListRequest?.metadata).toEqual({
client: "test-client",
});
} finally {
await server.stop();
}
});
it("should work with prompts/list", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
prompts: [
{
name: "test-prompt",
description: "A test prompt",
promptString: "test prompt",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"prompts/list",
"--metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("prompts");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const promptsListRequest = recordedRequests.find(
(r) => r.method === "prompts/list",
);
expect(promptsListRequest).toBeDefined();
expect(promptsListRequest?.metadata).toEqual({
client: "test-client",
});
} finally {
await server.stop();
}
});
it("should work with resources/read", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
resources: [
{
uri: "test://resource",
name: "test-resource",
text: "test content",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"resources/read",
"--uri",
"test://resource",
"--metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("contents");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const readRequest = recordedRequests.find(
(r) => r.method === "resources/read",
);
expect(readRequest).toBeDefined();
expect(readRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
it("should work with prompts/get", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
prompts: [
{
name: "test-prompt",
description: "A test prompt",
promptString: "test prompt",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"prompts/get",
"--prompt-name",
"test-prompt",
"--metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("messages");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const getPromptRequest = recordedRequests.find(
(r) => r.method === "prompts/get",
);
expect(getPromptRequest).toBeDefined();
expect(getPromptRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
});
describe("Tool-Specific Metadata", () => {
it("should work with tools/call", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=hello world",
"--tool-metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("content");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
it("should work with complex tool", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createAddTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"add",
"--tool-arg",
"a=10",
"b=20",
"--tool-metadata",
"client=test-client",
"--transport",
"http",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("content");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
});
describe("Metadata Merging", () => {
it("should merge general and tool-specific metadata (tool-specific overrides)", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=hello world",
"--metadata",
"client=general-client",
"shared_key=shared_value",
"--tool-metadata",
"client=tool-specific-client",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate metadata was merged correctly (tool-specific overrides general)
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({
client: "tool-specific-client", // Tool-specific overrides general
shared_key: "shared_value", // General metadata is preserved
});
} finally {
await server.stop();
}
});
});
describe("Metadata Parsing", () => {
it("should handle numeric values", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"integer_value=42",
"decimal_value=3.14159",
"negative_value=-10",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate metadata values are sent as strings
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
integer_value: "42",
decimal_value: "3.14159",
negative_value: "-10",
});
} finally {
await server.stop();
}
});
it("should handle JSON values", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
'json_object="{\\"key\\":\\"value\\"}"',
'json_array="[1,2,3]"',
'json_string="\\"quoted\\""',
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate JSON values are sent as strings
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
json_object: '{"key":"value"}',
json_array: "[1,2,3]",
json_string: '"quoted"',
});
} finally {
await server.stop();
}
});
it("JSON.stringifies object/array metadata (not String → [object Object])", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
'nested={"key":"value"}',
"list=[1,2,3]",
"flag=true",
"--transport",
"http",
]);
expectCliSuccess(result);
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest?.metadata).toEqual({
nested: '{"key":"value"}',
list: "[1,2,3]",
flag: "true",
});
} finally {
await server.stop();
}
});
it("should handle special characters", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"unicode=🚀🎉✨",
"special_chars=!@#$%^&*()",
"spaces=hello world with spaces",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate special characters are preserved
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
unicode: "🚀🎉✨",
special_chars: "!@#$%^&*()",
spaces: "hello world with spaces",
});
} finally {
await server.stop();
}
});
});
describe("Metadata Edge Cases", () => {
it("should handle single metadata entry", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"single_key=single_value",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate single metadata entry
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
single_key: "single_value",
});
} finally {
await server.stop();
}
});
it("should handle many metadata entries", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"key1=value1",
"key2=value2",
"key3=value3",
"key4=value4",
"key5=value5",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate all metadata entries
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
key1: "value1",
key2: "value2",
key3: "value3",
key4: "value4",
key5: "value5",
});
} finally {
await server.stop();
}
});
});
describe("Metadata Error Cases", () => {
it("should fail with invalid metadata format (missing equals)", async () => {
const result = await runCli([
NO_SERVER_SENTINEL,
"--cli",
"--method",
"tools/list",
"--metadata",
"invalid_format_no_equals",
]);
expectCliFailure(result);
});
it("should fail with invalid tool-metadata format (missing equals)", async () => {
const result = await runCli([
NO_SERVER_SENTINEL,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=test",
"--tool-metadata",
"invalid_format_no_equals",
]);
expectCliFailure(result);
});
});
describe("Metadata Impact", () => {
it("should handle tool-specific metadata precedence over general", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=precedence test",
"--metadata",
"client=general-client",
"--tool-metadata",
"client=tool-specific-client",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate tool-specific metadata overrides general
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({
client: "tool-specific-client",
});
} finally {
await server.stop();
}
});
it("should work with resources methods", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
resources: [
{
uri: "test://resource",
name: "test-resource",
text: "test content",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"resources/list",
"--metadata",
"resource_client=test-resource-client",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const resourcesListRequest = recordedRequests.find(
(r) => r.method === "resources/list",
);
expect(resourcesListRequest).toBeDefined();
expect(resourcesListRequest?.metadata).toEqual({
resource_client: "test-resource-client",
});
} finally {
await server.stop();
}
});
it("should work with prompts methods", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
prompts: [
{
name: "test-prompt",
description: "A test prompt",
promptString: "test prompt",
},
],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"prompts/get",
"--prompt-name",
"test-prompt",
"--metadata",
"prompt_client=test-prompt-client",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const getPromptRequest = recordedRequests.find(
(r) => r.method === "prompts/get",
);
expect(getPromptRequest).toBeDefined();
expect(getPromptRequest?.metadata).toEqual({
prompt_client: "test-prompt-client",
});
} finally {
await server.stop();
}
});
});
describe("Metadata Validation", () => {
it("should handle special characters in keys", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=special keys test",
"--metadata",
"key-with-dashes=value1",
"key_with_underscores=value2",
"key.with.dots=value3",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate special characters in keys are preserved
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({
"key-with-dashes": "value1",
key_with_underscores: "value2",
"key.with.dots": "value3",
});
} finally {
await server.stop();
}
});
});
describe("Metadata Integration", () => {
it("should work with all MCP methods", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"integration_test=true",
"test_phase=all_methods",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({
integration_test: "true",
test_phase: "all_methods",
});
} finally {
await server.stop();
}
});
it("should handle complex metadata scenario", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=complex test",
"--metadata",
"session_id=12345",
"user_id=67890",
"timestamp=2024-01-01T00:00:00Z",
"request_id=req-abc-123",
"--tool-metadata",
"tool_session=session-xyz-789",
"execution_context=test",
"priority=high",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate complex metadata merging
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({
session_id: "12345",
user_id: "67890",
timestamp: "2024-01-01T00:00:00Z",
request_id: "req-abc-123",
tool_session: "session-xyz-789",
execution_context: "test",
priority: "high",
});
} finally {
await server.stop();
}
});
it("should handle metadata parsing validation", async () => {
const server = createTestServerHttp({
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/call",
"--tool-name",
"echo",
"--tool-arg",
"message=parsing validation test",
"--metadata",
"valid_key=valid_value",
"numeric_key=123",
"boolean_key=true",
'json_key=\'{"test":"value"}\'',
"special_key=!@#$%^&*()",
"unicode_key=🚀🎉✨",
"--transport",
"http",
]);
expectCliSuccess(result);
// Validate all value types are sent as strings
// Note: The CLI parses metadata values, so single-quoted JSON strings
// are preserved with their quotes
const recordedRequests = server.getRecordedRequests();
const toolCallRequest = recordedRequests.find(
(r) => r.method === "tools/call",
);
expect(toolCallRequest).toBeDefined();
expect(toolCallRequest?.metadata).toEqual({
valid_key: "valid_value",
numeric_key: "123",
boolean_key: "true",
json_key: '\'{"test":"value"}\'', // Single quotes are preserved
special_key: "!@#$%^&*()",
unicode_key: "🚀🎉✨",
});
} finally {
await server.stop();
}
});
});
describe("SSE Transport Tests", () => {
it("should work with tools/list using SSE transport", async () => {
const server = createTestServerHttp({
serverType: "sse",
serverInfo: createTestServerInfo(),
tools: [createEchoTool()],
});
try {
await server.start();
const result = await runCli([
server.url,
"--cli",
"--method",
"tools/list",
"--metadata",
"client=test-client",
"--transport",
"sse",
]);
expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("tools");
// Validate metadata was sent
const recordedRequests = server.getRecordedRequests();
const toolsListRequest = recordedRequests.find(
(r) => r.method === "tools/list",
);
expect(toolsListRequest).toBeDefined();
expect(toolsListRequest?.metadata).toEqual({ client: "test-client" });
} finally {
await server.stop();
}
});
});
});