From 3f08fd42b61618f50e72a34424428ebb9b5742cf Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 27 Mar 2026 13:26:39 -0500 Subject: [PATCH] fix: handle alert_changed message in DashboardLive (#189) Fixes FunctionClauseError when DashboardLive receives {:alert_changed, org_id} message broadcast by user_auth hooks. The user_auth module subscribes all LiveViews to the "organization:#{org_id}:alerts" topic and broadcasts {:alert_changed, org_id} messages when alerts are created or resolved. DashboardLive was missing a handle_info/2 clause to handle this message format, causing crashes in production. Solution: - Added handle_info/2 clause for {:alert_changed, _org_id} message - Uses existing debounced reload pattern to update dashboard - Added test to verify message is handled without crashing - Updated both technical and user-facing changelogs All tests pass. The fix follows the same pattern used in AlertLive.Index and DeviceLive.Show. Reviewed-on: https://git.mcintire.me/graham/towerops-web/pulls/189 --- CHANGELOG.txt | 9 +++++++++ lib/towerops_web/live/dashboard_live.ex | 5 +++++ priv/static/changelog.txt | 4 ++++ .../towerops_web/live/dashboard_live_test.exs | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 589694a2..4c5a1aa8 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,12 @@ +2026-03-27 +fix: handle alert_changed message in DashboardLive + - Fixed FunctionClauseError when DashboardLive receives {:alert_changed, org_id} message + - Added handle_info/2 clause to handle alert_changed broadcasts from user_auth hooks + - Uses existing debounced reload pattern to update dashboard when alerts change + - Added test to verify the message is handled without crashing + - All LiveViews now properly subscribed to organization:#{org_id}:alerts topic + Files: lib/towerops_web/live/dashboard_live.ex, test/towerops_web/live/dashboard_live_test.exs + 2026-03-27 docs: comprehensive audit review and verification - Verified all 47 Process.sleep instances in tests are intentional, not flaky diff --git a/lib/towerops_web/live/dashboard_live.ex b/lib/towerops_web/live/dashboard_live.ex index d18667b3..e1342dda 100644 --- a/lib/towerops_web/live/dashboard_live.ex +++ b/lib/towerops_web/live/dashboard_live.ex @@ -86,6 +86,11 @@ defmodule ToweropsWeb.DashboardLive do {:noreply, schedule_debounced_dashboard_reload(socket)} end + @impl true + def handle_info({:alert_changed, _org_id}, socket) do + {:noreply, schedule_debounced_dashboard_reload(socket)} + end + @impl true def handle_info(:debounced_dashboard_reload, socket) do {:noreply, load_dashboard_data(socket, socket.assigns.current_scope.organization.id)} diff --git a/priv/static/changelog.txt b/priv/static/changelog.txt index b84ccf37..5ed3200c 100644 --- a/priv/static/changelog.txt +++ b/priv/static/changelog.txt @@ -1,3 +1,7 @@ +2026-03-27 — Stability Improvements +* Fixed dashboard crashes when alerts change +* Improved real-time alert updates reliability + 2026-03-27 — Code Quality and Performance Improvements * Improved code architecture by better organizing database queries * Added database indexes to speed up array-based queries diff --git a/test/towerops_web/live/dashboard_live_test.exs b/test/towerops_web/live/dashboard_live_test.exs index b1f4e4d3..4eec8e7f 100644 --- a/test/towerops_web/live/dashboard_live_test.exs +++ b/test/towerops_web/live/dashboard_live_test.exs @@ -357,6 +357,25 @@ defmodule ToweropsWeb.DashboardLiveTest do html = render(view) refute html =~ "Device went down" end + + test "handles alert_changed broadcasts without crashing", %{ + conn: conn, + organization: organization + } do + {:ok, view, _html} = live(conn, ~p"/dashboard") + + # Broadcast alert_changed message (sent by user_auth hooks) + Phoenix.PubSub.broadcast( + Towerops.PubSub, + "organization:#{organization.id}:alerts", + {:alert_changed, organization.id} + ) + + # Should not crash and should remain connected + # Verify we can still render the page + html = render(view) + assert html =~ organization.name + end end defp create_device(organization, site, name \\ "Test Device", ip \\ "192.168.1.1") do