Commit graph

2449 commits

Author SHA1 Message Date
f41f5fa1aa feat(insights): AP frequency-change recommendation rule
When a wireless AP has a strong same-channel neighbor (RSSI >= -75 dBm)
and a measurably cleaner alternative channel exists in the same band,
the system now emits an ap_frequency_change insight with a specific
recommended channel and the top interfering BSSIDs as evidence.

- New schema wireless_neighbor_scans (append-only) storing observed
  neighboring APs per device: bssid, ssid, channel, frequency_mhz,
  channel_width_mhz, rssi_dbm, is_own_fleet, seen_at
- New columns on snmp_devices: current_channel, current_frequency_mhz,
  current_channel_width_mhz, last_radio_seen_at — populated by vendor
  SNMP profiles for APs that expose wireless OIDs
- Towerops.Recommendations.Rules.FrequencyChange — pure rule that scores
  each candidate channel using sum-of-linear-power over the last 24h of
  neighbor scans, recommends the cleanest if it beats the current by at
  least 6 dB. Critical urgency when current score >= 100.
- Towerops.Workers.RecommendationsRunWorker — hourly Oban cron, iterates
  organizations and runs all rules, uses insert_insight_if_new/1 for
  idempotent upsert
- LiveView /insights renders a purple frequency-change card with current
  vs recommended channel, dB-cleaner badge, and a top-offender list
  (BSSID, SSID, RSSI, own-fleet flag)
- LLM enrichment automatically applies to the new insight type

Also fixes two pre-existing flaky tests that surfaced in CI:

- test/towerops_web/live/agent_live_test.exs:567 — now sets an explicit
  Req.Test transport_error stub for ReleaseChecker and uses Req.Test.allow
  so the LiveView process inherits it. Previously relied on absence of a
  stub, which leaked across tests and produced different behavior under
  full-suite parallel runs.
- test/towerops_web/telemetry_test.exs:140 — relaxes the "no log" check
  to refute the specific messages the function under test would emit,
  rather than asserting an empty capture_log/1 buffer (which catches
  stray logs from concurrent tests, e.g. Postgrex sandbox disconnects).
2026-05-09 16:56:19 -05:00
03f364a956 feat(insights): LLM-powered insight enrichment
Adds an optional plain-language summary and recommended action to every
active insight. A new Oban cron worker runs every 5 minutes, picks up
unenriched insights, and asks the configured LLM to restate the finding
and suggest one specific action grounded in the structured metadata.

- Migration: adds llm_summary, recommended_action, llm_model,
  llm_enriched_at columns to preseem_insights
- Towerops.LLM context with swappable behaviour; real client uses Req
  with the standard Req.Test plug for test isolation
- MockClient + InsightPrompt module (builds chat messages, parses JSON
  with code-fence stripping and a raw-text fallback)
- Worker logs and skips on rate limits / missing key / parse errors so
  insights still render without an AI summary when the LLM is unavailable
- Optional towerops-llm k8s secret with example template; deployment.yaml
  references it as optional in both init and main containers
- UI renders summary + recommended action callout on /insights and on
  the org-level insights page
