fix(connections): preserve wizard recovery and repository restrictions

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-07 00:22:55 -05:00
1 parent cf8578a9dd
commit 87945c8cf3
15 files changed
+750 -189

No files matched your search

+28 -29
View File
@@ -20,45 +20,44 @@ import {
async function exerciseGitHubReviewSetup(page: Page, mock: ChatMock, seed: Seed, provider: ProviderCase) {
await expect(page.getByRole("heading", { name: "Choose agent", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Choose an agent", exact: true }).click();
await page.getByRole("button", { name: "Select agent…", exact: true }).click();
await page.getByRole("button", { name: "Select Maya", exact: true }).click();
await expect(page.getByText("Maya is not configured for low-trust review")).toBeVisible();
await expect(page.getByRole("link", { name: "Learn about low-trust agents" })).toHaveAttribute("href", /trust-and-low-trust-review/);
await page.getByRole("button", { name: "Change Maya to a low trust agent" }).click();
await expect(page.getByText("Maya is not configured for low-trust review")).toHaveCount(0);
await page.getByRole("button", { name: "Continue", exact: true }).click();
await expect.poll(() => mock.createdWithAgentId).toBe(seed.agentId);
await expect(page.getByRole("heading", { name: "Connect GitHub App", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Use an existing App" }).click();
await expect(page.getByRole("heading", { name: "Connect GitHub", exact: true })).toBeVisible();
await expect(page.getByLabel("GitHub account")).toHaveValue("personal");
await expect(page.getByLabel("App name", { exact: true })).toHaveValue("Maya");
await page.getByLabel("GitHub account").selectOption("organization");
await page.getByLabel("Organization", { exact: true }).fill("paperclip-ai");
await page.getByRole("button", { name: "Save & exit", exact: true }).click();
await page.goto(`/${seed.prefix}/apps/chat/connect?provider=github&purpose=chat&resume=endpoint-github`);
await expect(page.getByLabel("GitHub account")).toHaveValue("organization");
await expect(page.getByLabel("Organization", { exact: true })).toHaveValue("paperclip-ai");
await expect(page.getByRole("button", { name: "Select agent…", exact: true })).toHaveCount(0);
// Existing-App recovery stays available and preserves entered credentials after failure.
await page.getByRole("button", { name: "I already have an App" }).click();
await page.getByLabel("App ID", { exact: true }).fill("123456");
await page.getByLabel("Private key", { exact: true }).fill(GITHUB_PRIVATE_KEY_PASTE_FIXTURE);
await page.getByLabel("Webhook secret", { exact: true }).fill("github-webhook-secret");
await page.getByRole("button", { name: "Connect App", exact: true }).click();
await page.getByRole("button", { name: "Connect existing App", exact: true }).click();
await expect(page.getByRole("alert")).toContainText("GitHub rejected the supplied App credentials.");
await expect(page.getByLabel("Private key", { exact: true })).toHaveValue(GITHUB_PRIVATE_KEY_PASTE_FIXTURE);
await page.getByRole("button", { name: "Connect App", exact: true }).click();
await expect(page.getByRole("heading", { name: "Install GitHub App", exact: true })).toBeVisible();
await expect(page.getByText(provider.resourceLabel, { exact: true })).toHaveCount(0);
await page.getByRole("button", { name: "I’ve installed the App" }).click();
await expect(page.getByRole("heading", { name: "Select repositories", exact: true })).toBeVisible();
await expect(page.getByRole("link", { name: "Configure access on GitHub" })).toHaveAttribute("href", "https://github.com/settings/installations/2468");
await page.getByRole("button", { name: "Refresh access" }).click();
await expect.poll(() => mock.githubRepositoryRefreshes).toBe(2);
await page.getByRole("switch", { name: provider.resourceLabel, exact: true }).click();
await page.getByRole("button", { name: "Save repositories" }).click();
await expect(page.getByRole("heading", { name: "Verify connection & tools", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Verify connection", exact: true }).click();
await expect(page.getByRole("button", { name: "Continue", exact: true })).toBeDisabled();
await page.getByRole("button", { name: "Assign this bot’s GitHub tools" }).click();
await page.getByRole("button", { name: "Continue", exact: true }).click();
await page.getByRole("button", { name: "Connect existing App", exact: true }).click();
await expect(page.getByRole("heading", { name: "Connect your account", exact: true })).toBeVisible();
await page.getByLabel("Your GitHub connection").selectOption("personal-github");
await page.getByRole("button", { name: "Verify my account" }).click();
await page.getByLabel("Existing GitHub connection").selectOption("personal-github");
await expect(page.getByRole("button", { name: "Check my account" })).toBeVisible();
await page.getByRole("button", { name: "Check my account" }).click();
expect(mock.githubIdentityConfirmed).toBe(false);
await page.getByRole("button", { name: "Confirm this is my account" }).click();
await page.getByRole("button", { name: "Confirm my account" }).click();
await expect.poll(() => mock.githubIdentityConfirmed).toBe(true);
await expect(page.getByRole("heading", { name: "Configure behavior", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Save behavior" }).click();
await expect(page.getByRole("heading", { name: "Try it", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Finish without test" }).click();
await expect(page.getByRole("heading", { name: "GitHub connected", exact: true })).toBeVisible();
await expect(page.getByText(provider.resourceLabel, { exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: /Finish|Verify connection|Refresh access|Save repositories/ })).toHaveCount(0);
await page.getByRole("link", { name: "Connection settings", exact: true }).click();
await expect(page).toHaveURL(/\/apps\/chat\/endpoint-github\/settings$/);
const nav = page.getByRole("navigation", { name: "Chat connection" });
for (const tab of ["Settings", "Access", "Reviews", "Conversations", "Activity"]) {
@@ -82,8 +81,8 @@ async function exerciseGitHubReviewSetup(page: Page, mock: ChatMock, seed: Seed,
await page.reload();
await page.getByText("Connection health and controls", { exact: true }).click();
await page.getByRole("button", { name: "Reconnect", exact: true }).click();
await expect(page.getByRole("heading", { name: "Connect GitHub App", exact: true })).toBeVisible();
await expect(page.getByText(/Leave the credentials blank to keep them/)).toBeVisible();
await expect(page.getByRole("heading", { name: "Connect GitHub", exact: true })).toBeVisible();
await expect(page.getByText(/Leave these blank to reuse/)).toBeVisible();
await expect(page.getByRole("button", { name: "Choose an agent", exact: true })).toHaveCount(0);
await expect(page.getByLabel("Private key", { exact: true })).toHaveValue("");
const reconnectResponse = page.waitForResponse((response) =>
@@ -95,7 +94,7 @@ async function exerciseGitHubReviewSetup(page: Page, mock: ChatMock, seed: Seed,
const reconnected = await reconnectResponse;
expect(reconnected.request().postDataJSON()).toEqual({ action: "reconnect" });
expect(await reconnected.json()).toMatchObject({ assignedAgentId: seed.agentId, assignedAgentName: "Maya" });
await expect(page.getByRole("heading", { name: "Verify connection & tools", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "GitHub connected", exact: true })).toBeVisible();
mock.setStatus("active");
await page.goto(`/${seed.prefix}/apps/chat/endpoint-github/settings`);
await expect(page.getByText(/Maya is permanently assigned to this bot/)).toBeVisible();
+13 -1
View File
@@ -425,6 +425,18 @@ export async function installChatControlPlaneMock(
if (provider.provider === "github" && pathname.startsWith(`/api/chat-endpoints/${endpoint.id}/github/`)) {
const operation = pathname.split("/github/")[1];
const body = method === "GET" ? {} : bodyOf(route);
if (operation === "setup") {
if (!githubAppConnected) { await fulfill(route, { endpointId: endpoint.id, state: "create" }); return; }
if (!state.githubIdentityConfirmed) { await fulfill(route, { endpointId: endpoint.id, state: "identity", identityMethod: "existing_connection" }); return; }
githubConfiguration.configuration.toolsEnabled = true;
resource.enabled = true;
Object.assign(endpoint, { status: "active", botExternalId: "123456", resources: [resource], setup: { ...endpoint.setup, step: "complete" } });
await fulfill(route, { endpointId: endpoint.id, state: "connected", identityLinked: true, verification: { ready: true, checks: [] } }); return;
}
if (operation === "draft") {
Object.assign(endpoint.setup, { github: { ...endpoint.setup.github, appName: body.name, ownerType: body.ownerType, ownerLogin: body.ownerLogin } });
await fulfill(route, endpoint); return;
}
if (operation === "configuration") {
if (method === "PUT") { expect(body.expectedRevision).toBe(githubConfiguration.revision); githubConfiguration = { revision: githubConfiguration.revision + 1, configuration: body.configuration as typeof githubConfiguration.configuration }; }
await fulfill(route, githubConfiguration); return;
@@ -438,7 +450,7 @@ export async function installChatControlPlaneMock(
state.configuredCredentialKeys = Object.keys(body).sort();
if (state.setupAttempts === 1) { await fulfill(route, { error: "GitHub rejected the supplied App credentials." }, 422); return; }
githubAppConnected = true;
Object.assign(endpoint, { status: "attention", botUsername: "maya-paperclip[bot]" });
Object.assign(endpoint, { status: "attention", botExternalId: "123456", botUsername: "maya-paperclip[bot]", botLabel: "Maya" });
Object.assign(endpoint.setup, { github: { stage: "install", appSlug: "maya-paperclip", installationUrl: "https://github.com/apps/maya-paperclip/installations/new", managementUrl: "https://github.com/settings/installations/2468" } });
await fulfill(route, endpoint); return;
}