"""Tests for training.scripted_attacker.""" from __future__ import annotations import pytest from sre_arena_env.models import AttackerAction from sre_arena_env.training.scripted_attacker import ScriptedAttacker, TEMPLATES class TestScriptedAttacker: def test_act_returns_attacker_action(self): assert isinstance(ScriptedAttacker(seed=0).act(), AttackerAction) def test_templates_list_has_8_entries(self): assert len(TEMPLATES) == 8 def test_templates_match_models_literals(self): expected = { "single_ip_flood", "ip_spray", "credential_stuffing", "payload_injection", "header_spoof", "slow_drip", "path_traversal", "mixed_legit_cover", } assert set(TEMPLATES) == expected def test_all_8_templates_reachable_in_100_calls(self): attacker = ScriptedAttacker(seed=42) seen = {attacker.act().template for _ in range(100)} assert seen == set(TEMPLATES) def test_same_seed_produces_same_sequence(self): results1 = [ScriptedAttacker(seed=7).act().template for _ in range(20)] results2 = [ScriptedAttacker(seed=7).act().template for _ in range(20)] assert results1 == results2 def test_different_seeds_produce_different_sequences(self): results1 = [ScriptedAttacker(seed=1).act().template for _ in range(20)] results2 = [ScriptedAttacker(seed=99).act().template for _ in range(20)] assert results1 != results2 def test_model_dump_has_required_keys_no_delay_ms(self): d = ScriptedAttacker(seed=0).act().model_dump(exclude={"delay_ms", "metadata"}) assert {"template", "count", "target_path", "source_ips", "payload"} == set(d.keys()) def test_count_at_least_1_for_all_outputs(self): attacker = ScriptedAttacker(seed=42) assert all(attacker.act().count >= 1 for _ in range(50)) def test_target_path_starts_with_slash(self): attacker = ScriptedAttacker(seed=42) assert all(attacker.act().target_path.startswith("/") for _ in range(50)) def test_act_never_raises(self): attacker = ScriptedAttacker(seed=123) for _ in range(50): attacker.act()