/** * Ported from backend/app/tests/test_mode_params.py — every pytest case * exists here with the same expected values, plus tests for * formatModeParams() (no Python counterpart: defined as the canonical * inverse of parseModeParams, matching the seed DSL strings). */ import { describe, expect, it } from 'vitest' import { formatModeParams, parseModeParams } from '../../app/engine/modeParams' describe('parseModeParams', () => { it('D30M50B.1G.2 => standard Licence G1 params', () => { const result = parseModeParams('D30M50B.1G.2') expect(result.duration_days).toBe(30) expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(0.1) expect(result.gradient_exponent).toBe(0.2) // Optional criteria absent expect(result.smith_exponent).toBeNull() expect(result.techcomm_exponent).toBeNull() }) it('D30M50B.1G.2S.1 => standard + smith_exponent=0.1', () => { const result = parseModeParams('D30M50B.1G.2S.1') expect(result.duration_days).toBe(30) expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(0.1) expect(result.gradient_exponent).toBe(0.2) expect(result.smith_exponent).toBe(0.1) expect(result.techcomm_exponent).toBeNull() }) it('D30M50B.1G.2T.1 => standard + techcomm_exponent=0.1', () => { const result = parseModeParams('D30M50B.1G.2T.1') expect(result.duration_days).toBe(30) expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(0.1) expect(result.gradient_exponent).toBe(0.2) expect(result.smith_exponent).toBeNull() expect(result.techcomm_exponent).toBe(0.1) }) it('D30M50B1G.5C10 => integer base, gradient=0.5, constant=10', () => { const result = parseModeParams('D30M50B1G.5C10') expect(result.duration_days).toBe(30) expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(1.0) expect(result.gradient_exponent).toBe(0.5) expect(result.constant_base).toBe(10.0) }) it('empty string returns all defaults', () => { const result = parseModeParams('') expect(result.duration_days).toBe(30) expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(0.1) expect(result.gradient_exponent).toBe(0.2) expect(result.constant_base).toBe(0.0) expect(result.smith_exponent).toBeNull() expect(result.techcomm_exponent).toBeNull() expect(result.ratio_multiplier).toBeNull() expect(result.is_ratio_mode).toBe(false) }) it('whitespace-only string treated as empty', () => { const result = parseModeParams(' ') expect(result.duration_days).toBe(30) }) it('result exposes exactly the expected keys', () => { const result = parseModeParams('D30M50B.1G.2') const expectedKeys = [ 'duration_days', 'majority_pct', 'base_exponent', 'gradient_exponent', 'constant_base', 'smith_exponent', 'techcomm_exponent', 'ratio_multiplier', 'is_ratio_mode', ].sort() expect(Object.keys(result).sort()).toEqual(expectedKeys) }) it('unknown code letter throws (Python parity)', () => { expect(() => parseModeParams('D30X5')).toThrow(/inconnu/) }) it('D7R1N2.5 => ratio mode with multiplier (Python cross-check)', () => { const result = parseModeParams('D7R1N2.5') expect(result.duration_days).toBe(7) expect(result.is_ratio_mode).toBe(true) expect(result.ratio_multiplier).toBe(2.5) // Untouched defaults expect(result.majority_pct).toBe(50) expect(result.base_exponent).toBe(0.1) expect(result.gradient_exponent).toBe(0.2) }) }) describe('formatModeParams', () => { it('defaults format to the canonical standard string', () => { expect(formatModeParams()).toBe('D30M50B.1G.2') expect(formatModeParams({})).toBe('D30M50B.1G.2') }) it('round-trips the seed DSL strings', () => { for (const s of ['D30M50B.1G.2', 'D30M50B.1G.2S.1', 'D30M50B.1G.2T.1']) { expect(formatModeParams(parseModeParams(s))).toBe(s) } }) it('formats integer base and constant', () => { expect(formatModeParams(parseModeParams('D30M50B1G.5C10'))).toBe('D30M50B1G.5C10') }) it('formats ratio mode and multiplier', () => { expect(formatModeParams(parseModeParams('D7R1N2.5'))).toBe('D7M50B.1G.2N2.5R1') }) it('parse(format(p)) is identical to p', () => { for (const s of ['D30M50B.1G.2S.1', 'D30M50B1G.5C10', 'D7R1N2.5', 'D90M66B.2G.3S.2T.1']) { const p = parseModeParams(s) expect(parseModeParams(formatModeParams(p))).toEqual(p) } }) })