208 lines
6.9 KiB
Elixir
208 lines
6.9 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 5 minutes minus 1 second (just inside the cutoff)
|
|
# Adding 1 second buffer to prevent flakiness from time advancement during test execution
|
|
{: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.add(1, :second) |> DateTime.truncate(:second)
|
|
)
|
|
|> Repo.update!()
|
|
|
|
# Execute the worker
|
|
assert :ok = BackupTimeoutWorker.perform(%Oban.Job{args: %{}})
|
|
|
|
# The cutoff uses `< cutoff`, so at 5 minutes - 1 second 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
|