/* * 사건이 **어느 물품을 건드렸나** — 브로드캐스팅이 바뀐 것만 만들 수 있게 (2026-08-21). * * 이 판정이 틀리면 **화면이 낡는다.** 그래서 지키는 것을 값으로 못 박는다: * ① 개체를 말한 자리를 전부 읽는다 — 관측·집약(부모/자식)·변환(입력/출력). * ② 부류 수량만 말한 사건은 「모른다」다 — 그 부류의 어느 개체인지 말하지 않았다. * ③ 물품을 만지지 않는다고 **선언된** 사건은 빈 범위다(작업·설비·인원·자산·오더·품질·확인·에너지). * ④ 낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때 알리지 않고 빠뜨리지 않는다. * * 「모른다」(`null`)와 「하나도 없다」(빈 집합)는 **다른 사실**이다. 앞은 전부 다시 만들게 하고 뒤는 * 물품을 건너뛰게 한다 — 둘을 같게 두면 둘 중 하나가 반드시 틀린다. */ import { test } from 'node:test' import assert from 'node:assert/strict' import { touchedItemKeys } from '../shared/touched-items.ts' const epcis = (data: any) => ({ eventType: 'epcis.object', data }) test('관측 사건은 말한 개체를 모두 낸다', () => { const got = touchedItemKeys(epcis({ epcList: ['urn:epc:id:sgtin:952.1.A', 'urn:epc:id:sgtin:952.1.B'] })) assert.deepEqual([...got!].sort(), ['urn:epc:id:sgtin:952.1.A', 'urn:epc:id:sgtin:952.1.B']) }) test('집약은 부모와 자식을 함께 낸다 — 부모만 내면 팔레트에 담긴 것이 낡는다', () => { const got = touchedItemKeys({ eventType: 'epcis.aggregation', data: { parentID: 'urn:epc:id:sscc:952.P1', childEPCs: ['urn:epc:id:sgtin:952.1.A'] } }) assert.deepEqual([...got!].sort(), ['urn:epc:id:sgtin:952.1.A', 'urn:epc:id:sscc:952.P1']) }) test('변환은 입력과 출력을 함께 낸다 — 소비된 것도 값이 바뀐다', () => { const got = touchedItemKeys({ eventType: 'epcis.transformation', data: { inputEPCList: ['in-1', 'in-2'], outputEPCList: ['out-1'] } }) assert.deepEqual([...got!].sort(), ['in-1', 'in-2', 'out-1']) }) test('부류 수량만 말한 사건은 「모른다」다 — 그 부류의 어느 개체인지 말하지 않았다', () => { const got = touchedItemKeys(epcis({ quantityList: [{ epcClass: 'urn:epc:idpat:sgtin:952.1.*', quantity: 3 }] })) assert.equal(got, null, '개체를 특정하지 못하면 전부 다시 만들어야 한다') }) test('물품을 만지지 않는다고 선언된 사건은 빈 범위다 — 전부 다시 만들 이유가 없다', () => { for (const eventType of ['task.status', 'equipment.status', 'person.status', 'asset.status', 'order.status', 'quality.output', 'attention.acked']) { const got = touchedItemKeys({ eventType, data: { taskId: 't1', moverId: 'fk-1' } }) assert.ok(got, `${eventType} 은 「모른다」가 아니다`) assert.equal(got!.size, 0, `${eventType} 은 물품을 건드리지 않는다`) } const energy = touchedItemKeys({ eventType: 'energy.measured', data: { id: 'm-1', kW: 12 } }) assert.equal(energy!.size, 0) }) test('낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때 알리지 않고 빠뜨리지 않는다', () => { assert.equal(touchedItemKeys({ eventType: 'something.new', data: { epcList: ['x'] } }), null) assert.equal(touchedItemKeys({ data: { epcList: ['x'] } }), null, '낱말이 없으면 판정할 수 없다') assert.equal(touchedItemKeys(undefined), null) assert.equal(touchedItemKeys({ eventType: 'epcis.object' }), null, '내용이 없으면 판정할 수 없다') })