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.
150 lines
4.6 KiB
Elixir
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
|