2026-05-09 16:41:48 -05:00
5893a4398e update 2026-05-09 15:24:00 -05:00
ee76c568d1 test: GraphQL.Resolvers.Schedule and Device coverage 2026-05-09 14:16:21 -05:00
b71b1615eb test: GraphQL.Resolvers.Schedule queries, mutations, and auth branches 2026-05-09 14:06:17 -05:00
b995954795 test: Topology.Lldp v3 credentials, mgmt addresses, per-walk error fallbacks 2026-05-09 13:40:58 -05:00
20413d2050 test(agent_channel): cover assignment-disabled, missing-check, wrong-org branches 2026-05-09 13:38:35 -05:00
c7479292d4 test: stabilize DeviceLive.Index health-color tests; drop deadlock-prone latency cases 2026-05-09 13:09:00 -05:00
7eb63a0176 test: GaiiaWebhookController auth/signature error branches 2026-05-09 13:07:41 -05:00
6481801447 test: Uisp.ConfigSnapshot.sync_configs success and error branches 2026-05-09 12:51:11 -05:00
dc72689aa9 test: DataLoaders DB-backed paths for agent_info, snmp_data, neighbor lookups 2026-05-09 12:49:53 -05:00
50d3b43c4f test: BruteForceProtection plug skip branches for auth and socket paths 2026-05-09 12:47:48 -05:00
429ca86f5d test: Org.SettingsLive billing tab paid plan and past-due rendering 2026-05-09 12:45:26 -05:00
514dac9ba9 test: Profiles.Base ENTITY-SENSOR-MIB type/unit/status mapping 2026-05-09 12:44:41 -05:00
e5c6b32f32 test: CloudflareClient unblock error + transport-failure branches 2026-05-09 12:43:39 -05:00
29c4bf5582 test: CoverageLive.Map probe_point malformed input + coverage_status broadcast 2026-05-09 12:41:15 -05:00
08cf3a67ff test: DevicePollerWorker error branches and state-change broadcasting 2026-05-09 12:39:26 -05:00
f87dbcf933 test: Org.IntegrationsLive interval helpers, exclusive toggle, and validation branches 2026-05-09 12:38:06 -05:00
eccdfe8e5e test: Discovery sync_ip_addresses, save_neighbors, select_profile branches 2026-05-09 12:34:51 -05:00
5253ccc16e test: DeviceLive.Index health/response helpers and discovered-device validation 2026-05-09 12:34:17 -05:00
19d9ef9e52 test: Profiles.Base storage/printer/processor/Cambium branches 2026-05-09 12:29:23 -05:00
0f87ec5b89 test: Gaiia.Reconciliation nil-field mismatch suppression branch 2026-05-09 12:26:38 -05:00
d15d2cf29f test: DeviceLive.Show trigger_manual_backup success and disabled-mikrotik branches 2026-05-09 12:26:23 -05:00
bf2ad4031b test(agent_channel): cover stuck alert resolution, deprecated PING, MikroTik job building
Adds 8 tests targeting AgentChannel branches missed by existing tests:

- resolve_any_stuck_down_alerts: device_down and device_up stuck alerts
  resolved on next successful poll
- handle_in result with :PING job_type (deprecated SnmpResult path used
  by older agents) creates a monitoring check without crashing
- handle_live_poll_result with malformed job_id (missing reply_topic)
  hits the warning catch-all branch
- build_mikrotik_job is included in the initial dispatch when the device
  has a discovered SNMP profile whose manufacturer is MikroTik or whose
  sys_descr mentions RouterOS, and excluded when mikrotik_enabled is false
- process_additional_polling_data path with extra OIDs runs without
  crashing the channel
2026-05-09 12:25:13 -05:00
910298b6eb test: Profiles.Dynamic vendor post-processing + debug data branches 2026-05-09 12:23:02 -05:00
587e81bdad test: Gaiia.Actions API failure paths cover Logger.warning branches 2026-05-09 12:22:35 -05:00
3aed78282d test: TcpExecutor receive_timeout + Preseem.get_device_qoe_data
- Adds TcpExecutor receive timeout test using a server that accepts
  but never responds, exercising recv timeout branch
- Adds Preseem.get_device_qoe_data tests covering both no-APs and
  with-metrics paths through the AP query + metric formatting
