@djodjonx/x32-simulator 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +28 -0
  3. package/dist/{UdpNetworkGateway-BrroQ6-Q.mjs → SchemaRegistry-BRVgnyaA.mjs} +990 -2
  4. package/dist/{UdpNetworkGateway-Ccdd7Us5.cjs → SchemaRegistry-CfDtw84j.cjs} +1033 -3
  5. package/dist/index.cjs +160 -6
  6. package/dist/index.d.cts +61 -11
  7. package/dist/index.d.mts +61 -11
  8. package/dist/index.mjs +146 -2
  9. package/dist/server.cjs +8 -927
  10. package/dist/server.mjs +1 -920
  11. package/package.json +5 -1
  12. package/.commitlintrc.json +0 -3
  13. package/.github/workflows/publish.yml +0 -38
  14. package/.husky/commit-msg +0 -1
  15. package/.husky/pre-commit +0 -1
  16. package/.oxlintrc.json +0 -56
  17. package/INSTALL.md +0 -107
  18. package/docs/OSC-Communication.md +0 -184
  19. package/docs/X32-INTERNAL.md +0 -262
  20. package/docs/X32-OSC.pdf +0 -0
  21. package/docs/behringer-x32-x32-osc-remote-protocol-en-44463.pdf +0 -0
  22. package/src/application/use-cases/BroadcastUpdatesUseCase.ts +0 -120
  23. package/src/application/use-cases/ManageSessionsUseCase.ts +0 -9
  24. package/src/application/use-cases/ProcessPacketUseCase.ts +0 -26
  25. package/src/application/use-cases/SimulationService.ts +0 -122
  26. package/src/domain/entities/SubscriptionManager.ts +0 -126
  27. package/src/domain/entities/X32State.ts +0 -78
  28. package/src/domain/models/MeterConfig.ts +0 -22
  29. package/src/domain/models/MeterData.ts +0 -59
  30. package/src/domain/models/OscMessage.ts +0 -93
  31. package/src/domain/models/X32Address.ts +0 -78
  32. package/src/domain/models/X32Node.ts +0 -43
  33. package/src/domain/models/types.ts +0 -96
  34. package/src/domain/ports/ILogger.ts +0 -27
  35. package/src/domain/ports/INetworkGateway.ts +0 -8
  36. package/src/domain/ports/IStateRepository.ts +0 -16
  37. package/src/domain/services/MeterService.ts +0 -46
  38. package/src/domain/services/OscMessageHandler.ts +0 -88
  39. package/src/domain/services/SchemaFactory.ts +0 -308
  40. package/src/domain/services/SchemaRegistry.ts +0 -67
  41. package/src/domain/services/StaticResponseService.ts +0 -52
  42. package/src/domain/services/strategies/BatchStrategy.ts +0 -74
  43. package/src/domain/services/strategies/MeterStrategy.ts +0 -45
  44. package/src/domain/services/strategies/NodeDiscoveryStrategy.ts +0 -36
  45. package/src/domain/services/strategies/OscCommandStrategy.ts +0 -22
  46. package/src/domain/services/strategies/StateAccessStrategy.ts +0 -71
  47. package/src/domain/services/strategies/StaticResponseStrategy.ts +0 -42
  48. package/src/domain/services/strategies/SubscriptionStrategy.ts +0 -56
  49. package/src/infrastructure/mappers/OscCodec.ts +0 -54
  50. package/src/infrastructure/repositories/InMemoryStateRepository.ts +0 -21
  51. package/src/infrastructure/services/ConsoleLogger.ts +0 -177
  52. package/src/infrastructure/services/UdpNetworkGateway.ts +0 -71
  53. package/src/presentation/cli/server.ts +0 -194
  54. package/src/presentation/library/library.ts +0 -9
  55. package/tests/application/use-cases/BroadcastUpdatesUseCase.test.ts +0 -104
  56. package/tests/application/use-cases/ManageSessionsUseCase.test.ts +0 -12
  57. package/tests/application/use-cases/ProcessPacketUseCase.test.ts +0 -49
  58. package/tests/application/use-cases/SimulationService.test.ts +0 -77
  59. package/tests/domain/entities/SubscriptionManager.test.ts +0 -50
  60. package/tests/domain/entities/X32State.test.ts +0 -52
  61. package/tests/domain/models/MeterData.test.ts +0 -23
  62. package/tests/domain/models/OscMessage.test.ts +0 -38
  63. package/tests/domain/models/X32Address.test.ts +0 -30
  64. package/tests/domain/models/X32Node.test.ts +0 -30
  65. package/tests/domain/services/MeterService.test.ts +0 -27
  66. package/tests/domain/services/OscMessageHandler.test.ts +0 -51
  67. package/tests/domain/services/SchemaRegistry.test.ts +0 -47
  68. package/tests/domain/services/StaticResponseService.test.ts +0 -15
  69. package/tests/domain/services/strategies/BatchStrategy.test.ts +0 -41
  70. package/tests/domain/services/strategies/MeterStrategy.test.ts +0 -19
  71. package/tests/domain/services/strategies/NodeDiscoveryStrategy.test.ts +0 -22
  72. package/tests/domain/services/strategies/StateAccessStrategy.test.ts +0 -49
  73. package/tests/domain/services/strategies/StaticResponseStrategy.test.ts +0 -15
  74. package/tests/domain/services/strategies/SubscriptionStrategy.test.ts +0 -45
  75. package/tests/infrastructure/mappers/OscCodec.test.ts +0 -41
  76. package/tests/infrastructure/repositories/InMemoryStateRepository.test.ts +0 -29
  77. package/tests/infrastructure/services/ConsoleLogger.test.ts +0 -74
  78. package/tests/infrastructure/services/UdpNetworkGateway.test.ts +0 -61
  79. package/tests/presentation/cli/server.test.ts +0 -178
  80. package/tests/presentation/library/library.test.ts +0 -13
  81. package/tsconfig.json +0 -21
  82. package/tsdown.config.ts +0 -15
  83. package/vitest.config.ts +0 -9
