Skip to content

Commit 65cef62

Browse files
committed
add some usage examples for mock overrides
1 parent 91db28b commit 65cef62

File tree

3 files changed

+302
-1
lines changed

3 files changed

+302
-1
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
import { getServerDetails } from "./actions";
3+
4+
// Mock the auth to bypass authentication
5+
vi.mock("@/lib/api-client", async (importOriginal) => {
6+
const original = await importOriginal<typeof import("@/lib/api-client")>();
7+
return {
8+
...original,
9+
getAuthenticatedClient: vi.fn(() =>
10+
original.getAuthenticatedClient("mock-token"),
11+
),
12+
};
13+
});
14+
15+
describe("getServerDetails", () => {
16+
beforeEach(() => {
17+
vi.clearAllMocks();
18+
});
19+
20+
describe("successful responses", () => {
21+
it("returns server data for valid server name and version", async () => {
22+
const result = await getServerDetails("awslabs/aws-nova-canvas", "1.0.0");
23+
24+
expect(result.error).toBeUndefined();
25+
expect(result.data?.server?.name).toBe("awslabs/aws-nova-canvas");
26+
expect(result.data?.server?.title).toBe("AWS Nova Canvas");
27+
});
28+
29+
it("returns server data when version is 'latest'", async () => {
30+
const result = await getServerDetails(
31+
"awslabs/aws-nova-canvas",
32+
"latest",
33+
);
34+
35+
expect(result.error).toBeUndefined();
36+
expect(result.data?.server?.name).toBe("awslabs/aws-nova-canvas");
37+
});
38+
39+
it("returns different servers from fixture", async () => {
40+
const result = await getServerDetails("google/mcp-google-apps", "1.0.0");
41+
42+
expect(result.error).toBeUndefined();
43+
expect(result.data?.server?.name).toBe("google/mcp-google-apps");
44+
expect(result.data?.server?.title).toBe("Google Workspace");
45+
});
46+
});
47+
48+
describe("error handling", () => {
49+
it("returns 404 for non-existent server", async () => {
50+
const result = await getServerDetails("non-existent/server", "1.0.0");
51+
52+
expect(result.response.status).toBe(404);
53+
});
54+
55+
it("returns 404 for wrong version", async () => {
56+
const result = await getServerDetails("awslabs/aws-nova-canvas", "9.9.9");
57+
58+
expect(result.response.status).toBe(404);
59+
});
60+
});
61+
62+
describe("fixture data", () => {
63+
it("includes metadata in response", async () => {
64+
const result = await getServerDetails("awslabs/aws-nova-canvas", "1.0.0");
65+
66+
expect(result.data?._meta).toBeDefined();
67+
});
68+
69+
it("returns full server details from fixture", async () => {
70+
const result = await getServerDetails("awslabs/aws-nova-canvas", "1.0.0");
71+
72+
expect(result.data?.server?.name).toBe("awslabs/aws-nova-canvas");
73+
expect(result.data?.server?.version).toBe("1.0.0");
74+
expect(result.data?.server?.description).toContain("MCP server");
75+
});
76+
});
77+
});

