/* * ★ 비직렬 수량이 들어오는 문 — **오류 없이 사라지지 않는다.** * * `CanonicalRecord` 에 `quantityList` 자리를 냈다(담김의 `childQuantityList` 가 하루 먼저 같은 이유로 * 받은 자리다). 원본이 「이 자리에 이 품목이 얼마 남았다」를 말할 길이 없어서, 그것이 주된 경로인 * 시스템(식품 제조 MES)은 라이브를 낼 수 없었다. * * ── 배포 뒤 (2026-08-21) ──────────────────────────────────────────────────── * 커널 0.7.40 에 `ObjectEventMapping.quantityList` 가 들어와 **수량만 오는 레코드가 통과한다.** * 그 전까지는 `epcList` 와 `quantityList` 가 둘 다 비어 검증이 거부했고, 이 시험은 그 거부가 **아무 말도 없지 * 않다**(이유와 함께 계수된다)는 것을 지켰다. * * 이제 통과 쪽으로 고정한다 — 통과와 거부를 둘 다 받아들이면 다시 막혔을 때 초록으로 남는다. */ import { test } from 'node:test' import assert from 'node:assert/strict' import { ingestCanonicalRecords } from '../server/engine/canonical-ingest.ts' const T0 = '2026-08-20T09:00:00.000Z' const CLASS = 'https://chef.example.com/class/product-7f3a' const qtyRecord = { action: 'OBSERVE', bizStep: 'urn:epcglobal:cbv:bizstep:storing', disposition: 'urn:epcglobal:cbv:disp:sellable_accessible', quantityList: [{ epcClass: CLASS, quantity: 3.5, uom: 'KGM' }], readPoint: 'chef-wh:w1', bizLocation: 'chef-wh:w1' } test('수량만 오는 레코드가 통과하고 수량이 봉투까지 실린다', () => { const { accepted, rejected } = ingestCanonicalRecords(qtyRecord as any, 'plantA', T0) assert.deepEqual(rejected, [], `거부됐다 — 커널에 quantityList 자리가 있는지 확인: ${JSON.stringify(rejected)}`) assert.equal(accepted.length, 1) /* 자리만 통과하고 값이 빠지면 더 나쁘다 — 실린 줄 알고 잃는다. */ const ev: any = (accepted[0] as any).data ?? (accepted[0] as any).event ?? accepted[0] assert.deepEqual(ev.quantityList, [{ epcClass: CLASS, quantity: 3.5, uom: 'KGM' }]) assert.deepEqual(ev.epcList, [], '개체가 없으므로 빈 배열이다 — 지어내지 않는다') }) test('개체 관측은 그대로 통과한다 — 문을 내면서 기존 길을 막지 않는다', () => { const { accepted, rejected } = ingestCanonicalRecords( { epc: 'urn:epc:id:sgtin:9521321.012345.1', action: 'OBSERVE', bizStep: 'urn:epcglobal:cbv:bizstep:storing', readPoint: 'chef-wh:w1' } as any, 'plantA', T0 ) assert.equal(rejected.length, 0, `개체 관측이 거부되면 안 된다: ${JSON.stringify(rejected)}`) assert.equal(accepted.length, 1) }) test('개체도 수량도 없으면 거부된다 — 무엇을 관측했는지 말하지 않았다', () => { const { accepted, rejected } = ingestCanonicalRecords( { action: 'OBSERVE', bizStep: 'urn:epcglobal:cbv:bizstep:storing', readPoint: 'chef-wh:w1' } as any, 'plantA', T0 ) assert.equal(accepted.length, 0) assert.equal(rejected.length, 1) assert.ok(rejected[0].errors?.length, '이유 없이 버리지 않는다') })