towerops/test/towerops/devices/backup_requests_test.exs

228 lines
7.3 KiB
Elixir

defmodule Towerops.Devices.BackupRequestsTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
alias Towerops.Devices
alias Towerops.Devices.BackupRequests
alias Towerops.Organizations
alias Towerops.Sites
describe "create_request/2" do
test "creates backup request with pending status" do
device = insert(:device)
job_id = "backup:#{device.id}:#{DateTime.to_unix(DateTime.utc_now())}"
assert {:ok, request} = BackupRequests.create_request(device.id, job_id)
assert request.device_id == device.id
assert request.job_id == job_id
assert request.status == "pending"
assert request.requested_at
assert is_nil(request.completed_at)
assert is_nil(request.error_message)
end
end
describe "get_request_by_job_id/1" do
test "returns request by job_id" do
device = insert(:device)
job_id = "backup:#{device.id}:12345"
{:ok, request} = BackupRequests.create_request(device.id, job_id)
fetched = BackupRequests.get_request_by_job_id(job_id)
assert fetched.id == request.id
end
test "returns nil when job_id not found" do
assert BackupRequests.get_request_by_job_id("nonexistent") == nil
end
end
describe "update_request_status/3" do
test "updates request to success status" do
device = insert(:device)
{:ok, request} = BackupRequests.create_request(device.id, "job123")
assert {:ok, updated} = BackupRequests.update_request_status(request.id, "success", nil)
assert updated.status == "success"
assert updated.completed_at
assert is_nil(updated.error_message)
end
test "updates request to failed status with error message" do
device = insert(:device)
{:ok, request} = BackupRequests.create_request(device.id, "job123")
assert {:ok, updated} =
BackupRequests.update_request_status(request.id, "failed", "Connection timeout")
assert updated.status == "failed"
assert updated.completed_at
assert updated.error_message == "Connection timeout"
end
test "updates request to timeout status" do
device = insert(:device)
{:ok, request} = BackupRequests.create_request(device.id, "job123")
assert {:ok, updated} =
BackupRequests.update_request_status(request.id, "timeout", "Request timed out")
assert updated.status == "timeout"
assert updated.completed_at
assert updated.error_message == "Request timed out"
end
end
describe "mark_timed_out_requests/1" do
test "marks old pending requests as timeout" do
device = insert(:device)
# Create request from 10 minutes ago
{:ok, old_request} = BackupRequests.create_request(device.id, "job_old")
old_request
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-600, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Create recent request
{:ok, _recent_request} = BackupRequests.create_request(device.id, "job_recent")
# Mark requests older than 5 minutes as timeout
cutoff = DateTime.add(DateTime.utc_now(), -300, :second)
count = BackupRequests.mark_timed_out_requests(cutoff)
assert count == 1
# Verify old request is marked timeout
updated = BackupRequests.get_request_by_job_id("job_old")
assert updated.status == "timeout"
assert updated.completed_at
assert updated.error_message == "Request timed out"
end
test "does not mark completed requests as timeout" do
device = insert(:device)
# Create old request that already succeeded
{:ok, old_request} = BackupRequests.create_request(device.id, "job_old")
old_request
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-600, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
BackupRequests.update_request_status(old_request.id, "success", nil)
# Mark old pending requests
cutoff = DateTime.add(DateTime.utc_now(), -300, :second)
count = BackupRequests.mark_timed_out_requests(cutoff)
assert count == 0
end
end
describe "summary_since/1" do
test "returns summary of backup request statuses" do
device = insert(:device)
# Create various requests
Enum.each(1..5, fn i ->
{:ok, req} = BackupRequests.create_request(device.id, "success_#{i}")
BackupRequests.update_request_status(req.id, "success", nil)
end)
Enum.each(1..2, fn i ->
{:ok, req} = BackupRequests.create_request(device.id, "failed_#{i}")
BackupRequests.update_request_status(req.id, "failed", "Error")
end)
{:ok, req} = BackupRequests.create_request(device.id, "timeout_1")
BackupRequests.update_request_status(req.id, "timeout", "Timed out")
{:ok, _pending} = BackupRequests.create_request(device.id, "pending_1")
# Get summary from beginning of time
since = DateTime.add(DateTime.utc_now(), -86_400, :second)
summary = BackupRequests.summary_since(since)
assert summary.success == 5
assert summary.failed == 2
assert summary.timeout == 1
assert summary.pending == 1
end
end
describe "list_failed_devices_since/1" do
test "returns devices with failed or timeout requests" do
device1 = insert(:device)
device2 = insert(:device)
device3 = insert(:device)
# device1: failed
{:ok, req1} = BackupRequests.create_request(device1.id, "job1")
BackupRequests.update_request_status(req1.id, "failed", "SNMP error")
# device2: timeout
{:ok, req2} = BackupRequests.create_request(device2.id, "job2")
BackupRequests.update_request_status(req2.id, "timeout", "No response")
# device3: success (should not appear)
{:ok, req3} = BackupRequests.create_request(device3.id, "job3")
BackupRequests.update_request_status(req3.id, "success", nil)
since = DateTime.add(DateTime.utc_now(), -86_400, :second)
failed_devices = BackupRequests.list_failed_devices_since(since)
assert length(failed_devices) == 2
device_ids = Enum.map(failed_devices, & &1.device_id)
assert device1.id in device_ids
assert device2.id in device_ids
refute device3.id in device_ids
end
test "includes error messages and status" do
device = insert(:device)
{:ok, req} = BackupRequests.create_request(device.id, "job_fail")
BackupRequests.update_request_status(req.id, "failed", "Connection refused")
since = DateTime.add(DateTime.utc_now(), -86_400, :second)
[result] = BackupRequests.list_failed_devices_since(since)
assert result.device_id == device.id
assert result.status == "failed"
assert result.error_message == "Connection refused"
end
end
# Helper functions
defp insert(:device) do
user = user_fixture()
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
{:ok, device} =
Devices.create_device(
%{
name: "Test Device",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
monitoring_enabled: false,
snmp_enabled: false
},
bypass_limits: true
)
device
end
end