import 'reflect-metadata'; import { GUARDS_METADATA } from '@nestjs/common/constants'; import { Role } from '@prisma/client'; import { describe, expect, it } from 'vitest'; import { ROLES_KEY } from '../auth/decorators/roles.decorator'; import { DkvController } from '../dkv/dkv.controller'; import { DomainsController } from '../domains/domains.controller'; import { ModuleGrantsController } from '../groups/module-grants.controller'; import { HandelswareDatevController } from '../handelsware-datev/handelsware-datev.controller'; import { KantineDatevController } from '../kantine-datev/kantine-datev.controller'; import { NextcloudFilesController } from '../nextcloud-files/nextcloud-files.controller'; import { NextcloudStatusController } from '../nextcloud-status/nextcloud-status.controller'; import { ProxmoxController } from '../proxmox/proxmox.controller'; import { TendersController } from '../tenders/tenders.controller'; import { MODULE_MANAGE_KEY, MODULE_SLUG_KEY, ModuleGuard } from './module.guard'; import { ModuleRegistryController } from './module-registry.controller'; /** * Metadaten-Beweis für die Freigabestufe Verwalten (261002-icv, L-04/L-09): * welche Handler auf `@ModuleManage` umgestellt wurden und welche bewusst * Administratoren vorbehalten bleiben (T-icv-01/06/07/08). Reine Metadaten — * kein Nest-Start, keine Datenbank. */ const ADMIN_ONLY = [Role.ADMIN, Role.SUPER_ADMIN]; function methodsOf(controller: { prototype: object }): string[] { return Object.getOwnPropertyNames(controller.prototype).filter( (name) => name !== 'constructor' && typeof (controller.prototype as any)[name] === 'function', ); } function handler(controller: { prototype: object }, name: string) { return (controller.prototype as any)[name]; } function expectManage(controller: { prototype: object }, name: string, slug: string) { const fn = handler(controller, name); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn), `${name} MODULE_MANAGE_KEY`).toBe(true); expect(Reflect.getMetadata(MODULE_SLUG_KEY, fn), `${name} MODULE_SLUG_KEY`).toBe(slug); expect(Reflect.getMetadata(GUARDS_METADATA, fn), `${name} guards`).toContain(ModuleGuard); expect(Reflect.getMetadata(ROLES_KEY, fn), `${name} ROLES_KEY`).toBeUndefined(); } function expectAdminOnly(controller: { prototype: object }, name: string) { const fn = handler(controller, name); expect(Reflect.getMetadata(ROLES_KEY, fn), `${name} ROLES_KEY`).toEqual(ADMIN_ONLY); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn), `${name} MODULE_MANAGE_KEY`).toBeUndefined(); } describe('Umgestellte Handler (Verwalten)', () => { it('DkvController: ganze Klasse Verwalten, kein Handler trägt @Roles', () => { expect(Reflect.getMetadata(MODULE_SLUG_KEY, DkvController)).toBe('dkv-fleet'); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, DkvController)).toBe(true); expect(Reflect.getMetadata(GUARDS_METADATA, DkvController)).toContain(ModuleGuard); const names = methodsOf(DkvController).filter((n) => Reflect.hasMetadata('path', handler(DkvController, n)), ); expect(names.length).toBe(11); for (const name of names) { expect(Reflect.getMetadata(ROLES_KEY, handler(DkvController, name)), name).toBeUndefined(); } }); it.each([ 'create', 'update', 'remove', 'poll', 'test', 'testDraft', ])('ProxmoxController.%s verlangt Verwalten für proxmox', (name) => { expectManage(ProxmoxController, name, 'proxmox'); }); it('ProxmoxController.list bleibt auf Benutzen-Ebene', () => { const fn = handler(ProxmoxController, 'list'); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn)).toBeUndefined(); expect(Reflect.getMetadata(ROLES_KEY, fn)).toBeUndefined(); }); it.each([ 'create', 'update', 'remove', 'checkAll', 'checkOne', 'uploadLogo', 'removeLogo', ])('NextcloudStatusController.%s verlangt Verwalten für nextcloud-status', (name) => { expectManage(NextcloudStatusController, name, 'nextcloud-status'); }); it.each([ 'list', 'logo', 'subscribe', 'unsubscribe', 'recentAlerts', ])('NextcloudStatusController.%s bleibt auf Benutzen-Ebene', (name) => { const fn = handler(NextcloudStatusController, name); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn)).toBeUndefined(); expect(Reflect.getMetadata(ROLES_KEY, fn)).toBeUndefined(); }); it.each([ 'getSettings', 'saveSettings', 'testConnection', 'createCustomer', 'updateCustomer', 'deleteCustomer', 'createContact', 'assignContacts', 'checkAvailability', 'getNameServers', 'createOrder', 'submitOrder', 'cancelOrder', ])('DomainsController.%s verlangt Verwalten für domains (quick-261008-dts)', (name) => { expectManage(DomainsController, name, 'domains'); }); it.each([ 'getStatus', 'listCustomers', 'listContacts', 'listDomains', 'listOrders', 'refreshOrders', ])('DomainsController.%s bleibt auf Benutzen-Ebene', (name) => { const fn = handler(DomainsController, name); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn)).toBeUndefined(); expect(Reflect.getMetadata(ROLES_KEY, fn)).toBeUndefined(); }); it.each([ 'getSettings', 'testSettings', ])('NextcloudFilesController.%s verlangt Verwalten für nextcloud-files (quick-261008-mzu)', (name) => { expectManage(NextcloudFilesController, name, 'nextcloud-files'); }); // Spätere Aufgaben von quick-261008-mzu ergänzen diese Liste um ihre Benutzen-Handler. it.each([ 'getStatus', 'getServer', 'getServerLogo', 'connectPassword', 'startFlow', 'pollFlow', 'cancelFlow', 'disconnect', 'list', 'remove', 'createFolder', 'move', 'preview', 'download', 'downloadZip', 'startUpload', 'putSingle', 'putChunk', 'completeUpload', 'uploadState', 'abortUpload', 'getSharePolicy', 'listSharesForPath', 'searchSharees', 'createShare', 'listMyShares', 'listReceivedShares', 'updateShare', 'deleteShare', 'acceptShare', ])('NextcloudFilesController.%s bleibt auf Benutzen-Ebene', (name) => { const fn = handler(NextcloudFilesController, name); expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn)).toBeUndefined(); expect(Reflect.getMetadata(ROLES_KEY, fn)).toBeUndefined(); }); it('KantineDatevController.saveSettings und HandelswareDatevController.saveSettings verlangen Verwalten', () => { expectManage(KantineDatevController, 'saveSettings', 'kantine-datev'); expectManage(HandelswareDatevController, 'saveSettings', 'handelsware-datev'); }); }); describe('Bewusst nur für Administratoren (T-icv-01, T-icv-07)', () => { it.each([ 'getSourceConfig', 'saveSourceConfig', 'pollNow', ])('TendersController.%s bleibt @Roles(ADMIN, SUPER_ADMIN)', (name) => { expectAdminOnly(TendersController, name); }); it.each([ 'matrix', 'userAccess', 'create', 'remove', ])('ModuleGrantsController.%s bleibt @Roles(ADMIN, SUPER_ADMIN)', (name) => { expectAdminOnly(ModuleGrantsController, name); }); it.each([ 'activate', 'deactivate', ])('ModuleRegistryController.%s bleibt @Roles(ADMIN, SUPER_ADMIN)', (name) => { expectAdminOnly(ModuleRegistryController, name); }); it('NextcloudFilesController.saveSettings: die Nextcloud-Adresse legt nur ein Administrator fest (quick-261008-mzu, CR-02)', () => { expectAdminOnly(NextcloudFilesController, 'saveSettings'); }); });