/* * 인제스트 공간 판정 — "이 현장에 더한다" 가 알리지 않고 빗나가지 않게. * * 엔진 본체는 DB·TypeORM 을 물고 있어 단위테스트로 부를 수 없다. 그래서 **결정만** 순수 모듈로 * 떼어 놓고 여기서 본다(엔진은 공간 존재 여부를 조회해 넘기는 일만 한다). */ import { test } from 'node:test' import assert from 'node:assert/strict' import { resolveIngestSpace, refuseImportSpace, collidingIds, INGEST_SPACE_NOT_FOUND } from '../server/service/reference/ingest-space.ts' test('고르지 않으면 마스터가 파생한 공간으로 — 지금까지의 거동을 바꾸지 않는다', () => { for (const req of [undefined, null, '', ' ']) { const c = resolveIngestSpace('hatiolab-wms-site', req as any, false) assert.deepEqual(c, { spaceId: 'hatiolab-wms-site', joined: false }, `요청=${JSON.stringify(req)}`) } }) test('기존 현장을 고르면 그 공간으로 들어가고 합치기로 표시된다', () => { const c = resolveIngestSpace('trailer-yard-new-site', 'rosarito-site', true) assert.deepEqual(c, { spaceId: 'rosarito-site', joined: true }) }) test('마스터가 이미 그 공간이면 합치기가 아니다 — 같은 공간의 재인제스트다', () => { const c = resolveIngestSpace('rosarito-site', 'rosarito-site', true) assert.deepEqual(c, { spaceId: 'rosarito-site', joined: false }) }) test('없는 현장은 거절한다 — 알리지 않고 새 공간을 만들지 않는다', () => { /* 여기서 새 공간을 만들어 주면 사용자는 합쳤다고 믿고 화면은 따로 논다. 그때는 이미 양쪽에 이벤트가 쌓여 있어 손으로 합치는 것이 훨씬 비싸다. */ assert.throws( () => resolveIngestSpace('a-site', 'typo-site', false), (e: any) => e.code === INGEST_SPACE_NOT_FOUND && /typo-site/.test(e.message) ) }) test('앞뒤 공백은 다듬는다 — 사용자가 붙여 넣은 id 로 거절당하지 않게', () => { const c = resolveIngestSpace('a-site', ' rosarito-site ', true) assert.equal(c.spaceId, 'rosarito-site') }) test('같은 id 를 쓰는 것들을 찾아낸다 — 합쳐지는 것을 모르고 지나가지 않게', () => { const existing = [{ id: 'a-yard' }, { id: 'a-dock' }] const incoming = [{ id: 'a-yard' }, { id: 'a-line' }, { id: 'a-dock' }] assert.deepEqual(collidingIds(existing, incoming), ['a-dock', 'a-yard'], '정렬된 교집합') assert.deepEqual(collidingIds(existing, [{ id: 'a-line' }]), [], '겹치지 않으면 빈 배열') assert.deepEqual(collidingIds(undefined, undefined), [], '없는 쪽은 빈 것으로 본다') assert.deepEqual(collidingIds([{}, { id: '' }], [{ id: '' }]), [], 'id 없는 항목은 세지 않는다') }) /* ── 실 시스템 import 의 현장 선택 (2026-08-13) ───────────────────────────── */ test('현장을 고르지 않으면 사이트가 몇이든 진행한다 — 지금까지의 거동', () => { for (const n of [0, 1, 5, 40]) { assert.equal(refuseImportSpace(n, undefined), undefined) assert.equal(refuseImportSpace(n, ''), undefined) } }) test('사이트 하나면 그 현장에 넣는다 — 렌즈를 더하는 정상 경로', () => { assert.equal(refuseImportSpace(1, 'rosarito-site'), undefined) }) test('사이트 여럿을 한 현장에 넣는 것은 거절한다 — 서로 다른 현장이 알리지 않고 합쳐진다', () => { const r = refuseImportSpace(3, 'rosarito-site') assert.ok(r, '거절해야 한다') assert.equal(r.code, 'space-with-multiple-sites') assert.equal(r.params?.count, 3) assert.equal(r.params?.spaceId, 'rosarito-site') assert.match(r.message, /pick one site/) }) test('거절 사유는 코드로 낸다 — 화면이 다섯 언어로 번역할 수 있게', () => { const r = refuseImportSpace(2, ' rosarito-site ') assert.equal(r?.code, 'space-with-multiple-sites') assert.equal(r?.params?.spaceId, 'rosarito-site', '앞뒤 공백은 다듬어 낸다') })