jest.mock("../../utils/sm4-util", () => ({ encryptDataForSm3: jest.fn((value) => Buffer.from(value).toString("base64")), decryptDataForSm3: jest.fn((value) => { const patientValues = { encryptedName: "张三", encryptedCertNo: "11010519491231002X", encryptedMobile: "13800138000", }; return patientValues[value] || Buffer.from(value, "base64").toString(); }), })); jest.mock("../hn-his", () => jest.fn()); const { ObjectId } = require("mongodb"); const Sm4Util = require("../../utils/sm4-util"); const hnHis = require("../hn-his"); const selfAuth = require("./index"); const OWNER_USER_ID = "507f1f77bcf86cd799439011"; const OTHER_USER_ID = "507f191e810c19729de860ea"; const PATIENT_ID = "507f1f77bcf86cd799439012"; const REQUEST_ID = "507f1f77bcf86cd799439013"; const ACCOUNT_ID = "2088000000000000"; function sameValue(left, right) { if (left instanceof ObjectId || right instanceof ObjectId) { return String(left) === String(right); } return left === right; } function matches(record, query) { return Object.entries(query).every(([key, expected]) => { if (expected && expected.$in) return expected.$in.includes(record[key]); return sameValue(record[key], expected); }); } function createDb({ requests = [], patient = null } = {}) { const state = { requests, patient }; const authCollection = { dropIndex: jest.fn().mockRejectedValue(Object.assign(new Error("index not found"), { code: 27, codeName: "IndexNotFound", })), createIndex: jest.fn().mockResolvedValue("index"), insertOne: jest.fn(async (record) => { if (!record._id) record._id = new ObjectId(); state.requests.push(record); return { insertedId: record._id }; }), findOne: jest.fn(async (query) => state.requests.find((record) => matches(record, query)) || null), findOneAndUpdate: jest.fn(async (query, update) => { const record = state.requests.find((entry) => matches(entry, query)); if (!record) return null; Object.assign(record, update.$set || {}); if (update.$inc) { Object.entries(update.$inc).forEach(([key, value]) => { record[key] = (record[key] || 0) + value; }); } return record; }), updateOne: jest.fn(async (query, update) => { const record = state.requests.find((entry) => matches(entry, query)); if (!record) return { modifiedCount: 0 }; Object.assign(record, update.$set || {}); if (update.$unset) Object.keys(update.$unset).forEach((key) => delete record[key]); return { modifiedCount: 1 }; }), }; const patientCollection = { findOne: jest.fn(async (query) => { if (!state.patient || !matches(state.patient, query)) return null; return state.patient; }), }; const db = { collection: jest.fn((name) => { if (name === "hlw-patient-self-auth") return authCollection; if (name === "hlw-patient") return patientCollection; throw new Error(`Unexpected collection: ${name}`); }), }; return { db, state, authCollection }; } function makePatient() { return { _id: new ObjectId(PATIENT_ID), accountId: ACCOUNT_ID, anotherName: "encryptedName", anotherIdNo: "encryptedCertNo", anotherMobile: "encryptedMobile", address: "测试地址", disabled: false, }; } function makeRequest(status = "pending", timestamp = Date.now()) { return { _id: new ObjectId(REQUEST_ID), patientId: new ObjectId(PATIENT_ID), ownerAccountId: ACCOUNT_ID, ownerUserId: OWNER_USER_ID, requestTimestamp: timestamp, status, attempts: 0, }; } function requestId(request) { return request._id.toString(); } beforeEach(() => { jest.clearAllMocks(); }); test("rejects malformed authorization ids", async () => { const { db } = createDb(); await expect(selfAuth({ type: "getSelfAuthPatient", id: "bad-id" }, db, {})) .resolves.toEqual({ success: false, message: "授权链接无效" }); expect(db.collection).not.toHaveBeenCalled(); }); test("creates a pending request owned by the authenticated user", async () => { const { db, state } = createDb({ patient: makePatient() }); const result = await selfAuth({ type: "createSelfAuthRequest", patientId: PATIENT_ID, accountId: ACCOUNT_ID, }, db, { userId: OWNER_USER_ID }); expect(result.success).toBe(true); expect(result.data.id).toMatch(/^[a-f\d]{24}$/i); expect(result.data.id).toBe(state.requests[0]._id.toString()); expect(state.requests[0]).toEqual(expect.objectContaining({ ownerAccountId: ACCOUNT_ID, ownerUserId: OWNER_USER_ID, status: "pending", })); expect(state.requests[0]).not.toHaveProperty("authId"); expect(state.requests[0]).not.toHaveProperty("psnToken"); }); test("returns masked patient details and rejects an expired request", async () => { const active = makeRequest(); const activeDb = createDb({ requests: [active], patient: makePatient() }).db; const activeResult = await selfAuth({ type: "getSelfAuthPatient", id: requestId(active) }, activeDb, {}); expect(activeResult).toEqual(expect.objectContaining({ success: true, data: expect.objectContaining({ name: "张三", certNo: "1101***********02X", sex: "女", }), })); const expired = makeRequest("pending", Date.now() - 11 * 60 * 1000); const expiredDb = createDb({ requests: [expired], patient: makePatient() }).db; const expiredResult = await selfAuth({ type: "getSelfAuthPatient", id: requestId(expired) }, expiredDb, {}); expect(expiredResult).toEqual({ success: false, message: "授权链接已过期,请重新发起授权" }); }); test("rejects missing requests and patients", async () => { const missingRequest = makeRequest(); const emptyDb = createDb({ patient: makePatient() }).db; await expect(selfAuth({ type: "getSelfAuthPatient", id: requestId(missingRequest) }, emptyDb, {})) .resolves.toEqual({ success: false, message: "授权链接不存在或已失效" }); const request = makeRequest(); const noPatientDb = createDb({ requests: [request] }).db; await expect(selfAuth({ type: "getSelfAuthPatient", id: requestId(request) }, noPatientDb, {})) .resolves.toEqual({ success: false, message: "就诊人不存在" }); }); test("reports zero years old for a valid infant id", () => { const now = new Date(); const date = [ now.getFullYear(), String(now.getMonth() + 1).padStart(2, "0"), String(now.getDate()).padStart(2, "0"), ].join(""); const body = `110105${date}001`; const factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; const parity = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; const sum = body.split("").reduce((total, digit, index) => total + Number(digit) * factors[index], 0); const certNo = `${body}${parity[sum % 11]}`; expect(selfAuth.getPersonInfo(certNo, now).age).toBe(0); }); test("stores only the encrypted matching HIS archive after successful submission", async () => { const request = makeRequest(); const { db, state } = createDb({ requests: [request], patient: makePatient() }); hnHis.mockResolvedValue({ success: true, list: [{ socialno: "11010519491231002X", isyb: "1", patientId: "his-patient", blhno: "card-no" }], tmbxx: [{ code: "11", name: "普通门诊" }], }); const result = await selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "secret-token" }, db, { userId: OTHER_USER_ID }); expect(result).toEqual({ success: true, message: "授权并建档成功", data: { status: "success" } }); expect(state.requests[0].status).toBe("success"); expect(state.requests[0].encryptedResult).toBeTruthy(); expect(state.requests[0]).not.toHaveProperty("psnToken"); expect(JSON.stringify(state.requests[0])).not.toContain("secret-token"); expect(Sm4Util.encryptDataForSm3).toHaveBeenCalledWith(expect.stringContaining("his-patient")); }); test("allows a failed request to be retried", async () => { const request = makeRequest("failed"); request.errorMessage = "previous failure"; const { db, state } = createDb({ requests: [request], patient: makePatient() }); hnHis.mockResolvedValue({ success: true, list: [{ socialno: "11010519491231002X", isyb: "1" }], tmbxx: [], }); const result = await selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "new-token" }, db, { userId: OTHER_USER_ID }); expect(result.success).toBe(true); expect(state.requests[0].status).toBe("success"); expect(state.requests[0].attempts).toBe(1); }); test("only one concurrent submission can call HIS", async () => { const request = makeRequest(); const { db } = createDb({ requests: [request], patient: makePatient() }); let releaseHis; hnHis.mockImplementation(() => new Promise((resolve) => { releaseHis = () => resolve({ success: true, list: [{ socialno: "11010519491231002X", isyb: "1" }], tmbxx: [], }); })); const first = selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "token-1" }, db, { userId: OTHER_USER_ID }); await new Promise((resolve) => setImmediate(resolve)); const second = await selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "token-2" }, db, { userId: OTHER_USER_ID }); expect(second).toEqual({ success: false, message: "授权正在处理中,请勿重复提交", data: { status: "processing" }, }); expect(hnHis).toHaveBeenCalledTimes(1); releaseHis(); await expect(first).resolves.toEqual({ success: true, message: "授权并建档成功", data: { status: "success" } }); }); test.each([ [{ success: true, list: [{ socialno: "330100000000000000", isyb: "1" }] }, "医保建档失败,未找到匹配档案"], [{ success: true, list: [{ socialno: "11010519491231002X", isyb: "0" }] }, "医保建档失败"], ])("marks invalid HIS results as failed", async (hisResult, message) => { const request = makeRequest(); const { db, state } = createDb({ requests: [request], patient: makePatient() }); hnHis.mockResolvedValue(hisResult); const result = await selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "token" }, db, { userId: OTHER_USER_ID }); expect(result.success).toBe(false); expect(result.message).toBe(message); expect(state.requests[0].status).toBe("failed"); }); test("is idempotent after success and restricts result access to the owner", async () => { const archive = { socialno: "11010519491231002X", isyb: "1" }; const request = { ...makeRequest("success"), encryptedResult: Buffer.from(JSON.stringify(archive)).toString("base64"), encryption: "SM4", }; const { db } = createDb({ requests: [request], patient: makePatient() }); const repeated = await selfAuth({ type: "submitSelfAuth", id: requestId(request), psnToken: "token" }, db, { userId: OTHER_USER_ID }); expect(repeated.success).toBe(true); expect(hnHis).not.toHaveBeenCalled(); const denied = await selfAuth({ type: "getSelfAuthResult", id: requestId(request) }, db, { userId: OTHER_USER_ID }); expect(denied).toEqual({ success: false, message: "授权记录不存在或无权访问" }); const allowed = await selfAuth({ type: "getSelfAuthResult", id: requestId(request) }, db, { userId: OWNER_USER_ID }); expect(allowed).toEqual({ success: true, message: "授权成功", data: { status: "success", hisArchive: archive } }); });