towerops/test/towerops/workers/backup_timeout_worker_test.exs

206 lines
6.8 KiB
Elixir

defmodule Towerops.Workers.BackupTimeoutWorkerTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
alias Towerops.Devices
alias Towerops.Devices.BackupRequests
alias Towerops.Organizations
alias Towerops.Sites
alias Towerops.Workers.BackupTimeoutWorker
describe "perform/1" do
test "marks pending requests older than 5 minutes as timeout" do
device = insert_device()
# Create a pending request from 10 minutes ago (older than 5 minute timeout)
{: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!()
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# Verify request was marked as timeout
updated = Repo.reload(old_request)
assert updated.status == "timeout"
assert updated.error_message == "Request timed out"
assert updated.completed_at
end
test "does not mark recent pending requests" do
device = insert_device()
# Create a pending request from 2 minutes ago (within 5 minute timeout)
{:ok, recent_request} = BackupRequests.create_request(device.id, "job_recent")
recent_request
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-120, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# Verify request still pending
updated = Repo.reload(recent_request)
assert updated.status == "pending"
assert is_nil(updated.completed_at)
end
test "does not mark completed requests" do
device = insert_device()
# Create an 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!()
{:ok, _} = BackupRequests.update_request_status(old_request.id, "success", nil)
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# Verify request still has success status
updated = Repo.reload(old_request)
assert updated.status == "success"
end
test "handles multiple requests with mixed statuses" do
device = insert_device()
# Create old pending request (should timeout)
{:ok, old_pending} = BackupRequests.create_request(device.id, "job_old_pending")
old_pending
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-600, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Create recent pending request (should stay pending)
{:ok, recent_pending} = BackupRequests.create_request(device.id, "job_recent_pending")
recent_pending
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-120, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Create old completed request (should stay success)
{:ok, old_success} = BackupRequests.create_request(device.id, "job_old_success")
old_success
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-600, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
{:ok, _} = BackupRequests.update_request_status(old_success.id, "success", nil)
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# Verify only old pending was marked as timeout
assert Repo.reload(old_pending).status == "timeout"
assert Repo.reload(recent_pending).status == "pending"
assert Repo.reload(old_success).status == "success"
end
test "handles boundary case at exactly 5 minutes" do
device = insert_device()
# Create a request at exactly 5 minutes (300 seconds) ago
{:ok, boundary_request} = BackupRequests.create_request(device.id, "job_boundary")
boundary_request
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-300, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# The cutoff uses `< cutoff`, so exactly 5 minutes should NOT timeout
updated = Repo.reload(boundary_request)
assert updated.status == "pending"
end
test "returns :ok when no requests need timeout" do
device = insert_device()
# Create only recent request
{:ok, recent_request} = BackupRequests.create_request(device.id, "job_recent")
recent_request
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-60, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
end
test "returns :ok when no requests exist at all" do
# Execute the worker with no requests in the database
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
end
test "handles multiple devices with stale requests" do
device1 = insert_device()
device2 = insert_device()
# Create old pending requests for both devices
{:ok, request1} = BackupRequests.create_request(device1.id, "job1")
request1
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-600, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
{:ok, request2} = BackupRequests.create_request(device2.id, "job2")
request2
|> Ecto.Changeset.change(
requested_at: DateTime.utc_now() |> DateTime.add(-700, :second) |> DateTime.truncate(:second)
)
|> Repo.update!()
# Execute the worker
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
# Verify both were marked as timeout
assert Repo.reload(request1).status == "timeout"
assert Repo.reload(request2).status == "timeout"
end
end
# Helper function to create a device
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 #{System.unique_integer()}",
ip_address: "192.168.1.#{:rand.uniform(254)}",
site_id: site.id,
organization_id: organization.id
})
device
end
end