mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
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:
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");
|
||||
|
||||
Reference in new issue
Block a user