Skip to content

Commit be87691

Browse files
committed
test: add unit tests for OAuth discovery endpoints
1 parent 1af962f commit be87691

File tree

2 files changed

+179
-1
lines changed

2 files changed

+179
-1
lines changed

src/auth/discovery.test.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest";
2+
import type { Request, Response } from "express";
3+
import {
4+
createAuthorizationServerMetadataHandler,
5+
createProtectedResourceMetadataHandler,
6+
} from "./discovery.ts";
7+
import { logger } from "../logger.ts";
8+
9+
// Mock logger
10+
vi.mock("../logger.ts", () => ({
11+
logger: {
12+
info: vi.fn(),
13+
error: vi.fn(),
14+
warn: vi.fn(),
15+
debug: vi.fn(),
16+
},
17+
}));
18+
19+
describe("OAuth Discovery Endpoints", () => {
20+
let mockReq: Request;
21+
let mockRes: Response;
22+
let jsonSpy: ReturnType<typeof vi.fn>;
23+
let statusSpy: ReturnType<typeof vi.fn>;
24+
25+
beforeEach(() => {
26+
jsonSpy = vi.fn();
27+
statusSpy = vi.fn().mockReturnValue({ json: jsonSpy });
28+
29+
mockReq = {
30+
get: vi.fn().mockReturnValue("auth.example.com"),
31+
// ...other required Request properties can be added here as needed
32+
} as unknown as Request;
33+
Object.defineProperty(mockReq, "protocol", {
34+
value: "https",
35+
writable: true,
36+
configurable: true,
37+
enumerable: true,
38+
});
39+
40+
mockRes = {
41+
json: jsonSpy,
42+
status: statusSpy,
43+
// @ts-ignore: Only properties used by handler are needed
44+
} as unknown as Response;
45+
46+
vi.clearAllMocks();
47+
});
48+
49+
describe("createAuthorizationServerMetadataHandler", () => {
50+
it("should return OAuth authorization server metadata", () => {
51+
const handler = createAuthorizationServerMetadataHandler();
52+
handler(mockReq, mockRes);
53+
54+
expect(jsonSpy).toHaveBeenCalledWith({
55+
issuer: "https://auth.example.com",
56+
authorization_endpoint: "https://auth.example.com/oauth/authorize",
57+
token_endpoint: "https://auth.example.com/oauth/token",
58+
response_types_supported: ["code"],
59+
grant_types_supported: ["authorization_code"],
60+
code_challenge_methods_supported: ["S256"],
61+
scopes_supported: ["read", "write", "mcp"],
62+
token_endpoint_auth_methods_supported: ["none"],
63+
});
64+
});
65+
66+
it("should log metadata request", () => {
67+
const handler = createAuthorizationServerMetadataHandler();
68+
69+
handler(mockReq, mockRes);
70+
71+
expect(logger.info).toHaveBeenCalledWith(
72+
"OAuth authorization server metadata requested",
73+
{ issuer: "https://auth.example.com" },
74+
);
75+
});
76+
77+
it("should handle errors gracefully", () => {
78+
const handler = createAuthorizationServerMetadataHandler();
79+
80+
// Mock req.get to throw an error
81+
vi.mocked(mockReq.get).mockImplementation(() => {
82+
throw new Error("Request error");
83+
});
84+
85+
handler(mockReq, mockRes);
86+
87+
expect(logger.error).toHaveBeenCalledWith(
88+
"Error serving authorization server metadata",
89+
{ error: "Request error" },
90+
);
91+
92+
expect(statusSpy).toHaveBeenCalledWith(500);
93+
expect(jsonSpy).toHaveBeenCalledWith({
94+
error: "server_error",
95+
error_description: "Failed to serve authorization server metadata",
96+
});
97+
});
98+
99+
it("should construct correct URLs with different protocols", () => {
100+
Object.defineProperty(mockReq, "protocol", { value: "http" });
101+
vi.mocked(mockReq.get).mockReturnValue("localhost:3000");
102+
103+
const handler = createAuthorizationServerMetadataHandler();
104+
handler(mockReq, mockRes);
105+
106+
expect(jsonSpy).toHaveBeenCalledWith(
107+
expect.objectContaining({
108+
issuer: "http://localhost:3000",
109+
authorization_endpoint: "http://localhost:3000/oauth/authorize",
110+
token_endpoint: "http://localhost:3000/oauth/token",
111+
}),
112+
);
113+
});
114+
});
115+
116+
describe("createProtectedResourceMetadataHandler", () => {
117+
it("should return OAuth protected resource metadata", () => {
118+
const handler = createProtectedResourceMetadataHandler();
119+
handler(mockReq, mockRes);
120+
121+
expect(jsonSpy).toHaveBeenCalledWith({
122+
resource: "https://auth.example.com",
123+
authorization_servers: ["https://auth.example.com"],
124+
scopes_supported: ["read", "write", "mcp"],
125+
bearer_methods_supported: ["header"],
126+
resource_documentation: "https://auth.example.com/docs",
127+
});
128+
});
129+
130+
it("should log metadata request", () => {
131+
const handler = createProtectedResourceMetadataHandler();
132+
133+
handler(mockReq, mockRes);
134+
135+
expect(logger.info).toHaveBeenCalledWith(
136+
"OAuth protected resource metadata requested",
137+
{ resource: "https://auth.example.com" },
138+
);
139+
});
140+
141+
it("should handle errors gracefully", () => {
142+
const handler = createProtectedResourceMetadataHandler();
143+
144+
// Mock req.get to throw an error
145+
vi.mocked(mockReq.get).mockImplementation(() => {
146+
throw new Error("Resource error");
147+
});
148+
149+
handler(mockReq, mockRes);
150+
151+
expect(logger.error).toHaveBeenCalledWith(
152+
"Error serving protected resource metadata",
153+
{ error: "Resource error" },
154+
);
155+
156+
expect(statusSpy).toHaveBeenCalledWith(500);
157+
expect(jsonSpy).toHaveBeenCalledWith({
158+
error: "server_error",
159+
error_description: "Failed to serve protected resource metadata",
160+
});
161+
});
162+
163+
it("should construct correct URLs with different hosts", () => {
164+
Object.defineProperty(mockReq, "protocol", { value: "http" });
165+
vi.mocked(mockReq.get).mockReturnValue("api.myservice.com");
166+
167+
const handler = createProtectedResourceMetadataHandler();
168+
handler(mockReq, mockRes);
169+
170+
expect(jsonSpy).toHaveBeenCalledWith(
171+
expect.objectContaining({
172+
resource: "http://api.myservice.com",
173+
authorization_servers: ["http://api.myservice.com"],
174+
resource_documentation: "http://api.myservice.com/docs",
175+
}),
176+
);
177+
});
178+
});
179+
});

src/auth/discovery.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { Request, Response } from "express";
2-
import OAuth2Server from "@node-oauth/oauth2-server";
32
import { logger } from "../logger.ts";
43

54
/**

0 commit comments

Comments
 (0)