@@ -1,9 +0,0 @@
1
- export { SimulationService } from '../../application/use-cases/SimulationService';
2
- export type { INetworkGateway } from '../../domain/ports/INetworkGateway';
3
- export type { ILogger, LogData } from '../../domain/ports/ILogger';
4
- export { LogCategory } from '../../domain/ports/ILogger';
5
- export type { IStateRepository } from '../../domain/ports/IStateRepository';
6
- export { InMemoryStateRepository } from '../../infrastructure/repositories/InMemoryStateRepository';
7
- export { ConsoleLogger } from '../../infrastructure/services/ConsoleLogger';
8
- export { UdpNetworkGateway } from '../../infrastructure/services/UdpNetworkGateway';
9
- export * from '../../domain/models/types';
@@ -1,104 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { BroadcastUpdatesUseCase } from '../../../src/application/use-cases/BroadcastUpdatesUseCase';
3
- import { SubscriptionManager } from '../../../src/domain/entities/SubscriptionManager';
4
- import { X32State } from '../../../src/domain/entities/X32State';
5
- import { INetworkGateway } from '../../../src/domain/ports/INetworkGateway';
6
- import { MeterService } from '../../../src/domain/services/MeterService';
7
- import { SchemaRegistry } from '../../../src/domain/services/SchemaRegistry';
8
- import { X32Node } from '../../../src/domain/models/X32Node';
9
-
10
- describe('BroadcastUpdatesUseCase', () => {
11
- it('should broadcast single change to xremote', () => {
12
- const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
13
- const manager = new SubscriptionManager(logger);
14
- manager.addPathSubscriber({ address: '1.2.3.4', port: 1234 }, '/xremote');
15
-
16
- const state = new X32State({});
17
- const gateway = { send: vi.fn() } as unknown as INetworkGateway;
18
- const meterService = new MeterService();
19
- const registry = {} as unknown as SchemaRegistry;
20
-
21
- const useCase = new BroadcastUpdatesUseCase(manager, state, gateway, logger as any, meterService, registry);
22
- useCase.broadcastSingleChange('/ch/01/mix/on', 1);
23
-
24
- expect(gateway.send).toHaveBeenCalledWith(
25
- expect.objectContaining({ address: '1.2.3.4' }),
26
- '/ch/01/mix/on',
27
- [1]
28
- );
29
- });
30
-
31
- it('should execute batch updates', () => {
32
- const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
33
- const manager = new SubscriptionManager(logger);
34
- const client = { address: '1.2.3.4', port: 1234 };
35
-
36
- // Add a batch subscriber
37
- manager.addBatchSubscriber(client, '/batch/alias', ['/mix/fader'], 0, 1, 0);
38
-
39
- const state = new X32State({ '/ch/01/mix/fader': new X32Node('f', 0.5) });
40
- const gateway = { send: vi.fn() } as unknown as INetworkGateway;
41
- const meterService = new MeterService();
42
- const registry = {
43
- getRootFromIndex: vi.fn().mockReturnValue('/ch/01'),
44
- getNode: vi.fn().mockReturnValue(new X32Node('f', 0.0))
45
- } as unknown as SchemaRegistry;
46
-
47
- const useCase = new BroadcastUpdatesUseCase(manager, state, gateway, logger as any, meterService, registry);
48
- useCase.execute();
49
-
50
- expect(gateway.send).toHaveBeenCalledWith(
51
- expect.objectContaining({ address: '1.2.3.4' }),
52
- '/batch/alias',
53
- [expect.any(Buffer)]
54
- );
55
- });
56
-
57
- it('should execute format updates', () => {
58
- const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
59
- const manager = new SubscriptionManager(logger);
60
- const client = { address: '1.2.3.4', port: 1234 };
61
-
62
- // Add a format subscriber: alias, pattern, start, count, factor
63
- manager.addFormatSubscriber(client, '/format/alias', '/ch/*/mix/fader', 1, 2, 1);
64
-
65
- const state = new X32State({
66
- '/ch/01/mix/fader': new X32Node('f', 0.5),
67
- '/ch/02/mix/fader': new X32Node('f', 0.6)
68
- });
69
- const gateway = { send: vi.fn() } as unknown as INetworkGateway;
70
- const meterService = new MeterService();
71
- const registry = {
72
- getNode: vi.fn().mockReturnValue(new X32Node('f', 0.0))
73
- } as unknown as SchemaRegistry;
74
-
75
- const useCase = new BroadcastUpdatesUseCase(manager, state, gateway, logger as any, meterService, registry);
76
- useCase.execute();
77
-
78
- expect(gateway.send).toHaveBeenCalledWith(
79
- expect.objectContaining({ address: '1.2.3.4' }),
80
- '/format/alias',
81
- [expect.any(Buffer)]
82
- );
83
- });
84
-
85
- it('should broadcast meter updates', () => {
86
- const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
87
- const manager = new SubscriptionManager(logger);
88
- manager.addMeterSubscriber({ address: '1.2.3.4', port: 1234 }, '/meters/1');
89
-
90
- const state = new X32State({});
91
- const gateway = { send: vi.fn() } as unknown as INetworkGateway;
92
- const meterService = new MeterService();
93
- const registry = {} as unknown as SchemaRegistry;
94
-
95
- const useCase = new BroadcastUpdatesUseCase(manager, state, gateway, logger as any, meterService, registry);
96
- useCase.execute();
97
-
98
- expect(gateway.send).toHaveBeenCalledWith(
99
- expect.objectContaining({ address: '1.2.3.4' }),
100
- '/meters/1',
101
- [expect.any(Buffer)]
102
- );
103
- });
104
- });
@@ -1,12 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { ManageSessionsUseCase } from '../../../src/application/use-cases/ManageSessionsUseCase';
3
- import { SubscriptionManager } from '../../../src/domain/entities/SubscriptionManager';
4
-
5
- describe('ManageSessionsUseCase', () => {
6
- it('should cleanup sessions', () => {
7
- const manager = { cleanup: vi.fn() } as unknown as SubscriptionManager;
8
- const useCase = new ManageSessionsUseCase(manager);
9
- useCase.cleanup();
10
- expect(manager.cleanup).toHaveBeenCalled();
11
- });
12
- });
@@ -1,49 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { ProcessPacketUseCase } from '../../../src/application/use-cases/ProcessPacketUseCase';
3
- import { OscMessageHandler } from '../../../src/domain/services/OscMessageHandler';
4
- import { INetworkGateway } from '../../../src/domain/ports/INetworkGateway';
5
-
6
- describe('ProcessPacketUseCase', () => {
7
- it('should process message and send replies', () => {
8
- const handler = {
9
- handle: vi.fn().mockReturnValue([{ address: '/reply', args: [1] }])
10
- } as unknown as OscMessageHandler;
11
-
12
- const gateway = {
13
- send: vi.fn()
14
- } as unknown as INetworkGateway;
15
-
16
- const useCase = new ProcessPacketUseCase(handler, gateway);
17
-
18
- useCase.execute(
19
- { oscType: 'message', address: '/test', args: [123] },
20
- { address: '1.2.3.4', port: 1234 }
21
- );
22
-
23
- expect(handler.handle).toHaveBeenCalled();
24
- expect(gateway.send).toHaveBeenCalledWith({ address: '1.2.3.4', port: 1234 }, '/reply', [1]);
25
- });
26
-
27
- it('should process bundles', () => {
28
- const handler = {
29
- handle: vi.fn().mockReturnValue([])
30
- } as unknown as OscMessageHandler;
31
- const gateway = { send: vi.fn() } as unknown as INetworkGateway;
32
- const useCase = new ProcessPacketUseCase(handler, gateway);
33
-
34
- useCase.execute(
35
- {
36
- oscType: 'bundle',
37
- address: '', // Bundles don't have address
38
- args: [],
39
- elements: [
40
- { oscType: 'message', address: '/msg1', args: [] },
41
- { oscType: 'message', address: '/msg2', args: [] }
42
- ]
43
- },
44
- { address: '1.2.3.4', port: 1234 }
45
- );
46
-
47
- expect(handler.handle).toHaveBeenCalledTimes(2);
48
- });
49
- });
@@ -1,77 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { SimulationService } from '../../../src/application/use-cases/SimulationService';
3
- import { INetworkGateway } from '../../../src/domain/ports/INetworkGateway';
4
- import { ILogger } from '../../../src/domain/ports/ILogger';
5
- import { IStateRepository } from '../../../src/domain/ports/IStateRepository';
6
- import { SchemaRegistry } from '../../../src/domain/services/SchemaRegistry';
7
- import { X32State } from '../../../src/domain/entities/X32State';
8
- import * as os from 'os';
9
-
10
- vi.mock('os', () => ({
11
- networkInterfaces: vi.fn().mockReturnValue({
12
- eth0: [{ family: 'IPv4', internal: false, address: '192.168.1.1' }]
13
- })
14
- }));
15
-
16
- describe('SimulationService', () => {
17
- let service: SimulationService;
18
- let gateway: INetworkGateway;
19
- let logger: ILogger;
20
- let stateRepo: IStateRepository;
21
- let schemaRegistry: SchemaRegistry;
22
- let state: X32State;
23
-
24
- beforeEach(() => {
25
- gateway = {
26
- start: vi.fn().mockResolvedValue(undefined),
27
- stop: vi.fn().mockResolvedValue(undefined),
28
- send: vi.fn(),
29
- onPacket: vi.fn()
30
- };
31
- logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
32
- state = new X32State({});
33
- stateRepo = {
34
- getState: vi.fn().mockReturnValue(state),
35
- reset: vi.fn()
36
- };
37
- schemaRegistry = {
38
- getSchema: vi.fn().mockReturnValue({}),
39
- getNode: vi.fn(),
40
- getAllPaths: vi.fn().mockReturnValue([])
41
- } as unknown as SchemaRegistry;
42
-
43
- // Use 0.0.0.0 to trigger getLocalIp
44
- service = new SimulationService(
45
- gateway,
46
- logger as any,
47
- stateRepo,
48
- schemaRegistry,
49
- 10023,
50
- '0.0.0.0'
51
- );
52
- });
53
-
54
- it('should start the service and detect local IP', async () => {
55
- await service.start();
56
- expect(gateway.start).toHaveBeenCalledWith(10023, '0.0.0.0');
57
- expect(os.networkInterfaces).toHaveBeenCalled();
58
- });
59
-
60
- it('should stop the service', async () => {
61
- await service.start();
62
- await service.stop();
63
- expect(gateway.stop).toHaveBeenCalled();
64
- });
65
-
66
- it('should handle state change events', () => {
67
- // Trigger change on the state
68
- state.emit('change', { address: '/test', value: 1 });
69
- // Should log and broadcast (indirectly verified by coverage)
70
- expect(logger.info).toHaveBeenCalled();
71
- });
72
-
73
- it('should reset state', () => {
74
- service.resetState();
75
- expect(stateRepo.reset).toHaveBeenCalled();
76
- });
77
- });
@@ -1,50 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { SubscriptionManager } from '../../../src/domain/entities/SubscriptionManager';
3
- import { ILogger } from '../../../src/domain/ports/ILogger';
4
-
5
- describe('SubscriptionManager', () => {
6
- const logger: ILogger = {
7
- debug: vi.fn(),
8
- info: vi.fn(),
9
- warn: vi.fn(),
10
- error: vi.fn()
11
- };
12
-
13
- it('should add a path subscriber', () => {
14
- const manager = new SubscriptionManager(logger);
15
- const client = { address: '127.0.0.1', port: 10000 };
16
-
17
- manager.addPathSubscriber(client, '/ch/01/mix/fader');
18
- const subs = manager.getSubscribers();
19
-
20
- expect(subs).toHaveLength(1);
21
- expect(subs[0].type).toBe('path');
22
- expect(subs[0].path).toBe('/ch/01/mix/fader');
23
- });
24
-
25
- it('should remove subscriber', () => {
26
- const manager = new SubscriptionManager(logger);
27
- const client = { address: '127.0.0.1', port: 10000 };
28
-
29
- manager.addPathSubscriber(client, '/test');
30
- manager.removeSubscriber(client, '/test');
31
-
32
- expect(manager.getSubscribers()).toHaveLength(0);
33
- });
34
-
35
- it('should cleanup expired subscribers', () => {
36
- const manager = new SubscriptionManager(logger);
37
- const client = { address: '127.0.0.1', port: 10000 };
38
-
39
- manager.addPathSubscriber(client, '/test');
40
-
41
- // Mock Date.now to fast forward
42
- const originalNow = Date.now;
43
- Date.now = vi.fn(() => originalNow() + 20000); // +20s
44
-
45
- manager.cleanup();
46
- expect(manager.getSubscribers()).toHaveLength(0);
47
-
48
- Date.now = originalNow;
49
- });
50
- });
@@ -1,52 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { X32State } from '../../../src/domain/entities/X32State';
3
- import { X32Node } from '../../../src/domain/models/X32Node';
4
-
5
- describe('X32State', () => {
6
- const schema = {
7
- '/ch/01/mix/fader': new X32Node('f', 0.0),
8
- '/ch/01/mix/on': new X32Node('i', 0),
9
- '/ch/01/grp/mute': new X32Node('i', 0), // needed for mute group logic
10
- };
11
-
12
- it('should initialize with default values', () => {
13
- const state = new X32State(schema);
14
- expect(state.get('/ch/01/mix/fader')).toBe(0.0);
15
- });
16
-
17
- it('should update values and emit events', () => {
18
- const state = new X32State(schema);
19
- const spy = vi.fn();
20
- state.on('change', spy);
21
-
22
- state.set('/ch/01/mix/fader', 0.5);
23
- expect(state.get('/ch/01/mix/fader')).toBe(0.5);
24
- expect(spy).toHaveBeenCalledWith({ address: '/ch/01/mix/fader', value: 0.5 });
25
- });
26
-
27
- it('should reset to defaults', () => {
28
- const state = new X32State(schema);
29
- state.set('/ch/01/mix/fader', 0.8);
30
- state.reset();
31
- expect(state.get('/ch/01/mix/fader')).toBe(0.0);
32
- });
33
-
34
- // Mute Group Logic Test
35
- it('should handle mute group side effects', () => {
36
- const state = new X32State(schema);
37
- // Assign CH 01 to Mute Group 1 (Bit 0)
38
- state.set('/ch/01/grp/mute', 1); // 1 << 0
39
-
40
- const spy = vi.fn();
41
- state.on('change', spy);
42
-
43
- // Turn Mute Group 1 ON (1)
44
- state.handleMuteGroupChange(1, 1);
45
-
46
- // Expect CH 01 Mute to be 0 (Muted? Logic: isOn=1 -> target=0? Check code: isOn=1 -> target=0. Wait, 0 usually means OFF/Muted or ON/Active? In X32 'on' means passing audio. So Mute Group ON means 'on' parameter becomes 0)
47
- // Code: const targetMute = isOn === 1 ? 0 : 1;
48
- // So if MG is ON, channel.on = 0 (Muted). Correct.
49
- expect(state.get('/ch/01/mix/on')).toBe(0);
50
- expect(spy).toHaveBeenCalledWith({ address: '/ch/01/mix/on', value: 0 });
51
- });
52
- });
@@ -1,23 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { MeterData } from '../../../src/domain/models/MeterData';
3
-
4
- describe('MeterData', () => {
5
- it('should create and store values', () => {
6
- const data = new MeterData('/meters/1', [0.1, 0.2]);
7
- expect(data.path).toBe('/meters/1');
8
- expect(data.values).toEqual([0.1, 0.2]);
9
- });
10
-
11
- it('should encode to binary blob', () => {
12
- const data = new MeterData('/meters/1', [0.5]);
13
- const blob = data.toBlob();
14
-
15
- // Size = 4 (total size) + 4 (count) + 4 (float) = 12 bytes body + header?
16
- // Implementation: 4 byte size + 4 byte count + floats
17
- // totalSize = 4 + 4 + (1 * 4) = 12
18
-
19
- expect(blob.readInt32BE(0)).toBe(12); // Header Size
20
- expect(blob.readInt32LE(4)).toBe(1); // Count
21
- expect(blob.readFloatLE(8)).toBeCloseTo(0.5); // Value
22
- });
23
- });
@@ -1,38 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { OscMessage } from '../../../src/domain/models/OscMessage';
3
-
4
- describe('OscMessage', () => {
5
- it('should create an instance correctly', () => {
6
- const msg = new OscMessage('/test', [1, 'string', 0.5]);
7
- expect(msg.address).toBe('/test');
8
- expect(msg.args).toEqual([1, 'string', 0.5]);
9
- });
10
-
11
- it('should validate address prefix', () => {
12
- const msg = new OscMessage('/ch/01/mix', []);
13
- expect(msg.startsWith('/ch')).toBe(true);
14
- expect(msg.startsWith('/bus')).toBe(false);
15
- });
16
-
17
- it('should retrieve typed arguments safely', () => {
18
- const msg = new OscMessage('/test', [123, 'hello']);
19
- expect(msg.getArgAsNumber(0)).toBe(123);
20
- expect(msg.getArgAsString(1)).toBe('hello');
21
- });
22
-
23
- it('should throw when retrieving wrong type', () => {
24
- const msg = new OscMessage('/test', [123]);
25
- expect(() => msg.getArgAsString(0)).toThrow();
26
- });
27
-
28
- it('should create from packet', () => {
29
- const packet = {
30
- oscType: 'message' as const,
31
- address: '/foo',
32
- args: [{ type: 'i', value: 10 }, 20]
33
- };
34
- const msg = OscMessage.fromPacket(packet);
35
- expect(msg.address).toBe('/foo');
36
- expect(msg.args).toEqual([10, 20]);
37
- });
38
- });
@@ -1,30 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { X32Address } from '../../../src/domain/models/X32Address';
3
-
4
- describe('X32Address', () => {
5
- it('should parse path correctly', () => {
6
- const addr = new X32Address('/ch/01/mix/fader');
7
- expect(addr.path).toBe('/ch/01/mix/fader');
8
- expect(addr.root).toBe('ch');
9
- expect(addr.index).toBe('01');
10
- expect(addr.suffix).toBe('/mix/fader');
11
- });
12
-
13
- it('should handle short paths', () => {
14
- const addr = new X32Address('/status');
15
- expect(addr.root).toBe('status');
16
- expect(addr.index).toBeUndefined();
17
- expect(addr.suffix).toBe('');
18
- });
19
-
20
- it('should match category', () => {
21
- const addr = new X32Address('/ch/01');
22
- expect(addr.isCategory('ch')).toBe(true);
23
- expect(addr.isCategory('bus')).toBe(false);
24
- });
25
-
26
- it('should match regex pattern', () => {
27
- const addr = new X32Address('/ch/01/mix/on');
28
- expect(addr.matches(/^\/ch\/\d+\/mix\/on$/)).toBe(true);
29
- });
30
- });
@@ -1,30 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { X32Node } from '../../../src/domain/models/X32Node';
3
-
4
- describe('X32Node', () => {
5
- it('should validate float types', () => {
6
- const node = new X32Node('f', 0.0);
7
- expect(node.validate(0.5)).toBe(true);
8
- expect(node.validate(1)).toBe(true);
9
- expect(node.validate('0.5')).toBe(false);
10
- });
11
-
12
- it('should validate int types', () => {
13
- const node = new X32Node('i', 0);
14
- expect(node.validate(1)).toBe(true);
15
- expect(node.validate(1.5)).toBe(true); // JS numbers are floats, simulation logic accepts it
16
- expect(node.validate('1')).toBe(false);
17
- });
18
-
19
- it('should validate string types', () => {
20
- const node = new X32Node('s', '');
21
- expect(node.validate('text')).toBe(true);
22
- expect(node.validate(123)).toBe(false);
23
- });
24
-
25
- it('should create from factory', () => {
26
- const node = X32Node.from({ type: 'i', default: 1 });
27
- expect(node.type).toBe('i');
28
- expect(node.default).toBe(1);
29
- });
30
- });
@@ -1,27 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { MeterService } from '../../../src/domain/services/MeterService';
3
- import { X32State } from '../../../src/domain/entities/X32State';
4
- import { X32Node } from '../../../src/domain/models/X32Node';
5
-
6
- describe('MeterService', () => {
7
- it('should generate meter data with noise floor', () => {
8
- const service = new MeterService();
9
- // /meters/0 has 70 meters
10
- const data = service.generateMeterData('/meters/0');
11
- expect(data.path).toBe('/meters/0');
12
- expect(data.values).toHaveLength(70);
13
- // Expect small random values
14
- expect(data.values[0]).toBeGreaterThanOrEqual(0);
15
- expect(data.values[0]).toBeLessThan(0.06);
16
- });
17
-
18
- it('should simulate fader signal', () => {
19
- const service = new MeterService();
20
- const state = new X32State({ '/ch/01/mix/fader': new X32Node('f', 0.0) });
21
- state.set('/ch/01/mix/fader', 0.8);
22
-
23
- const data = service.generateMeterData('/meters/1', state);
24
- // Meter 1 corresponds to CH 01
25
- expect(data.values[0]).toBeGreaterThan(0.5); // Should reflect fader level
26
- });
27
- });
@@ -1,51 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { OscMessageHandler } from '../../../src/domain/services/OscMessageHandler';
3
- import { X32State } from '../../../src/domain/entities/X32State';
4
- import { SubscriptionManager } from '../../../src/domain/entities/SubscriptionManager';
5
- import { MeterService } from '../../../src/domain/services/MeterService';
6
- import { SchemaRegistry } from '../../../src/domain/services/SchemaRegistry';
7
- import { StaticResponseService } from '../../../src/domain/services/StaticResponseService';
8
- import { ILogger } from '../../../src/domain/ports/ILogger';
9
-
10
- describe('OscMessageHandler', () => {
11
- let handler: OscMessageHandler;
12
- let state: X32State;
13
- let subManager: SubscriptionManager;
14
- let logger: ILogger;
15
-
16
- beforeEach(() => {
17
- state = new X32State({});
18
- logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
19
- subManager = new SubscriptionManager(logger);
20
- const meterService = new MeterService();
21
- const schemaRegistry = {
22
- has: vi.fn().mockReturnValue(false),
23
- getNode: vi.fn(),
24
- getAllPaths: vi.fn().mockReturnValue([])
25
- } as unknown as SchemaRegistry;
26
- const staticResponseService = new StaticResponseService();
27
-
28
- handler = new OscMessageHandler(
29
- state,
30
- subManager,
31
- logger,
32
- '127.0.0.1',
33
- 'Mixer',
34
- 'X32',
35
- meterService,
36
- schemaRegistry,
37
- staticResponseService
38
- );
39
- });
40
-
41
- it('should dispatch /status to StaticResponseStrategy', () => {
42
- const replies = handler.handle({ address: '/status', args: [] }, { address: '1.2.3.4', port: 1234 });
43
- expect(replies).toHaveLength(1);
44
- expect(replies[0].address).toBe('/status');
45
- });
46
-
47
- it('should warn on unknown command', () => {
48
- handler.handle({ address: '/unknown', args: [] }, { address: '1.2.3.4', port: 1234 });
49
- expect(logger.warn).toHaveBeenCalled();
50
- });
51
- });
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { SchemaRegistry } from '../../../src/domain/services/SchemaRegistry';
3
- import { SchemaFactory } from '../../../src/domain/services/SchemaFactory';
4
-
5
- describe('SchemaRegistry', () => {
6
- const factory = new SchemaFactory();
7
- const registry = new SchemaRegistry(factory);
8
-
9
- it('should load full schema', () => {
10
- const schema = registry.getSchema();
11
- expect(Object.keys(schema).length).toBeGreaterThan(1000);
12
- });
13
-
14
- it('should find specific nodes', () => {
15
- const node = registry.getNode('/ch/01/mix/fader');
16
- expect(node).toBeDefined();
17
- expect(node?.type).toBe('f');
18
- });
19
-
20
- it('should map index to root', () => {
21
- expect(registry.getRootFromIndex(0)).toBe('/ch/01');
22
- expect(registry.getRootFromIndex(31)).toBe('/ch/32');
23
- expect(registry.getRootFromIndex(32)).toBe('/auxin/01');
24
- expect(registry.getRootFromIndex(39)).toBe('/auxin/08');
25
- expect(registry.getRootFromIndex(40)).toBe('/fxrtn/01');
26
- expect(registry.getRootFromIndex(47)).toBe('/fxrtn/08');
27
- expect(registry.getRootFromIndex(48)).toBe('/bus/01');
28
- expect(registry.getRootFromIndex(63)).toBe('/bus/16');
29
- expect(registry.getRootFromIndex(64)).toBe('/mtx/01');
30
- expect(registry.getRootFromIndex(69)).toBe('/mtx/06');
31
- expect(registry.getRootFromIndex(70)).toBe('/main/st');
32
- expect(registry.getRootFromIndex(71)).toBe('/main/m');
33
- expect(registry.getRootFromIndex(72)).toBe('/dca/01');
34
- expect(registry.getRootFromIndex(79)).toBe('/dca/08');
35
- expect(registry.getRootFromIndex(80)).toBeNull();
36
- });
37
-
38
- it('should check if path exists', () => {
39
- expect(registry.has('/ch/01/mix/fader')).toBe(true);
40
- expect(registry.has('/non/existent')).toBe(false);
41
- });
42
-
43
- it('should return all paths', () => {
44
- const paths = registry.getAllPaths();
45
- expect(paths).toContain('/ch/01/mix/fader');
46
- });
47
- });
@@ -1,15 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { StaticResponseService } from '../../../src/domain/services/StaticResponseService';
3
-
4
- describe('StaticResponseService', () => {
5
- it('should return static data', () => {
6
- const service = new StaticResponseService();
7
- const resp = service.getResponse('/status');
8
- expect(resp).toEqual(['active', '{{ip}}', '{{name}}']);
9
- });
10
-
11
- it('should return undefined for unknown path', () => {
12
- const service = new StaticResponseService();
13
- expect(service.getResponse('/unknown')).toBeUndefined();
14
- });
15
- });