towerops/test/towerops/workers/data_retention_worker_test.exs

303 lines
9.2 KiB
Elixir

defmodule Towerops.Workers.DataRetentionWorkerTest do
use Towerops.DataCase, async: false
import Towerops.AccountsFixtures
import Towerops.DevicesFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Organizations
alias Towerops.Repo
alias Towerops.Workers.DataRetentionWorker
describe "perform/1" do
test "deletes monitoring_checks older than org retention period" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, org} = Organizations.update_organization(org, %{data_retention_days: 30})
device = device_fixture(%{organization_id: org.id})
now = Towerops.Time.now()
old_time = DateTime.add(now, -31, :day)
recent_time = DateTime.add(now, -10, :day)
old_id = Ecto.UUID.generate()
recent_id = Ecto.UUID.generate()
Repo.insert_all("monitoring_checks", [
%{
id: Ecto.UUID.dump!(old_id),
device_id: Ecto.UUID.dump!(device.id),
status: "success",
checked_at: old_time,
inserted_at: old_time
},
%{
id: Ecto.UUID.dump!(recent_id),
device_id: Ecto.UUID.dump!(device.id),
status: "success",
checked_at: recent_time,
inserted_at: recent_time
}
])
assert {:ok, result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
assert result.total_deleted >= 1
# Old record should be deleted
remaining =
Repo.all(
from(m in "monitoring_checks",
where: m.device_id == type(^device.id, :binary_id),
select: m.id
)
)
remaining_uuids = Enum.map(remaining, &Ecto.UUID.cast!/1)
refute old_id in remaining_uuids
assert recent_id in remaining_uuids
end
test "deletes check_results older than org retention (direct org_id)" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, org} = Organizations.update_organization(org, %{data_retention_days: 30})
now = Towerops.Time.now()
old_time = DateTime.add(now, -31, :day)
recent_time = DateTime.add(now, -10, :day)
# Create a check for the org
{:ok, check} =
Towerops.Monitoring.create_check(%{
organization_id: org.id,
check_type: "http",
name: "Test Check",
interval_seconds: 60,
config: %{"url" => "http://example.com"}
})
old_id = Ecto.UUID.generate()
recent_id = Ecto.UUID.generate()
Repo.insert_all("check_results", [
%{
id: Ecto.UUID.dump!(old_id),
organization_id: Ecto.UUID.dump!(org.id),
check_id: Ecto.UUID.dump!(check.id),
status: 0,
checked_at: old_time
},
%{
id: Ecto.UUID.dump!(recent_id),
organization_id: Ecto.UUID.dump!(org.id),
check_id: Ecto.UUID.dump!(check.id),
status: 0,
checked_at: recent_time
}
])
assert {:ok, result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
assert result.total_deleted >= 1
remaining =
Repo.all(
from(cr in "check_results",
where: cr.organization_id == type(^org.id, :binary_id),
select: cr.id
)
)
remaining_uuids = Enum.map(remaining, &Ecto.UUID.cast!/1)
refute old_id in remaining_uuids
assert recent_id in remaining_uuids
end
test "respects different retention periods per org" do
user1 = user_fixture()
org1 = organization_fixture(user1.id)
{:ok, org1} = Organizations.update_organization(org1, %{data_retention_days: 60})
device1 = device_fixture(%{organization_id: org1.id})
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, org2} = Organizations.update_organization(org2, %{data_retention_days: 90})
device2 = device_fixture(%{organization_id: org2.id})
now = Towerops.Time.now()
# 75 days old: past org1's 60-day retention, within org2's 90-day retention
mid_time = DateTime.add(now, -75, :day)
org1_check_id = Ecto.UUID.generate()
org2_check_id = Ecto.UUID.generate()
Repo.insert_all("monitoring_checks", [
%{
id: Ecto.UUID.dump!(org1_check_id),
device_id: Ecto.UUID.dump!(device1.id),
status: "success",
checked_at: mid_time,
inserted_at: mid_time
},
%{
id: Ecto.UUID.dump!(org2_check_id),
device_id: Ecto.UUID.dump!(device2.id),
status: "success",
checked_at: mid_time,
inserted_at: mid_time
}
])
assert {:ok, _result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
# org1 record should be deleted (75 days > 60 day retention)
org1_remaining =
Repo.all(
from(m in "monitoring_checks",
where: m.device_id == type(^device1.id, :binary_id),
select: m.id
)
)
assert org1_remaining == []
# org2 record should remain (75 days < 90 day retention)
org2_remaining =
Repo.all(
from(m in "monitoring_checks",
where: m.device_id == type(^device2.id, :binary_id),
select: m.id
)
)
org2_remaining_uuids = Enum.map(org2_remaining, &Ecto.UUID.cast!/1)
assert org2_check_id in org2_remaining_uuids
end
test "does NOT delete data within retention window" do
user = user_fixture()
org = organization_fixture(user.id)
# Default 365-day retention
device = device_fixture(%{organization_id: org.id})
now = Towerops.Time.now()
recent_time = DateTime.add(now, -100, :day)
recent_id = Ecto.UUID.generate()
Repo.insert_all("monitoring_checks", [
%{
id: Ecto.UUID.dump!(recent_id),
device_id: Ecto.UUID.dump!(device.id),
status: "success",
checked_at: recent_time,
inserted_at: recent_time
}
])
assert {:ok, result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
remaining =
Repo.all(
from(m in "monitoring_checks",
where: m.device_id == type(^device.id, :binary_id),
select: m.id
)
)
remaining_uuids = Enum.map(remaining, &Ecto.UUID.cast!/1)
assert recent_id in remaining_uuids
assert result.total_deleted == 0
end
test "returns summary with deletion counts" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, org} = Organizations.update_organization(org, %{data_retention_days: 30})
device = device_fixture(%{organization_id: org.id})
now = Towerops.Time.now()
old_time = DateTime.add(now, -31, :day)
Repo.insert_all("monitoring_checks", [
%{
id: Ecto.UUID.dump!(Ecto.UUID.generate()),
device_id: Ecto.UUID.dump!(device.id),
status: "success",
checked_at: old_time,
inserted_at: old_time
},
%{
id: Ecto.UUID.dump!(Ecto.UUID.generate()),
device_id: Ecto.UUID.dump!(device.id),
status: "success",
checked_at: old_time,
inserted_at: old_time
}
])
assert {:ok, result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
assert result.total_deleted >= 2
assert is_map(result.details)
assert Map.get(result.details, "monitoring_checks") >= 2
end
test "only deletes resolved alerts, keeps unresolved regardless of age" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, org} = Organizations.update_organization(org, %{data_retention_days: 30})
device = device_fixture(%{organization_id: org.id})
now = Towerops.Time.now()
old_time = DateTime.add(now, -31, :day)
resolved_id = Ecto.UUID.generate()
unresolved_id = Ecto.UUID.generate()
Repo.insert_all("alerts", [
%{
id: Ecto.UUID.dump!(resolved_id),
organization_id: Ecto.UUID.dump!(org.id),
device_id: Ecto.UUID.dump!(device.id),
alert_type: "device_down",
triggered_at: old_time,
resolved_at: old_time,
storm_suppressed: false,
notification_sent: false,
inserted_at: old_time,
updated_at: old_time
},
%{
id: Ecto.UUID.dump!(unresolved_id),
organization_id: Ecto.UUID.dump!(org.id),
device_id: Ecto.UUID.dump!(device.id),
alert_type: "device_down",
triggered_at: old_time,
resolved_at: nil,
storm_suppressed: false,
notification_sent: false,
inserted_at: old_time,
updated_at: old_time
}
])
assert {:ok, _result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
remaining =
Repo.all(
from(a in "alerts",
where: a.organization_id == type(^org.id, :binary_id),
select: a.id
)
)
remaining_uuids = Enum.map(remaining, &Ecto.UUID.cast!/1)
refute resolved_id in remaining_uuids
assert unresolved_id in remaining_uuids
end
test "works when no organizations exist" do
assert {:ok, result} = DataRetentionWorker.perform(%Oban.Job{args: %{}})
assert result.total_deleted == 0
end
end
end