"""Tests for ban response mappers.""" from app.mappers import ( map_domain_active_ban_list_to_response, map_domain_active_ban_to_response, map_domain_bans_by_country_to_response, map_domain_bans_by_jail_to_response, map_domain_ban_trend_to_response, map_domain_dashboard_ban_item_to_response, map_domain_dashboard_ban_list_to_response, ) from app.models.ban_domain import ( DomainActiveBan, DomainActiveBanList, DomainBansByCountry, DomainBansByJail, DomainBanTrend, DomainBanTrendBucket, DomainDashboardBanItem, DomainDashboardBanList, DomainJailBanCount, ) class TestActiveBanMapper: """Test mapping from DomainActiveBan to ActiveBan.""" def test_maps_all_fields(self) -> None: """All fields are correctly mapped.""" domain_ban = DomainActiveBan( ip="192.168.1.1", jail="sshd", banned_at="2026-04-28T07:00:00+00:00", expires_at="2026-04-28T08:00:00+00:00", ban_count=3, country="DE", ) result = map_domain_active_ban_to_response(domain_ban) assert result.ip == "192.168.1.1" assert result.jail == "sshd" assert result.banned_at == "2026-04-28T07:00:00+00:00" assert result.expires_at == "2026-04-28T08:00:00+00:00" assert result.ban_count == 3 assert result.country == "DE" def test_handles_null_timestamps(self) -> None: """Null timestamps are preserved.""" domain_ban = DomainActiveBan( ip="10.0.0.1", jail="test", banned_at=None, expires_at=None, ) result = map_domain_active_ban_to_response(domain_ban) assert result.banned_at is None assert result.expires_at is None class TestActiveBanListMapper: """Test mapping from DomainActiveBanList to ActiveBanListResponse.""" def test_maps_list_and_total(self) -> None: """List and total are correctly mapped.""" domain_list = DomainActiveBanList( bans=[ DomainActiveBan(ip="1.1.1.1", jail="sshd", ban_count=1), DomainActiveBan(ip="2.2.2.2", jail="httpd", ban_count=2), ], total=2, ) result = map_domain_active_ban_list_to_response(domain_list) assert result.total == 2 assert len(result.items) == 2 assert result.items[0].ip == "1.1.1.1" assert result.items[1].ip == "2.2.2.2" def test_handles_empty_list(self) -> None: """Empty list is handled correctly.""" domain_list = DomainActiveBanList(bans=[], total=0) result = map_domain_active_ban_list_to_response(domain_list) assert result.total == 0 assert len(result.items) == 0 class TestDashboardBanItemMapper: """Test mapping from DomainDashboardBanItem to DashboardBanItem.""" def test_maps_all_fields(self) -> None: """All fields are correctly mapped.""" domain_item = DomainDashboardBanItem( ip="203.0.113.1", jail="sshd", banned_at="2026-04-28T07:00:00+00:00", service="SSH login attempt", country_code="US", country_name="United States", asn="AS15169", org="Google LLC", ban_count=5, origin="selfblock", ) result = map_domain_dashboard_ban_item_to_response(domain_item) assert result.ip == "203.0.113.1" assert result.jail == "sshd" assert result.banned_at == "2026-04-28T07:00:00+00:00" assert result.service == "SSH login attempt" assert result.country_code == "US" assert result.country_name == "United States" assert result.asn == "AS15169" assert result.org == "Google LLC" assert result.ban_count == 5 assert result.origin == "selfblock" class TestDashboardBanListMapper: """Test mapping from DomainDashboardBanList to DashboardBanListResponse.""" def test_maps_pagination_and_items(self) -> None: """Pagination metadata and items are correctly mapped.""" domain_list = DomainDashboardBanList( items=[ DomainDashboardBanItem( ip="1.1.1.1", jail="sshd", banned_at="2026-04-28T07:00:00+00:00", ban_count=1, ), ], total=100, page=2, page_size=50, ) result = map_domain_dashboard_ban_list_to_response(domain_list) assert result.pagination.total == 100 assert result.pagination.page == 2 assert result.pagination.page_size == 50 assert len(result.items) == 1 assert result.items[0].ip == "1.1.1.1" class TestBansByCountryMapper: """Test mapping from DomainBansByCountry to BansByCountryResponse.""" def test_maps_aggregation_and_items(self) -> None: """Country aggregation and companion items are correctly mapped.""" domain_data = DomainBansByCountry( countries={"US": 10, "DE": 5, "GB": 3}, country_names={"US": "United States", "DE": "Germany", "GB": "United Kingdom"}, items=[ DomainDashboardBanItem( ip="1.1.1.1", jail="sshd", banned_at="2026-04-28T07:00:00+00:00", ban_count=1, origin="selfblock", ), ], total=18, ) result = map_domain_bans_by_country_to_response(domain_data) assert result.countries == {"US": 10, "DE": 5, "GB": 3} assert result.country_names == {"US": "United States", "DE": "Germany", "GB": "United Kingdom"} assert result.total == 18 assert len(result.bans) == 1 class TestBanTrendMapper: """Test mapping from DomainBanTrend to BanTrendResponse.""" def test_maps_buckets_and_size_label(self) -> None: """Buckets and size label are correctly mapped.""" domain_trend = DomainBanTrend( buckets=[ DomainBanTrendBucket(timestamp="2026-04-28T00:00:00+00:00", count=10), DomainBanTrendBucket(timestamp="2026-04-28T01:00:00+00:00", count=15), ], bucket_size="1h", ) result = map_domain_ban_trend_to_response(domain_trend) assert result.bucket_size == "1h" assert len(result.buckets) == 2 assert result.buckets[0].timestamp == "2026-04-28T00:00:00+00:00" assert result.buckets[0].count == 10 class TestBansByJailMapper: """Test mapping from DomainBansByJail to BansByJailResponse.""" def test_maps_jail_counts(self) -> None: """Jail counts are correctly mapped.""" domain_data = DomainBansByJail( jails=[ DomainJailBanCount(jail="sshd", count=50), DomainJailBanCount(jail="httpd", count=20), ], total=70, ) result = map_domain_bans_by_jail_to_response(domain_data) assert result.total == 70 assert len(result.jails) == 2 assert result.jails[0].jail == "sshd" assert result.jails[0].count == 50