test: expand coverage for devices, workers, and LiveView modules

Add tests for untested Devices context functions (get_device_by_ip,
get_mikrotik_config, get_snmpv3_config, credential propagation,
resolve_snmp_community, list_mikrotik_devices_with_api), activity
log action descriptions, firmware version extraction edge cases,
and discovery worker enqueue/retryable_error paths.
This commit is contained in:
Graham McIntire 2026-02-12 09:06:41 -06:00
parent 1c0cccdf25
commit 9f8b925ecd
No known key found for this signature in database
6 changed files with 926 additions and 0 deletions

View file

@ -1752,4 +1752,604 @@ defmodule Towerops.EquipmentTest do
refute_receive {:device_status_changed, _}
end
end
describe "get_device_by_ip/3" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "returns device matching IP and site", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
found = Devices.get_device_by_ip("192.168.1.1", site.id)
assert found.id == device.id
end
test "returns nil when IP not found in site", %{site: site} do
assert Devices.get_device_by_ip("10.0.0.1", site.id) == nil
end
test "returns nil when site_id is nil" do
assert Devices.get_device_by_ip("192.168.1.1", nil) == nil
end
test "excludes device with given exclude_id", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
assert Devices.get_device_by_ip("192.168.1.1", site.id, device.id) == nil
end
end
describe "get_mikrotik_config/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "returns mikrotik config from device", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "MikroTik Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
mikrotik_enabled: true,
mikrotik_username: "admin",
mikrotik_port: 8729,
mikrotik_use_ssl: true
})
config = Devices.get_mikrotik_config(device)
assert config.username == "admin"
assert config.port == 8729
assert config.use_ssl == true
assert config.enabled == true
end
test "returns defaults when fields are nil", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
config = Devices.get_mikrotik_config(device)
assert config.port == 8729
assert config.ssh_port == 22
assert config.use_ssl == true
assert config.enabled == false
end
test "accepts device_id string", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
mikrotik_enabled: true,
mikrotik_username: "admin"
})
config = Devices.get_mikrotik_config(device.id)
assert config.username == "admin"
end
end
describe "get_snmpv3_config/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "returns nil for non-v3 device", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_version: "2c"
})
assert Devices.get_snmpv3_config(device) == nil
end
test "returns v3 config for v3 device", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_version: "3",
snmpv3_security_level: "authPriv",
snmpv3_username: "snmpuser",
snmpv3_auth_protocol: "SHA-256",
snmpv3_auth_password: "authpass123",
snmpv3_priv_protocol: "AES",
snmpv3_priv_password: "privpass123"
})
config = Devices.get_snmpv3_config(device)
assert config.security_level == "authPriv"
assert config.username == "snmpuser"
assert config.auth_protocol == "SHA-256"
assert config.priv_protocol == "AES"
end
test "accepts device_id string", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_version: "3",
snmpv3_username: "snmpuser",
snmpv3_auth_password: "authpass123",
snmpv3_priv_password: "privpass123"
})
config = Devices.get_snmpv3_config(device.id)
assert config.username == "snmpuser"
end
end
describe "propagate_site_community_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Set community on site so devices will inherit from it
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
snmp_community: "site-community"
})
%{organization: organization, site: site}
end
test "updates devices inheriting from site", %{organization: organization, site: site} do
# Device created without community → inherits from site, source = "site"
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: nil
})
assert device.snmp_community_source == "site"
assert :ok = Devices.propagate_site_community_change(site.id, "new-community")
updated = Devices.get_device!(device.id)
assert updated.snmp_community == "new-community"
end
test "does not update devices with device-level community", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: "device-community"
})
assert device.snmp_community_source == "device"
assert :ok = Devices.propagate_site_community_change(site.id, "new-community")
updated = Devices.get_device!(device.id)
assert updated.snmp_community == "device-community"
end
end
describe "propagate_organization_community_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} =
Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Update org with community so devices can inherit from it
{:ok, organization} =
Towerops.Organizations.update_organization(organization, %{snmp_community: "org-community"})
# Site without community so devices inherit from org
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "updates devices inheriting from organization", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: nil
})
assert device.snmp_community_source == "organization"
assert :ok = Devices.propagate_organization_community_change(organization.id, "new-community")
updated = Devices.get_device!(device.id)
assert updated.snmp_community == "new-community"
end
test "does not update devices with device-level source", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: "device-community"
})
assert device.snmp_community_source == "device"
assert :ok = Devices.propagate_organization_community_change(organization.id, "new-community")
updated = Devices.get_device!(device.id)
assert updated.snmp_community == "device-community"
end
end
describe "propagate_site_mikrotik_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Set mikrotik creds on site so devices inherit
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
mikrotik_username: "site-user"
})
%{organization: organization, site: site}
end
test "updates devices inheriting mikrotik from site", %{organization: organization, site: site} do
# Device without mikrotik_username → inherits from site
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
assert device.mikrotik_credential_source == "site"
assert :ok =
Devices.propagate_site_mikrotik_change(site.id, %{
mikrotik_username: "new-user"
})
updated = Devices.get_device!(device.id)
assert updated.mikrotik_username == "new-user"
end
end
describe "propagate_organization_mikrotik_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Set mikrotik creds on org so devices inherit
{:ok, organization} =
Towerops.Organizations.update_organization(organization, %{mikrotik_username: "org-user"})
# Site without mikrotik creds so devices inherit from org
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "updates devices inheriting mikrotik from organization", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
assert device.mikrotik_credential_source == "organization"
assert :ok =
Devices.propagate_organization_mikrotik_change(organization.id, %{
mikrotik_username: "new-user"
})
updated = Devices.get_device!(device.id)
assert updated.mikrotik_username == "new-user"
end
end
describe "propagate_site_snmpv3_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Set snmpv3 creds on site
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
snmpv3_username: "site-v3-user",
snmpv3_auth_password: "authpass123",
snmpv3_priv_password: "privpass123"
})
%{organization: organization, site: site}
end
test "updates devices inheriting snmpv3 from site", %{organization: organization, site: site} do
# Create device and manually set snmpv3_credential_source to "site"
# (can't use create_device with v3 + no username due to changeset validation order)
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmpv3_username: "site-v3-user",
snmpv3_auth_password: "authpass123",
snmpv3_priv_password: "privpass123",
snmp_version: "3"
})
# Force source to "site" for propagation test
device
|> Ecto.Changeset.change(%{snmpv3_credential_source: "site"})
|> Towerops.Repo.update!()
assert :ok =
Devices.propagate_site_snmpv3_change(site.id, %{
snmpv3_username: "new-v3-user"
})
updated = Devices.get_device!(device.id)
assert updated.snmpv3_username == "new-v3-user"
end
end
describe "propagate_organization_snmpv3_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
# Set snmpv3 creds on org (with passwords required for v3 validation)
{:ok, organization} =
Towerops.Organizations.update_organization(organization, %{
snmpv3_username: "org-v3-user",
snmpv3_auth_password: "authpass123",
snmpv3_priv_password: "privpass123"
})
# Site without snmpv3 creds
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "updates devices inheriting snmpv3 from organization", %{organization: organization, site: site} do
# Create device with v3 credentials and force source to "organization"
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmpv3_username: "org-v3-user",
snmpv3_auth_password: "authpass123",
snmpv3_priv_password: "privpass123",
snmp_version: "3"
})
# Force source to "organization" for propagation test
device
|> Ecto.Changeset.change(%{snmpv3_credential_source: "organization"})
|> Towerops.Repo.update!()
assert :ok =
Devices.propagate_organization_snmpv3_change(organization.id, %{
snmpv3_username: "new-v3-user"
})
updated = Devices.get_device!(device.id)
assert updated.snmpv3_username == "new-v3-user"
end
end
describe "resolve_snmp_community/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "returns device community when source is device", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: "my-community"
})
assert Devices.resolve_snmp_community(device) == "my-community"
end
test "returns site community when device has none", %{organization: organization, site: site} do
{:ok, site} = Towerops.Sites.update_site(site, %{snmp_community: "site-community"})
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: nil,
snmp_community_source: "site"
})
assert Devices.resolve_snmp_community(device) == "site-community"
end
test "returns org community when device and site have none", %{organization: organization, site: site} do
{:ok, _org} =
Towerops.Organizations.update_organization(organization, %{snmp_community: "org-community"})
{:ok, device} =
Devices.create_device(%{
name: "Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_community: nil,
snmp_community_source: "organization"
})
assert Devices.resolve_snmp_community(device) == "org-community"
end
end
describe "list_mikrotik_devices_with_api/0" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
%{organization: organization, site: site}
end
test "returns devices with mikrotik enabled and username", %{organization: organization, site: site} do
{:ok, device} =
Devices.create_device(%{
name: "MikroTik Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
mikrotik_enabled: true,
mikrotik_username: "admin"
})
result = Devices.list_mikrotik_devices_with_api()
device_ids = Enum.map(result, & &1.id)
assert device.id in device_ids
end
test "excludes devices without mikrotik enabled", %{organization: organization, site: site} do
{:ok, _device} =
Devices.create_device(%{
name: "Regular Router",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
mikrotik_enabled: false
})
result = Devices.list_mikrotik_devices_with_api()
assert result == []
end
end
end

