test(knowledge_fs): restore parser branch coverage

This commit is contained in:
Stephen Zhou 2026-08-25 20:17:19 +08:00
parent 979f09b30d
commit 63efda734c
No known key found for this signature in database
3 changed files with 130 additions and 3 deletions

View File

@ -1,6 +1,6 @@
{
"schemaVersion": 5,
"subtreeTree": "3915987c133895325c3953f994c5e81ac7aafdfe",
"subtreeTree": "37121cd70481e76320d1f67060a3830c81450fdd",
"openapiSha256": "2cf348c68bbe65dd51bbde9a0a4f91398beeebd79e89e9288c9386b26ae09796",
"capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7",
"capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3",

View File

@ -26,6 +26,8 @@
- Added behavior tests for declared MIME types, octet-stream inference, native lightweight-text
routing, and the browser file-picker contract. The new tests were observed failing before the
implementation and passing afterward.
- Added deadline and reasonless-abort coverage for the bounded Unstructured request lifecycle after
the CI branch-coverage gate exposed the remaining paths.
## Why
@ -37,8 +39,8 @@ parser restores compatibility without adding a new parser, storage path, or netw
- `pnpm --filter @knowledge/api exec vitest run src/document-upload-utils.test.ts` — passed (21 tests).
- `pnpm --filter @knowledge/parsers exec vitest run src/parser.test.ts` — passed (55 tests).
- `pnpm --filter @knowledge/parsers test:coverage` — passed with 95.69% statements/lines,
90.02% branches, and 97.52% functions.
- `pnpm --filter @knowledge/parsers test:coverage` — passed with 95.99% statements/lines,
90.10% branches, and 98.34% functions (62 tests).
- `pnpm --filter @knowledge/api-app exec vitest run src/parser-options.test.ts` — passed (5 tests).
- `vp test run --project unit features/new-rag/__tests__/documents-page.spec.tsx` — passed (203 tests).
- KnowledgeFS typechecks — passed; the full Turbo test pipeline passed (22 tasks), including the API

View File

@ -1819,6 +1819,35 @@ describe("parser adapters", () => {
expect(sleepCalls).toBe(0);
});
it.each([0, 1])(
"uses the bounded default delay before retrying a transient provider failure (%d ms)",
async (retryDelayMs) => {
let fetchCalls = 0;
const parser = createUnstructuredParserClient({
endpoint: "https://unstructured.example.test",
fetch: async () => {
fetchCalls += 1;
return new Response(JSON.stringify([{ text: "Recovered", type: "NarrativeText" }]), {
status: fetchCalls === 1 ? 500 : 200,
});
},
maxRetries: 1,
retryDelayMs,
});
await expect(
parser.parse({
body: new Uint8Array([1]),
documentAssetId,
filename: "delayed-retry.pdf",
mimeType: "application/pdf",
version: 1,
}),
).resolves.toMatchObject({ parser: "unstructured" });
expect(fetchCalls).toBe(2);
},
);
it("preserves caller cancellation while an Unstructured request is active", async () => {
const controller = new AbortController();
let fetchStarted = false;
@ -1849,6 +1878,102 @@ describe("parser adapters", () => {
await expect(pending).rejects.toMatchObject({ name: "AbortError" });
});
it("rejects an Unstructured request whose caller signal is already aborted", async () => {
const controller = new AbortController();
controller.abort();
const parser = createUnstructuredParserClient({
endpoint: "https://unstructured.example.test",
fetch: async () => new Response("[]"),
});
await expect(
parser.parse({
body: new Uint8Array([1]),
documentAssetId,
filename: "already-aborted.pdf",
mimeType: "application/pdf",
signal: controller.signal,
version: 1,
}),
).rejects.toMatchObject({ name: "AbortError" });
});
it("aborts an active Unstructured request when its deadline expires", async () => {
const parser = createUnstructuredParserClient({
endpoint: "https://unstructured.example.test",
fetch: async (input) => {
const request = input instanceof Request ? input : new Request(input);
return await new Promise<Response>((_resolve, reject) => {
request.signal.addEventListener("abort", () => reject(request.signal.reason), {
once: true,
});
});
},
requestTimeoutMs: 1,
});
await expect(
parser.parse({
body: new Uint8Array([1]),
documentAssetId,
filename: "timeout.pdf",
mimeType: "application/pdf",
version: 1,
}),
).rejects.toThrow("Unstructured parser request timed out after requestTimeoutMs=1");
});
it("rejects a successful Unstructured response completed after its deadline", async () => {
const parser = createUnstructuredParserClient({
endpoint: "https://unstructured.example.test",
fetch: async () => {
await new Promise((resolve) => setTimeout(resolve, 5));
return new Response(JSON.stringify([{ text: "Too late", type: "NarrativeText" }]));
},
requestTimeoutMs: 1,
});
await expect(
parser.parse({
body: new Uint8Array([1]),
documentAssetId,
filename: "late-success.pdf",
mimeType: "application/pdf",
version: 1,
}),
).rejects.toThrow("Unstructured parser request timed out after requestTimeoutMs=1");
});
it("uses the standard AbortError when an abort event has no signal reason", async () => {
const controller = new AbortController();
let fetchStarted = false;
const parser = createUnstructuredParserClient({
endpoint: "https://unstructured.example.test",
fetch: async (input) => {
const request = input instanceof Request ? input : new Request(input);
fetchStarted = true;
return await new Promise<Response>((_resolve, reject) => {
request.signal.addEventListener("abort", () => reject(request.signal.reason), {
once: true,
});
});
},
});
const pending = parser.parse({
body: new Uint8Array([1]),
documentAssetId,
filename: "abort-event.pdf",
mimeType: "application/pdf",
signal: controller.signal,
version: 1,
});
await waitForCondition(() => fetchStarted);
controller.signal.dispatchEvent(new Event("abort"));
await expect(pending).rejects.toMatchObject({ name: "AbortError" });
});
it("validates Unstructured retry and resource bounds", () => {
const base = {
endpoint: "https://unstructured.example.test",