History

Verify saved MCP apps through provider consent

Rhys Sullivan committed 1 month ago

Commit 41a4d2d1 parent dab9e291 file · +29 −11Browse files

Unified · Split

e2e/scenarios/connection-setup-ux.test.ts+29−11
@@ -327,6 +327,14 @@ for (const origin of ["integration", "workspace"] as const) { · 326 unmodified lines
327327 });
328328 yield* Effect.addFinalizer(() =>
329329 Effect.gen(function* () {
330+ const connections = yield* client.connections.list({ query: { integration: slug } });
331+ for (const connection of connections) {
332+ yield* client.connections
333+ .remove({
334+ params: { owner: connection.owner, integration: slug, name: connection.name },
335+ })
336+ .pipe(Effect.ignore);
337+ }
330338 yield* client.mcp.removeServer({ params: { slug } }).pipe(Effect.ignore);
331339 yield* client.oauth
332340 .removeClient({ params: { slug: app }, payload: { owner: "org" } })
@@ -355,17 +363,13 @@ for (const origin of ["integration", "workspace"] as const) { · 22 unmodified lines
355363 ...(origin === "integration" ? { originIntegration: slug } : {}),
356364 },
357365 });
358− const savedClients = yield* client.oauth.listClients({});
359− expect(savedClients.find((saved) => saved.slug === app)?.origin).toEqual(
360− origin === "integration"
361− ? { kind: "manual", integration: slug }
362− : { kind: "manual", integration: null },
363− );
364366 yield* Effect.promise(() => emulator.ledger.clear());
365367 yield* browser.session(identity, async ({ page, step }) => {
366368 await step("Open an integration that already has a saved OAuth app", async () => {
367369 await visit(page, `/integrations/${slug}?addAccount=1`);
368370 await page.getByRole("tab", { name: "OAuth", exact: true }).waitFor();
371+ });
372+ await step("Connect once using the saved app", async () => {
369373 const opened = page.waitForEvent("popup");
370374 await page.getByRole("button", { name: /^Connect(?: with OAuth)?$/ }).click();
371375 const popup = await opened;
@@ -374,14 +378,28 @@ for (const origin of ["integration", "workspace"] as const) { · 2 unmodified lines
374378 new URL(popup.url()).searchParams.get("client_id"),
375379 "the existing app is reused without another registration step",
376380 ).toBe(registered.client_id);
377− await popup.close();
381+ });
382+ await step("Approve provider sign-in and save the connection", async () => {
383+ const popup = page
384+ .context()
385+ .pages()
386+ .find((candidate) => candidate !== page);
387+ if (!popup) throw new Error("Provider sign-in window was not open");
388+ await popup.getByRole("button", { name: /admin/ }).click();
378389 await page
379− .getByRole("dialog")
380− .getByRole("button", { name: "Close", exact: true })
381− .last()
382− .click();
390+ .getByRole("heading", { name: /Add connection/ })
391+ .waitFor({ state: "hidden", timeout: 30_000 });
383392 });
384393 });
394+ const connections = yield* client.connections.list({ query: { integration: slug } });
395+ expect(connections, "provider consent saves the connection").toHaveLength(1);
396+ expect(connections[0]?.owner, "a shared app keeps the connection personal").toBe("user");
397+ const savedClients = yield* client.oauth.listClients({});
398+ expect(savedClients.find((saved) => saved.slug === app)?.origin).toEqual(
399+ origin === "integration"
400+ ? { kind: "manual", integration: slug }
401+ : { kind: "manual", integration: null },
402+ );
385403 const ledger = yield* Effect.promise(() => emulator.ledger.list());
386404 expect(
387405 ledger.filter((entry) => entry.method === "POST" && entry.path === "/register"),