towerops/test/towerops/workers/uisp_sync_worker_test.exs
Graham McIntire fde6158251 ci: enable PostGIS in test workflows + add tests covering ~3pp more
CI: switch postgres service image to timescale/timescaledb-ha:pg17-all
which bundles PostGIS, fixing the lidar migration failure in PR and
production workflows.

Tests: ~30 new test files / extensions, ~600+ new tests, taking total
coverage from 70.42% to 73.4%. Targets included GraphQL resolver
unauthenticated paths, SNMP context queries (mempools, processors,
ip_addresses, neighbors, wireless_clients, arp_entries), worker
routing/lifecycle (alert notification, alert digest, weather sync,
job cleanup, cn_maestro, uisp, mikrotik backup, report worker),
SNMP monitoring executors (storage, interface, processor) end-to-end
via stub adapter, NetBox + Sonar sync integration paths, ConfigChanges
listing and correlator, organizations membership query, and live-view
smoke tests for org-scoped settings, integrations, maintenance, agent,
device deep paths, schedules. Property tests used for query
composition and pure helpers where natural.
2026-05-05 10:35:22 -05:00

150 lines
4.6 KiB
Elixir

defmodule Towerops.Workers.UispSyncWorkerTest do
use Towerops.DataCase, async: false
use Oban.Testing, repo: Towerops.Repo
import ExUnit.CaptureLog
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Integrations
alias Towerops.Uisp.Client
alias Towerops.Workers.UispSyncWorker
describe "perform/1 dispatcher (empty args)" do
test "returns :ok with no integrations" do
assert :ok = perform_job(UispSyncWorker, %{})
assert [] = all_enqueued(worker: UispSyncWorker)
end
test "skips disabled uisp integrations" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, _disabled} =
Integrations.create_integration(org.id, %{
provider: "uisp",
enabled: false,
credentials: %{"url" => "https://uisp.example.com", "api_key" => "k"}
})
assert :ok = perform_job(UispSyncWorker, %{})
assert [] = all_enqueued(worker: UispSyncWorker)
end
test "enqueues per-integration jobs for enabled UISP integrations that are due" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, integration} =
Integrations.create_integration(org.id, %{
provider: "uisp",
enabled: true,
credentials: %{"url" => "https://uisp.example.com", "api_key" => "k"}
})
assert :ok = perform_job(UispSyncWorker, %{})
jobs = all_enqueued(worker: UispSyncWorker)
assert length(jobs) == 1
[job] = jobs
assert job.args == %{"integration_id" => integration.id}
end
test "does not enqueue an integration that was just synced" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, integration} =
Integrations.create_integration(org.id, %{
provider: "uisp",
enabled: true,
sync_interval_minutes: 60,
credentials: %{"url" => "https://uisp.example.com", "api_key" => "k"}
})
# Mark it freshly synced
{:ok, _} =
Integrations.update_integration(integration, %{
last_synced_at: DateTime.utc_now(),
last_sync_status: "success"
})
assert :ok = perform_job(UispSyncWorker, %{})
assert [] = all_enqueued(worker: UispSyncWorker)
end
end
describe "perform/1 (with integration_id)" do
test "returns {:error, :not_found} for missing integration" do
missing_id = Ecto.UUID.generate()
capture_log(fn ->
assert {:error, :not_found} =
perform_job(UispSyncWorker, %{"integration_id" => missing_id})
end)
end
test "swallows API errors and returns :ok (so Oban doesn't retry forever)" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, integration} =
Integrations.create_integration(org.id, %{
provider: "uisp",
enabled: true,
credentials: %{
"url" => "https://uisp.example.com/nms/api/v2.1",
"api_key" => "bad-key"
}
})
Req.Test.stub(Client, fn conn ->
conn |> Plug.Conn.put_status(401) |> Req.Test.json(%{"error" => "unauthorized"})
end)
capture_log(fn ->
assert :ok = perform_job(UispSyncWorker, %{"integration_id" => integration.id})
end)
# Worker swallows sync errors and reports :ok; verify the integration
# status was updated to "failed" via the sync log path.
{:ok, refreshed} = Integrations.get_integration_by_id(integration.id)
assert refreshed.last_sync_status == "failed"
end
end
describe "should_sync?/1" do
test "true when last_synced_at is nil" do
assert UispSyncWorker.should_sync?(%{
last_synced_at: nil,
sync_interval_minutes: 10
})
end
test "false when last_synced_at is recent" do
refute UispSyncWorker.should_sync?(%{
last_synced_at: DateTime.utc_now(),
sync_interval_minutes: 10
})
end
test "true once enough time elapsed" do
long_ago = DateTime.add(DateTime.utc_now(), -3600, :second)
assert UispSyncWorker.should_sync?(%{
last_synced_at: long_ago,
sync_interval_minutes: 10
})
end
test "defaults sync_interval_minutes to 10 when nil" do
# 11 minutes ago should be due with the default
eleven_min_ago = DateTime.add(DateTime.utc_now(), -11 * 60, :second)
assert UispSyncWorker.should_sync?(%{
last_synced_at: eleven_min_ago,
sync_interval_minutes: nil
})
end
end
end