feat(260929-if2): Erinnerungen anlegen und zur Faelligkeit benachrichtigen (Tracer)
- Reminder-Tabelle mit Zeilenschutz (Mandant+Benutzer, Systemlesen fuer den E-Mail-Planer), API reminders (Liste, Anlegen) - Kachel "Erinnerungen", globaler Melder im Portalrahmen (Browser und Desktop, je Faelligkeit einmal) - Desktop: Laufzeit-Berechtigung fuer Benachrichtigungen nur fuer die gespeicherte Server-Adresse - Zugriffsklassifikation nachgemessen Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import 'reflect-metadata';
|
||||
import { ForbiddenException, ValidationPipe } from '@nestjs/common';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { ROLES_KEY } from '../auth/decorators/roles.decorator';
|
||||
import { CreateReminderDto } from './dto/reminder.dto';
|
||||
import { RemindersController } from './reminders.controller';
|
||||
|
||||
function makeService() {
|
||||
return {
|
||||
list: vi.fn(async (..._args: unknown[]) => []),
|
||||
create: vi.fn(async (..._args: unknown[]) => ({})),
|
||||
};
|
||||
}
|
||||
|
||||
const req = (tenantId?: string) => ({ tenantId }) as any;
|
||||
const user = { id: 'u1', username: 'u', role: 'USER', tenantId: 't1' } as any;
|
||||
const proto = RemindersController.prototype as any;
|
||||
|
||||
describe('RemindersController — Rollen', () => {
|
||||
it.each(['list', 'create'])('%s traegt keine Routen-Rolle (jeder Angemeldete)', (name) => {
|
||||
expect(Reflect.getMetadata(ROLES_KEY, proto[name])).toBeUndefined();
|
||||
});
|
||||
|
||||
it('haengt an Pfad reminders', () => {
|
||||
expect(Reflect.getMetadata('path', RemindersController)).toBe('reminders');
|
||||
});
|
||||
});
|
||||
|
||||
describe('RemindersController — Mandant', () => {
|
||||
it('reicht req.tenantId und die Benutzerkennung an den Dienst weiter', async () => {
|
||||
const service = makeService();
|
||||
const controller = new RemindersController(service as any);
|
||||
await controller.list(req('t1'), user);
|
||||
await controller.create(req('t1'), user, { title: 'a', dueAt: '2099-01-01T10:00:00.000Z' });
|
||||
expect(service.list).toHaveBeenCalledWith('t1', 'u1');
|
||||
expect(service.create.mock.calls[0].slice(0, 2)).toEqual(['t1', 'u1']);
|
||||
});
|
||||
|
||||
it('wirft ForbiddenException ohne req.tenantId', async () => {
|
||||
const controller = new RemindersController(makeService() as any);
|
||||
await expect(controller.list(req(), user)).rejects.toBeInstanceOf(ForbiddenException);
|
||||
await expect(
|
||||
controller.create(req(), user, { title: 'a', dueAt: '2099-01-01T10:00:00.000Z' }),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
});
|
||||
|
||||
it('die globale Pipe verwirft untergeschobene Felder (T-IF2-02)', async () => {
|
||||
const pipe = new ValidationPipe({ whitelist: true, transform: true });
|
||||
const out: any = await pipe.transform(
|
||||
{
|
||||
title: ' a ',
|
||||
dueAt: '2099-01-01T10:00:00.000Z',
|
||||
tenantId: 'evil',
|
||||
userId: 'evil',
|
||||
emailSentAt: '2020-01-01T00:00:00.000Z',
|
||||
emailAttempts: 9,
|
||||
},
|
||||
{ type: 'body', metatype: CreateReminderDto },
|
||||
);
|
||||
expect(out).not.toHaveProperty('tenantId');
|
||||
expect(out).not.toHaveProperty('userId');
|
||||
expect(out).not.toHaveProperty('emailSentAt');
|
||||
expect(out).not.toHaveProperty('emailAttempts');
|
||||
expect(out.title).toBe('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('RemindersController — Routen-Reihenfolge (statisch vor :id)', () => {
|
||||
it('deklariert list vor jeder :id-Route', () => {
|
||||
const methods = Object.getOwnPropertyNames(RemindersController.prototype);
|
||||
const listIdx = methods.indexOf('list');
|
||||
expect(listIdx).toBeGreaterThanOrEqual(0);
|
||||
for (const name of methods) {
|
||||
const path = Reflect.getMetadata('path', proto[name]);
|
||||
if (typeof path === 'string' && path.startsWith(':id')) {
|
||||
expect(listIdx).toBeLessThan(methods.indexOf(name));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user