towerops/test/towerops_web/live/config_timeline_live_test.exs
2026-06-15 11:15:46 -05:00

146 lines
4.9 KiB
Elixir

defmodule ToweropsWeb.ConfigTimelineLiveTest do
use ToweropsWeb.ConnCase, async: true
import Phoenix.LiveViewTest
import Towerops.DevicesFixtures
alias Towerops.ConfigChanges.ConfigChangeEvent
alias Towerops.Devices.MikrotikBackups
setup :register_and_log_in_user
setup %{user: user} do
{:ok, organization} =
Towerops.Organizations.create_organization(%{name: "CT Org"}, user.id)
%{organization: organization}
end
describe "mount + handle_params" do
test "redirects to /devices when device is not in user's org",
%{conn: conn, organization: org} do
conn = put_session(conn, :current_organization_id, org.id)
assert {:error, {:redirect, %{to: to}}} =
live(conn, ~p"/devices/#{Ecto.UUID.generate()}/config-timeline")
assert to == ~p"/devices"
end
test "renders the timeline for an accessible device",
%{conn: conn, organization: org} do
conn = put_session(conn, :current_organization_id, org.id)
device = device_fixture(%{organization_id: org.id})
assert {:ok, _view, html} =
live(conn, ~p"/devices/#{device.id}/config-timeline")
assert html =~ "Config Timeline"
assert html =~ device.name
end
end
describe "events" do
setup %{conn: conn, organization: org} do
conn = put_session(conn, :current_organization_id, org.id)
device = device_fixture(%{organization_id: org.id})
%{conn: conn, device: device}
end
test "select_range patches the URL with new range",
%{conn: conn, device: device} do
{:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/config-timeline")
_ = render_hook(view, "select_range", %{"range" => "24h"})
assert Process.alive?(view.pid)
end
test "close_event clears the selected event", %{conn: conn, device: device} do
{:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/config-timeline")
_ = render_hook(view, "close_event", %{})
assert Process.alive?(view.pid)
end
test "renders timeline rows when device has change events", %{
conn: conn,
device: device,
organization: org
} do
# Insert two change events in the device's recent history.
# Need real backup ids since the template builds /backups/compare URLs.
{:ok, b1} = MikrotikBackups.create_backup(device.id, "/ip dns set\n")
{:ok, b2} = MikrotikBackups.create_backup(device.id, "/ip firewall add\n")
now = DateTime.truncate(DateTime.utc_now(), :second)
earlier = DateTime.add(now, -3 * 3600, :second)
e1 = insert_event!(device.id, org.id, earlier, ["firewall"], 12, b1.id, b2.id)
_e2 = insert_event!(device.id, org.id, now, ["dns", "firewall"], 4, b1.id, b2.id)
{:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/config-timeline")
html = render(view)
# Section badges from the template's :for over event.sections_changed
assert html =~ "firewall"
assert html =~ "dns"
# change_size badge text
assert html =~ "12 lines"
assert html =~ "4 lines"
# select_event renders the modal panel (covers selected_event branches)
_ = render_hook(view, "select_event", %{"id" => e1.id})
html = render(view)
assert html =~ "Change Details"
assert html =~ "Sections Changed"
assert html =~ "Lines Changed"
end
test "selected event without diff_summary still renders the modal", %{
conn: conn,
device: device,
organization: org
} do
{:ok, b1} = MikrotikBackups.create_backup(device.id, "/ip dns set 1\n")
{:ok, b2} = MikrotikBackups.create_backup(device.id, "/ip dns set 2\n")
e =
%ConfigChangeEvent{}
|> ConfigChangeEvent.changeset(%{
device_id: device.id,
organization_id: org.id,
changed_at: DateTime.truncate(DateTime.utc_now(), :second),
# Force empty sections to exercise the "Unknown" branch
sections_changed: [],
change_size: 0,
diff_summary: nil,
backup_before_id: b1.id,
backup_after_id: b2.id
})
|> Towerops.Repo.insert!()
{:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/config-timeline")
_ = render_hook(view, "select_event", %{"id" => e.id})
html = render(view)
assert html =~ "Change Details"
# Empty sections branch renders "Unknown"
assert html =~ "Unknown"
end
end
defp insert_event!(device_id, org_id, changed_at, sections, change_size, before_id, after_id) do
%ConfigChangeEvent{}
|> ConfigChangeEvent.changeset(%{
device_id: device_id,
organization_id: org_id,
changed_at: changed_at,
diff_summary: "@@ -1,3 +1,4 @@\n+/ip firewall added\n",
sections_changed: sections,
change_size: change_size,
backup_before_id: before_id,
backup_after_id: after_id
})
|> Towerops.Repo.insert!()
end
end