feat(identity): redesign identity module and introduce RBAC foundation
- Redesign the Identity module with a richer domain model. - Extend the User entity to support username, Authentik integration, activity tracking, and storage information. - Add Role and Permission domain models with many-to-many relationships. - Implement RBAC foundation using UserRole, RolePermission, and UserPermission mappings. - Add user storage quota and usage fields with default values. - Introduce Authentik identifiers and synchronization metadata. - Refactor user domain logic for role and permission management. - Update Prisma schema to support the new identity architecture. - Improve JWT authentication and permission guard integration. - Update repositories, handlers, controllers, mappers, DTOs, and Swagger configuration. - Refresh environment configuration and project dependencies.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
const fs = require('fs');
|
||||
|
||||
class CustomReporter {
|
||||
onEnd(config, result) {
|
||||
const outputPath = 'tests/output/latest-result.txt';
|
||||
|
||||
if (!result) {
|
||||
// test run aborted or failed to initialize
|
||||
if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true });
|
||||
fs.writeFileSync(outputPath, 'TEST RUN FAILED OR ABORTED', 'utf-8');
|
||||
return;
|
||||
}
|
||||
|
||||
const failed = result.status === 'failed' || result.numFailedTests > 0;
|
||||
|
||||
const date = new Date().toISOString();
|
||||
const duration = (result.duration || 0) + 'ms';
|
||||
|
||||
if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true });
|
||||
|
||||
if (!failed) {
|
||||
const content = [
|
||||
'==================================',
|
||||
'TEST RESULT',
|
||||
'PASSED',
|
||||
`Date: ${date}`,
|
||||
`Total Test: ${result.total || 0}`,
|
||||
`Passed: ${result.passed || 0}`,
|
||||
`Failed: ${result.failed || 0}`,
|
||||
`Duration: ${duration}`,
|
||||
'==================================',
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(outputPath, content, 'utf-8');
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
lines.push('==================================');
|
||||
lines.push('FAILED');
|
||||
lines.push(`Date: ${date}`);
|
||||
lines.push(`Duration: ${duration}`);
|
||||
lines.push('==================================');
|
||||
|
||||
const buildFailureDetails = (suites) => {
|
||||
for (const s of suites) {
|
||||
if (s.suites && s.suites.length) buildFailureDetails(s.suites);
|
||||
if (s.tests && s.tests.length) {
|
||||
for (const t of s.tests) {
|
||||
if (t.status === 'failed') {
|
||||
lines.push('Test Name: ' + (t.title || t.titleText || ''));
|
||||
for (const r of t.results || []) {
|
||||
if (r.status === 'failed') {
|
||||
const error = r.error || {};
|
||||
lines.push('Error Message: ' + (error.message || ''));
|
||||
if (error.stack) lines.push('Stack Trace: ' + error.stack);
|
||||
}
|
||||
}
|
||||
lines.push('----------------------------------');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (result.suites) buildFailureDetails(result.suites);
|
||||
|
||||
fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomReporter;
|
||||
@@ -0,0 +1,78 @@
|
||||
import fs from 'fs';
|
||||
import { FullConfig, Reporter, Suite, TestCase, TestError } from '@playwright/test/reporter';
|
||||
|
||||
class CustomReporter implements Reporter {
|
||||
onEnd(config: FullConfig, result: any) {
|
||||
const outputPath = 'tests/output/latest-result.txt';
|
||||
|
||||
const allTests = result.suites || [];
|
||||
|
||||
const failed = result.status === 'failed' || result.numFailedTests > 0;
|
||||
|
||||
const date = new Date().toISOString();
|
||||
const duration = (result.duration || 0) + 'ms';
|
||||
|
||||
if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true });
|
||||
|
||||
if (!failed) {
|
||||
const content = [
|
||||
'==================================',
|
||||
'TEST RESULT',
|
||||
'PASSED',
|
||||
`Date: ${date}`,
|
||||
`Total Test: ${result.total || 0}`,
|
||||
`Passed: ${result.passed || 0}`,
|
||||
`Failed: ${result.failed || 0}`,
|
||||
`Duration: ${duration}`,
|
||||
'==================================',
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(outputPath, content, 'utf-8');
|
||||
return;
|
||||
}
|
||||
|
||||
// Failed: build detailed report
|
||||
const lines: string[] = [];
|
||||
lines.push('==================================');
|
||||
lines.push('FAILED');
|
||||
lines.push(`Date: ${date}`);
|
||||
lines.push(`Duration: ${duration}`);
|
||||
lines.push('==================================');
|
||||
|
||||
for (const res of result.report || []) {
|
||||
// older Playwright may not provide report; fallback to result.annotations
|
||||
}
|
||||
|
||||
// Walk tests
|
||||
const buildFailureDetails = (suites: any[]) => {
|
||||
for (const s of suites) {
|
||||
if (s.suites && s.suites.length) buildFailureDetails(s.suites);
|
||||
if (s.tests && s.tests.length) {
|
||||
for (const t of s.tests) {
|
||||
if (t.status === 'failed') {
|
||||
lines.push('Test Name: ' + t.title.join(' > '));
|
||||
// try extract location
|
||||
const location = t.location ? `${t.location.file}:${t.location.line}` : '';
|
||||
if (location) lines.push('Location: ' + location);
|
||||
for (const r of t.results || []) {
|
||||
if (r.status === 'failed') {
|
||||
const error = r.error as TestError | undefined;
|
||||
lines.push('Error Message: ' + (error?.message || ''));
|
||||
if (r.stdout && r.stdout.length) lines.push('Stdout: ' + r.stdout.join('\n'));
|
||||
if (error?.stack) lines.push('Stack Trace: ' + error.stack);
|
||||
}
|
||||
}
|
||||
lines.push('----------------------------------');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (result.suites) buildFailureDetails(result.suites);
|
||||
|
||||
fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomReporter;
|
||||
Reference in New Issue
Block a user