mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
feat(profile): capture the insurer IK number
Add an optional German insurer institution number (IKNR, 9 digits) to the user profile, alongside the existing insurer name and KVNR. The IKNR is a public registry identifier for the institution, not a per-person quasi-identifier, so it is stored plaintext, matching insurer_name rather than the encrypted KVNR column. The profile GET echoes the field; PATCH/PUT accept it via Zod — optional, empty string maps to null, and a non-empty value must be exactly 9 digits (422 otherwise, no checksum). The write is built field-by-field. /api/user/profile gains an OpenAPI registration with the profile read/write schemas. Migration 0102 adds the nullable column, NULL-defaulted, forward-only.
This commit is contained in:
1 parent
9ecd798119
commit
b99b07ad38
15 files changed
+645
No files matched your search
@@ -66,6 +66,7 @@ export const GET = apiHandler(async () => {
|
||||
// v1.7.0 — health-record export identity fields. All optional.
|
||||
fullName: user.fullName ?? null,
|
||||
insurerName: user.insurerName ?? null,
|
||||
insurerIkNumber: user.insurerIkNumber ?? null,
|
||||
insuranceNumber,
|
||||
});
|
||||
});
|
||||
@@ -33,6 +33,7 @@ export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
timezone: result.user.timezone,
|
||||
fullName: result.user.fullName,
|
||||
insurerName: result.user.insurerName,
|
||||
insurerIkNumber: result.user.insurerIkNumber,
|
||||
hasInsuranceNumber: result.user.hasInsuranceNumber,
|
||||
});
|
||||
});
|
||||
@@ -81,6 +81,33 @@ describe("GET /api/user/profile", () => {
|
||||
expect(body.data.heightCm).toBe(180);
|
||||
expect(body.data.locale).toBe("de");
|
||||
});
|
||||
|
||||
it("echoes the insurer IK number on GET", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
||||
username: "marc",
|
||||
displayName: null,
|
||||
email: null,
|
||||
dateOfBirth: null,
|
||||
gender: null,
|
||||
heightCm: null,
|
||||
locale: null,
|
||||
timezone: "Europe/Berlin",
|
||||
moodReminderEnabled: false,
|
||||
fullName: null,
|
||||
insurerName: "Example Insurer",
|
||||
insurerIkNumber: "101234567",
|
||||
insuranceNumberEncrypted: null,
|
||||
} as never);
|
||||
|
||||
const res = await callGet(makeGetReq());
|
||||
expect(res.status).toBe(200);
|
||||
const body = (await res.json()) as {
|
||||
data: { insurerIkNumber: string | null; insurerName: string | null };
|
||||
};
|
||||
expect(body.data.insurerIkNumber).toBe("101234567");
|
||||
expect(body.data.insurerName).toBe("Example Insurer");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PATCH /api/user/profile", () => {
|
||||
@@ -132,4 +159,45 @@ describe("PATCH /api/user/profile", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts a valid IKNR and echoes it back", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
|
||||
vi.mocked(prisma.user.update).mockResolvedValue({
|
||||
id: "user-1",
|
||||
username: "marc",
|
||||
displayName: null,
|
||||
email: null,
|
||||
role: "USER",
|
||||
heightCm: null,
|
||||
dateOfBirth: null,
|
||||
gender: null,
|
||||
timezone: "Europe/Berlin",
|
||||
locale: null,
|
||||
moodReminderEnabled: false,
|
||||
fullName: null,
|
||||
insurerName: null,
|
||||
insurerIkNumber: "101234567",
|
||||
insuranceNumberEncrypted: null,
|
||||
} as never);
|
||||
|
||||
const res = await PATCH(req({ insurerIkNumber: "101234567" }));
|
||||
expect(res.status).toBe(200);
|
||||
expect(prisma.user.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({ insurerIkNumber: "101234567" }),
|
||||
}),
|
||||
);
|
||||
const body = (await res.json()) as {
|
||||
data: { insurerIkNumber: string | null };
|
||||
};
|
||||
expect(body.data.insurerIkNumber).toBe("101234567");
|
||||
});
|
||||
|
||||
it("returns 422 for a malformed IKNR", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
const res = await PATCH(req({ insurerIkNumber: "abc" }));
|
||||
expect(res.status).toBe(422);
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -39,6 +39,7 @@ export const GET = apiHandler(async () => {
|
||||
moodReminderEnabled: true,
|
||||
fullName: true,
|
||||
insurerName: true,
|
||||
insurerIkNumber: true,
|
||||
insuranceNumberEncrypted: true,
|
||||
},
|
||||
});
|
||||
@@ -66,6 +67,7 @@ export const GET = apiHandler(async () => {
|
||||
moodReminderEnabled: dbUser?.moodReminderEnabled ?? false,
|
||||
fullName: dbUser?.fullName ?? null,
|
||||
insurerName: dbUser?.insurerName ?? null,
|
||||
insurerIkNumber: dbUser?.insurerIkNumber ?? null,
|
||||
insuranceNumber,
|
||||
});
|
||||
});
|
||||
@@ -95,6 +97,7 @@ export const PATCH = apiHandler(async (request: NextRequest) => {
|
||||
moodReminderEnabled: result.user.moodReminderEnabled,
|
||||
fullName: result.user.fullName,
|
||||
insurerName: result.user.insurerName,
|
||||
insurerIkNumber: result.user.insurerIkNumber,
|
||||
hasInsuranceNumber: result.user.hasInsuranceNumber,
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user