From 030ed437086b68c485ff1622a01bced7f2c43053 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 11 Feb 2026 16:25:33 -0600 Subject: [PATCH] test: add coverage for ExpiredBanCleanupWorker 7 tests exercising perform/1: expired/active/permanent ban handling, mixed scenarios, empty database, and PubSub broadcast behavior. --- CHANGELOG.txt | 6 + .../expired_ban_cleanup_worker_test.exs | 108 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 test/towerops/workers/expired_ban_cleanup_worker_test.exs diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6a79531c..30d61fdd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,12 @@ CHANGELOG - towerops-web ======================== +2026-02-11 - test: add 100% coverage for ExpiredBanCleanupWorker + - File: test/towerops/workers/expired_ban_cleanup_worker_test.exs (new) + Added 7 tests covering perform/1: expired ban deletion, active ban preservation, + permanent ban preservation, mixed scenarios, empty database, PubSub broadcast + on deletion, and no broadcast when nothing deleted. + 2026-02-11 - fix: handle "null" string values in SNMP parsing - File: lib/towerops/snmp/profiles/base.ex (parse_integer/1, parse_float/1) Added explicit handling for "null" string values before attempting integer/float diff --git a/test/towerops/workers/expired_ban_cleanup_worker_test.exs b/test/towerops/workers/expired_ban_cleanup_worker_test.exs new file mode 100644 index 00000000..de9cb55b --- /dev/null +++ b/test/towerops/workers/expired_ban_cleanup_worker_test.exs @@ -0,0 +1,108 @@ +defmodule Towerops.Workers.ExpiredBanCleanupWorkerTest do + use Towerops.DataCase, async: false + + alias Towerops.Security.IpBlock + alias Towerops.Workers.ExpiredBanCleanupWorker + + describe "perform/1" do + test "deletes expired temporary bans" do + insert_ip_block(%{ + ip_address: "10.0.0.1", + banned_until: DateTime.add(DateTime.utc_now(), -60, :second) + }) + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + assert [] = Repo.all(IpBlock) + end + + test "keeps active temporary bans" do + block = + insert_ip_block(%{ + ip_address: "10.0.0.2", + banned_until: DateTime.add(DateTime.utc_now(), 300, :second) + }) + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + assert Repo.get(IpBlock, block.id) + end + + test "keeps permanent bans" do + block = + insert_ip_block(%{ + ip_address: "10.0.0.3", + offense_count: 3, + banned_until: nil + }) + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + assert Repo.get(IpBlock, block.id) + end + + test "deletes only expired bans among mixed records" do + expired = + insert_ip_block(%{ + ip_address: "10.0.0.10", + banned_until: DateTime.add(DateTime.utc_now(), -120, :second) + }) + + active = + insert_ip_block(%{ + ip_address: "10.0.0.11", + banned_until: DateTime.add(DateTime.utc_now(), 600, :second) + }) + + permanent = + insert_ip_block(%{ + ip_address: "10.0.0.12", + offense_count: 3, + banned_until: nil + }) + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + refute Repo.get(IpBlock, expired.id) + assert Repo.get(IpBlock, active.id) + assert Repo.get(IpBlock, permanent.id) + end + + test "returns :ok when no records exist" do + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + end + + test "broadcasts to PubSub when bans are deleted" do + Phoenix.PubSub.subscribe(Towerops.PubSub, "security:blocks") + + insert_ip_block(%{ + ip_address: "10.0.0.20", + banned_until: DateTime.add(DateTime.utc_now(), -60, :second) + }) + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + assert_receive :blocks_updated + end + + test "does not broadcast to PubSub when nothing is deleted" do + Phoenix.PubSub.subscribe(Towerops.PubSub, "security:blocks") + + assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}}) + + refute_receive :blocks_updated + end + end + + defp insert_ip_block(attrs) do + defaults = %{ + offense_count: 1, + last_violation_at: DateTime.utc_now(), + reason: "test ban" + } + + %IpBlock{} + |> IpBlock.changeset(Map.merge(defaults, attrs)) + |> Repo.insert!() + end +end