feat(260929-if2): Erinnerung zusaetzlich per E-Mail, Doku und Aenderungsliste
- E-Mail-Planer: Anspruch vor dem Senden (genau eine Mail je Faelligkeit, hoechstens 3 Versuche), Systemlesen nur fuer die Kandidatenabfrage - MailService.sendReminderEmail (Berliner Zeit, nur Text), GET /reminders/email-status, Haken im Formular mit Erklaerung - Zugriffsklassifikation und Erlaubnisliste fuer forSystem nachgezogen, Aenderungsliste und Anwenderanleitung Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,12 +46,18 @@ function makeFakePrisma() {
|
||||
).length;
|
||||
}),
|
||||
};
|
||||
return { reminder, rows };
|
||||
const user = {
|
||||
findFirst: vi.fn(async ({ where }: { where: any }) => ({
|
||||
email: where.id === 'u-ohne-mail' ? null : `${where.id}@example.invalid`,
|
||||
})),
|
||||
};
|
||||
return { reminder, user, rows };
|
||||
}
|
||||
|
||||
function setup() {
|
||||
function setup(smtp: unknown = { host: 'smtp.example.invalid' }) {
|
||||
const prisma = makeFakePrisma();
|
||||
return { prisma, service: new RemindersService(prisma as any) };
|
||||
const settings = { getSmtpConfig: vi.fn(async (_tenantId: string) => smtp) };
|
||||
return { prisma, settings, service: new RemindersService(prisma as any, settings as any) };
|
||||
}
|
||||
|
||||
const inHours = (h: number) => new Date(Date.now() + h * 3600_000).toISOString();
|
||||
@@ -244,3 +250,67 @@ describe('RemindersService — loeschen (E-02)', () => {
|
||||
expect(prisma.rows.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RemindersService — E-Mail-Erinnerung (Aufgabe 3, E-09)', () => {
|
||||
it('getEmailAvailability: SMTP eingerichtet und Adresse vorhanden', async () => {
|
||||
const { service, settings } = setup();
|
||||
await expect(service.getEmailAvailability('t1', 'u1')).resolves.toEqual({
|
||||
smtpConfigured: true,
|
||||
hasEmail: true,
|
||||
});
|
||||
expect(settings.getSmtpConfig).toHaveBeenCalledWith('t1');
|
||||
});
|
||||
|
||||
it('getEmailAvailability: ohne SmtpConfig-Zeile ist der Versand nicht eingerichtet', async () => {
|
||||
const { service } = setup(null);
|
||||
await expect(service.getEmailAvailability('t1', 'u1')).resolves.toEqual({
|
||||
smtpConfigured: false,
|
||||
hasEmail: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('getEmailAvailability: ohne Adresse im Konto hasEmail false, gelesen ueber den gebundenen Klienten', async () => {
|
||||
const { prisma, service } = setup();
|
||||
await expect(service.getEmailAvailability('t1', 'u-ohne-mail')).resolves.toEqual({
|
||||
smtpConfigured: true,
|
||||
hasEmail: false,
|
||||
});
|
||||
expect(forTenant).toHaveBeenLastCalledWith(expect.anything(), 't1', 'u-ohne-mail');
|
||||
expect(prisma.user.findFirst.mock.calls[0]?.[0]?.where).toEqual({ id: 'u-ohne-mail', tenantId: 't1' });
|
||||
});
|
||||
|
||||
it('anlegen mit emailEnabled speichert das Feld, wenn E-Mail moeglich ist', async () => {
|
||||
const { prisma, service } = setup();
|
||||
await service.create('t1', 'u1', { title: 'a', dueAt: inHours(1), emailEnabled: true });
|
||||
expect(prisma.reminder.create.mock.calls[0]?.[0]?.data.emailEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it('anlegen ohne emailEnabled speichert false', async () => {
|
||||
const { prisma, service } = setup();
|
||||
await service.create('t1', 'u1', { title: 'a', dueAt: inHours(1) });
|
||||
expect(prisma.reminder.create.mock.calls[0]?.[0]?.data.emailEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it('anlegen mit emailEnabled ergibt 400, wenn SMTP fehlt oder die Adresse fehlt', async () => {
|
||||
const noSmtp = setup(null);
|
||||
await expect(
|
||||
noSmtp.service.create('t1', 'u1', { title: 'a', dueAt: inHours(1), emailEnabled: true }),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
const noMail = setup();
|
||||
await expect(
|
||||
noMail.service.create('t1', 'u-ohne-mail', { title: 'a', dueAt: inHours(1), emailEnabled: true }),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(noSmtp.prisma.rows.size).toBe(0);
|
||||
expect(noMail.prisma.rows.size).toBe(0);
|
||||
});
|
||||
|
||||
it('aendern mit emailEnabled true ergibt 400 ohne SMTP; emailEnabled false geht immer', async () => {
|
||||
const { prisma, service } = setup(null);
|
||||
seed(prisma, 'x', future());
|
||||
await expect(service.update('t1', 'u1', 'x', { emailEnabled: true })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
await service.update('t1', 'u1', 'x', { emailEnabled: false });
|
||||
expect(prisma.reminder.update.mock.calls[0]?.[0]?.data).toEqual({ emailEnabled: false });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user