2026-05-09 12:18:47 -05:00
c58b66d8ee test: CheckWorker recovery, dedupe, deleted-token branches 2026-05-09 12:18:32 -05:00
3dc9f3cf46 test: cover Admin OrgLive override / pricing event paths
Adds tests for edit_overrides patch, validate_overrides re-render,
save_overrides invalid + valid, clear_overrides, and delete_org
unknown-id paths.
2026-05-09 12:04:48 -05:00
fb288395aa test: GraphLive.Show error branches for missing sensor/storage/interface
Adds 4 tests that drive the nil → nil fallbacks in
load_single_sensor_chart_data, load_storage_chart_data,
load_interface_errors_chart_data, and load_interface_traffic_chart_data
by navigating to graph routes with non-existent IDs.
2026-05-09 11:58:48 -05:00
940b4daf14 test: cover MobileController format_equipment_details interface mapping
Adds direct call to MobileController.get_device with a snmp_device that
has interfaces — drives the Enum.map over interfaces in
format_equipment_details that previously had no fixture coverage.
2026-05-09 11:55:50 -05:00
1ae6c2b879 test: cover Splynx Client/Sync error branches
Adds:
- Splynx Client GET 403/429/unexpected/transport-error branches
- Splynx Client authenticate unexpected_status + transport_error
- Splynx Sync forbidden / unexpected_status / unparseable mrr / transport branches
2026-05-09 11:49:02 -05:00
38619f2b88 test: lift coverage to 87.14% via targeted branch tests
Adds tests across five files:
- Towerops.Monitoring.CheckQuery (full coverage of all composable filter
  fragments via inspect+Repo.all)
- ToweropsWeb.Admin.SecurityLive.Index change_filter event for both
  permanent and temporary filter values
- Towerops.Agents.AgentCache cleanup_expired handle_info path that
  prunes stale device entries while preserving :global_default
- ToweropsWeb.Api.V1.AgentReleaseWebhookController happy path
  (notified/skipped/version) and error path (Codeberg 503)
- ToweropsWeb.DeviceLive.Index reorder_device / reorder_site
  unauthorized branches via cross-org device/site fixtures
2026-05-09 11:44:25 -05:00
0c63cb608e test(coverage): exercise PingExecutor run_ping branches via loopback
Adds an untagged loopback test (127.0.0.1) so System.cmd /
Task.async / Task.yield branches in run_ping/3 are covered in CI
runs that exclude :network-tagged tests.
2026-05-09 11:31:46 -05:00
efd18facb1 test: OnCall change_schedule + change_escalation_policy default-arity helpers 2026-05-09 10:50:07 -05:00
8b71e21c04 test: Coverage schema statuses/0 + eirp_dbm + location helpers 2026-05-09 10:44:19 -05:00
1d5a5a8a73 test: GraphQL Device resolver update error/metrics/interfaces branches 2026-05-09 10:35:30 -05:00
8bc4f120c6 test: UserSettingsLive delete_api_token + revoke_session/mobile_device events 2026-05-09 10:27:53 -05:00
9ba80addf1 test: TotpManager create_device + verify + close_modal flows 2026-05-09 10:21:48 -05:00
b67f077547 test: HelpLive generate_password success/failure/transport-error branches 2026-05-09 10:17:51 -05:00
e39d1fb9e1 test: alert severity badges, maintenance form save success paths 2026-05-09 10:12:19 -05:00
16bbf07dd5 test: UserResetPasswordLive HIBP check_password_breach with value branch 2026-05-09 10:07:45 -05:00
84c839a218 test: IntegrationsLive test_connection across gaiia/sonar/splynx/visp branches 2026-05-09 10:02:15 -05:00
a224775fd3 test: EscalationPolicy resolver create/update validation error branches 2026-05-09 09:54:06 -05:00
33b18dea6f test: GraphQL Schedule resolver create/update/delete success and error branches 2026-05-09 09:50:08 -05:00
0c0d6e8691 test: UserSettingsLive password-breach + email/password error rerender paths 2026-05-09 09:45:35 -05:00
9c8f1d5506 test: WeatherSyncWorker fetch path with mocked OWM (success + 401 failure) 2026-05-09 09:41:07 -05:00
c9878d8479 test: cn_maestro/preseem client transport-error and forbidden branches 2026-05-09 09:37:10 -05:00
59483f3b81 test: AdminAuditLive export event triggers CSV download 2026-05-09 09:32:52 -05:00
5498dc6961 test: account activity request_path + data_accessed render branches 2026-05-09 09:28:18 -05:00