src/app/catalog/actions.test.ts

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import { mockedGetRegistryV01Servers } from "@mocks/fixtures/registry_v0_1_servers/get";
2+
import { HttpResponse } from "msw";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
4+
import { getServers } from "./actions";
5+
6+
// Mock the auth to bypass authentication
7+
vi.mock("@/lib/api-client", async (importOriginal) => {
8+
const original = await importOriginal<typeof import("@/lib/api-client")>();
9+
return {
10+
...original,
11+
getAuthenticatedClient: vi.fn(() =>
12+
original.getAuthenticatedClient("mock-token"),
13+
),
14+
};
15+
});
16+
17+
describe("getServers", () => {
18+
beforeEach(() => {
19+
vi.clearAllMocks();
20+
});
21+
22+
describe("successful responses", () => {
23+
it("returns servers from default fixture", async () => {
24+
const servers = await getServers();
25+
26+
expect(servers.length).toBeGreaterThan(0);
27+
expect(servers[0].name).toBe("awslabs/aws-nova-canvas");
28+
expect(servers[0].title).toBe("AWS Nova Canvas");
29+
});
30+
31+
it("returns empty array when API returns no servers", async () => {
32+
mockedGetRegistryV01Servers.override(() =>
33+
HttpResponse.json({ servers: [], metadata: { count: 0 } }),
34+
);
35+
36+
const servers = await getServers();
37+
38+
expect(servers).toEqual([]);
39+
});
40+
41+
it("returns empty array when API returns null data", async () => {
42+
mockedGetRegistryV01Servers.override(() => HttpResponse.json(null));
43+
44+
const servers = await getServers();
45+
46+
expect(servers).toEqual([]);
47+
});
48+
});
49+
50+
describe("error handling", () => {
51+
it("throws on 500 server error", async () => {
52+
mockedGetRegistryV01Servers.override(() =>
53+
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 }),
54+
);
55+
56+
await expect(getServers()).rejects.toBeDefined();
57+
});
58+
59+
it("throws on 401 unauthorized", async () => {
60+
mockedGetRegistryV01Servers.override(() =>
61+
HttpResponse.json({ error: "Unauthorized" }, { status: 401 }),
62+
);
63+
64+
await expect(getServers()).rejects.toBeDefined();
65+
});
66+
67+
it("throws on network error", async () => {
68+
mockedGetRegistryV01Servers.override(() => HttpResponse.error());
69+
70+
await expect(getServers()).rejects.toBeDefined();
71+
});
72+
});
73+
74+
describe("data transformation", () => {
75+
it("filters out null servers from response", async () => {
76+
mockedGetRegistryV01Servers.override((data) =>
77+
HttpResponse.json({
78+
...data,
79+
servers: [
80+
{ server: { name: "valid/server", title: "Valid" } },
81+
{ server: null },
82+
{ server: { name: "another/server", title: "Another" } },
83+
],
84+
metadata: { count: 3 },
85+
}),
86+
);
87+
88+
const servers = await getServers();
89+
90+
expect(servers).toHaveLength(2);
91+
expect(servers.map((s) => s.name)).toEqual([
92+
"valid/server",
93+
"another/server",
94+
]);
95+
});
96+
97+
it("filters out undefined servers from response", async () => {
98+
mockedGetRegistryV01Servers.override(() =>
99+
HttpResponse.json({
100+
servers: [
101+
{ server: { name: "valid/server", title: "Valid" } },
102+
{ server: undefined },
103+
{},
104+
],
105+
metadata: { count: 3 },
106+
}),
107+
);
108+
109+
const servers = await getServers();
110+
111+
expect(servers).toHaveLength(1);
112+
expect(servers[0].name).toBe("valid/server");
113+
});
114+
115+
it("extracts server objects from nested response structure", async () => {
116+
mockedGetRegistryV01Servers.override(() =>
117+
HttpResponse.json({
118+
servers: [
119+
{
120+
server: {
121+
name: "test/server",
122+
title: "Test Server",
123+
description: "A test server",
124+
version: "2.0.0",
125+
},
126+
_meta: { "some/key": { status: "active" } },
127+
},
128+
],
129+
metadata: { count: 1 },
130+
}),
131+
);
132+
133+
const servers = await getServers();
134+
135+
expect(servers).toHaveLength(1);
136+
expect(servers[0]).toEqual({
137+
name: "test/server",
138+
title: "Test Server",
139+
description: "A test server",
140+
version: "2.0.0",
141+
});
142+
});
143+
});
144+
145+
describe("using default data in overrides", () => {
146+
it("can modify specific server titles", async () => {
147+
mockedGetRegistryV01Servers.override((data) =>
148+
HttpResponse.json({
149+
...data,
150+
servers: data.servers?.map((item) => ({
151+
...item,
152+
server: {
153+
...item.server,
154+
title: `Modified: ${item.server?.title}`,
155+
},
156+
})),
157+
}),
158+
);
159+
160+
const servers = await getServers();
161+
162+
expect(servers[0].title).toBe("Modified: AWS Nova Canvas");
163+
});
164+
165+
it("can limit the number of returned servers", async () => {
166+
mockedGetRegistryV01Servers.override((data) =>
167+
HttpResponse.json({
168+
...data,
169+
servers: data.servers?.slice(0, 3),
170+
metadata: { count: 3 },
171+
}),
172+
);
173+
174+
const servers = await getServers();
175+
176+
expect(servers).toHaveLength(3);
177+
});
178+
179+
it("can filter servers by criteria", async () => {
180+
mockedGetRegistryV01Servers.override((data) =>
181+
HttpResponse.json({
182+
...data,
183+
servers: data.servers?.filter((item) =>
184+
item.server?.name?.includes("google"),
185+
),
186+
}),
187+
);
188+
189+
const servers = await getServers();
190+
191+
expect(servers.every((s) => s.name?.includes("google"))).toBe(true);
192+
});
193+
});
194+
195+
describe("request-aware overrides", () => {
196+
it("can access request info in override", async () => {
197+
let capturedUrl: string | undefined;
198+
199+
mockedGetRegistryV01Servers.override((data, info) => {
200+
capturedUrl = info.request.url;
201+
return HttpResponse.json(data);
202+
});
203+
204+
await getServers();
205+
206+
expect(capturedUrl).toContain("/registry/v0.1/servers");
207+
});
208+
209+
it("can vary response based on request headers", async () => {
210+
mockedGetRegistryV01Servers.override((data, info) => {
211+
const authHeader = info.request.headers.get("Authorization");
212+
if (authHeader?.includes("mock-token")) {
213+
return HttpResponse.json(data);
214+
}
215+
return HttpResponse.json({ error: "Unauthorized" }, { status: 401 });
216+
});
217+
218+
// Our mock provides "mock-token", so this should succeed
219+
const servers = await getServers();
220+
expect(servers.length).toBeGreaterThan(0);
221+
});
222+
});
223+
});

src/mocks/mocker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,10 @@ export function autoGenerateHandlers() {
347347
const result: RequestHandler[] = [];
348348

349349
// Prefer Vite glob import when available (Vitest/Vite runtime)
350+
// Note: We don't use { import: "default" } since fixtures use named exports
350351
const fixtureImporters: Record<string, () => Promise<unknown>> =
351352
typeof import.meta.glob === "function"
352-
? import.meta.glob("./fixtures/**", { import: "default" })
353+
? import.meta.glob("./fixtures/**")
353354
: {};
354355

355356
const specPaths = Object.entries(

0 commit comments

Comments
 (0)