towerops/test/towerops/workers/preseem_sync_worker_test.exs

171 lines
5.4 KiB
Elixir

defmodule Towerops.Workers.PreseemSyncWorkerTest do
use Towerops.DataCase, async: true
use Oban.Testing, repo: Towerops.Repo
import Towerops.AccountsFixtures
import Towerops.IntegrationsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Preseem.Client
alias Towerops.Workers.PreseemSyncWorker
setup do
user = user_fixture()
org = organization_fixture(user.id)
%{org: org}
end
describe "perform/1 dispatcher (cron)" do
test "enqueues individual sync jobs for enabled integrations", %{org: org} do
integration = integration_fixture(org.id)
assert :ok = perform_job(PreseemSyncWorker, %{})
# Should have enqueued a job with this integration's ID
assert [job] = all_enqueued(worker: PreseemSyncWorker)
assert job.args == %{"integration_id" => integration.id}
assert job.scheduled_at
end
test "staggers jobs across the 600-second window", %{org: org} do
# Create a second org with its own integration
user2 = user_fixture()
org2 = organization_fixture(user2.id)
_int1 = integration_fixture(org.id)
_int2 = integration_fixture(org2.id)
assert :ok = perform_job(PreseemSyncWorker, %{})
jobs = all_enqueued(worker: PreseemSyncWorker)
assert length(jobs) == 2
# Jobs should have scheduled_at times within 0-600 seconds from now
now = DateTime.utc_now()
for job <- jobs do
diff = DateTime.diff(job.scheduled_at, now, :second)
assert diff >= 0 and diff < 600, "Expected offset in [0, 600), got #{diff}"
end
end
test "skips integrations synced recently", %{org: org} do
_integration =
integration_fixture(org.id, %{
last_synced_at: Towerops.Time.now(),
last_sync_status: "success"
})
assert :ok = perform_job(PreseemSyncWorker, %{})
# No jobs should be enqueued for recently-synced integrations
assert [] = all_enqueued(worker: PreseemSyncWorker)
end
test "handles no enabled integrations" do
assert :ok = perform_job(PreseemSyncWorker, %{})
assert [] = all_enqueued(worker: PreseemSyncWorker)
end
test "respects sync_interval_minutes", %{org: org} do
# Last synced 5 minutes ago with a 10-minute interval -> should skip
five_minutes_ago =
DateTime.utc_now()
|> DateTime.add(-5, :minute)
|> DateTime.truncate(:second)
_integration =
integration_fixture(org.id, %{
sync_interval_minutes: 10,
last_synced_at: five_minutes_ago,
last_sync_status: "success"
})
assert :ok = perform_job(PreseemSyncWorker, %{})
assert [] = all_enqueued(worker: PreseemSyncWorker)
end
test "enqueues job when sync interval has elapsed", %{org: org} do
# Last synced 15 minutes ago with a 10-minute interval -> should sync
fifteen_minutes_ago =
DateTime.utc_now()
|> DateTime.add(-15, :minute)
|> DateTime.truncate(:second)
integration =
integration_fixture(org.id, %{
sync_interval_minutes: 10,
last_synced_at: fifteen_minutes_ago,
last_sync_status: "success"
})
assert :ok = perform_job(PreseemSyncWorker, %{})
assert [job] = all_enqueued(worker: PreseemSyncWorker)
assert job.args == %{"integration_id" => integration.id}
end
test "enqueues job for integrations that have never been synced", %{org: org} do
integration = integration_fixture(org.id)
assert integration.last_synced_at == nil
assert :ok = perform_job(PreseemSyncWorker, %{})
assert [job] = all_enqueued(worker: PreseemSyncWorker)
assert job.args == %{"integration_id" => integration.id}
end
end
describe "perform/1 individual sync" do
test "syncs the given integration", %{org: org} do
integration = integration_fixture(org.id)
Req.Test.stub(Client, fn conn ->
Req.Test.json(conn, [
%{
"element_uuid" => "ap-1",
"name" => "Test AP",
"system_mac_address" => "AA:BB:CC:DD:EE:FF",
"management_ip" => "10.0.0.1"
}
])
end)
assert :ok = perform_job(PreseemSyncWorker, %{"integration_id" => integration.id})
updated = Repo.reload!(integration)
assert updated.last_synced_at
assert updated.last_sync_status in ["success", "partial"]
end
test "handles empty string response body from API", %{org: org} do
integration = integration_fixture(org.id)
Req.Test.stub(Client, fn conn ->
# Preseem sometimes returns an empty string for the scores endpoint
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(200, ~s(""))
end)
assert :ok = perform_job(PreseemSyncWorker, %{"integration_id" => integration.id})
end
test "handles API errors without crashing", %{org: org} do
integration = integration_fixture(org.id)
Req.Test.stub(Client, fn conn ->
conn
|> Plug.Conn.put_status(401)
|> Req.Test.json(%{"error" => "unauthorized"})
end)
assert :ok = perform_job(PreseemSyncWorker, %{"integration_id" => integration.id})
end
test "returns error for nonexistent integration" do
assert {:error, :not_found} =
perform_job(PreseemSyncWorker, %{"integration_id" => Ecto.UUID.generate()})
end
end
end