feat: Phase 6 – Modul-API-Vertrag mit platform-module-sdk

This commit is contained in:
MPM Dev
2026-10-07 16:19:20 +02:00
parent 4d7610328c
commit 0abe7b7abc
8 changed files with 261 additions and 18 deletions

View File

@@ -2,7 +2,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
roots: ['<rootDir>/src', '<rootDir>/test'],
testRegex: '.*\\.spec\\.ts$',
moduleFileExtensions: ['ts', 'js', 'json'],
collectCoverageFrom: ['src/**/*.ts', '!src/main.ts', '!src/**/*.module.ts'],

View File

@@ -3,6 +3,7 @@ import { ConfigModule } from '../config/config.module';
import { DatabaseModule } from '../database/database.module';
import { AuditModule } from '../audit/audit.module';
import { SessionService } from '../auth/session.service';
import { ModulePermissionRepository } from '../modules/module-permission.repository';
import { ModulePermissionsService } from '../modules/module-permissions.service';
import { ModuleRepository } from '../modules/module.repository';
import { PasswordHasher } from './password-hasher';
@@ -25,6 +26,7 @@ import { UsersService } from './users.service';
ProfileService,
SessionService,
ModuleRepository,
ModulePermissionRepository,
ModulePermissionsService,
],
exports: [UserRepository, PasswordHasher],

View File

@@ -0,0 +1,106 @@
/**
* SDK-Tests (Phase 6): Vertrags-Helper des Modul-SDKs.
* Das SDK ist CommonJS und wird direkt mit Node getestet.
*/
const {
GATEWAY_HEADERS,
extractIdentity,
healthResponse,
meResponse,
} = require('../../../modules/demo/backend/platform-module-sdk');
describe('platform-module-sdk', () => {
describe('extractIdentity', () => {
it('extrahiert die Identität aus korrekten Gateway-Headern', () => {
const identity = extractIdentity({
[GATEWAY_HEADERS.userId]: 'user-123',
[GATEWAY_HEADERS.username]: 'max',
[GATEWAY_HEADERS.displayName]: 'Max Mustermann',
[GATEWAY_HEADERS.role]: 'USER',
});
expect(identity).toEqual({
userId: 'user-123',
username: 'max',
displayName: 'Max Mustermann',
role: 'USER',
});
});
it('ist case-insensitive bei Header-Namen', () => {
const identity = extractIdentity({
'X-User-Id': 'user-123',
'X-User-Username': 'max',
'X-User-Role': 'ADMIN',
});
expect(identity).not.toBeNull();
expect(identity?.role).toBe('ADMIN');
});
it('gibt null zurück, wenn Header fehlen (kein Gateway-Request)', () => {
expect(extractIdentity({})).toBeNull();
expect(extractIdentity({ 'x-user-id': 'user-123' })).toBeNull();
});
it('gibt null bei ungültiger Rolle zurück', () => {
const identity = extractIdentity({
[GATEWAY_HEADERS.userId]: 'user-123',
[GATEWAY_HEADERS.username]: 'max',
[GATEWAY_HEADERS.role]: 'SUPERADMIN',
});
expect(identity).toBeNull();
});
it('verwendet den Benutzernamen als Fallback für den Anzeigenamen', () => {
const identity = extractIdentity({
[GATEWAY_HEADERS.userId]: 'user-123',
[GATEWAY_HEADERS.username]: 'max',
[GATEWAY_HEADERS.role]: 'USER',
});
expect(identity?.displayName).toBe('max');
});
});
describe('healthResponse', () => {
it('erzeugt eine vertragskonforme /health-Antwort', () => {
expect(healthResponse('demo', '1.0.0')).toEqual({
moduleId: 'demo',
version: '1.0.0',
status: 'healthy',
});
});
});
describe('meResponse', () => {
it('erzeugt eine vertragskonforme /api/me-Antwort', () => {
const identity = {
userId: 'user-123',
username: 'max',
displayName: 'Max Mustermann',
role: 'USER' as const,
};
expect(meResponse(identity, 'demo', ['demo.read'])).toEqual({
user: {
id: 'user-123',
username: 'max',
displayName: 'Max Mustermann',
platformRole: 'USER',
},
module: { id: 'demo' },
permissions: ['demo.read'],
});
});
it('verwendet leere Permissions als Default', () => {
const identity = {
userId: 'user-123',
username: 'max',
displayName: 'Max',
role: 'ADMIN' as const,
};
expect(meResponse(identity, 'demo').permissions).toEqual([]);
});
});
});