View file

@ -4,6 +4,7 @@ defmodule Towerops.Workers.DeviceMonitorWorkerTest do
import Towerops.AccountsFixtures
alias Towerops.Devices
alias Towerops.Monitoring.PingMock
alias Towerops.Organizations
alias Towerops.Sites
alias Towerops.Workers.DeviceMonitorWorker
@ -87,4 +88,78 @@ defmodule Towerops.Workers.DeviceMonitorWorkerTest do
refute cancelled_jobs == []
end
end
describe "perform/1" do
test "skips check for deleted device" do
fake_id = Ecto.UUID.generate()
assert :ok == DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => fake_id}})
end
test "performs check and reschedules for monitoring-enabled device", %{site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Monitor Me",
ip_address: "192.168.1.10",
site_id: site.id,
organization_id: site.organization_id,
monitoring_enabled: true
})
Mox.expect(PingMock, :ping, fn _ip -> {:ok, 5.0} end)
assert :ok == DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => device.id}})
# Device should be marked up
updated = Devices.get_device!(device.id)
assert updated.status == :up
end
test "handles ping failure and marks device down", %{site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Failing Device",
ip_address: "192.168.1.11",
site_id: site.id,
organization_id: site.organization_id,
monitoring_enabled: true
})
Mox.expect(PingMock, :ping, fn _ip -> {:error, :timeout} end)
assert :ok == DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => device.id}})
updated = Devices.get_device!(device.id)
assert updated.status == :down
end
test "does not check when monitoring is disabled", %{site: site} do
{:ok, device} =
Devices.create_device(%{
name: "No Monitor",
ip_address: "192.168.1.12",
site_id: site.id,
organization_id: site.organization_id,
monitoring_enabled: false
})
# No mock expectation - ping should not be called
assert :ok == DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => device.id}})
end
end
describe "trigger_check/1" do
test "inserts an immediate job", %{site: site} do
{:ok, device} =
Devices.create_device(%{
name: "Trigger Me",
ip_address: "192.168.1.13",
site_id: site.id,
organization_id: site.organization_id,
monitoring_enabled: true
})
assert {:ok, job} = DeviceMonitorWorker.trigger_check(device.id)
assert job.args["device_id"] == device.id
end
end
end

