test(260928-ujj): rote Tests fuer Dashboard-Hintergrund in der Datenbank
- PATCH me/dashboard-background: Allowlist, UUID-Bildkennung, Mandantenbindung - getMe liefert dashboardBackground normalisiert - Web: Uebernahme der alten localStorage-Wahl, Hook liest aus dem Auth-Store Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ interface FakeUserRow {
|
||||
mustChangePassword: boolean;
|
||||
avatarPath?: string | null;
|
||||
accentColor?: string | null;
|
||||
dashboardBackground?: unknown;
|
||||
}
|
||||
|
||||
interface BoundCall {
|
||||
@@ -501,6 +502,8 @@ describe('AuthService.getMe', () => {
|
||||
mustChangePassword: false,
|
||||
avatarPath: 'avatars/u1.png',
|
||||
accentColor: '#3b82f6',
|
||||
// quick-260928-ujj: gespeicherter Zusatzschluessel wird bei der Ausgabe verworfen.
|
||||
dashboardBackground: { kind: 'preset', id: 'dunes', extra: 'weg' },
|
||||
};
|
||||
|
||||
const ldapUserRow: FakeUserRow = {
|
||||
@@ -515,6 +518,7 @@ describe('AuthService.getMe', () => {
|
||||
mustChangePassword: false,
|
||||
avatarPath: null,
|
||||
accentColor: null,
|
||||
dashboardBackground: null,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -535,6 +539,7 @@ describe('AuthService.getMe', () => {
|
||||
tenantId: 't1',
|
||||
mustChangePassword: false,
|
||||
accentColor: '#3b82f6',
|
||||
dashboardBackground: { kind: 'preset', id: 'dunes' },
|
||||
isLocalUser: true,
|
||||
hasAvatar: true,
|
||||
});
|
||||
@@ -549,6 +554,30 @@ describe('AuthService.getMe', () => {
|
||||
expect(result).toMatchObject({ isLocalUser: false, hasAvatar: false });
|
||||
});
|
||||
|
||||
it('quick-260928-ujj: dashboardBackground NULL (nie gewaehlt) kommt als null zurueck', async () => {
|
||||
const result = await service.getMe('t1', 'u2');
|
||||
|
||||
expect(result).toHaveProperty('dashboardBackground', null);
|
||||
});
|
||||
|
||||
it('quick-260928-ujj: ungueltiger gespeicherter Hintergrund kommt als null zurueck (T-ujj-01)', async () => {
|
||||
prisma.__users.set('u1', {
|
||||
...prisma.__users.get('u1'),
|
||||
dashboardBackground: { kind: 'image', imageId: '") ; background: url("x' },
|
||||
});
|
||||
|
||||
const result = await service.getMe('t1', 'u1');
|
||||
|
||||
expect(result).toHaveProperty('dashboardBackground', null);
|
||||
});
|
||||
|
||||
it('quick-260928-ujj: dashboardBackground steht im select neben accentColor', async () => {
|
||||
await service.getMe('t1', 'u1');
|
||||
|
||||
const call = prisma.__boundCallLog.find((c: any) => c.method === 'findUnique');
|
||||
expect(call.args.select).toMatchObject({ accentColor: true, dashboardBackground: true });
|
||||
});
|
||||
|
||||
it('FREMDER Mandant (Klient unter t2, Zeile unter t1): liefert null, kein Fehler', async () => {
|
||||
const result = await service.getMe('t2', 'u1');
|
||||
|
||||
|
||||
@@ -580,4 +580,87 @@ describe('UserController', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* quick-260928-ujj: Dashboard-Hintergrund pro Benutzer in der Datenbank.
|
||||
* Der Selbstbedienungsweg schreibt ausschliesslich die Zeile des
|
||||
* angemeldeten Benutzers ueber den an dessen Mandanten gebundenen Klienten
|
||||
* (T-ujj-02) und nimmt nur die erlaubten Formen an (T-ujj-01): gespeichert
|
||||
* wird immer ein frisch aufgebautes Objekt ohne Zusatzschluessel.
|
||||
*/
|
||||
describe('Dashboard-Hintergrund (quick-260928-ujj)', () => {
|
||||
const me = { role: Role.USER, tenantId: 't1', id: 'u-me', username: 'me', mustChangePassword: false };
|
||||
const IMAGE_ID = '3f2b8c1e-9a4d-4e7f-8b21-0c5d6e7f8a9b';
|
||||
|
||||
function seedMe(tenantId = 't1', id = 'u-me') {
|
||||
prisma.__seedUser({ id, username: id, tenantId, dashboardBackground: null });
|
||||
}
|
||||
|
||||
async function storedFor(tenantId: string, id: string) {
|
||||
const row = await prisma.__makeBoundClient(tenantId).user.findUnique({ where: { id } });
|
||||
return row.dashboardBackground;
|
||||
}
|
||||
|
||||
it.each([
|
||||
[{ kind: 'none' }, { kind: 'none' }],
|
||||
[{ kind: 'preset', id: 'dunes' }, { kind: 'preset', id: 'dunes' }],
|
||||
[{ kind: 'preset', id: 'mosaic', extra: 'weg' }, { kind: 'preset', id: 'mosaic' }],
|
||||
[{ kind: 'image', imageId: IMAGE_ID }, { kind: 'image', imageId: IMAGE_ID }],
|
||||
[{ kind: 'image', imageId: IMAGE_ID.toUpperCase(), id: 'mist' }, { kind: 'image', imageId: IMAGE_ID.toUpperCase() }],
|
||||
[{ kind: 'none', id: 'dunes', imageId: IMAGE_ID }, { kind: 'none' }],
|
||||
])('%j wird normalisiert als %j gespeichert, gebunden an den eigenen Mandanten', async (input, expected) => {
|
||||
seedMe();
|
||||
|
||||
const result = await controller.updateDashboardBackground({ background: input }, me);
|
||||
|
||||
expect(result).toEqual({ success: true, dashboardBackground: expected });
|
||||
expect(await storedFor('t1', 'u-me')).toEqual(expected);
|
||||
expectBoundCall(prisma, 't1', 'user', 'update');
|
||||
expect(prisma.__boundCallLog.filter((c: any) => c.method === 'update').every((c: any) => c.tenantId === 't1')).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ kind: 'preset', id: 'regenbogen' }],
|
||||
[{ kind: 'preset' }],
|
||||
[{ kind: 'preset', id: 42 }],
|
||||
[{ kind: 'gradient', id: 'mist' }],
|
||||
[{ kind: 'image', imageId: 'img-1' }],
|
||||
[{ kind: 'image', imageId: '") ; background: url("https://boese.invalid/x' }],
|
||||
[{ kind: 'image', imageId: `${IMAGE_ID})` }],
|
||||
[{ kind: 'image', imageId: `${IMAGE_ID}0` }],
|
||||
[{ kind: 'image', imageId: '' }],
|
||||
[{ kind: 'image' }],
|
||||
[{}],
|
||||
[null],
|
||||
[undefined],
|
||||
['none'],
|
||||
[42],
|
||||
[[{ kind: 'none' }]],
|
||||
])('ungueltige Wahl %j → BadRequestException, nichts geschrieben', async (background) => {
|
||||
seedMe();
|
||||
|
||||
await expect(controller.updateDashboardBackground({ background }, me)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(prisma.__boundCallLog.some((c: any) => c.method === 'update')).toBe(false);
|
||||
expect(await storedFor('t1', 'u-me')).toBeNull();
|
||||
});
|
||||
|
||||
it('fehlender Rumpf → BadRequestException', async () => {
|
||||
seedMe();
|
||||
|
||||
await expect(
|
||||
controller.updateDashboardBackground(undefined as unknown as { background: unknown }, me),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(prisma.__boundCallLog.some((c: any) => c.method === 'update')).toBe(false);
|
||||
});
|
||||
|
||||
it('zwei Benutzer in zwei Mandanten: nur die Zeile des Anfragenden aendert sich', async () => {
|
||||
seedMe('t1', 'u-me');
|
||||
seedMe('t2', 'u-other');
|
||||
|
||||
await controller.updateDashboardBackground({ background: { kind: 'preset', id: 'mist' } }, me);
|
||||
|
||||
expect(await storedFor('t1', 'u-me')).toEqual({ kind: 'preset', id: 'mist' });
|
||||
expect(await storedFor('t2', 'u-other')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user