228 lines
7.3 KiB
Elixir
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
|