feat: T6 wave 2 – medicine book, document rights, label particulars, herd events
- Veterinary medicine catalogue, lots and append-only movement ledger (rest always computed; corrections as counter-bookings; inactive medicines take no new stock; unit must match on lot moves). - Documents of medicine lots, authority notices and restrictions need the domain right on top of documents:* (403 before any lookup). - Label particulars: pure checkLabel with six mandatory gaps and four warnings, AFM font metrics, Berlin-day dates, geometric reading order for split addresses, new master-data fields with row-locked updates. - Herd events ColonyCreated, ColonyMoved, ApiaryChanged and TskReferenceDayReached (ids only, never forwarded to webhooks); CSV import emits ColonyCreated with audit. - ARCHITECTURE.md §12 Compliance (T6), Annahmen 59–70. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
8a31c42578
commit
c18860c75c
73 files changed
+7121
-309
No files matched your search
@@ -31,6 +31,8 @@ describe('Public batch origin page (e2e)', () => {
|
||||
let bottlingBatchId: string;
|
||||
const lotNumber = `WH-E2E-${Date.now()}`;
|
||||
const apiaryName = `Stand Wald ${Date.now()}`;
|
||||
const legalName = `Rechtsname-${Date.now()}-honig`;
|
||||
const foodBusinessAddress = `Geheimweg ${Date.now()}, 86450 Altenmünster`;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
@@ -90,7 +92,7 @@ describe('Public batch origin page (e2e)', () => {
|
||||
});
|
||||
honeyBatchId = honeyBatch.id;
|
||||
const product = await prisma.product.create({
|
||||
data: { name: 'Sommerhonig 500g' },
|
||||
data: { name: 'Sommerhonig 500g', legalName: legalName },
|
||||
});
|
||||
productId = product.id;
|
||||
const bottlingBatch = await prisma.bottlingBatch.create({
|
||||
@@ -139,6 +141,40 @@ describe('Public batch origin page (e2e)', () => {
|
||||
expect(body.harvestDates).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('the public origin page exposes neither foodBusinessAddress nor legalName', async () => {
|
||||
const before = await prisma.siteSettings.findUnique({
|
||||
where: { id: 'singleton' },
|
||||
});
|
||||
await prisma.siteSettings.upsert({
|
||||
where: { id: 'singleton' },
|
||||
update: { foodBusinessName: 'Imkerei Geheim', foodBusinessAddress },
|
||||
create: {
|
||||
id: 'singleton',
|
||||
foodBusinessName: 'Imkerei Geheim',
|
||||
foodBusinessAddress,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v1/public/batches/${lotNumber}`)
|
||||
.expect(200);
|
||||
const text = JSON.stringify(response.body);
|
||||
expect(text).not.toContain(foodBusinessAddress);
|
||||
expect(text).not.toContain('Imkerei Geheim');
|
||||
expect(text).not.toContain(legalName);
|
||||
expect(response.body).not.toHaveProperty('legalName');
|
||||
expect(response.body).not.toHaveProperty('foodBusinessAddress');
|
||||
} finally {
|
||||
await prisma.siteSettings.update({
|
||||
where: { id: 'singleton' },
|
||||
data: {
|
||||
foodBusinessName: before?.foodBusinessName ?? null,
|
||||
foodBusinessAddress: before?.foodBusinessAddress ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('never leaks the apiary name, location, address or coordinates', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v1/public/batches/${lotNumber}`)
|
||||
|
||||
Reference in new issue
Block a user