fix(auth): harden diagnostic cleanup races

This commit is contained in:
2026-08-17 18:41:40 +02:00
parent af16464e68
commit d12b629420
7 changed files with 387 additions and 36 deletions
@@ -327,6 +327,7 @@ test("ignores an older validation response that completes after a newer connecti
act(() => releaseTest());
const authentication = await screen.findByTestId("workspace-authentication");
expect(within(authentication).getByText(/Newer connection result/)).toBeVisible();
expect(screen.getByRole("button", { name: "Validate workspace source" })).not.toBeDisabled();
act(() => releaseValidation());
await act(async () => {
@@ -335,6 +336,163 @@ test("ignores an older validation response that completes after a newer connecti
});
expect(within(authentication).getByText(/Newer connection result/)).toBeVisible();
expect(within(authentication).queryByText("Passed")).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "Validate workspace source" })).not.toBeDisabled();
});
test("does not apply a diagnostic that completes after its dialog is closed and reopened", async () => {
const user = userEvent.setup();
let release!: () => void;
let started!: () => void;
let settled!: () => void;
const held = new Promise<void>((resolve) => { release = resolve; });
const requestStarted = new Promise<void>((resolve) => { started = resolve; });
const requestSettled = new Promise<void>((resolve) => { settled = resolve; });
server.use(http.post("/api/workspaces/validate", async () => {
started();
try {
await held;
return HttpResponse.json({
workspace, contract: {}, activatable: true, diagnostics: [], authentication: readyAuthentication,
});
} finally {
settled();
}
}));
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
const onClose = vi.fn();
const view = render(
<QueryClientProvider client={client}>
<WorkspaceManager open onClose={onClose} canManageWorkspace canManageSecrets />
</QueryClientProvider>,
);
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
await user.click(screen.getByRole("button", { name: "Validate workspace source" }));
await requestStarted;
await user.click(screen.getByRole("button", { name: "Close workspace management" }));
expect(onClose).toHaveBeenCalledTimes(1);
view.rerender(
<QueryClientProvider client={client}>
<WorkspaceManager open={false} onClose={onClose} canManageWorkspace canManageSecrets />
</QueryClientProvider>,
);
view.rerender(
<QueryClientProvider client={client}>
<WorkspaceManager open onClose={onClose} canManageWorkspace canManageSecrets />
</QueryClientProvider>,
);
expect(await screen.findByRole("button", { name: "Validate workspace source" })).not.toBeDisabled();
act(() => release());
await act(async () => {
await requestSettled;
await new Promise((resolve) => { setTimeout(resolve, 50); });
});
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
});
test("does not apply a diagnostic that completes after returning through Level 1", async () => {
const user = userEvent.setup();
let release!: () => void;
let started!: () => void;
let settled!: () => void;
const held = new Promise<void>((resolve) => { release = resolve; });
const requestStarted = new Promise<void>((resolve) => { started = resolve; });
const requestSettled = new Promise<void>((resolve) => { settled = resolve; });
server.use(http.post("/api/workspaces/validate", async () => {
started();
try {
await held;
return HttpResponse.json({
workspace, contract: {}, activatable: true, diagnostics: [], authentication: readyAuthentication,
});
} finally {
settled();
}
}));
renderManager();
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
await user.click(screen.getByRole("button", { name: "Validate workspace source" }));
await requestStarted;
await user.click(within(screen.getByRole("navigation", { name: "Workspaces" }))
.getByRole("button", { name: "Back to Level 1" }));
expect(screen.getByTestId("workspace-overview")).toBeVisible();
await user.click(screen.getByRole("button", { name: "PSD Clinical" }));
expect(await screen.findByRole("button", { name: "Validate workspace source" })).not.toBeDisabled();
act(() => release());
await act(async () => {
await requestSettled;
await new Promise((resolve) => { setTimeout(resolve, 50); });
});
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
});
test("does not apply a diagnostic after changing workspaces and returning", async () => {
const user = userEvent.setup();
const alternateId = "other-clinical";
const alternateWorkspace = canonicalWorkspaceFixture(alternateId);
const alternateRevision = workspaceRevisionFixture(alternateId);
let release!: () => void;
let started!: () => void;
let settled!: () => void;
const held = new Promise<void>((resolve) => { release = resolve; });
const requestStarted = new Promise<void>((resolve) => { started = resolve; });
const requestSettled = new Promise<void>((resolve) => { settled = resolve; });
server.use(
http.get("/api/workspaces", () => HttpResponse.json([
workspaceSummaryFixture("psd-clinical", {
displayName: "PSD Clinical",
description: "Clinical data",
configurationState: "configuration_required",
revision,
}),
workspaceSummaryFixture(alternateId, {
displayName: "Other Clinical",
description: "Other data",
configurationState: "configuration_required",
revision: alternateRevision,
}),
])),
http.get(`/api/workspaces/${alternateId}`, () => HttpResponse.json({
workspace: alternateWorkspace,
revision: alternateRevision,
})),
http.get(`/api/workspaces/${alternateId}/runtime-configuration`, () => HttpResponse.json({
workspaceId: alternateId,
revision: alternateRevision,
configurationState: "configuration_required",
requirements: [{ ...requirement }],
})),
http.post("/api/workspaces/validate", async () => {
started();
try {
await held;
return HttpResponse.json({
workspace, contract: {}, activatable: true, diagnostics: [], authentication: readyAuthentication,
});
} finally {
settled();
}
}),
);
renderManager();
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
await user.click(screen.getByRole("button", { name: "Validate workspace source" }));
await requestStarted;
await user.click(screen.getByRole("button", { name: "Other Clinical" }));
expect(await screen.findByRole("heading", { name: "Other Clinical" })).toBeVisible();
await user.click(screen.getByRole("button", { name: "PSD Clinical" }));
expect(await screen.findByRole("button", { name: "Validate workspace source" })).not.toBeDisabled();
act(() => release());
await act(async () => {
await requestSettled;
await new Promise((resolve) => { setTimeout(resolve, 50); });
});
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
});
test("never renders a hostile authentication field rejected by the API decoder", async () => {
+30 -16
View File
@@ -87,8 +87,37 @@ export function WorkspaceManager({
const operationEpochRef = useRef(0);
const diagnosticEpochRef = useRef(0);
const selectedIdRef = useRef(selectedId);
const wasOpenRef = useRef(open);
selectedIdRef.current = selectedId;
const clearGlobalMessages = () => {
setNotice(undefined);
setDiagnostics([]);
};
const clearDiagnosticMessages = () => {
diagnosticEpochRef.current += 1;
setValidationNotice(undefined);
setValidationDiagnostics([]);
setConnectionNotice(undefined);
setConnectionDiagnostics([]);
setAuthentication(undefined);
setBusyAction((current) => current === "validate" || current === "test" ? undefined : current);
};
const clearMessages = () => {
clearGlobalMessages();
clearDiagnosticMessages();
};
useEffect(() => () => { operationEpochRef.current += 1; }, []);
useEffect(() => {
if (wasOpenRef.current && !open) {
setSecretValues({});
clearMessages();
}
wasOpenRef.current = open;
}, [open]);
async function guardedQuery<T>(request: () => Promise<T>, targetId?: string): Promise<T> {
const guard = captureAuthOperation({
@@ -131,21 +160,6 @@ export function WorkspaceManager({
enabled: Boolean(open && selectedId),
});
const clearMessages = () => {
setNotice(undefined);
setDiagnostics([]);
setValidationNotice(undefined);
setValidationDiagnostics([]);
setConnectionNotice(undefined);
setConnectionDiagnostics([]);
setAuthentication(undefined);
};
const clearGlobalMessages = () => {
setNotice(undefined);
setDiagnostics([]);
};
const close = () => {
setSecretValues({});
clearMessages();
@@ -167,8 +181,8 @@ export function WorkspaceManager({
async function updateRepository() {
const guard = captureAuthOperation({ disposalEpoch: operationEpochRef.current });
if (!guard) return;
setBusyAction("repository");
clearMessages();
setBusyAction("repository");
try {
await pullWorkspaceRegistry();
if (!isAuthOperationCurrent(guard, { disposalEpoch: operationEpochRef.current })) return;