Files
computer-playbook/roles/web-app-openwebui/files/playwright/test-mcp-servers.js
kevinveenbirkenbachandClaude Opus 5 a0dd564955 fix(openwebui): declare the sign-in every MCP spec actually needs
Each of these specs declared the service it exercises and then, as its first
action, signed in through the dashboard's OIDC button. In a round that ships
no identity provider that button does not exist, so the spec spent its whole
timeout waiting for it and reported the missing login rather than anything
about MCP. The junit already said as much, carrying the SSO_SERVICE_ENABLED
flag as the skip reason on the neighbouring tests.

Nine specs in the directory sign in that way; two already declared sso. The
other seven now do too, through the same gate they already imported.

Signing in as the deployed identity instead was not open to us: that helper
authenticates as the administrator whatever username it is handed, which
contradicts the non-member spec asserting the very endpoint the administrator
is asserted to reach. Nor can a native member stand in, because the account is
created by the OIDC login itself, so a round without one has no members to
grant anything to.

This buys the honest label at a price worth naming: a round that ships
seaweedfs without sso now reports green with no seaweedfs coverage, where it
used to report red for the wrong reason. Covering that needs a sign-in the
suite does not have yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:47:35 +02:00

101 lines
3.7 KiB
JavaScript

const { test, expect } = require("@playwright/test");
const { resolveTimeout } = require("./timeouts");
const { skipUnlessServiceEnabled } = require("./service-gating");
exports.register = function (shared) {
test("administrator: every discovered MCP server is registered as a tool server", async ({ page }) => {
skipUnlessServiceEnabled("mcp");
skipUnlessServiceEnabled("sso");
test.setTimeout(resolveTimeout(120_000));
await shared.signInViaDashboardOidc(
page,
shared.env.adminUsername,
shared.env.adminPassword,
"administrator"
);
const token = await page.evaluate(() => window.localStorage.getItem("token"));
expect(
token,
"OpenWebUI must store a session token in localStorage after login"
).toBeTruthy();
const base = shared.env.openwebuiBaseUrl.replace(/\/+$/, "");
const response = await page.request.get(`${base}/api/v1/configs/tool_servers`, {
headers: { Authorization: `Bearer ${token}` },
});
expect(
response.ok(),
`the admin tool-server config must answer the authenticated session (HTTP ${response.status()})`
).toBeTruthy();
const body = await response.json();
const connections = Array.isArray(body?.TOOL_SERVER_CONNECTIONS)
? body.TOOL_SERVER_CONNECTIONS
: [];
const mcpConnections = connections.filter((c) => c?.type === "mcp");
const expected = (shared.env.mcpExpectedServers || "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
expect(
expected.length,
"the deploy must have discovered at least one MCP server when the mcp service is enabled"
).toBeGreaterThan(0);
const registered = mcpConnections.map((c) => String(c?.info?.id ?? ""));
for (const id of expected) {
expect(
registered,
`the administrator must see ${id} in the configured MCP tool servers (got ${JSON.stringify(registered)})`
).toContain(id);
}
for (const connection of mcpConnections) {
expect(
connection.auth_type,
`${connection?.info?.id} must authenticate with a bearer token`
).toBe("bearer");
expect(
String(connection.key || "").length,
`${connection?.info?.id} must carry a non-empty bearer, an empty one is rejected by the server`
).toBeGreaterThan(0);
const grants = connection?.config?.access_grants ?? [];
expect(
grants,
`${connection?.info?.id} must be scoped to its role's mcp group; an empty grant list falls back to administrator-only`
).toHaveLength(1);
expect(
grants[0],
`${connection?.info?.id} must grant read to a group, not to a user or to everyone`
).toMatchObject({ principal_type: "group", permission: "read" });
expect(
String(grants[0]?.principal_id || ""),
`${connection?.info?.id} must name a resolved OpenWebUI group id`
).not.toHaveLength(0);
expect(
connection?.config?.enable,
`${connection?.info?.id} must be enabled; the deploy enables a server only together with its grant`
).toBeTruthy();
}
const tools = await page.request.get(`${base}/api/v1/tools/`, {
headers: { Authorization: `Bearer ${token}` },
});
expect(
tools.ok(),
`the tool list must answer the administrator (HTTP ${tools.status()})`
).toBeTruthy();
const served = JSON.stringify(await tools.json());
for (const id of expected) {
expect(
served,
`mcp is granted per application, so the administrator role alone must not serve ${id}; BYPASS_ADMIN_ACCESS_CONTROL=false is what keeps an administrator out of a grant they do not hold`
).not.toContain(id);
}
});
};