test(ai-chat): safety-critical coverage + a11y + pure refactors

Unit tests for the safety-critical paths: crypto secret-box (round-trip,
tamper detection, wrong key), the SSRF guard (blocked ranges + DNS-rebinding),
the ai-chat tools service, the page-embedding repo, and the
assistant-parts/serialization helpers. Those server helpers (assistantParts,
rowToUiMessage, serializeSteps) are exported ONLY for the tests — no runtime
change.

Also: keyboard a11y on the chat history header and conversation rows
(role/tabIndex/Enter+Space), and DRY refactors that move shared logic into one
place (isToolPart -> tool-parts util; buildInitialValues in the MCP form).

The behaviour-changing edits that previously rode along in this commit are
split out into the following two commits, per review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-20 17:58:44 +03:00
committed by vvzvlad
parent c8af637654
commit f1980cf425
13 changed files with 571 additions and 236 deletions

View File

@@ -47,6 +47,21 @@ interface AiMcpServerFormProps {
onClose: () => void;
}
// Build the form's field values from a (possibly undefined) server. Used both
// for the initial mount and for re-hydration when the modal is reused for a
// different server, so the two stay in sync. authHeader is always empty: it is
// a write-only secret buffer never echoed back from the server.
function buildInitialValues(server?: IAiMcpServer): FormValues {
return {
name: server?.name ?? "",
transport: server?.transport ?? "http",
url: server?.url ?? "",
authHeader: "",
toolAllowlist: server?.toolAllowlist ?? [],
enabled: server?.enabled ?? true,
};
}
// Tavily preset (§8.10): the API key goes in the Authorization HEADER, not the URL.
const TAVILY_PRESET = {
name: "Tavily",
@@ -72,26 +87,12 @@ export default function AiMcpServerForm({
const form = useForm<FormValues>({
validate: zod4Resolver(formSchema),
initialValues: {
name: server?.name ?? "",
transport: server?.transport ?? "http",
url: server?.url ?? "",
authHeader: "",
toolAllowlist: server?.toolAllowlist ?? [],
enabled: server?.enabled ?? true,
},
initialValues: buildInitialValues(server),
});
// Re-hydrate when the target server changes (e.g. reusing the modal).
useEffect(() => {
form.setValues({
name: server?.name ?? "",
transport: server?.transport ?? "http",
url: server?.url ?? "",
authHeader: "",
toolAllowlist: server?.toolAllowlist ?? [],
enabled: server?.enabled ?? true,
});
form.setValues(buildInitialValues(server));
form.resetDirty();
setHasHeaders(server?.hasHeaders ?? false);
setHeadersCleared(false);