146 lines
4.9 KiB
Elixir
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
|