const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadTypeScript } = require("./load-typescript.cjs"); const base = "app/(with-base-layout)/oper/dredging/lib/"; const { createDredgingRegistrationSession } = loadTypeScript( `${base}registration-request.ts`, ); /** 실제 wire 필드 외에 화면 전용 ID·관경·거리까지 포함해 구분을 검증한다. */ const fixture = () => [ { localId: "ui-group", registrationMode: "PHOTO", dredgingDate: "2026-09-01", locationSource: "EXIF", location: { longitude: 127.1, latitude: 37.1 }, pipe: { fid: "F-1", mngInstCd: "4173000", fcltMngNo: "P-1", pipeDiameterMm: 600, distanceMeters: 1, }, photos: [ { key: "photo-1", file: { name: "관로.jpg" }, metadata: { capturedAt: "2026-09-01T09:10:00", location: { longitude: 127.1, latitude: 37.1 }, }, }, ], }, ]; const attachments = () => new Map([["photo-1", { atflCd: "A", atflId: "B" }]]); test("HTTP 응답 유실 후 재시도는 동일 payload와 clientRequestId를 전송", async () => { let issued = 0; const session = createDredgingRegistrationSession( () => `request-${++issued}`, ); const requests = []; const client = loadTypeScript(`${base}client.ts`, { "@/components/lib/api": { postRawJson: async (url, body) => { requests.push({ url, body }); if (requests.length === 1) throw new TypeError("network lost"); return { alreadyRegisteredCount: 1 }; }, }, "@/components/lib/utils": {}, "@/shared/lib/fetch-all-pages": {}, }); await assert.rejects( client.saveDredgingRegistrations(session.prepare(fixture(), attachments())), ); await client.saveDredgingRegistrations( session.prepare(fixture(), attachments()), ); assert.equal(issued, 1); assert.deepEqual(requests[0], requests[1]); assert.equal(requests[0].url, "v1/dredging/registrations"); assert.deepEqual(requests[0].body.groups[0].photos[0], { atflCd: "A", atflId: "B", originalFileName: "관로.jpg", clientCapturedAt: "2026-09-01T09:10:00", clientLongitude: 127.1, clientLatitude: 37.1, }); }); test("날짜·위치·관로·사진 변경은 새 ID, 원래 입력 복원은 기존 ID", () => { let issued = 0; const session = createDredgingRegistrationSession( () => `request-${++issued}`, ); const first = session.prepare(fixture(), attachments()).clientRequestId; const edits = [ (group) => { group.dredgingDate = "2026-09-02"; }, (group) => { group.location.latitude = 37.2; }, (group) => { group.pipe.fid = "F-2"; }, (group) => { group.photos[0].metadata.capturedAt = "2026-09-01T10:00:00"; }, ]; for (const edit of edits) { const groups = fixture(); edit(groups[0]); assert.notEqual( session.prepare(groups, attachments()).clientRequestId, first, ); } const changedAttachments = attachments(); changedAttachments.set("photo-1", { atflCd: "C", atflId: "D" }); assert.notEqual( session.prepare(fixture(), changedAttachments).clientRequestId, first, ); assert.equal( session.prepare(fixture(), attachments()).clientRequestId, first, ); }); test("화면 전용 그룹 ID·관경·거리 변화는 중복방지 키를 바꾸지 않는다", () => { let issued = 0; const session = createDredgingRegistrationSession( () => `request-${++issued}`, ); const first = session.prepare(fixture(), attachments()); const groups = fixture(); groups[0].localId = "new-ui"; groups[0].pipe.pipeDiameterMm = 900; groups[0].pipe.distanceMeters = 3; assert.deepEqual(session.prepare(groups, attachments()), first); }); test("새 등록 세션은 같은 입력이라도 새 UUID를 발급", () => { const first = createDredgingRegistrationSession().prepare( fixture(), attachments(), ).clientRequestId; const second = createDredgingRegistrationSession().prepare( fixture(), attachments(), ).clientRequestId; assert.notEqual(first, second); assert.match( first, /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, ); }); test("첨부 객체의 속성 순서가 달라도 같은 등록 ID를 재사용", () => { let issued = 0; const session = createDredgingRegistrationSession(() => `request-${++issued}`); const first = session.prepare(fixture(), attachments()); const reordered = new Map([["photo-1", { atflId: "B", atflCd: "A" }]]); assert.deepEqual(session.prepare(fixture(), reordered), first); assert.equal(issued, 1); }); test("실제 저장의 누락 첨부는 키 발급 전 거부하며 선행 모의 모드는 허용", () => { let issued = 0; const session = createDredgingRegistrationSession( () => `request-${++issued}`, ); assert.throws(() => session.prepare(fixture(), new Map()), /첨부 식별자/); assert.equal(issued, 0); assert.equal( session.prepare(fixture(), new Map(), false).clientRequestId, "request-1", ); });