import math import time from server.escalation import EscalationEngine class TestIntensityCurve: def test_intensity_at_zero(self): """Intensity is 0 at start.""" engine = EscalationEngine(rate=0.05) assert engine.get_intensity(elapsed=0.0) == 0.0 def test_intensity_at_40s(self): """Intensity ~1.0 at 40s with default rate.""" engine = EscalationEngine(rate=0.05) intensity = engine.get_intensity(elapsed=40.0) assert abs(intensity - math.log(1 + 40 * 0.05)) < 0.01 def test_intensity_monotonic(self): """Intensity always increases.""" engine = EscalationEngine(rate=0.05) prev = 0.0 for t in [10, 30, 60, 120, 300, 600, 1800]: val = engine.get_intensity(elapsed=float(t)) assert val > prev prev = val def test_intensity_never_peaks(self): """Even at 1 hour, intensity is still rising.""" engine = EscalationEngine(rate=0.05) at_30m = engine.get_intensity(elapsed=1800.0) at_60m = engine.get_intensity(elapsed=3600.0) assert at_60m > at_30m class TestPhaseParams: def test_low_intensity_params(self): """Low intensity produces slow, subtle params.""" engine = EscalationEngine(rate=0.05) params = engine.get_phase_params(intensity=0.5) assert params["morph_speed"] < 0.3 assert params["shader_severity"] < 0.3 def test_high_intensity_params(self): """High intensity produces fast, severe params.""" engine = EscalationEngine(rate=0.05) params = engine.get_phase_params(intensity=4.0) assert params["morph_speed"] > 0.6 assert params["shader_severity"] > 0.7 def test_params_are_clamped(self): """Params stay in 0-1 range even at extreme intensity.""" engine = EscalationEngine(rate=0.05) params = engine.get_phase_params(intensity=100.0) for key in ["morph_speed", "shader_severity", "voice_frequency", "noise_level"]: assert 0.0 <= params[key] <= 1.0 class TestAssetSwapInterval: def test_low_intensity_slow_swaps(self): """Low intensity means long intervals between swaps.""" engine = EscalationEngine(rate=0.05) interval = engine.get_asset_swap_interval(intensity=0.5) assert interval >= 5.0 def test_high_intensity_fast_swaps(self): """High intensity means short intervals.""" engine = EscalationEngine(rate=0.05) interval = engine.get_asset_swap_interval(intensity=5.0) assert interval <= 4.0 def test_interval_has_randomness(self): """Consecutive calls produce different intervals.""" engine = EscalationEngine(rate=0.05) intervals = [engine.get_asset_swap_interval(intensity=2.0) for _ in range(20)] assert len(set(round(i, 2) for i in intervals)) > 1 class TestVoiceInterval: def test_low_intensity_rare_voices(self): """Low intensity = voices are rare.""" engine = EscalationEngine(rate=0.05) # Run many trials since exponential distribution is stochastic intervals = [engine.get_voice_interval(intensity=0.5) for _ in range(50)] mean = sum(intervals) / len(intervals) assert mean > 20.0 # Mean should be around 40 def test_high_intensity_frequent_voices(self): """High intensity = voices are frequent.""" engine = EscalationEngine(rate=0.05) intervals = [engine.get_voice_interval(intensity=5.0) for _ in range(50)] mean = sum(intervals) / len(intervals) assert mean < 20.0 class TestSessionTiming: def test_start_sets_time(self): """Starting a session records the start time.""" engine = EscalationEngine(rate=0.05) engine.start_session() assert engine.session_start is not None def test_elapsed_time(self): """Elapsed time increases after start.""" engine = EscalationEngine(rate=0.05) engine.start_session() elapsed = engine.get_elapsed() assert elapsed >= 0.0 def test_reset_clears_session(self): """Reset restarts the session.""" engine = EscalationEngine(rate=0.05) engine.start_session() time.sleep(0.01) engine.reset() assert engine.get_elapsed() < 0.1