fix(auth): harden interactive diagnostic lifecycle
This commit is contained in:
@@ -273,8 +273,68 @@ test("clears a previous authentication result as soon as validation is retried",
|
||||
await waitFor(() => expect(calls).toBe(2));
|
||||
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
|
||||
|
||||
releaseRetry();
|
||||
expect(await screen.findByTestId("workspace-authentication")).toBeVisible();
|
||||
releaseRetry();
|
||||
expect(await screen.findByTestId("workspace-authentication")).toBeVisible();
|
||||
});
|
||||
|
||||
test("ignores an older validation response that completes after a newer connection test", async () => {
|
||||
const user = userEvent.setup();
|
||||
let releaseValidation!: () => void;
|
||||
let releaseTest!: () => void;
|
||||
let validationStarted!: () => void;
|
||||
let testStarted!: () => void;
|
||||
let validationSettled!: () => void;
|
||||
const heldValidation = new Promise<void>((resolve) => { releaseValidation = resolve; });
|
||||
const heldTest = new Promise<void>((resolve) => { releaseTest = resolve; });
|
||||
const validationRequestStarted = new Promise<void>((resolve) => { validationStarted = resolve; });
|
||||
const testRequestStarted = new Promise<void>((resolve) => { testStarted = resolve; });
|
||||
const validationRequestSettled = new Promise<void>((resolve) => { validationSettled = resolve; });
|
||||
server.use(
|
||||
http.post("/api/workspaces/validate", async () => {
|
||||
validationStarted();
|
||||
try {
|
||||
await heldValidation;
|
||||
return HttpResponse.json({
|
||||
workspace, contract: {}, activatable: true, diagnostics: [], authentication: readyAuthentication,
|
||||
});
|
||||
} finally {
|
||||
validationSettled();
|
||||
}
|
||||
}),
|
||||
http.post("/api/workspaces/psd-clinical/test", async () => {
|
||||
testStarted();
|
||||
await heldTest;
|
||||
return HttpResponse.json({
|
||||
activatable: false,
|
||||
diagnostics: [],
|
||||
authentication: {
|
||||
ready: false,
|
||||
mode: "oidc",
|
||||
checks: [{ level: "error", code: "oidc_secret_missing", message: "Newer connection result." }],
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
renderManager();
|
||||
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Validate workspace source" }));
|
||||
await validationRequestStarted;
|
||||
await user.click(screen.getByRole("button", { name: "Test workspace connections" }));
|
||||
await testRequestStarted;
|
||||
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
|
||||
|
||||
act(() => releaseTest());
|
||||
const authentication = await screen.findByTestId("workspace-authentication");
|
||||
expect(within(authentication).getByText(/Newer connection result/)).toBeVisible();
|
||||
|
||||
act(() => releaseValidation());
|
||||
await act(async () => {
|
||||
await validationRequestSettled;
|
||||
await new Promise((resolve) => { setTimeout(resolve, 50); });
|
||||
});
|
||||
expect(within(authentication).getByText(/Newer connection result/)).toBeVisible();
|
||||
expect(within(authentication).queryByText("Passed")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("never renders a hostile authentication field rejected by the API decoder", async () => {
|
||||
|
||||
@@ -85,6 +85,7 @@ export function WorkspaceManager({
|
||||
const [authentication, setAuthentication] = useState<AuthDiagnostics>();
|
||||
const [busyAction, setBusyAction] = useState<string>();
|
||||
const operationEpochRef = useRef(0);
|
||||
const diagnosticEpochRef = useRef(0);
|
||||
const selectedIdRef = useRef(selectedId);
|
||||
selectedIdRef.current = selectedId;
|
||||
useEffect(() => () => { operationEpochRef.current += 1; }, []);
|
||||
@@ -192,6 +193,7 @@ export function WorkspaceManager({
|
||||
if (!detailQuery.data) return;
|
||||
const guard = captureAuthOperation({ sessionId: selectedId, disposalEpoch: operationEpochRef.current });
|
||||
if (!guard) return;
|
||||
const diagnosticEpoch = ++diagnosticEpochRef.current;
|
||||
setBusyAction("validate");
|
||||
clearGlobalMessages();
|
||||
setAuthentication(undefined);
|
||||
@@ -199,15 +201,18 @@ export function WorkspaceManager({
|
||||
setValidationDiagnostics([]);
|
||||
try {
|
||||
const result = await validateWorkspace(detailQuery.data.workspace);
|
||||
if (!isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) return;
|
||||
if (diagnosticEpoch !== diagnosticEpochRef.current ||
|
||||
!isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) return;
|
||||
setAuthentication(result.authentication);
|
||||
setValidationNotice(result.activatable ? "Workspace source and authentication are valid." : "Workspace source is valid.");
|
||||
} catch (error) {
|
||||
if (isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) {
|
||||
if (diagnosticEpoch === diagnosticEpochRef.current &&
|
||||
isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) {
|
||||
setValidationDiagnostics([publicError(error, "workspace_invalid: Workspace validation could not be completed")]);
|
||||
}
|
||||
} finally {
|
||||
if (isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) setBusyAction(undefined);
|
||||
if (diagnosticEpoch === diagnosticEpochRef.current &&
|
||||
isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) setBusyAction(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +221,7 @@ export function WorkspaceManager({
|
||||
const targetId = selectedId;
|
||||
const guard = captureAuthOperation({ sessionId: targetId, disposalEpoch: operationEpochRef.current });
|
||||
if (!guard) return;
|
||||
const diagnosticEpoch = ++diagnosticEpochRef.current;
|
||||
setBusyAction("test");
|
||||
clearGlobalMessages();
|
||||
setAuthentication(undefined);
|
||||
@@ -223,7 +229,8 @@ export function WorkspaceManager({
|
||||
setConnectionDiagnostics([]);
|
||||
try {
|
||||
const result = await testWorkspace(selectedId);
|
||||
if (!isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) return;
|
||||
if (diagnosticEpoch !== diagnosticEpochRef.current ||
|
||||
!isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) return;
|
||||
setAuthentication(result.authentication);
|
||||
const issues = result.diagnostics.filter(({ level }) => level !== "info");
|
||||
const informational = result.diagnostics.find(({ level }) => level === "info");
|
||||
@@ -236,11 +243,13 @@ export function WorkspaceManager({
|
||||
: "Workspace connection test completed.");
|
||||
}
|
||||
} catch (error) {
|
||||
if (isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) {
|
||||
if (diagnosticEpoch === diagnosticEpochRef.current &&
|
||||
isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) {
|
||||
setConnectionDiagnostics([publicError(error, "connector_unavailable: Workspace connections could not be tested")]);
|
||||
}
|
||||
} finally {
|
||||
if (isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) setBusyAction(undefined);
|
||||
if (diagnosticEpoch === diagnosticEpochRef.current &&
|
||||
isAuthOperationCurrent(guard, { sessionId: selectedIdRef.current, disposalEpoch: operationEpochRef.current })) setBusyAction(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user