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:
279
tests/core/StateManager.test.js
Normal file
279
tests/core/StateManager.test.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user