/* * 사이트 발견 결과 — **"못 찾았다" 를 "없다" 로 말하지 않게.** * * 실 시스템을 연결해 트윈을 만드는 경로에서, 발견이 실패하면 화면이 "사이트를 찾지 못했습니다" 라고만 * 말했다. 레퍼런스 미등록·어댑터 미상·조회 실패·정말 0건이 그 한 문장에 섞여 있었다 — 자격이 * 틀렸는지 주소가 틀렸는지 알 수 없었다. */ import { test } from 'node:test' import assert from 'node:assert/strict' import { discovered, failed, detailOf, referenceNotFound, adapterUnknown, discoveryFailed, discoveryTimedOut } from '../server/service/reference/discovery-result.ts' test('사이트가 없는 것은 실패가 아니다 — 사유 없는 빈 목록이 "없다" 를 뜻한다', () => { /* 권한이 좁거나 아직 등록되지 않아 공장이 하나도 없는 시스템은 있을 수 있다. 그것은 사실이므로 실패로 뭉개면 사용자가 있지도 않은 문제를 찾는다. */ const r = discovered([]) assert.deepEqual(r, { sites: [] }) assert.equal(r.error, undefined, '사유가 없다 = 성공') }) test('찾은 것을 그대로 돌려주되 입력을 붙들지 않는다', () => { const src = [{ siteId: 'PL1' }, { siteId: 'PL2' }] const r = discovered(src) assert.deepEqual(r.sites, src) src.push({ siteId: 'PL3' }) assert.equal(r.sites.length, 2, '나중에 바뀐 원본이 결과를 바꾸지 않는다') }) test('없는 목록·null 도 견딘다', () => { assert.deepEqual(discovered(null), { sites: [] }) assert.deepEqual(discovered(undefined), { sites: [] }) }) test('실패는 사유와 함께, 사이트는 비워서 — 반쪽 목록을 성공처럼 보이지 않게', () => { const r = failed(referenceNotFound('sap-ewm-1710')) assert.deepEqual(r.sites, []) assert.equal(r.error?.code, 'reference-not-found') assert.equal(r.error?.params?.source, 'sap-ewm-1710') assert.match(r.error!.message, /not registered/, '영어 canonical 문장이 로그·폴백으로 남는다') }) test('네 가지 사유가 서로 구별된다 — 이것이 이 작업의 요점이다', () => { const codes = [ referenceNotFound('s').code, adapterUnknown('sap-ewm', 's').code, discoveryFailed('s', '401 Unauthorized').code, discoveryTimedOut('s', 15).code ] assert.deepEqual(codes, ['reference-not-found', 'adapter-unknown', 'discovery-failed', 'discovery-timeout']) assert.equal(new Set(codes).size, 4, '넷이 한 코드로 합쳐지지 않는다') }) test('원 시스템이 준 사유를 감추지 않는다 — 사용자가 관리자에게 물어볼 수 있게', () => { const e = discoveryFailed('sap-ewm-1710', '401 Unauthorized') assert.equal(e.params?.detail, '401 Unauthorized') assert.match(e.message, /401 Unauthorized/) }) test('타임아웃은 초 단위로 알린다 — 얼마를 기다렸는지 알 수 있게', () => { const e = discoveryTimedOut('s', 15) assert.equal(e.params?.seconds, 15) assert.match(e.message, /15s/) }) test('어댑터가 낸 오류에서 사유를 뽑는다 — 형태를 가정하지 않는다', () => { assert.equal(detailOf(new Error('ECONNREFUSED')), 'ECONNREFUSED') assert.equal(detailOf('404 Not Found'), '404 Not Found') /* 무엇이 왔는지 모르면 그렇게 말한다 — 빈 문장으로 "이유 없음" 처럼 보이게 하지 않는다. */ for (const weird of [undefined, null, {}, 0, new Error('')]) { assert.match(detailOf(weird), /without a message/, `이상한 입력: ${JSON.stringify(weird)}`) } })