import { beforeEach, describe, expect, it } from 'vitest' import { listAllProjects } from '~/data/repositories/projects' import { performSaveProject } from '~/lib/admin-projects' import { adminViewer, auditEntries, form, makeBrand, makeProject, moderatorViewer, resetAccounts, } from '../support/admin' beforeEach(resetAccounts) describe('performSaveProject legt an', () => { it('speichert ein Projekt und schreibt einen Eintrag ins audit_log', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'MTA Telematik', slug: '', code: 'tel', description: 'Telemetrie für die Flotte', color: '#B4761A', brandId: brand.id, sort: '3', isActive: 'on', }), ) expect(state.status).toBe('ok') expect(state.created).toBe(true) const projects = await listAllProjects() expect(projects).toHaveLength(1) expect(projects[0]).toMatchObject({ slug: 'mta-telematik', name: 'MTA Telematik', code: 'TEL', color: '#b4761a', sort: 3, isActive: true, brandId: brand.id, }) const entries = await auditEntries() expect(entries).toHaveLength(1) expect(entries[0]).toMatchObject({ action: 'project.create', entity: 'project', entityId: projects[0]!.id, actorId: 'admin-1', }) }) it('legt ein stillgelegtes Projekt an, wenn der Haken fehlt', async () => { const brand = await makeBrand() await performSaveProject(adminViewer(), form({ name: 'Altprojekt', brandId: brand.id, color: '#191c20' })) const projects = await listAllProjects() expect(projects[0]?.isActive).toBe(false) }) }) describe('performSaveProject prüft den Slug', () => { it('lehnt einen vergebenen Slug ab', async () => { const brand = await makeBrand() await makeProject(brand.id, 'trakk', 'Trakk') const state = await performSaveProject( adminViewer(), form({ name: 'Trakk Zwei', slug: 'trakk', brandId: brand.id, color: '#191c20', isActive: 'on' }), ) expect(state.status).toBe('error') expect(state.message).toBe('slugTaken') expect(state.fields?.slug).toBe('slugTaken') expect(await listAllProjects()).toHaveLength(1) }) it('erlaubt beim Bearbeiten den eigenen Slug', async () => { const brand = await makeBrand() const project = await makeProject(brand.id, 'trakk', 'Trakk') const state = await performSaveProject( adminViewer(), form({ id: project.id, name: 'Trakk', slug: 'trakk', brandId: brand.id, color: '#2e7d5b', sort: '1', isActive: 'on', }), ) expect(state.status).toBe('ok') expect(state.created).toBe(false) const entries = await auditEntries() expect(entries[0]?.action).toBe('project.update') }) it('schreibt einen eingetippten Slug klein', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'Trakk', slug: 'Trakk', brandId: brand.id, color: '#191c20' }), ) expect(state.status).toBe('ok') const projects = await listAllProjects() expect(projects[0]?.slug).toBe('trakk') }) it('lehnt einen Slug mit Leerzeichen ab', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'Trakk', slug: 'trakk web', brandId: brand.id, color: '#191c20' }), ) expect(state.status).toBe('error') expect(state.fields?.slug).toBe('slugInvalid') }) }) describe('performSaveProject prüft die übrigen Felder', () => { it('verlangt einen Namen', async () => { const brand = await makeBrand() const state = await performSaveProject(adminViewer(), form({ name: '', brandId: brand.id, color: '#191c20' })) expect(state.fields?.name).toBe('nameRequired') }) it('lehnt eine unbekannte Marke ab', async () => { await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'Trakk', brandId: '11111111-1111-4111-8111-111111111111', color: '#191c20', }), ) expect(state.fields?.brandId).toBe('brandUnknown') expect(await listAllProjects()).toHaveLength(0) }) it('lehnt eine Farbe ab, die kein Hexwert ist', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'Trakk', brandId: brand.id, color: 'rot' }), ) expect(state.fields?.color).toBe('colorInvalid') }) it('lehnt eine Sortierung ab, die keine Zahl ist', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ name: 'Trakk', brandId: brand.id, color: '#191c20', sort: 'oben' }), ) expect(state.fields?.sort).toBe('sortInvalid') }) }) describe('performSaveProject prüft die Rechte', () => { it('lässt einen Moderator kein Projekt anlegen', async () => { const brand = await makeBrand() const project = await makeProject(brand.id) const state = await performSaveProject( moderatorViewer([project.id]), form({ name: 'Neues Projekt', brandId: brand.id, color: '#191c20', isActive: 'on' }), ) expect(state.status).toBe('error') expect(state.message).toBe('forbidden') expect(await listAllProjects()).toHaveLength(1) expect(await auditEntries()).toHaveLength(0) }) it('lässt einen Moderator sein eigenes Projekt nicht bearbeiten', async () => { const brand = await makeBrand() const project = await makeProject(brand.id) const state = await performSaveProject( moderatorViewer([project.id]), form({ id: project.id, name: 'Umbenannt', brandId: brand.id, color: '#191c20', isActive: 'on' }), ) expect(state.message).toBe('forbidden') const projects = await listAllProjects() expect(projects[0]?.name).toBe('Trakk') }) it('meldet einen unbekannten Datensatz beim Bearbeiten', async () => { const brand = await makeBrand() const state = await performSaveProject( adminViewer(), form({ id: '11111111-1111-4111-8111-111111111111', name: 'Trakk', brandId: brand.id, color: '#191c20', }), ) expect(state.message).toBe('notFound') }) })