towerops/test/towerops/agents/cloud_poller_test.exs
Graham McIntire a61abd836c
fix: cloud poller automatic rebalancing
- create MonitoringCheck schema for the monitoring_checks table, fixing
  silent data loss where ping results were dropped by the wrong schema
- update agent channel and device monitor worker to use new schema
- fix Stats queries to use MonitoringCheck instead of Check schema
- add list_online_cloud_pollers and list_cloud_polled_devices queries
- add CloudLatencyProbeWorker to dispatch cross-agent latency probes
  every 8 hours via PubSub to all online cloud pollers
- scope AgentLatencyEvaluator to cloud pollers only with cloud_only
  filter, lower min_checks (5), wider time window (48h), and
  device-level assignments to avoid changing site/org defaults
- update cron schedule: probes at 0/8/16h, evaluation at 0:30/8:30/16:30
- refactor monitoring.ex and http_executor.ex to fix credo complexity
2026-02-12 15:32:33 -06:00

213 lines
6.3 KiB
Elixir

defmodule Towerops.Agents.CloudPollerTest do
use Towerops.DataCase
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Agents
alias Towerops.Repo
alias Towerops.Sites
describe "list_online_cloud_pollers/0" do
test "returns cloud pollers seen within 5 minutes" do
{:ok, cloud_poller, _token} = Agents.create_cloud_poller("Cloud 1")
cloud_poller
|> Ecto.Changeset.change(%{last_seen_at: DateTime.truncate(DateTime.utc_now(), :second)})
|> Repo.update!()
pollers = Agents.list_online_cloud_pollers()
assert length(pollers) == 1
assert hd(pollers).id == cloud_poller.id
end
test "excludes cloud pollers not seen recently" do
{:ok, cloud_poller, _token} = Agents.create_cloud_poller("Stale Cloud")
ten_min_ago = DateTime.utc_now() |> DateTime.add(-10, :minute) |> DateTime.truncate(:second)
cloud_poller
|> Ecto.Changeset.change(%{last_seen_at: ten_min_ago})
|> Repo.update!()
assert Agents.list_online_cloud_pollers() == []
end
test "excludes disabled cloud pollers" do
{:ok, cloud_poller, _token} = Agents.create_cloud_poller("Disabled Cloud")
cloud_poller
|> Ecto.Changeset.change(%{
last_seen_at: DateTime.truncate(DateTime.utc_now(), :second),
enabled: false
})
|> Repo.update!()
assert Agents.list_online_cloud_pollers() == []
end
test "excludes regular (non-cloud) agents" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, agent, _token} = Agents.create_agent_token(org.id, "Regular Agent")
agent
|> Ecto.Changeset.change(%{last_seen_at: DateTime.truncate(DateTime.utc_now(), :second)})
|> Repo.update!()
assert Agents.list_online_cloud_pollers() == []
end
end
describe "list_cloud_polled_devices/0" do
test "returns devices with no agent assignment" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Unassigned Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: true
})
devices = Agents.list_cloud_polled_devices()
assert length(devices) == 1
assert hd(devices).id == device.id
end
test "returns devices assigned to a cloud poller" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, cloud_poller, _token} = Agents.create_cloud_poller("Cloud 1")
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Cloud Polled Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: true
})
{:ok, _} = Agents.assign_device_to_agent(cloud_poller.id, device.id)
devices = Agents.list_cloud_polled_devices()
assert length(devices) == 1
assert hd(devices).id == device.id
end
test "excludes devices assigned to a local agent" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, local_agent, _token} = Agents.create_agent_token(org.id, "Local Agent")
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Local Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: true
})
{:ok, _} = Agents.assign_device_to_agent(local_agent.id, device.id)
assert Agents.list_cloud_polled_devices() == []
end
test "excludes devices with monitoring disabled" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, _device} =
Towerops.Devices.create_device(%{
name: "No Monitoring Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: false
})
assert Agents.list_cloud_polled_devices() == []
end
test "includes devices whose site default is a cloud poller" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, cloud_poller, _token} = Agents.create_cloud_poller("Cloud 1")
{:ok, site} =
Sites.create_site(%{
name: "Cloud Site",
organization_id: org.id,
agent_token_id: cloud_poller.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Site Cloud Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: true
})
devices = Agents.list_cloud_polled_devices()
assert length(devices) == 1
assert hd(devices).id == device.id
end
test "excludes devices whose site default is a local agent" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, local_agent, _token} = Agents.create_agent_token(org.id, "Local Agent")
{:ok, site} =
Sites.create_site(%{
name: "Local Site",
organization_id: org.id,
agent_token_id: local_agent.id
})
{:ok, _device} =
Towerops.Devices.create_device(%{
name: "Site Local Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: org.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c",
monitoring_enabled: true
})
assert Agents.list_cloud_polled_devices() == []
end
end
end