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
327 });327 });
328 yield* Effect.addFinalizer(() =>328 yield* Effect.addFinalizer(() =>
329 Effect.gen(function* () {329 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 }
330 yield* client.mcp.removeServer({ params: { slug } }).pipe(Effect.ignore);338 yield* client.mcp.removeServer({ params: { slug } }).pipe(Effect.ignore);
331 yield* client.oauth339 yield* client.oauth
332 .removeClient({ params: { slug: app }, payload: { owner: "org" } })340 .removeClient({ params: { slug: app }, payload: { owner: "org" } })
@@ -355,17 +363,13 @@ for (const origin of ["integration", "workspace"] as const) { · 22 unmodified lines
355 ...(origin === "integration" ? { originIntegration: slug } : {}),363 ...(origin === "integration" ? { originIntegration: slug } : {}),
356 },364 },
357 });365 });
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 );
364 yield* Effect.promise(() => emulator.ledger.clear());366 yield* Effect.promise(() => emulator.ledger.clear());
365 yield* browser.session(identity, async ({ page, step }) => {367 yield* browser.session(identity, async ({ page, step }) => {
366 await step("Open an integration that already has a saved OAuth app", async () => {368 await step("Open an integration that already has a saved OAuth app", async () => {
367 await visit(page, `/integrations/${slug}?addAccount=1`);369 await visit(page, `/integrations/${slug}?addAccount=1`);
368 await page.getByRole("tab", { name: "OAuth", exact: true }).waitFor();370 await page.getByRole("tab", { name: "OAuth", exact: true }).waitFor();
371 });
372 await step("Connect once using the saved app", async () => {
369 const opened = page.waitForEvent("popup");373 const opened = page.waitForEvent("popup");
370 await page.getByRole("button", { name: /^Connect(?: with OAuth)?$/ }).click();374 await page.getByRole("button", { name: /^Connect(?: with OAuth)?$/ }).click();
371 const popup = await opened;375 const popup = await opened;
@@ -374,14 +378,28 @@ for (const origin of ["integration", "workspace"] as const) { · 2 unmodified lines
374 new URL(popup.url()).searchParams.get("client_id"),378 new URL(popup.url()).searchParams.get("client_id"),
375 "the existing app is reused without another registration step",379 "the existing app is reused without another registration step",
376 ).toBe(registered.client_id);380 ).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();
378 await page389 await page
379 .getByRole("dialog")390 .getByRole("heading", { name: /Add connection/ })
380 .getByRole("button", { name: "Close", exact: true })391 .waitFor({ state: "hidden", timeout: 30_000 });
381 .last()
382 .click();
383 });392 });
384 });393 });
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 );
385 const ledger = yield* Effect.promise(() => emulator.ledger.list());403 const ledger = yield* Effect.promise(() => emulator.ledger.list());
386 expect(404 expect(
387 ledger.filter((entry) => entry.method === "POST" && entry.path === "/register"),405 ledger.filter((entry) => entry.method === "POST" && entry.path === "/register"),