defmodule ToweropsWeb.NetworkMapLiveTest do use ToweropsWeb.ConnCase, async: true import Phoenix.LiveViewTest @moduletag :skip setup :register_and_log_in_user setup %{user: user} do {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id) %{organization: organization} end describe "mount and render" do test "mounts and renders the network map page", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "Network Map" assert html =~ "Experimental" assert html =~ "Topology Visualization" end test "shows stats bar with default zeros", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "Total Devices" assert html =~ "Added Devices" assert html =~ "Discovered" assert html =~ "Connections" end test "shows tab navigation with added and all tabs", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "Added Devices" assert html =~ "All Devices" end test "shows empty state when no devices exist", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "No network topology available" assert html =~ "Add devices" end test "shows refresh button", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "Refresh" end test "shows legend with role and confidence indicators", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "Roles:" assert html =~ "Router" assert html =~ "Switch" assert html =~ "Firewall" assert html =~ "Discovered" assert html =~ "Links:" assert html =~ "High confidence" end test "shows cytoscape container with network map hook", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") assert html =~ "cy-container" assert html =~ "NetworkMap" end end describe "tab navigation" do test "defaults to added tab", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map") # The "Added Devices" tab should have active styling assert html =~ "Added Devices" end test "switches to all tab via params", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/network-map?tab=all") assert html =~ "All Devices" end end describe "events" do test "refresh_topology reloads data and shows flash", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/network-map") html = render_click(view, "refresh_topology") assert html =~ "Network map refreshed" end end describe "topology update events" do test "pushes update_topology event on mount", %{conn: conn, organization: organization} do {:ok, site} = Towerops.Sites.create_site(%{name: "Test Site", organization_id: organization.id}) {:ok, _device} = Towerops.Devices.create_device( %{ name: "Test-Router", ip_address: "10.0.0.1", snmp_enabled: true, snmp_version: "2c", snmp_community: "public", snmp_port: 161, site_id: site.id, organization_id: organization.id }, bypass_limits: true ) {:ok, view, _html} = live(conn, ~p"/network-map") # Verify the topology data is loaded (not empty) topology = view |> element("#cy-container") |> render() assert topology =~ "data-topology" end test "refreshes topology on PubSub message", %{conn: conn, organization: organization} do {:ok, view, _html} = live(conn, ~p"/network-map") # Broadcast a topology update Phoenix.PubSub.broadcast( Towerops.PubSub, "topology:#{organization.id}", {:topology_updated, organization.id} ) # Give LiveView time to process _ = render(view) # Verify it still renders without error html = render(view) assert html =~ "Topology Visualization" end end describe "node detail panel" do setup %{organization: organization} do {:ok, site} = Towerops.Sites.create_site(%{name: "Test Site", organization_id: organization.id}) {:ok, device} = Towerops.Devices.create_device( %{ name: "Test-Router", ip_address: "10.0.0.1", snmp_enabled: true, snmp_version: "2c", snmp_community: "public", snmp_port: 161, site_id: site.id, organization_id: organization.id }, bypass_limits: true ) %{site: site, device: device} end test "node_clicked event assigns selected node detail", %{conn: conn, device: device} do {:ok, view, _html} = live(conn, ~p"/network-map") html = render_click(view, "node_clicked", %{"node_id" => device.id, "node_type" => "managed"}) assert html =~ "Test-Router" assert html =~ "10.0.0.1" end test "close button clears selection", %{conn: conn, device: device} do {:ok, view, _html} = live(conn, ~p"/network-map") # Open panel render_click(view, "node_clicked", %{"node_id" => device.id, "node_type" => "managed"}) # Close panel html = render_click(view, "close_detail_panel") refute html =~ "Node Details" end test "?node= param opens detail panel on mount", %{conn: conn, device: device} do {:ok, _view, html} = live(conn, ~p"/network-map?node=#{device.id}") assert html =~ "Test-Router" assert html =~ "10.0.0.1" end end describe "site grouping" do test "network map renders site groupings in topology data", %{conn: conn, organization: organization} do {:ok, site} = Towerops.Sites.create_site(%{name: "Main Office", organization_id: organization.id}) {:ok, _device} = Towerops.Devices.create_device( %{ name: "Office-Router", ip_address: "10.0.0.1", snmp_enabled: true, snmp_version: "2c", snmp_community: "public", snmp_port: 161, site_id: site.id, organization_id: organization.id }, bypass_limits: true ) {:ok, _view, html} = live(conn, ~p"/network-map") # The topology data should include site info in JSON assert html =~ "Main Office" end end describe "unauthenticated access" do test "requires authentication" do conn = build_conn() {:error, redirect} = live(conn, ~p"/network-map") assert {:redirect, %{to: path}} = redirect assert path == ~p"/users/log-in" end end end