714f731ac9
Neue Felder loginHintDirectory/loginHintLocal (Platzhalter, Pflicht, max. 1000), Migration 20260930170000 (nullable, leer = Standard aus @tessera/shared). Knopf Passwort festlegen und Gueltigkeitshinweis bleiben fest; local-no-link wird nie erzeugt und bleibt fest. Vorschau springt beim Bearbeiten auf die passende Kontoart. Lokal im Browser nachgewiesen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
154 lines
5.7 KiB
TypeScript
154 lines
5.7 KiB
TypeScript
import { DEFAULT_WELCOME_MAIL_TEXTS } from '@tessera/shared';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { WelcomeMailTemplateService } from './welcome-mail-template.service';
|
|
|
|
/**
|
|
* WelcomeMailTemplateService — Speicherung der eigenen Vorlage je Mandant.
|
|
*
|
|
* Festgenagelt: jeder Zugriff gebunden an den uebergebenen Mandanten
|
|
* (`forTenant`), Standardtexte ohne Vorlage, Speichern als upsert auf
|
|
* `tenantId`, Zuruecksetzen loescht die Vorlage.
|
|
*/
|
|
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
|
forTenant: vi.fn((prisma: any, tenantId: string) => prisma.__bound(tenantId)),
|
|
}));
|
|
|
|
type Row = {
|
|
tenantId: string;
|
|
subject: string;
|
|
heading: string;
|
|
intro: string;
|
|
loginHintDirectory?: string | null;
|
|
loginHintLocal?: string | null;
|
|
closing: string;
|
|
updatedBy: string | null;
|
|
updatedAt: Date;
|
|
};
|
|
|
|
function makePrisma(rows: Row[] = []) {
|
|
const log: { tenantId: string; model: string; method: string; args: any }[] = [];
|
|
return {
|
|
__log: log,
|
|
__rows: rows,
|
|
__bound(tenantId: string) {
|
|
return {
|
|
welcomeMailTemplate: {
|
|
findUnique: vi.fn(async (args: any) => {
|
|
log.push({ tenantId, model: 'welcomeMailTemplate', method: 'findUnique', args });
|
|
// RLS-Nachbildung: nur Zeilen des gebundenen Mandanten sichtbar
|
|
return (
|
|
rows.find((r) => r.tenantId === args.where.tenantId && r.tenantId === tenantId) ??
|
|
null
|
|
);
|
|
}),
|
|
upsert: vi.fn(async (args: any) => {
|
|
log.push({ tenantId, model: 'welcomeMailTemplate', method: 'upsert', args });
|
|
const existing = rows.find((r) => r.tenantId === args.where.tenantId);
|
|
if (existing) Object.assign(existing, args.update, { updatedAt: new Date() });
|
|
else rows.push({ ...args.create, updatedAt: new Date() });
|
|
return {};
|
|
}),
|
|
deleteMany: vi.fn(async (args: any) => {
|
|
log.push({ tenantId, model: 'welcomeMailTemplate', method: 'deleteMany', args });
|
|
const before = rows.length;
|
|
for (let i = rows.length - 1; i >= 0; i--) {
|
|
if (rows[i].tenantId === args.where.tenantId) rows.splice(i, 1);
|
|
}
|
|
return { count: before - rows.length };
|
|
}),
|
|
},
|
|
tenant: {
|
|
findUnique: vi.fn(async (args: any) => {
|
|
log.push({ tenantId, model: 'tenant', method: 'findUnique', args });
|
|
return args.where.id === 't1' ? { name: 'Beispiel GmbH' } : null;
|
|
}),
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
const texts = {
|
|
subject: 'S {{name}}',
|
|
heading: 'H',
|
|
intro: 'I',
|
|
loginHintDirectory: 'LD {{benutzername}}',
|
|
loginHintLocal: 'LL',
|
|
closing: 'C',
|
|
};
|
|
|
|
let prisma: ReturnType<typeof makePrisma>;
|
|
let service: WelcomeMailTemplateService;
|
|
|
|
beforeEach(() => {
|
|
prisma = makePrisma();
|
|
service = new WelcomeMailTemplateService(prisma as any);
|
|
});
|
|
|
|
describe('WelcomeMailTemplateService', () => {
|
|
it('ohne Vorlage: Standardtexte, custom=false, getCustomTexts=null', async () => {
|
|
const state = await service.getState('t1');
|
|
expect(state.custom).toBe(false);
|
|
expect(state.texts).toEqual(DEFAULT_WELCOME_MAIL_TEXTS);
|
|
expect(state.defaults).toEqual(DEFAULT_WELCOME_MAIL_TEXTS);
|
|
expect(await service.getCustomTexts('t1')).toBeNull();
|
|
expect(prisma.__log.every((c) => c.tenantId === 't1')).toBe(true);
|
|
});
|
|
|
|
it('speichern: upsert auf tenantId, gebunden, mit Bearbeiter; danach custom=true', async () => {
|
|
const state = await service.save('t1', texts, 'ada');
|
|
const upsert = prisma.__log.find((c) => c.method === 'upsert');
|
|
expect(upsert?.tenantId).toBe('t1');
|
|
expect(upsert?.args.where).toEqual({ tenantId: 't1' });
|
|
expect(upsert?.args.create).toEqual({ tenantId: 't1', ...texts, updatedBy: 'ada' });
|
|
expect(state.custom).toBe(true);
|
|
expect(state.texts).toEqual(texts);
|
|
expect(state.updatedBy).toBe('ada');
|
|
expect(await service.getCustomTexts('t1')).toEqual(texts);
|
|
});
|
|
|
|
it('aeltere Vorlage ohne Anmeldehinweise (NULL) → Standardtexte fuer diese Felder, Rest bleibt eigen', async () => {
|
|
prisma.__rows.push({
|
|
tenantId: 't1',
|
|
subject: 'Alt',
|
|
heading: 'H',
|
|
intro: 'I',
|
|
loginHintDirectory: null,
|
|
loginHintLocal: null,
|
|
closing: 'C',
|
|
updatedBy: null,
|
|
updatedAt: new Date(),
|
|
});
|
|
const state = await service.getState('t1');
|
|
expect(state.custom).toBe(true);
|
|
expect(state.texts.subject).toBe('Alt');
|
|
expect(state.texts.loginHintDirectory).toBe(DEFAULT_WELCOME_MAIL_TEXTS.loginHintDirectory);
|
|
expect(state.texts.loginHintLocal).toBe(DEFAULT_WELCOME_MAIL_TEXTS.loginHintLocal);
|
|
});
|
|
|
|
it('Mandantenbindung: Vorlage von t1 ist fuer t2 nicht sichtbar', async () => {
|
|
await service.save('t1', texts, 'ada');
|
|
expect(await service.getCustomTexts('t2')).toBeNull();
|
|
expect(prisma.__log.filter((c) => c.method === 'findUnique').at(-1)?.tenantId).toBe('t2');
|
|
});
|
|
|
|
it('zuruecksetzen: loescht gebunden, danach wieder Standard', async () => {
|
|
await service.save('t1', texts, 'ada');
|
|
const state = await service.reset('t1');
|
|
const del = prisma.__log.find((c) => c.method === 'deleteMany');
|
|
expect(del?.tenantId).toBe('t1');
|
|
expect(del?.args.where).toEqual({ tenantId: 't1' });
|
|
expect(state.custom).toBe(false);
|
|
expect(state.texts).toEqual(DEFAULT_WELCOME_MAIL_TEXTS);
|
|
});
|
|
|
|
it('Firmenname gebunden gelesen; unbekannter Mandant → leer', async () => {
|
|
expect(await service.getTenantName('t1')).toBe('Beispiel GmbH');
|
|
expect(await service.getTenantName('tx')).toBe('');
|
|
expect(prisma.__log.filter((c) => c.model === 'tenant').map((c) => c.tenantId)).toEqual([
|
|
't1',
|
|
'tx',
|
|
]);
|
|
});
|
|
});
|