test: await connection setup and persisted runtime identity

Complete the dialog fixture context and wait for the actual setup render. Observe the post-spawn PID update before checking the starting service identity, preserving provisioning and readiness assertions.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-29 13:04:12 -05:00
1 parent 3a12ec4bef
commit ed3a0df1c1
2 files changed
+43 -3

No files matched your search

@@ -6636,7 +6636,7 @@ describeEmbeddedPostgres("workspace runtime service control persistence", () =>
}
throw new Error("Timed out waiting for runtime service process marker");
};
const waitForPersistedStatus = async (status: string) => {
const waitForPersistedStatus = async (status: string, requireProviderRef = false) => {
const deadline = Date.now() + 5_000;
while (Date.now() < deadline) {
const row = await db
@@ -6644,7 +6644,9 @@ describeEmbeddedPostgres("workspace runtime service control persistence", () =>
.from(workspaceRuntimeServices)
.where(eq(workspaceRuntimeServices.executionWorkspaceId, executionWorkspaceId))
.then((rows) => rows[0] ?? null);
if (row?.status === status) return row;
// The provisional starting row is durable before spawn; the child can
// write its marker before the subsequent PID update reaches the DB.
if (row?.status === status && (!requireProviderRef || /^\d+$/.test(row.providerRef ?? ""))) return row;
await new Promise((resolve) => setTimeout(resolve, 25));
}
throw new Error(`Timed out waiting for persisted runtime service status ${status}`);
@@ -6711,7 +6713,7 @@ describeEmbeddedPostgres("workspace runtime service control persistence", () =>
expect(existsSync(markerPath)).toBe(false);
await waitForMarker(markerPath);
const startingRow = await waitForPersistedStatus("starting");
const startingRow = await waitForPersistedStatus("starting", true);
expect(startingRow).toMatchObject({
companyId,
projectId,
@@ -62,6 +62,15 @@ const connectionIntentsApiMocks = vi.hoisted(() => ({
decline: vi.fn(),
}));
const routerMocks = vi.hoisted(() => ({
searchParams: new URLSearchParams(),
readSearchParams: vi.fn(),
setSearchParams: vi.fn(),
navigate: vi.fn(),
setBreadcrumbs: vi.fn(),
pushToast: vi.fn(),
}));
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
async function act(callback: () => void | Promise<void>) {
@@ -79,11 +88,30 @@ async function act(callback: () => void | Promise<void>) {
}
vi.mock("@/lib/router", () => ({
useParams: () => ({}),
useSearchParams: () => {
routerMocks.readSearchParams();
return [routerMocks.searchParams, routerMocks.setSearchParams];
},
useNavigate: () => routerMocks.navigate,
Link: ({ to, children, className }: { to: string; children: ReactNode; className?: string }) => (
<a href={to} className={className}>{children}</a>
),
}));
vi.mock("@/context/CompanyContext", async (importOriginal) => ({
...await importOriginal<typeof import("../context/CompanyContext")>(),
useCompany: () => ({ selectedCompanyId: issueThreadInteractionFixtureMeta.companyId, selectedCompany: null }),
}));
vi.mock("@/context/BreadcrumbContext", async (importOriginal) => ({
...await importOriginal<typeof import("../context/BreadcrumbContext")>(),
useBreadcrumbs: () => ({ setBreadcrumbs: routerMocks.setBreadcrumbs }),
}));
vi.mock("@/context/ToastContext", async (importOriginal) => ({
...await importOriginal<typeof import("../context/ToastContext")>(),
useToast: () => ({ pushToast: routerMocks.pushToast }),
}));
vi.mock("@/api/connection-intents", () => ({ connectionIntentsApi: connectionIntentsApiMocks }));
vi.mock("./task-chat/TaskChatRichInput", () => ({
@@ -128,6 +156,7 @@ afterEach(() => {
root = null;
container = null;
localStorage.clear();
routerMocks.readSearchParams.mockClear();
});
describe("IssueThreadInteractionCard", () => {
@@ -196,6 +225,15 @@ describe("IssueThreadInteractionCard", () => {
"interaction-connection-intent-default",
),
);
// setupOptions being called precedes React Query publishing its result.
// Wait for the real setup flow, not just the dialog's loading shell.
await vi.waitFor(async () => {
await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); });
const setupDialog = document.body.querySelector('[role="dialog"]');
expect(setupDialog).toBeTruthy();
expect(setupDialog?.textContent).not.toContain("Loading connection options");
expect(routerMocks.readSearchParams).toHaveBeenCalled();
});
const dialog = document.body.querySelector('[role="dialog"]');
expect(dialog).toBeTruthy();
expect(dialog?.textContent).toContain("Connect Notion");