feat(quick-261003-387): Kategorien-API - Anlegen, Sortieren, Zuordnen, Loeschen mit Verschieben, Ueberlagerung in allen Modullisten

- Verwaltungs-Endpunkte nur fuer Administratoren, statische Routen vor :key
- Loeschen verschiebt alle Eintraege inkl. persoenlicher eigener Module in einer Transaktion, ohne Ziel 409
- /modules, /modules/catalog und /module-grants/matrix liefern wirksame Kategorie und Reihenfolge
- eigene Module pruefen die Kategorie gegen die Organisation (400 bei unbekannter)
- Zugriffsinventar nachgezogen

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-03 02:39:36 +02:00
parent 8ec116c75a
commit af1878d5ee
15 changed files with 1152 additions and 37 deletions
@@ -29,11 +29,23 @@ describe('CreateCustomModuleDto', () => {
expect(await errorsFor(CreateCustomModuleDto, { ...valid, url })).toContain('url');
});
it('lehnt eine unbekannte Kategorie ab', async () => {
expect(await errorsFor(CreateCustomModuleDto, { ...valid, category: 'other' })).toContain(
'category',
);
});
// quick-261003-387: das DTO prueft nur das Format der Kennung; ob die
// Organisation sie fuehrt, entscheidet der Dienst (400, siehe Dienst-Spec).
it.each(['', 'Gross', 'mit leerzeichen', '-fuehrend', 'x'.repeat(61)])(
'lehnt die Kategorie-Kennung %j ab',
async (category) => {
expect(await errorsFor(CreateCustomModuleDto, { ...valid, category })).toContain('category');
},
);
it.each(['fleet', 'custom-modules', 'werkzeuge-tools'])(
'akzeptiert die Kategorie-Kennung %s',
async (category) => {
expect(await errorsFor(CreateCustomModuleDto, { ...valid, category })).not.toContain(
'category',
);
},
);
it.each(['', ' '])('lehnt den Namen %j ab', async (name) => {
expect(await errorsFor(CreateCustomModuleDto, { ...valid, name })).toContain('name');
@@ -61,7 +73,7 @@ describe('UpdateCustomModuleDto', () => {
it('prueft jedes gesetzte Feld gleich', async () => {
expect(await errorsFor(UpdateCustomModuleDto, { url: 'http://example.com' })).toContain('url');
expect(await errorsFor(UpdateCustomModuleDto, { category: 'other' })).toContain('category');
expect(await errorsFor(UpdateCustomModuleDto, { category: 'Nicht Gueltig' })).toContain('category');
expect(await errorsFor(UpdateCustomModuleDto, { name: ' ' })).toContain('name');
});