towerops/test/towerops/workers/agent_latency_evaluator_test.exs

517 lines
15 KiB
Elixir

defmodule Towerops.Workers.AgentLatencyEvaluatorTest do
use Towerops.DataCase, async: false
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Agents
alias Towerops.Monitoring
alias Towerops.Sites
alias Towerops.Workers.AgentLatencyEvaluator
setup do
# Start the worker for testing
{:ok, pid} = start_supervised(AgentLatencyEvaluator)
%{worker_pid: pid}
end
describe "evaluate_latency/0" do
test "reassigns device to faster agent when 20%+ improvement available" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_agent, _} = Agents.create_agent_token(org.id, "Slow Agent")
{:ok, fast_agent, _} = Agents.create_agent_token(org.id, "Fast Agent")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: org.id,
agent_token_id: slow_agent.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks: slow = 100ms, fast = 50ms (50% improvement)
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify site was reassigned
updated_site = Sites.get_site!(site.id)
assert updated_site.agent_token_id == fast_agent.id
end
test "does not reassign when improvement is below 20% threshold" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, agent1, _} = Agents.create_agent_token(org.id, "Agent 1")
{:ok, agent2, _} = Agents.create_agent_token(org.id, "Agent 2")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: org.id,
agent_token_id: agent1.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks: agent1 = 100ms, agent2 = 85ms (15% improvement, below 20%)
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: agent1.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: agent2.id,
status: :success,
response_time_ms: 85,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify site was NOT reassigned
updated_site = Sites.get_site!(site.id)
assert updated_site.agent_token_id == agent1.id
end
test "reassigns organization-level assignment" do
user = user_fixture()
{:ok, slow_agent, _} = Agents.create_cloud_poller("Slow Cloud")
{:ok, fast_agent, _} = Agents.create_cloud_poller("Fast Cloud")
org =
organization_fixture(user.id, %{
default_agent_token_id: slow_agent.id
})
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks showing fast agent is better
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 150,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify organization was reassigned
updated_org = Towerops.Organizations.get_organization!(org.id)
assert updated_org.default_agent_token_id == fast_agent.id
end
test "creates device assignment for global default assignment" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_cloud, _} = Agents.create_cloud_poller("Slow Global")
{:ok, fast_cloud, _} = Agents.create_cloud_poller("Fast Global")
# Set slow cloud as global default
{:ok, _} = Towerops.Settings.set_global_default_cloud_poller(slow_cloud.id)
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks showing fast cloud is better
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_cloud.id,
status: :success,
response_time_ms: 200,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_cloud.id,
status: :success,
response_time_ms: 60,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify device assignment was created
assignment = Agents.get_device_assignment(device.id)
assert assignment.agent_token_id == fast_cloud.id
# Cleanup global default
{:ok, _} = Towerops.Settings.set_global_default_cloud_poller(nil)
end
test "creates device assignment for cloud polling (no assignment)" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_cloud, _} = Agents.create_cloud_poller("Slow Cloud")
{:ok, fast_cloud, _} = Agents.create_cloud_poller("Fast Cloud")
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks from both cloud agents
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_cloud.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_cloud.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(500)
# Verify device assignment was created to the fast agent
assignment = Agents.get_device_assignment(device.id)
assert assignment != nil, "Expected device assignment to be created"
assert assignment.agent_token_id == fast_cloud.id
end
test "does not reassign direct device assignments" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_agent, _} = Agents.create_agent_token(org.id, "Slow Agent")
{:ok, fast_agent, _} = Agents.create_agent_token(org.id, "Fast Agent")
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create direct device assignment
{:ok, _} = Agents.assign_device_to_agent(slow_agent.id, device.id)
# Create checks showing fast agent is better
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify device assignment was NOT changed
assignment = Agents.get_device_assignment(device.id)
assert assignment.agent_token_id == slow_agent.id
end
test "requires minimum check count" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_agent, _} = Agents.create_agent_token(org.id, "Slow Agent")
{:ok, fast_agent, _} = Agents.create_agent_token(org.id, "Fast Agent")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: org.id,
agent_token_id: slow_agent.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create only 5 checks (below default minimum of 10)
for _ <- 1..5 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify site was NOT reassigned (insufficient data)
updated_site = Sites.get_site!(site.id)
assert updated_site.agent_token_id == slow_agent.id
end
test "handles multiple devices in single evaluation" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_agent, _} = Agents.create_agent_token(org.id, "Slow Agent")
{:ok, fast_agent, _} = Agents.create_agent_token(org.id, "Fast Agent")
{:ok, site1} =
Sites.create_site(%{
name: "Site 1",
organization_id: org.id,
agent_token_id: slow_agent.id
})
{:ok, site2} =
Sites.create_site(%{
name: "Site 2",
organization_id: org.id,
agent_token_id: slow_agent.id
})
{:ok, device1} =
Towerops.Devices.create_device(%{
name: "Router 1",
ip_address: "192.168.1.1",
site_id: site1.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
{:ok, device2} =
Towerops.Devices.create_device(%{
name: "Router 2",
ip_address: "192.168.2.1",
site_id: site2.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks for both devices
for device_id <- [device1.id, device2.id] do
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device_id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device_id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
end
# Trigger evaluation
AgentLatencyEvaluator.trigger_evaluation()
# Wait for async processing
Process.sleep(100)
# Verify both sites were reassigned
updated_site1 = Sites.get_site!(site1.id)
updated_site2 = Sites.get_site!(site2.id)
assert updated_site1.agent_token_id == fast_agent.id
assert updated_site2.agent_token_id == fast_agent.id
end
end
describe "handle_info(:evaluate_latency)" do
test "worker processes evaluation on schedule" do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, slow_agent, _} = Agents.create_agent_token(org.id, "Slow Agent")
{:ok, fast_agent, _} = Agents.create_agent_token(org.id, "Fast Agent")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: org.id,
agent_token_id: slow_agent.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
snmp_enabled: true,
snmp_community: "public",
snmp_version: "2c"
})
# Create checks
for _ <- 1..15 do
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: slow_agent.id,
status: :success,
response_time_ms: 100,
checked_at: DateTime.utc_now()
})
Monitoring.create_check(%{
device_id: device.id,
agent_token_id: fast_agent.id,
status: :success,
response_time_ms: 50,
checked_at: DateTime.utc_now()
})
end
# Send the message directly to the worker
send(Process.whereis(AgentLatencyEvaluator), :evaluate_latency)
# Wait for async processing
Process.sleep(100)
# Verify site was reassigned
updated_site = Sites.get_site!(site.id)
assert updated_site.agent_token_id == fast_agent.id
end
end
end