import { describe, expect, it } from 'vitest'; import { loadConfig } from '../src/config.js'; describe('loadConfig', () => { it('applies defaults from an empty environment', () => { const config = loadConfig({}); expect(config.host).toBe('0.0.0.0'); expect(config.port).toBe(8080); expect(config.bearerToken).toBeUndefined(); expect(config.arena.baseUrl).toBe('https://api.arenasolutions.com/v1'); expect(config.arena.sessionTtlMs).toBe(24 * 60_000); // Arena TTL ~25 min; refresh 1 min early expect(config.arena.httpTimeoutMs).toBe(30_000); expect(config.pollIntervalMs).toBe(300_000); expect(config.syncObjectOrder).toEqual(['PRODUCT', 'CHANGE_MASTER', 'BOM', 'DOCUMENTS']); expect(config.webhookQueueMaxSize).toBe(10_000); expect(config.shutdownTimeoutMs).toBe(30_000); }); it('parses a custom sync order case-insensitively (ARN-001 config knob)', () => { const config = loadConfig({ SYNC_OBJECT_ORDER: 'bom, product' }); expect(config.syncObjectOrder).toEqual(['BOM', 'PRODUCT']); }); it('rejects unknown sync order entries', () => { expect(() => loadConfig({ SYNC_OBJECT_ORDER: 'PRODUCT,WIDGET' })).toThrow(/WIDGET/); }); it('rejects duplicate sync order entries (same rule as the HTTP contract uniqueItems)', () => { expect(() => loadConfig({ SYNC_OBJECT_ORDER: 'BOM,BOM' })).toThrow(/duplicate entry "BOM"/); expect(() => loadConfig({ SYNC_OBJECT_ORDER: 'product, bom, Product' })).toThrow(/duplicate entry "PRODUCT"/); }); it('strips trailing slashes from the Arena base URL and parses numbers', () => { const config = loadConfig({ ARENA_API_URL: 'https://api.arenasolutions.com/v1//', ARENA_WORKSPACE_ID: '4242', ARENA_SESSION_TTL_MINUTES: '10', ARENA_HTTP_TIMEOUT_MS: '5000', WORKER_PORT: '9090', WEBHOOK_QUEUE_MAX_SIZE: '25', SHUTDOWN_TIMEOUT_MS: '1500', }); expect(config.arena.baseUrl).toBe('https://api.arenasolutions.com/v1'); expect(config.arena.workspaceId).toBe(4242); expect(config.arena.sessionTtlMs).toBe(600_000); expect(config.arena.httpTimeoutMs).toBe(5000); expect(config.port).toBe(9090); expect(config.webhookQueueMaxSize).toBe(25); expect(config.shutdownTimeoutMs).toBe(1500); }); it('rejects non-numeric numeric env values', () => { expect(() => loadConfig({ WORKER_PORT: 'abc' })).toThrow(/WORKER_PORT/); }); it('rejects numeric env values with trailing garbage (whole string must be digits)', () => { expect(() => loadConfig({ WORKER_PORT: '8080abc' })).toThrow(/WORKER_PORT.*"8080abc"/); expect(() => loadConfig({ ARENA_SESSION_TTL_MINUTES: '24m' })).toThrow(/ARENA_SESSION_TTL_MINUTES/); expect(() => loadConfig({ WEBHOOK_QUEUE_MAX_SIZE: '1e4' })).toThrow(/WEBHOOK_QUEUE_MAX_SIZE/); }); it('rejects zero and negative numeric env values', () => { expect(() => loadConfig({ WEBHOOK_QUEUE_MAX_SIZE: '0' })).toThrow(/WEBHOOK_QUEUE_MAX_SIZE/); expect(() => loadConfig({ SHUTDOWN_TIMEOUT_MS: '-1' })).toThrow(/SHUTDOWN_TIMEOUT_MS/); }); });