feat: T6 wave 4 – notice PDFs, compliance notifications, readiness core
- Authority notice PDFs: pure model plus renderer, TSK count in A4 landscape, FEHLT for missing particulars, stored totals with correction note; GET /authority-notices/:id/pdf with compliance:read. - Compliance notifications in category COMPLIANCE: herd change and TSK reference day, at most one unread per topic and user (advisory lock); daily rule TskReferenceDayReached with Berlin day and catch-up; handlers in API and worker. - Readiness core: live evaluation of seven pure rules, fingerprinted acknowledgements that reopen when revoked or stale, subject masking, read-only GET paths; READINESS_RULES injectable; kept out of the worker. - e2e hygiene: no unscoped deletes, seeded settings rows saved/restored. - ARCHITECTURE.md §12.8–12.10, §10 handler rows, Annahmen 78–85. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
edc193f128
commit
b912d29166
54 files changed
+5300
-14
No files matched your search
@@ -373,4 +373,56 @@ describe('Authority notices (e2e)', () => {
|
||||
.send({ tskReferenceDay: '01-01' })
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('GET /authority-notices/:id/pdf answers application/pdf with Cache-Control no-store and the file name anzeige-<id>.pdf', async () => {
|
||||
const created = await request(server())
|
||||
.post('/api/v1/authority-notices')
|
||||
.set(as(beekeeperToken))
|
||||
.set('Idempotency-Key', nextKey())
|
||||
.send({ kind: 'TSK_REPORT', year: 2027 })
|
||||
.expect(201);
|
||||
const id = (created.body as NoticeBody).id;
|
||||
|
||||
const response = await request(server())
|
||||
.get(`/api/v1/authority-notices/${id}/pdf`)
|
||||
.set(as(staffToken))
|
||||
.buffer(true)
|
||||
.parse((res, callback) => {
|
||||
const chunks: Buffer[] = [];
|
||||
res.on('data', (chunk: Buffer) => chunks.push(chunk));
|
||||
res.on('end', () => callback(null, Buffer.concat(chunks)));
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.headers['content-type']).toContain('application/pdf');
|
||||
expect(response.headers['cache-control']).toContain('no-store');
|
||||
expect(response.headers['content-disposition']).toBe(
|
||||
`attachment; filename="anzeige-${id}.pdf"`,
|
||||
);
|
||||
expect((response.body as Buffer).subarray(0, 4).toString('latin1')).toBe(
|
||||
'%PDF',
|
||||
);
|
||||
await request(server())
|
||||
.get('/api/v1/authority-notices/does-not-exist/pdf')
|
||||
.set(as(staffToken))
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('the pdf route needs compliance:read', async () => {
|
||||
const created = await request(server())
|
||||
.post('/api/v1/authority-notices')
|
||||
.set(as(beekeeperToken))
|
||||
.set('Idempotency-Key', nextKey())
|
||||
.send({ kind: 'REGISTRATION' })
|
||||
.expect(201);
|
||||
const id = (created.body as NoticeBody).id;
|
||||
|
||||
await request(server())
|
||||
.get(`/api/v1/authority-notices/${id}/pdf`)
|
||||
.set(as(noRightsToken))
|
||||
.expect(403);
|
||||
await request(server())
|
||||
.get(`/api/v1/authority-notices/${id}/pdf`)
|
||||
.expect(401);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user