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,279 @@
/**
* @jest-environment jsdom
*/
import { StateManager } from '../../resources/js/core/StateManager.js';
// Mock Logger
jest.mock('../../resources/js/core/logger.js', () => ({
Logger: {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn()
}
}));
describe('StateManager', () => {
let stateManager;
beforeEach(() => {
stateManager = new StateManager();
stateManager.setContext('test-module');
});
describe('State registration', () => {
test('should register state with default value', () => {
stateManager.register('testKey', 'defaultValue');
expect(stateManager.get('testKey')).toBe('defaultValue');
});
test('should not allow duplicate registration', () => {
stateManager.register('testKey', 'value1');
stateManager.register('testKey', 'value2');
expect(stateManager.get('testKey')).toBe('value1');
});
test('should track state ownership', () => {
stateManager.setContext('module-a');
stateManager.register('keyA', 'valueA');
stateManager.setContext('module-b');
stateManager.register('keyB', 'valueB');
expect(stateManager.stateOwners.get('keyA')).toBe('module-a');
expect(stateManager.stateOwners.get('keyB')).toBe('module-b');
});
});
describe('State access', () => {
beforeEach(() => {
stateManager.register('testKey', 42);
});
test('should get current state value', () => {
expect(stateManager.get('testKey')).toBe(42);
});
test('should return undefined for unknown keys', () => {
expect(stateManager.get('unknownKey')).toBeUndefined();
});
test('should allow owner to set value', () => {
const success = stateManager.set('testKey', 100);
expect(success).toBe(true);
expect(stateManager.get('testKey')).toBe(100);
});
test('should prevent non-owner from setting value', () => {
stateManager.setContext('other-module');
const success = stateManager.set('testKey', 200);
expect(success).toBe(false);
expect(stateManager.get('testKey')).toBe(42);
});
test('should allow forced set by non-owner', () => {
stateManager.setContext('other-module');
const success = stateManager.set('testKey', 300, true);
expect(success).toBe(true);
expect(stateManager.get('testKey')).toBe(300);
});
test('should not trigger subscribers for same value', () => {
const callback = jest.fn();
stateManager.subscribe('testKey', callback);
stateManager.set('testKey', 42); // Same value
expect(callback).not.toHaveBeenCalled();
});
});
describe('State subscriptions', () => {
beforeEach(() => {
stateManager.register('testKey', 'initial');
});
test('should subscribe to state changes', () => {
const callback = jest.fn();
const subscriptionId = stateManager.subscribe('testKey', callback);
expect(subscriptionId).toBeTruthy();
expect(typeof subscriptionId).toBe('string');
});
test('should notify subscribers on state change', () => {
const callback = jest.fn();
stateManager.subscribe('testKey', callback);
stateManager.set('testKey', 'changed');
expect(callback).toHaveBeenCalledWith('changed', 'initial', 'testKey');
});
test('should handle multiple subscribers', () => {
const callback1 = jest.fn();
const callback2 = jest.fn();
stateManager.subscribe('testKey', callback1);
stateManager.subscribe('testKey', callback2);
stateManager.set('testKey', 'updated');
expect(callback1).toHaveBeenCalledWith('updated', 'initial', 'testKey');
expect(callback2).toHaveBeenCalledWith('updated', 'initial', 'testKey');
});
test('should unsubscribe correctly', () => {
const callback = jest.fn();
const subscriptionId = stateManager.subscribe('testKey', callback);
stateManager.unsubscribe(subscriptionId);
stateManager.set('testKey', 'after-unsubscribe');
expect(callback).not.toHaveBeenCalled();
});
test('should handle subscriber errors gracefully', () => {
const errorCallback = jest.fn(() => {
throw new Error('Subscriber error');
});
const goodCallback = jest.fn();
stateManager.subscribe('testKey', errorCallback);
stateManager.subscribe('testKey', goodCallback);
expect(() => {
stateManager.set('testKey', 'trigger-error');
}).not.toThrow();
expect(goodCallback).toHaveBeenCalled();
});
test('should not subscribe to unknown keys', () => {
const callback = jest.fn();
const subscriptionId = stateManager.subscribe('unknownKey', callback);
expect(subscriptionId).toBeNull();
});
});
describe('State reset', () => {
beforeEach(() => {
stateManager.register('testKey', 'default');
stateManager.set('testKey', 'changed');
});
test('should reset to default value', () => {
const success = stateManager.reset('testKey');
expect(success).toBe(true);
expect(stateManager.get('testKey')).toBe('default');
});
test('should notify subscribers on reset', () => {
const callback = jest.fn();
stateManager.subscribe('testKey', callback);
stateManager.reset('testKey');
expect(callback).toHaveBeenCalledWith('default', 'changed', 'testKey');
});
test('should not reset unknown keys', () => {
const success = stateManager.reset('unknownKey');
expect(success).toBe(false);
});
});
describe('Module scoped interface', () => {
test('should create scoped state manager', () => {
const scoped = stateManager.createScope('scoped-module');
expect(scoped).toHaveProperty('register');
expect(scoped).toHaveProperty('get');
expect(scoped).toHaveProperty('set');
expect(scoped).toHaveProperty('subscribe');
expect(scoped).toHaveProperty('cleanup');
});
test('should use module context in scoped operations', () => {
const scoped = stateManager.createScope('scoped-module');
scoped.register('scopedKey', 'scopedValue');
expect(stateManager.stateOwners.get('scopedKey')).toBe('scoped-module');
});
test('should cleanup module subscriptions', () => {
const scoped = stateManager.createScope('cleanup-module');
const callback = jest.fn();
scoped.register('cleanupKey', 'value');
const subscriptionId = scoped.subscribe('cleanupKey', callback);
scoped.cleanup();
// Subscription should be cleaned up
stateManager.set('cleanupKey', 'after-cleanup', true);
expect(callback).not.toHaveBeenCalled();
});
});
describe('Debugging and introspection', () => {
test('should provide state snapshot', () => {
stateManager.register('key1', 'value1');
stateManager.register('key2', 42);
const snapshot = stateManager.getSnapshot();
expect(snapshot).toHaveProperty('state');
expect(snapshot).toHaveProperty('owners');
expect(snapshot).toHaveProperty('subscriptions');
expect(snapshot.state.key1).toBe('value1');
expect(snapshot.state.key2).toBe(42);
});
test('should reset entire state manager', () => {
stateManager.register('key1', 'value1');
stateManager.subscribe('key1', jest.fn());
stateManager.resetAll();
expect(stateManager.state.size).toBe(0);
expect(stateManager.subscribers.size).toBe(0);
expect(stateManager.stateOwners.size).toBe(0);
});
});
describe('Performance', () => {
test('should handle many state operations efficiently', () => {
const start = performance.now();
// Register many state keys
for (let i = 0; i < 100; i++) {
stateManager.register(`key${i}`, i);
}
// Subscribe to all keys
for (let i = 0; i < 100; i++) {
stateManager.subscribe(`key${i}`, jest.fn());
}
// Update all keys
for (let i = 0; i < 100; i++) {
stateManager.set(`key${i}`, i * 2);
}
const end = performance.now();
const duration = end - start;
// Should complete in reasonable time
expect(duration).toBeLessThan(100);
});
});
});