View file

@ -132,6 +132,39 @@ defmodule Towerops.Workers.DiscoveryWorkerTest do
end
end
describe "enqueue/1" do
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Enqueue Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Enqueue Site",
organization_id: organization.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Enqueue Device",
ip_address: "192.168.1.50",
snmp_enabled: true,
site_id: site.id,
organization_id: organization.id
})
{:ok, device: device}
end
test "enqueues a discovery job", %{device: device} do
assert :ok = DiscoveryWorker.enqueue(device.id)
end
test "deduplicates jobs within 60 seconds", %{device: device} do
assert :ok = DiscoveryWorker.enqueue(device.id)
assert :ok = DiscoveryWorker.enqueue(device.id)
end
end
describe "retryable_error?/1" do
test "timeout is retryable" do
assert DiscoveryWorker.retryable_error?(:timeout)
@ -157,6 +190,14 @@ defmodule Towerops.Workers.DiscoveryWorkerTest do
refute DiscoveryWorker.retryable_error?(:invalid_config)
end
test "no_snmp_credentials is not retryable" do
refute DiscoveryWorker.retryable_error?(:no_snmp_credentials)
end
test "device_deleted is not retryable" do
refute DiscoveryWorker.retryable_error?(:device_deleted)
end
test "unknown errors are retryable by default" do
assert DiscoveryWorker.retryable_error?(:some_unknown_error)
end

