Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,343 @@
/**
* @jest-environment jsdom
*/
import { DependencyManager } from '../../resources/js/core/DependencyManager.js';
// Mock Logger
jest.mock('../../resources/js/core/logger.js', () => ({
Logger: {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn()
}
}));
describe('DependencyManager', () => {
let depManager;
beforeEach(() => {
depManager = new DependencyManager();
});
describe('Module registration', () => {
test('should register module with valid definition', () => {
const definition = {
name: 'test-module',
version: '1.0.0',
dependencies: [],
provides: ['test-service'],
priority: 0
};
depManager.register(definition);
expect(depManager.getModule('test-module')).toEqual(definition);
});
test('should reject invalid module definitions', () => {
const invalidDefinitions = [
{ version: '1.0.0', dependencies: [] }, // No name
{ name: 'test', dependencies: [] }, // No version
{ name: 'test', version: '1.0.0' }, // No dependencies array
{ name: 'test', version: '1.0.0', dependencies: [{}] } // Invalid dependency
];
invalidDefinitions.forEach(def => {
depManager.register(def);
expect(depManager.getModule(def.name)).toBeUndefined();
});
});
test('should not allow duplicate registration', () => {
const definition1 = {
name: 'duplicate',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
};
const definition2 = {
name: 'duplicate',
version: '2.0.0',
dependencies: [],
provides: [],
priority: 0
};
depManager.register(definition1);
depManager.register(definition2);
expect(depManager.getModule('duplicate').version).toBe('1.0.0');
});
test('should build dependents map correctly', () => {
const moduleA = {
name: 'module-a',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
};
const moduleB = {
name: 'module-b',
version: '1.0.0',
dependencies: [{ name: 'module-a', version: '1.0.0', optional: false }],
provides: [],
priority: 0
};
depManager.register(moduleA);
depManager.register(moduleB);
expect(depManager.getDependents('module-a')).toContain('module-b');
});
});
describe('Dependency resolution', () => {
beforeEach(() => {
// Register test modules
depManager.register({
name: 'core',
version: '1.0.0',
dependencies: [],
provides: ['core-service'],
priority: 100
});
depManager.register({
name: 'ui',
version: '1.0.0',
dependencies: [{ name: 'core', version: '1.0.0', optional: false }],
provides: ['ui-service'],
priority: 50
});
depManager.register({
name: 'advanced',
version: '1.0.0',
dependencies: [
{ name: 'core', version: '1.0.0', optional: false },
{ name: 'ui', version: '1.0.0', optional: false }
],
provides: ['advanced-service'],
priority: 10
});
});
test('should calculate correct initialization order', () => {
const order = depManager.calculateInitializationOrder();
expect(order).toEqual(['core', 'ui', 'advanced']);
});
test('should handle optional dependencies', () => {
depManager.register({
name: 'optional-dep',
version: '1.0.0',
dependencies: [
{ name: 'core', version: '1.0.0', optional: false },
{ name: 'missing-optional', version: '1.0.0', optional: true }
],
provides: [],
priority: 0
});
const order = depManager.calculateInitializationOrder();
expect(order).toContain('optional-dep');
});
test('should detect circular dependencies', () => {
depManager.register({
name: 'circular-a',
version: '1.0.0',
dependencies: [{ name: 'circular-b', version: '1.0.0', optional: false }],
provides: [],
priority: 0
});
depManager.register({
name: 'circular-b',
version: '1.0.0',
dependencies: [{ name: 'circular-a', version: '1.0.0', optional: false }],
provides: [],
priority: 0
});
expect(() => {
depManager.calculateInitializationOrder();
}).toThrow('Circular dependency detected');
});
test('should respect priority in initialization order', () => {
depManager.register({
name: 'high-priority',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 200
});
const order = depManager.calculateInitializationOrder();
expect(order.indexOf('high-priority')).toBeLessThan(order.indexOf('core'));
});
});
describe('Dependency checking', () => {
beforeEach(() => {
depManager.register({
name: 'dependency',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
});
depManager.register({
name: 'dependent',
version: '1.0.0',
dependencies: [{ name: 'dependency', version: '1.0.0', optional: false }],
provides: [],
priority: 0
});
});
test('should check dependencies correctly', () => {
const check = depManager.checkDependencies('dependent');
expect(check.satisfied).toBe(false);
expect(check.missing).toContain('dependency (not initialized)');
});
test('should pass dependency check when dependencies are initialized', () => {
depManager.markInitialized('dependency');
const check = depManager.checkDependencies('dependent');
expect(check.satisfied).toBe(true);
expect(check.missing).toHaveLength(0);
});
test('should handle optional dependencies in check', () => {
depManager.register({
name: 'optional-user',
version: '1.0.0',
dependencies: [
{ name: 'dependency', version: '1.0.0', optional: false },
{ name: 'missing-optional', version: '1.0.0', optional: true }
],
provides: [],
priority: 0
});
depManager.markInitialized('dependency');
const check = depManager.checkDependencies('optional-user');
expect(check.satisfied).toBe(true);
expect(check.optional).toContain('missing-optional');
});
test('should check readiness for initialization', () => {
expect(depManager.isReadyToInitialize('dependency')).toBe(true);
expect(depManager.isReadyToInitialize('dependent')).toBe(false);
depManager.markInitialized('dependency');
expect(depManager.isReadyToInitialize('dependent')).toBe(true);
});
});
describe('Module state tracking', () => {
beforeEach(() => {
depManager.register({
name: 'test-module',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
});
});
test('should track initialization state', () => {
expect(depManager.initialized.has('test-module')).toBe(false);
expect(depManager.initializing.has('test-module')).toBe(false);
depManager.markInitializing('test-module');
expect(depManager.initializing.has('test-module')).toBe(true);
depManager.markInitialized('test-module');
expect(depManager.initialized.has('test-module')).toBe(true);
expect(depManager.initializing.has('test-module')).toBe(false);
});
test('should provide status report', () => {
depManager.register({
name: 'module-2',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
});
depManager.markInitialized('test-module');
depManager.markInitializing('module-2');
const status = depManager.getStatus();
expect(status.total).toBe(2);
expect(status.initialized).toBe(1);
expect(status.initializing).toBe(1);
expect(status.pending).toBe(0);
expect(status.modules.initialized).toContain('test-module');
expect(status.modules.initializing).toContain('module-2');
});
});
describe('Definition builder', () => {
test('should create definition with builder pattern', () => {
const definition = DependencyManager.createDefinition('builder-test', '2.0.0')
.depends('dep1', '1.0.0')
.depends('dep2', '2.0.0', true)
.provides('service1', 'service2')
.priority(50);
expect(definition.name).toBe('builder-test');
expect(definition.version).toBe('2.0.0');
expect(definition.dependencies).toHaveLength(2);
expect(definition.dependencies[0]).toEqual({
name: 'dep1',
version: '1.0.0',
optional: false
});
expect(definition.dependencies[1]).toEqual({
name: 'dep2',
version: '2.0.0',
optional: true
});
expect(definition.provides).toEqual(['service1', 'service2']);
expect(definition.priority).toBe(50);
});
});
describe('Reset and cleanup', () => {
test('should reset all state', () => {
depManager.register({
name: 'test',
version: '1.0.0',
dependencies: [],
provides: [],
priority: 0
});
depManager.markInitialized('test');
depManager.reset();
expect(depManager.modules.size).toBe(0);
expect(depManager.initialized.size).toBe(0);
expect(depManager.dependents.size).toBe(0);
});
});
});