Skip to content

Commit dc13a28

Browse files
dmealingclaude
andcommitted
test(codegen-ts): cover tryExtractLenient ok:false + optional-nested null/present at runtime
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 80d26d5 commit dc13a28

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

server/typescript/packages/codegen-ts/test/extractor-codegen.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ describe("Extractor codegen — import-and-RUN proof (bun dynamic import)", () =
139139
expect(order.tags.every((t: unknown) => typeof t === "string")).toBe(true);
140140
// optional scalar array present → null-filtered string[]
141141
expect(order.flags).toEqual(["fragile"]);
142+
// optional single nested object `shipTo` ABSENT in this input → the `m.shipTo ? toStrictCustomer(...) : null`
143+
// branch produces null at runtime (not undefined, not a partial object).
144+
expect(order.shipTo).toBeNull();
145+
146+
// optional single nested object PRESENT → the null-guard recurses into toStrictCustomer and populates.
147+
const withShipTo =
148+
'{ "customer": { "name": "Ada" }, "lines": [ { "sku": "A", "qty": 2 } ], "tags": ["x"], "shipTo": { "name": "Grace" } }';
149+
const shipped = ex.extractOrderOut(root, withShipTo);
150+
expect(shipped.shipTo).not.toBeNull();
151+
expect(shipped.shipTo.name).toBe("Grace");
152+
// and when shipTo is genuinely absent on a separate clean input, it is null (re-confirm the branch)
153+
const noShipTo = ex.extractOrderOut(
154+
root,
155+
'{ "customer": { "name": "Ada" }, "lines": [ { "sku": "A", "qty": 2 } ], "tags": ["x"] }',
156+
);
157+
expect(noShipTo.shipTo).toBeNull();
142158

143159
// missing required `customer` → throws
144160
expect(() => ex.extractOrderOut(root, '{ "lines": [] }')).toThrow();

server/typescript/packages/codegen-ts/test/fr010-output-codegen.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,24 @@ describe("FR-010 codegen — import-and-RUN proof (bun dynamic import)", () => {
300300
// tryExtract gate: non-empty, no required lost → ok
301301
const gate = parser.tryExtractLenientTicketOut(dirty);
302302
expect(gate.ok).toBe(true);
303+
// and the success wrapper still carries the full result (data + report)
304+
expect(gate.result.data.subject).toBe("Cannot log in");
305+
expect(gate.result.report.hasLostRequired()).toBe(false);
306+
307+
// ---- tryExtract gate ok:FALSE branch (run-asserted) ----
308+
// (a) a payload that LOST a @required field (omit `subject`/`priority`/`score`/`tags`) →
309+
// ok === false because report.hasLostRequired() is true; the wrapper still carries the report.
310+
const lostRequired = '{ "assignee": "Grace" }';
311+
const lost = parser.tryExtractLenientTicketOut(lostRequired);
312+
expect(lost.ok).toBe(false);
313+
expect(lost.result.report.hasLostRequired()).toBe(true);
314+
expect(lost.result.report.lostRequired()).toContain("subject");
315+
316+
// (b) empty/garbage input → ok === false because report.isEmpty() is true; report still present.
317+
const empty = parser.tryExtractLenientTicketOut(" ");
318+
expect(empty.ok).toBe(false);
319+
expect(empty.result.report.isEmpty()).toBe(true);
320+
expect(empty.result.report).toBeDefined();
303321

304322
// ---- render*Format(): comment-free guide fragment ----
305323
const fragment: string = prompt.renderTicketOutFormat();

0 commit comments

Comments
 (0)