View file

@ -128,6 +128,67 @@ defmodule Towerops.Workers.FirmwareVersionFetcherWorkerTest do
assert is_nil(data.release_date)
end
test "extracts version number from 'RouterOS X.Y.Z [stable]' format" do
rss_with_full_version = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>RouterOS latest stable version</title>
<item>
<title>RouterOS 7.21.2 [stable]</title>
<link>https://mikrotik.com/download</link>
<pubDate>Wed, 15 Jan 2025 12:00:00 +0000</pubDate>
<description>RouterOS 7.21.2 stable release</description>
</item>
</channel>
</rss>
"""
assert {:ok, data} = FirmwareVersionFetcherWorker.parse_rss_feed(rss_with_full_version)
assert data.version == "7.21.2"
assert data.metadata["raw_version"] == "RouterOS 7.21.2 [stable]"
end
test "extracts two-part version number" do
rss_with_two_part = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>RouterOS latest stable version</title>
<item>
<title>RouterOS 7.21 [stable]</title>
<link>https://mikrotik.com/download</link>
<pubDate>Wed, 15 Jan 2025 12:00:00 +0000</pubDate>
<description>Test</description>
</item>
</channel>
</rss>
"""
assert {:ok, data} = FirmwareVersionFetcherWorker.parse_rss_feed(rss_with_two_part)
assert data.version == "7.21"
end
test "handles missing pubDate" do
rss_no_date = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>RouterOS latest stable version</title>
<item>
<title>7.14.1</title>
<link>https://mikrotik.com/download</link>
<description>Test</description>
</item>
</channel>
</rss>
"""
assert {:ok, data} = FirmwareVersionFetcherWorker.parse_rss_feed(rss_no_date)
assert data.version == "7.14.1"
assert is_nil(data.release_date)
end
test "stores metadata correctly" do
rss_with_metadata = """
<?xml version="1.0" encoding="UTF-8"?>

View file

@ -3,6 +3,8 @@ defmodule ToweropsWeb.UserResetPasswordControllerTest do
import Towerops.AccountsFixtures
# Note: edit/update routes are handled by UserResetPasswordLive, not this controller
setup do
%{user: user_fixture(enable_totp: false)}
end

View file

@ -26,5 +26,152 @@ defmodule ToweropsWeb.AccountLive.ActivityTest do
assert html =~ "User data viewed"
end
test "shows sudo_mode_granted from setup", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "Sudo mode granted"
end
end
describe "action descriptions" do
test "displays user_data_exported action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "user_data_exported",
superuser_id: user.id,
target_user_id: user.id
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "exported your account data"
end
test "displays user_profile_updated action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "user_profile_updated",
superuser_id: user.id,
target_user_id: user.id
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "updated your profile"
end
test "displays device_created action with metadata", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "device_created",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"device_name" => "Test Router"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "Test Router"
end
test "displays device_deleted action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "device_deleted",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"device_name" => "Old Router"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "Old Router"
end
test "displays device_updated action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "device_updated",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"device_id" => "abc-123"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "abc-123"
end
test "displays failed_access_attempt action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "failed_access_attempt",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"resource" => "/admin/secret"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "/admin/secret"
end
test "displays privilege_escalation action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "privilege_escalation",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"from_role" => "member", "to_role" => "admin"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "member"
assert html =~ "admin"
end
test "displays impersonate_start action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "impersonate_start",
superuser_id: user.id,
target_user_id: user.id
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "impersonating"
end
test "displays impersonate_end action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "impersonate_end",
superuser_id: user.id,
target_user_id: user.id
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "stopped impersonating"
end
test "displays org_data_accessed action", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "org_data_accessed",
superuser_id: user.id,
target_user_id: user.id,
metadata: %{"organization_id" => "org-123", "action" => "exported"}
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "org-123"
end
test "displays fallback for action without handler", %{conn: conn, user: user} do
{:ok, _} =
Admin.create_audit_log(%{
action: "monitoring_data_queried",
superuser_id: user.id,
target_user_id: user.id
})
{:ok, _view, html} = live(conn, ~p"/account/activity")
assert html =~ "Monitoring data queried"
end
end
end