Emdash source with visual editor image upload fix
Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Integration test for MCP OAuth discovery against a real Astro dev server.
|
||||
*
|
||||
* Uses the MCP SDK's own discovery functions with real fetch() so we test
|
||||
* the actual Astro route registration, not just the handler logic. This
|
||||
* catches mismatches between the paths we register in routes.ts and the
|
||||
* paths the SDK constructs per RFC 8414 / RFC 9728.
|
||||
*/
|
||||
|
||||
import {
|
||||
discoverOAuthProtectedResourceMetadata,
|
||||
discoverAuthorizationServerMetadata,
|
||||
} from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
import type { TestServerContext } from "../server.js";
|
||||
import { assertNodeVersion, createTestServer } from "../server.js";
|
||||
|
||||
const PORT = 4401;
|
||||
|
||||
describe("MCP OAuth Discovery (real server)", () => {
|
||||
let ctx: TestServerContext;
|
||||
|
||||
beforeAll(async () => {
|
||||
assertNodeVersion();
|
||||
ctx = await createTestServer({ port: PORT });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await ctx?.cleanup();
|
||||
});
|
||||
|
||||
it("discovers protected resource metadata from the MCP server URL", async () => {
|
||||
const metadata = await discoverOAuthProtectedResourceMetadata(`${ctx.baseUrl}/_emdash/api/mcp`);
|
||||
|
||||
expect(metadata.resource).toBe(`${ctx.baseUrl}/_emdash/api/mcp`);
|
||||
expect(metadata.authorization_servers).toContain(`${ctx.baseUrl}/_emdash`);
|
||||
expect(metadata.scopes_supported).toContain("content:read");
|
||||
expect(metadata.bearer_methods_supported).toContain("header");
|
||||
});
|
||||
|
||||
it("discovers authorization server metadata via the RFC 8414 path", async () => {
|
||||
// Step 1: get the authorization server URL from protected resource metadata
|
||||
const resourceMeta = await discoverOAuthProtectedResourceMetadata(
|
||||
`${ctx.baseUrl}/_emdash/api/mcp`,
|
||||
);
|
||||
const authServerUrl = resourceMeta.authorization_servers![0]!;
|
||||
|
||||
// Step 2: the SDK constructs /.well-known/oauth-authorization-server/_emdash
|
||||
// per RFC 8414 (path component appended after well-known prefix).
|
||||
// This must resolve to a real route, not 404.
|
||||
const metadata = await discoverAuthorizationServerMetadata(authServerUrl);
|
||||
|
||||
expect(metadata).toBeDefined();
|
||||
expect(metadata!.issuer).toBe(`${ctx.baseUrl}/_emdash`);
|
||||
expect(metadata!.authorization_endpoint).toBe(`${ctx.baseUrl}/_emdash/oauth/authorize`);
|
||||
expect(metadata!.token_endpoint).toBe(`${ctx.baseUrl}/_emdash/api/oauth/token`);
|
||||
expect(metadata!.code_challenge_methods_supported).toContain("S256");
|
||||
expect(metadata!.response_types_supported).toContain("code");
|
||||
expect(metadata!.grant_types_supported).toContain("authorization_code");
|
||||
});
|
||||
|
||||
it("MCP endpoint returns 401 with resource_metadata in WWW-Authenticate", async () => {
|
||||
// Unauthenticated POST to MCP should return 401 with the discovery hint
|
||||
const res = await fetch(`${ctx.baseUrl}/_emdash/api/mcp`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "initialize",
|
||||
params: {
|
||||
protocolVersion: "2025-03-26",
|
||||
capabilities: {},
|
||||
clientInfo: { name: "test", version: "1.0" },
|
||||
},
|
||||
id: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
const wwwAuth = res.headers.get("WWW-Authenticate");
|
||||
expect(wwwAuth).toContain("resource_metadata=");
|
||||
expect(wwwAuth).toContain("/.well-known/oauth-protected-resource");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user