credo fixes

This commit is contained in:
Graham McIntire 2026-06-14 08:27:57 -05:00
parent 9c3319050f
commit d0160f7d8a
109 changed files with 273 additions and 656 deletions

View file

@ -188,16 +188,16 @@
# {:erlang, :binary_to_term, "Use Plug.Crypto.non_executable_binary_to_term/2 instead."}
# ]
# ]},
{Jump.CredoChecks.LiveViewFormCanBeRehydrated}
# {Jump.CredoChecks.UndeclaredExternalResource},
# {Jump.CredoChecks.PreferChangeOverUpDownMigrations, [start_after: "20250101000000"]},
# {Jump.CredoChecks.PreferTextColumns, [start_after: "20250101000000"]},
# {Jump.CredoChecks.SafeBinaryToTerm},
# {Jump.CredoChecks.TestHasNoAssertions,
# [custom_assertion_functions: [:assert_has, :refute_has, :assert_eventually]]},
# {Jump.CredoChecks.TooManyAssertions, [max_assertions: 20]},
# {Jump.CredoChecks.TopLevelAliasImportRequire},
# {Jump.CredoChecks.UnusedLiveViewAssign, [ignored_assigns: [:active_path, :current_scope]]},
{Jump.CredoChecks.LiveViewFormCanBeRehydrated},
{Jump.CredoChecks.UndeclaredExternalResource},
{Jump.CredoChecks.PreferChangeOverUpDownMigrations, [start_after: "20250101000000"]},
{Jump.CredoChecks.PreferTextColumns, [start_after: "20250101000000"]},
{Jump.CredoChecks.SafeBinaryToTerm},
{Jump.CredoChecks.TestHasNoAssertions,
[custom_assertion_functions: [:assert_has, :refute_has, :assert_eventually]]},
{Jump.CredoChecks.TooManyAssertions, [max_assertions: 20]},
{Jump.CredoChecks.TopLevelAliasImportRequire},
{Jump.CredoChecks.UnusedLiveViewAssign, [ignored_assigns: [:active_path, :current_scope, :current_token]]}
# {Jump.CredoChecks.UseObanProWorker},
# {Jump.CredoChecks.VacuousTest,
# [

View file

@ -68,6 +68,8 @@ defmodule SnmpKit.SnmpLib.ASN1 do
import Bitwise
require Logger
@type tag :: non_neg_integer()
@type length :: non_neg_integer()
@type content :: binary()
@ -154,8 +156,6 @@ defmodule SnmpKit.SnmpLib.ASN1 do
end
def encode_octet_string(value) when not is_binary(value) do
require Logger
Logger.error("encode_octet_string received non-binary value: #{inspect(value)}")
{:error, {:invalid_type, value}}
end

View file

@ -10,6 +10,8 @@ defmodule SnmpKit.SnmpMgr.Bulk do
alias SnmpKit.SnmpMgr.Core
alias SnmpKit.SnmpMgr.Format
require Logger
@default_max_repetitions 30
@default_non_repeaters 0
@ -232,8 +234,6 @@ defmodule SnmpKit.SnmpMgr.Bulk do
end
defp bulk_walk_subtree(target, current_oid, root_oid, acc, remaining, opts) when remaining > 0 do
require Logger
max_repetitions =
min(remaining, Keyword.get(opts, :max_repetitions, @default_max_repetitions))
@ -290,8 +290,6 @@ defmodule SnmpKit.SnmpMgr.Bulk do
end
defp filter_table_results(results, root_oid) do
require Logger
in_scope_results =
Enum.filter(results, fn
# Only accept 3-tuple format with proper type information

View file

@ -17,6 +17,8 @@ defmodule Towerops.Accounts do
alias Towerops.Accounts.UserNotifier
alias Towerops.Accounts.UserToken
alias Towerops.Admin.AuditLogger
alias Towerops.Organizations.Membership
alias Towerops.Organizations.Organization
alias Towerops.Repo
require Logger
@ -489,11 +491,8 @@ defmodule Towerops.Accounts do
Used to check if a user can safely delete their account without
orphaning organizations.
"""
@spec sole_owner_organizations(String.t()) :: [Towerops.Organizations.Organization.t()]
@spec sole_owner_organizations(String.t()) :: [Organization.t()]
def sole_owner_organizations(user_id) do
alias Towerops.Organizations.Membership
alias Towerops.Organizations.Organization
user_owner_org_ids =
Membership
|> where([m], m.user_id == ^user_id and m.role == :owner)

View file

@ -8,8 +8,12 @@ defmodule Towerops.Admin do
alias Towerops.Accounts.User
alias Towerops.Admin.AuditLog
alias Towerops.Billing
alias Towerops.Billing.StripeClient
alias Towerops.Organizations.Organization
alias Towerops.Repo
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
require Logger
@ -403,11 +407,6 @@ defmodule Towerops.Admin do
Returns {:ok, migration_report} or {:error, reason}.
"""
def update_global_pricing(attrs, superuser_id, ip_address) do
alias Towerops.Billing
alias Towerops.Billing.StripeClient
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
with {:ok, validated} <- validate_pricing_attrs(attrs),
old_values = get_current_pricing(),
{:ok, new_price_id, migration_report} <- maybe_update_stripe_price(validated, old_values),
@ -454,8 +453,6 @@ defmodule Towerops.Admin do
end
defp get_current_pricing do
alias Towerops.Settings
%{
free_devices: Settings.get_setting("default_free_devices"),
price: Settings.get_setting("default_price_per_device")
@ -463,9 +460,6 @@ defmodule Towerops.Admin do
end
defp maybe_update_stripe_price(validated, old_values) do
alias Towerops.Billing
alias Towerops.Billing.StripeClient
price_changed = validated.price && validated.price != old_values.price
if price_changed do
@ -483,9 +477,6 @@ defmodule Towerops.Admin do
end
defp update_pricing_settings(validated, new_price_id) do
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
if validated.free_devices do
Settings.update_setting("default_free_devices", validated.free_devices)
end

View file

@ -14,6 +14,7 @@ defmodule Towerops.Agents do
alias Towerops.Agents.AgentTokenQuery
alias Towerops.Agents.ReleaseChecker
alias Towerops.Devices.Device
alias Towerops.Preseem.Insight
alias Towerops.Repo
require Logger
@ -237,8 +238,6 @@ defmodule Towerops.Agents do
end
defp dismiss_agent_offline_insights(agent_token_id) do
alias Towerops.Preseem.Insight
now = Towerops.Time.now()
{count, _} =

View file

@ -6,6 +6,8 @@ defmodule Towerops.Billing do
Pricing: $2/device/month after first 10 free devices (configurable via ApplicationSettings).
"""
import Ecto.Query, warn: false
alias Towerops.Billing.StripeClient
alias Towerops.Organizations.Organization
alias Towerops.Organizations.SubscriptionLimits
@ -240,8 +242,6 @@ defmodule Towerops.Billing do
- failures: list of {:error, org, reason} tuples
"""
def migrate_all_subscriptions_to_price(new_price_id) do
import Ecto.Query
orgs =
Repo.all(
from o in Organization,

View file

@ -14,6 +14,8 @@ defmodule Towerops.Coverages.Antenna do
alias Towerops.Coverages.AntennaCatalog
require Logger
@enforce_keys [:slug, :model, :h_pattern, :v_pattern, :gain_dbi]
defstruct [
:slug,
@ -187,8 +189,6 @@ defmodule Towerops.Coverages.Antenna do
Map.put(acc, antenna.slug, antenna)
{:error, reason} ->
require Logger
Logger.warning("Failed to synthesize antenna #{spec.slug}: #{inspect(reason)}")
acc
end
@ -314,8 +314,6 @@ defmodule Towerops.Coverages.Antenna do
Map.put(acc, slug, antenna)
{:error, reason} ->
require Logger
Logger.warning("Failed to parse antenna #{filename}: #{inspect(reason)}")
acc
end

View file

@ -15,11 +15,14 @@ defmodule Towerops.Devices do
alias Towerops.Organizations
alias Towerops.Organizations.Organization
alias Towerops.Organizations.SubscriptionLimits
alias Towerops.Preseem.Insight
alias Towerops.Repo
alias Towerops.Sites
alias Towerops.Workers.DeviceMonitorWorker
alias Towerops.Workers.DiscoveryWorker
require Logger
@device_preloads [:organization, site: :organization]
@doc """
@ -270,8 +273,6 @@ defmodule Towerops.Devices do
# Guard against NotLoaded to prevent crashes if association isn't preloaded
case device.organization do
%Ecto.Association.NotLoaded{} ->
require Logger
Logger.error(
"Organization not preloaded for device #{device.id} in get_org_default_agent",
device_id: device.id
@ -554,8 +555,6 @@ defmodule Towerops.Devices do
* `:bypass_limits` - When true, bypasses subscription device limits (for superusers)
"""
def create_device(attrs, opts \\ []) do
require Logger
bypass_limits = Keyword.get(opts, :bypass_limits, false)
changeset = build_device_changeset(attrs)
@ -787,8 +786,6 @@ defmodule Towerops.Devices do
# Log warning if community is empty
defp log_empty_community(device, community) when community in [nil, ""] do
require Logger
Logger.warning("Empty SNMP community resolved for device",
device_id: device.id,
device_name: device.name,
@ -964,8 +961,6 @@ defmodule Towerops.Devices do
end
defp dismiss_poll_gap_insights(device_id) do
alias Towerops.Preseem.Insight
now = Towerops.Time.now()
{count, _} =
@ -974,8 +969,6 @@ defmodule Towerops.Devices do
|> Repo.update_all(set: [status: "dismissed", dismissed_at: now])
if count > 0 do
require Logger
Logger.info("Auto-dismissed #{count} poll gap insight(s) for device #{device_id}")
end

View file

@ -13,6 +13,8 @@ defmodule Towerops.Gaiia.Sync do
alias Towerops.Gaiia.Client
alias Towerops.Gaiia.SubscriberMatching
alias Towerops.Integrations
alias Towerops.Repo
alias Towerops.Sites.Site
require Logger
@ -119,11 +121,6 @@ defmodule Towerops.Gaiia.Sync do
end
defp find_matching_site_id(org_id, gaiia_name) when is_binary(gaiia_name) and gaiia_name != "" do
import Ecto.Query
alias Towerops.Repo
alias Towerops.Sites.Site
search = "%#{Towerops.QueryHelpers.sanitize_like(gaiia_name)}%"
Repo.one(

View file

@ -14,6 +14,8 @@ defmodule Towerops.Monitoring do
alias Towerops.Monitoring.CheckResult
alias Towerops.Monitoring.MonitoringCheck
alias Towerops.Repo
alias Towerops.Snmp.SensorReading
alias Towerops.Workers.CheckExecutorWorker
alias Towerops.Workers.PollingOffset
## Checks
@ -254,8 +256,6 @@ defmodule Towerops.Monitoring do
Queues a CheckExecutorWorker job with polling offset to distribute load.
"""
def schedule_check(%Check{} = check) do
alias Towerops.Workers.CheckExecutorWorker
offset = PollingOffset.calculate_offset(check.id, check.interval_seconds)
%{check_id: check.id}
@ -355,7 +355,6 @@ defmodule Towerops.Monitoring do
# If SNMP check with source_id, also query old snmp_sensor_readings for backfill
historical_results =
if check.check_type == "snmp_sensor" && check.source_id do
alias Towerops.Snmp.SensorReading
# Find earliest check_result timestamp to avoid overlap
earliest_check_result =
recent_results

View file

@ -5,6 +5,7 @@ defmodule Towerops.Preseem do
import Ecto.Query
alias Towerops.Devices
alias Towerops.Preseem.AccessPoint
alias Towerops.Preseem.Insights
alias Towerops.Preseem.SubscriberMetric
@ -126,8 +127,6 @@ defmodule Towerops.Preseem do
Returns a map with average QoE, latency, capacity info, and per-AP breakdowns.
"""
def get_site_qoe_summary(site_id) do
alias Towerops.Devices
device_ids =
site_id
|> Devices.list_site_devices()

View file

@ -9,10 +9,10 @@ defmodule Towerops.Preseem.FleetIntelligence do
alias Towerops.Preseem.FleetProfile
alias Towerops.Repo
require Logger
@doc "Compute fleet profiles for all matched APs in an organization."
def compute_fleet_profiles(organization_id) do
require Logger
now = Towerops.Time.now()
matched_aps =
@ -94,8 +94,6 @@ defmodule Towerops.Preseem.FleetIntelligence do
# PostgreSQL treats NULLs as distinct in unique indexes, so ON CONFLICT won't match
# rows with NULL firmware_version. Ecto's get_by also rejects nil values.
defp upsert_fleet_profile(organization_id, attrs) do
require Logger
full_attrs = Map.put(attrs, :organization_id, organization_id)
existing =

View file

@ -10,6 +10,7 @@ defmodule Towerops.Preseem.Sync do
alias Towerops.Preseem.AccessPoint
alias Towerops.Preseem.Client
alias Towerops.Preseem.DeviceMatcher
alias Towerops.Preseem.SubscriberMetric
alias Towerops.Preseem.SyncLog
alias Towerops.Repo
@ -75,8 +76,6 @@ defmodule Towerops.Preseem.Sync do
Inserts a subscriber metric record for the given access point.
"""
def insert_metrics(access_point_id, metrics_data) do
alias Towerops.Preseem.SubscriberMetric
recorded_at =
case metrics_data["recorded_at"] do
nil -> Towerops.Time.now()

View file

@ -4,6 +4,7 @@ defmodule Towerops.Proto.TupleDecode do
Converts Elixir structs to tagged tuples for backward compatibility.
"""
alias Towerops.Agent.AgentJob
alias Towerops.Agent.MikrotikCommand
alias Towerops.Agent.MikrotikDevice
alias Towerops.Agent.SnmpDevice
@ -211,8 +212,6 @@ defmodule Towerops.Proto.TupleDecode do
# Convert from Types.AgentJob to Agent.AgentJob
defp types_job_to_agent_job(%Types.AgentJob{} = j) do
alias Towerops.Agent.AgentJob
%AgentJob{
job_id: j.job_id,
job_type: j.job_type,

View file

@ -138,8 +138,6 @@ defmodule Towerops.Snmp do
end
defp create_sensor_check(device, sensor) do
alias Towerops.Monitoring
attrs = %{
organization_id: device.organization_id,
device_id: device.id,
@ -156,10 +154,10 @@ defmodule Towerops.Snmp do
}
}
case Monitoring.create_check(attrs) do
case Towerops.Monitoring.create_check(attrs) do
{:ok, check} ->
# Schedule first execution
_ = Monitoring.schedule_check(check)
_ = Towerops.Monitoring.schedule_check(check)
{:ok, check}
error ->
@ -168,8 +166,6 @@ defmodule Towerops.Snmp do
end
defp create_interface_check(device, interface) do
alias Towerops.Monitoring
name = interface.if_alias || interface.if_name || interface.if_descr || "Interface #{interface.if_index}"
attrs = %{
@ -187,9 +183,9 @@ defmodule Towerops.Snmp do
}
}
case Monitoring.create_check(attrs) do
case Towerops.Monitoring.create_check(attrs) do
{:ok, check} ->
_ = Monitoring.schedule_check(check)
_ = Towerops.Monitoring.schedule_check(check)
{:ok, check}
error ->
@ -198,8 +194,6 @@ defmodule Towerops.Snmp do
end
defp create_processor_check(device, processor) do
alias Towerops.Monitoring
attrs = %{
organization_id: device.organization_id,
device_id: device.id,
@ -215,9 +209,9 @@ defmodule Towerops.Snmp do
}
}
case Monitoring.create_check(attrs) do
case Towerops.Monitoring.create_check(attrs) do
{:ok, check} ->
_ = Monitoring.schedule_check(check)
_ = Towerops.Monitoring.schedule_check(check)
{:ok, check}
error ->
@ -226,8 +220,6 @@ defmodule Towerops.Snmp do
end
defp create_storage_check(device, storage) do
alias Towerops.Monitoring
name = storage.description || storage.device_name || "Storage #{storage.storage_index}"
attrs = %{
@ -245,9 +237,9 @@ defmodule Towerops.Snmp do
}
}
case Monitoring.create_check(attrs) do
case Towerops.Monitoring.create_check(attrs) do
{:ok, check} ->
_ = Monitoring.schedule_check(check)
_ = Towerops.Monitoring.schedule_check(check)
{:ok, check}
error ->

View file

@ -27,12 +27,15 @@ defmodule Towerops.Snmp.Discovery do
alias Towerops.Snmp.EntityPhysical
alias Towerops.Snmp.Interface
alias Towerops.Snmp.IpAddress
alias Towerops.Snmp.Mempool
alias Towerops.Snmp.NeighborDiscovery
alias Towerops.Snmp.PrinterSupply
alias Towerops.Snmp.Processor
alias Towerops.Snmp.Profiles.Base
alias Towerops.Snmp.Profiles.Dynamic
alias Towerops.Snmp.Sensor
alias Towerops.Snmp.Storage
alias Towerops.Snmp.Transceiver
alias Towerops.Snmp.Vlan
# alias Towerops.Topology # Commented out - auto-inference disabled
alias Towerops.Workers.DiscoveryWorker
@ -1387,8 +1390,6 @@ defmodule Towerops.Snmp.Discovery do
Sync memory pools from discovery/polling results to database.
"""
def sync_mempools(device, discovered_mempools) do
alias Towerops.Snmp.Mempool
# Get existing mempools for this device, indexed by mempool_index
existing_mempools =
from(m in Mempool, where: m.snmp_device_id == ^device.id)
@ -1463,8 +1464,6 @@ defmodule Towerops.Snmp.Discovery do
Creates, updates, or deletes transceiver entries to match discovered state.
"""
def sync_transceivers(snmp_device, discovered_transceivers) do
alias Towerops.Snmp.Transceiver
# Get existing transceivers for this device, indexed by port_index
existing_transceivers =
from(t in Transceiver, where: t.snmp_device_id == ^snmp_device.id)
@ -1521,8 +1520,6 @@ defmodule Towerops.Snmp.Discovery do
- Deletes supplies that are no longer discovered
"""
def sync_printer_supplies(snmp_device, discovered_supplies) do
alias Towerops.Snmp.PrinterSupply
# Get existing supplies for this device, indexed by supply_index
existing_supplies =
from(s in PrinterSupply, where: s.snmp_device_id == ^snmp_device.id)

View file

@ -6,6 +6,9 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
alias Towerops.Snmp.Client
alias Towerops.Snmp.Discovery
alias Towerops.Snmp.Profiles.Base
alias Towerops.Snmp.Profiles.Vendors.Arista
alias Towerops.Snmp.Profiles.Vendors.Powervault
alias Towerops.Snmp.Profiles.Vendors.Registry, as: VendorRegistry
require Logger
@ -66,8 +69,6 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
@spec discover_sensors(map(), Client.connection_opts()) ::
{:ok, [Discovery.sensor_data()]}
def discover_sensors(profile, client_opts) do
alias Towerops.Snmp.Profiles.Base
# Always discover ENTITY-SENSOR-MIB sensors (most devices support this)
{:ok, base_sensors} = Base.discover_sensors(client_opts)
@ -119,8 +120,6 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
@spec discover_interfaces(map(), Client.connection_opts()) ::
{:ok, [Discovery.interface_data()]}
def discover_interfaces(_profile, client_opts) do
alias Towerops.Snmp.Profiles.Base
Base.discover_interfaces(client_opts)
end
@ -540,20 +539,14 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
case name do
"arista_eos" ->
# Apply Arista-specific enhancements
alias Towerops.Snmp.Profiles.Vendors.Arista
Arista.post_process_sensors(sensors, client_opts)
"arista-mos" ->
# Apply Arista-specific enhancements
alias Towerops.Snmp.Profiles.Vendors.Arista
Arista.post_process_sensors(sensors, client_opts)
"dell-powervault" ->
# Parse text-based sensor messages from FCMGMT-MIB
alias Towerops.Snmp.Profiles.Vendors.Powervault
Powervault.post_process_sensors(sensors, client_opts)
_ ->

View file

@ -11,6 +11,8 @@ defmodule Towerops.Workers.AlertDigestWorker do
queue: :notifications,
max_attempts: 3
import Ecto.Query
alias Towerops.Alerts
alias Towerops.Alerts.NotificationRateLimiter
@ -19,7 +21,6 @@ defmodule Towerops.Workers.AlertDigestWorker do
@impl Oban.Worker
def perform(%Oban.Job{args: %{"user_id" => "__cron__"}}) do
# Cron mode: find all pending digests and enqueue delivery
import Ecto.Query
pending =
Towerops.Repo.all(

View file

@ -13,6 +13,8 @@ defmodule Towerops.Workers.CheckWorker do
states: [:available, :scheduled, :executing, :retryable, :suspended]
]
import Ecto.Query
alias Towerops.Agents
alias Towerops.Alerts
alias Towerops.Monitoring
@ -114,8 +116,6 @@ defmodule Towerops.Workers.CheckWorker do
Stops executing a check by cancelling its jobs.
"""
def stop_check(check_id) do
import Ecto.Query
Oban.cancel_all_jobs(
from(j in Oban.Job,
where: j.worker == "Towerops.Workers.CheckWorker",

View file

@ -19,6 +19,8 @@ defmodule Towerops.Workers.DeviceMonitorWorker do
available: [:scheduled_at]
]
import Ecto.Query
alias Towerops.Agents
alias Towerops.Alerts
alias Towerops.Alerts.StormDetector
@ -105,8 +107,6 @@ defmodule Towerops.Workers.DeviceMonitorWorker do
Stops monitoring for a device by cancelling its jobs.
"""
def stop_monitoring(device_id) do
import Ecto.Query
Oban.cancel_all_jobs(
from(j in Oban.Job,
where: j.worker == "Towerops.Workers.DeviceMonitorWorker",
@ -227,8 +227,6 @@ defmodule Towerops.Workers.DeviceMonitorWorker do
:ok
{:error, reason} ->
require Logger
Logger.error("Failed to create device up alert for #{device.name}: #{inspect(reason)}",
device_id: device.id
)

View file

@ -25,6 +25,8 @@ defmodule Towerops.Workers.DevicePollerWorker do
available: [:scheduled_at]
]
import Ecto.Query
alias Towerops.Agents
alias Towerops.Devices
alias Towerops.Gaiia.SubscriberMatching
@ -129,8 +131,6 @@ defmodule Towerops.Workers.DevicePollerWorker do
Stops polling for a device by cancelling its jobs.
"""
def stop_polling(device_id) do
import Ecto.Query
Oban.cancel_all_jobs(
from(j in Oban.Job,
where: j.worker == "Towerops.Workers.DevicePollerWorker",

View file

@ -8,9 +8,15 @@ defmodule Towerops.Workers.GaiiaInsightWorker do
use Oban.Worker, queue: :maintenance
use Gettext, backend: ToweropsWeb.Gettext
import Ecto.Query
alias Towerops.Gaiia.Reconciliation
alias Towerops.Integrations
alias Towerops.Preseem.Insight
alias Towerops.Preseem.Insights
alias Towerops.Repo
require Logger
@impl Oban.Worker
def perform(%Oban.Job{}) do
@ -39,11 +45,6 @@ defmodule Towerops.Workers.GaiiaInsightWorker do
defp maybe_dismiss_resolved_insights(organization_id, dedup_key, current_items) do
# If the issue is resolved (no items), dismiss any active insights
if Enum.empty?(current_items) do
import Ecto.Query
alias Towerops.Preseem.Insight
alias Towerops.Repo
now = Towerops.Time.now()
{count, _} =
@ -57,8 +58,6 @@ defmodule Towerops.Workers.GaiiaInsightWorker do
|> Repo.update_all(set: [status: "dismissed", dismissed_at: now])
if count > 0 do
require Logger
Logger.info(
"Auto-dismissed #{count} reconciliation_finding insight(s) for #{dedup_key} in org #{organization_id}"
)

View file

@ -12,6 +12,8 @@ defmodule ToweropsNative do
- Throughput: ~50,000-100,000 resolutions/second per core
"""
require Logger
@on_load :load_nif
def load_nif do
@ -22,8 +24,6 @@ defmodule ToweropsNative do
:ok
{:error, _reason} ->
require Logger
Logger.warning("ToweropsNative NIF not available, using stubs")
:ok
end

View file

@ -50,7 +50,11 @@ defmodule ToweropsWeb.AgentChannel do
alias Towerops.Monitoring
alias Towerops.Snmp
alias Towerops.Snmp.AgentDiscovery
alias Towerops.Snmp.ArpDiscovery
alias Towerops.Snmp.Discovery
alias Towerops.Snmp.MacDiscovery
alias Towerops.Snmp.NeighborDiscovery
alias Towerops.Snmp.Profiles.Base, as: SnmpProfiles
alias Towerops.Snmp.SensorChangeDetector
alias Towerops.Snmp.SensorScale
alias Towerops.Topology
@ -2042,11 +2046,6 @@ defmodule ToweropsWeb.AgentChannel do
Task.Supervisor.start_child(Towerops.TaskSupervisor, fn ->
try do
alias Towerops.Snmp.ArpDiscovery
alias Towerops.Snmp.MacDiscovery
alias Towerops.Snmp.NeighborDiscovery
alias Towerops.Snmp.Profiles.Base
# Fetch interfaces once for reuse across multiple operations
interfaces = Snmp.list_interfaces(snmp_device_id)
@ -2082,8 +2081,6 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_neighbors(client_opts, device_id, interfaces_with_device_id) do
alias Towerops.Snmp.NeighborDiscovery
case NeighborDiscovery.discover_neighbors(client_opts, interfaces_with_device_id) do
{:ok, neighbors} when neighbors != [] ->
Discovery.save_neighbors(device_id, neighbors)
@ -2094,8 +2091,6 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_arp_entries(client_opts, device_id, interfaces) do
alias Towerops.Snmp.ArpDiscovery
case ArpDiscovery.discover_arp_table(client_opts) do
{:ok, arp_entries} when arp_entries != [] ->
Discovery.save_arp_entries(device_id, arp_entries, interfaces)
@ -2106,8 +2101,6 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_mac_addresses(client_opts, device_id, interfaces) do
alias Towerops.Snmp.MacDiscovery
case MacDiscovery.discover_mac_table(client_opts) do
{:ok, mac_addresses} when mac_addresses != [] ->
Snmp.upsert_mac_addresses(device_id, mac_addresses, interfaces)
@ -2118,9 +2111,7 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_ip_addresses(client_opts, device_id, interfaces) do
alias Towerops.Snmp.Profiles.Base
case Base.discover_all_ip_addresses(client_opts) do
case SnmpProfiles.discover_all_ip_addresses(client_opts) do
{:ok, ip_addresses} when ip_addresses != [] ->
discovered_device = %{
device_id: device_id,
@ -2135,9 +2126,7 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_processors(client_opts, snmp_device_id) do
alias Towerops.Snmp.Profiles.Base
case Base.discover_processors(client_opts) do
case SnmpProfiles.discover_processors(client_opts) do
{:ok, processors} when processors != [] ->
discovered_device = %{id: snmp_device_id}
Discovery.sync_processors(discovered_device, processors)
@ -2148,9 +2137,7 @@ defmodule ToweropsWeb.AgentChannel do
end
defp process_storage(client_opts, snmp_device_id) do
alias Towerops.Snmp.Profiles.Base
case Base.discover_storage(client_opts) do
case SnmpProfiles.discover_storage(client_opts) do
{:ok, storage} when storage != [] ->
discovered_device = %{id: snmp_device_id}
Discovery.sync_storage(discovered_device, storage)

View file

@ -9,6 +9,7 @@ defmodule ToweropsWeb.Layouts do
alias ToweropsWeb.Components.CookieConsent
alias ToweropsWeb.Helpers.StatusHelpers
alias ToweropsWeb.Live.Components.GlobalSearchComponent
alias ToweropsWeb.TimeHelpers
# Embed all files in layouts/* within this module.
# The default root.html.heex file contains the HTML
@ -988,8 +989,6 @@ defmodule ToweropsWeb.Layouts do
attr :timezone, :string, default: "UTC"
def footer(assigns) do
alias ToweropsWeb.TimeHelpers
build_time = Towerops.Application.build_timestamp()
timezone = Map.get(assigns, :timezone, "UTC")

View file

@ -4,8 +4,12 @@ defmodule ToweropsWeb.AdminController do
"""
use ToweropsWeb, :controller
import Ecto.Query
alias ToweropsWeb.UserAuth
require Logger
@doc """
Start impersonating a user.
"""
@ -30,10 +34,6 @@ defmodule ToweropsWeb.AdminController do
POST /admin/oban/flush?states=scheduled,retryable
"""
def flush_oban(conn, %{"states" => states_param}) do
import Ecto.Query
require Logger
valid_states = ~w(scheduled retryable available)
requested = states_param |> String.split(",") |> Enum.map(&String.trim/1)
states = Enum.filter(requested, &(&1 in valid_states))

View file

@ -17,6 +17,8 @@ defmodule ToweropsWeb.Api.V1.CoveragesController do
alias Towerops.Coverages
require Logger
@doc """
GET /api/v1/coverages
@ -199,8 +201,6 @@ defmodule ToweropsWeb.Api.V1.CoveragesController do
|> json(%{error: "Coverage is not ready yet"})
{:error, reason} ->
require Logger
# Log the underlying reason server-side so we can debug, but don't
# leak filesystem paths / zip errors / IO details to the caller.
Logger.error("Failed to build KMZ for coverage: #{inspect(reason)}")

View file

@ -17,6 +17,8 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do
alias ToweropsWeb.Api.ParamFilter
alias ToweropsWeb.ScopedResource
require Logger
# --- Policy CRUD ---
@doc "GET /api/v1/escalation_policies"
@ -238,8 +240,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do
json(conn, %{success: true})
{:error, reason} ->
require Logger
Logger.error("Failed to delete escalation target #{target_id}: #{inspect(reason)}")
conn |> put_status(:internal_server_error) |> json(%{error: "Failed to delete escalation target"})
end

View file

@ -6,6 +6,8 @@ defmodule ToweropsWeb.HealthController do
alias Ecto.Adapters.SQL
alias Towerops.Repo
require Logger
# Liveness probe: confirms the BEAM/web stack is responsive. Deliberately does
# NOT touch the database or redis — liveness failure causes k8s to kill the
# pod, so it must not react to transient dependency outages. Dependency health
@ -18,8 +20,6 @@ defmodule ToweropsWeb.HealthController do
end
def index(conn, _params) do
require Logger
db_status = check_database()
redis_status = check_redis()
@ -61,8 +61,6 @@ defmodule ToweropsWeb.HealthController do
end
defp check_redis do
require Logger
redis_config = Application.get_env(:towerops, :redis, [])
if Keyword.has_key?(redis_config, :host) do

View file

@ -8,10 +8,10 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
alias Towerops.Accounts
require Logger
@impl true
def mount(_params, _session, socket) do
require Logger
user = socket.assigns.current_scope.user
# If user already has TOTP enabled, redirect them
@ -45,9 +45,7 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
|> assign(:page_title, t("Set Up Two-Factor Authentication"))
|> assign(:secret_base32, secret_base32)
|> assign(:qr_code, qr_code)
|> assign(:code, "")
|> assign(:error, nil)
|> assign(:enrollment_complete, false)
|> assign(:recovery_codes, [])
|> assign(:show_recovery_codes, false)
@ -60,9 +58,7 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
|> assign(:page_title, t("Set Up Two-Factor Authentication"))
|> assign(:secret_base32, nil)
|> assign(:qr_code, nil)
|> assign(:code, "")
|> assign(:error, nil)
|> assign(:enrollment_complete, false)
|> assign(:recovery_codes, [])
|> assign(:show_recovery_codes, false)
@ -73,8 +69,6 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
@impl true
def handle_event("verify_code", %{"code" => code}, socket) do
require Logger
user = socket.assigns.current_scope.user
secret_base32 = socket.assigns.secret_base32
@ -123,7 +117,6 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
# Show recovery codes to user - they can only be displayed once
{:noreply,
socket
|> assign(:enrollment_complete, true)
|> assign(:recovery_codes, codes)
|> assign(:show_recovery_codes, true)}

View file

@ -31,10 +31,7 @@ defmodule ToweropsWeb.Admin.MonitoringLive do
|> assign(:active_jobs, [])
|> assign(:executing_jobs, [])
|> assign(:stuck_jobs, [])
|> assign(:stuck_jobs_count, 0)
|> assign(:failed_jobs, [])
|> assign(:failed_jobs_count, 0)
|> assign(:recent_jobs, [])
|> assign(:recent_events, [])
|> assign(:metrics, %{})
|> assign(:health_metrics, %{})
@ -77,10 +74,7 @@ defmodule ToweropsWeb.Admin.MonitoringLive do
socket
|> assign(:executing_jobs, executing_jobs)
|> assign(:stuck_jobs, stuck_jobs)
|> assign(:stuck_jobs_count, length(stuck_jobs))
|> assign(:failed_jobs, failed_jobs)
|> assign(:failed_jobs_count, length(failed_jobs))
|> assign(:recent_jobs, recent_jobs)
|> assign(:recent_events, recent_jobs)
|> assign(:metrics, metrics)
|> assign(:health_metrics, metrics)

View file

@ -9,6 +9,7 @@ defmodule ToweropsWeb.Admin.OrgLive.Index do
alias Towerops.Admin
alias Towerops.Billing
alias Towerops.Organizations.Organization
alias Towerops.Repo
on_mount {ToweropsWeb.UserAuth, :require_superuser}
@ -262,8 +263,6 @@ defmodule ToweropsWeb.Admin.OrgLive.Index do
end
defp count_active_subscriptions do
alias Towerops.Repo
Repo.one(
from o in Organization,
where: o.subscription_status == "active" and not is_nil(o.stripe_subscription_id),

View file

@ -8,7 +8,6 @@ defmodule ToweropsWeb.AgentLive.Index do
alias Towerops.Accounts.Scope
alias Towerops.Agents
alias Towerops.Agents.Stats
alias Towerops.Settings
@impl true
@ -45,11 +44,6 @@ defmodule ToweropsWeb.AgentLive.Index do
%{}
end
# Get organization-wide agent health statistics
agent_health_stats = Stats.get_organization_agent_health(organization.id)
assignment_breakdown = Stats.get_device_assignment_breakdown(organization.id)
offline_agents = Stats.get_offline_agents(organization.id)
# Get agent image URL from config or use default
agent_image =
Application.get_env(
@ -72,9 +66,6 @@ defmodule ToweropsWeb.AgentLive.Index do
|> assign(:selected_global_default, global_default_cloud_poller_id || "")
|> assign(:device_counts, equipment_counts)
|> assign(:cloud_poller_counts, cloud_poller_counts)
|> assign(:agent_health_stats, agent_health_stats)
|> assign(:assignment_breakdown, assignment_breakdown)
|> assign(:offline_agents, offline_agents)
|> assign(:agent_image, agent_image)
|> assign(:new_token, nil)
|> assign(:show_token_modal, false)
@ -223,10 +214,6 @@ defmodule ToweropsWeb.AgentLive.Index do
equipment_counts = calculate_device_counts(agent_tokens)
cloud_poller_counts = calculate_device_counts(cloud_pollers)
agent_health_stats = Stats.get_organization_agent_health(organization.id)
assignment_breakdown = Stats.get_device_assignment_breakdown(organization.id)
offline_agents = Stats.get_offline_agents(organization.id)
success_message = get_success_message(is_cloud_poller)
{:noreply,
@ -238,9 +225,6 @@ defmodule ToweropsWeb.AgentLive.Index do
|> assign(:has_cloud_pollers, cloud_pollers != [])
|> assign(:device_counts, equipment_counts)
|> assign(:cloud_poller_counts, cloud_poller_counts)
|> assign(:agent_health_stats, agent_health_stats)
|> assign(:assignment_breakdown, assignment_breakdown)
|> assign(:offline_agents, offline_agents)
|> assign(:new_token, %{agent_token: agent_token, token: token})
|> assign(:agent_form, to_form(%{"name" => "", "is_cloud_poller" => false}))
|> put_flash(:info, success_message)
@ -338,10 +322,6 @@ defmodule ToweropsWeb.AgentLive.Index do
equipment_counts = calculate_device_counts(agent_tokens)
cloud_poller_counts = calculate_device_counts(cloud_pollers)
agent_health_stats = Stats.get_organization_agent_health(organization.id)
assignment_breakdown = Stats.get_device_assignment_breakdown(organization.id)
offline_agents = Stats.get_offline_agents(organization.id)
socket
|> stream(:agent_tokens, agent_tokens, reset: true)
|> assign(:has_agents, agent_tokens != [])
@ -350,9 +330,6 @@ defmodule ToweropsWeb.AgentLive.Index do
|> assign(:has_cloud_pollers, cloud_pollers != [])
|> assign(:device_counts, equipment_counts)
|> assign(:cloud_poller_counts, cloud_poller_counts)
|> assign(:agent_health_stats, agent_health_stats)
|> assign(:assignment_breakdown, assignment_breakdown)
|> assign(:offline_agents, offline_agents)
end
defp maybe_update_agent(socket, agent_token_id, organization_id) do
@ -388,14 +365,7 @@ defmodule ToweropsWeb.AgentLive.Index do
end
defp update_health_stats(socket) do
organization = socket.assigns.current_scope.organization
agent_health_stats = Stats.get_organization_agent_health(organization.id)
offline_agents = Stats.get_offline_agents(organization.id)
socket
|> assign(:agent_health_stats, agent_health_stats)
|> assign(:offline_agents, offline_agents)
end
defp agent_authorized_for_deletion?(socket, agent_token) do
@ -429,10 +399,6 @@ defmodule ToweropsWeb.AgentLive.Index do
equipment_counts = calculate_device_counts(agent_tokens)
cloud_poller_counts = calculate_device_counts(cloud_pollers)
agent_health_stats = Stats.get_organization_agent_health(organization.id)
assignment_breakdown = Stats.get_device_assignment_breakdown(organization.id)
offline_agents = Stats.get_offline_agents(organization.id)
{:noreply,
socket
|> stream(:agent_tokens, agent_tokens, reset: true)
@ -444,9 +410,6 @@ defmodule ToweropsWeb.AgentLive.Index do
|> assign(:selected_global_default, global_default_cloud_poller_id || "")
|> assign(:device_counts, equipment_counts)
|> assign(:cloud_poller_counts, cloud_poller_counts)
|> assign(:agent_health_stats, agent_health_stats)
|> assign(:assignment_breakdown, assignment_breakdown)
|> assign(:offline_agents, offline_agents)
|> put_flash(
:info,
t_equipment("Agent deleted successfully. Devices now fall back to site/org defaults or cloud polling.")

View file

@ -196,7 +196,6 @@ defmodule ToweropsWeb.AlertLive.Index do
site_subscribers = load_site_subscribers(filtered_alerts)
sorted_alerts = sort_alerts(filtered_alerts, socket.assigns.sort_by, site_subscribers)
grouped = group_by_site(sorted_alerts, site_subscribers)
counts = %{
all: Alerts.count_organization_alerts(organization_id),
@ -208,7 +207,6 @@ defmodule ToweropsWeb.AlertLive.Index do
socket
|> assign(:alerts, sorted_alerts)
|> assign(:site_subscribers, site_subscribers)
|> assign(:grouped_alerts, grouped)
|> assign(:counts, counts)
end
@ -245,32 +243,6 @@ defmodule ToweropsWeb.AlertLive.Index do
def severity_weight(%{alert_type: "device_down"}), do: 1
def severity_weight(_alert), do: 2
defp group_by_site(alerts, site_subscribers) do
alerts
|> Enum.group_by(fn alert ->
if alert.device.site do
{alert.device.site.id, alert.device.site.name}
else
{nil, "No Site"}
end
end)
|> Enum.map(fn {{site_id, site_name}, site_alerts} ->
sub_info = if site_id, do: site_subscribers[site_id]
critical_count = Enum.count(site_alerts, &(&1.alert_type == "device_down" and is_nil(&1.resolved_at)))
%{
site_id: site_id,
site_name: site_name,
alerts: site_alerts,
total_count: length(site_alerts),
critical_count: critical_count,
subscriber_info: sub_info,
total_subscribers: get_site_subscriber_count(sub_info)
}
end)
|> Enum.sort_by(fn group -> {-group.critical_count, -group.total_subscribers, -group.total_count} end)
end
@doc false
def get_site_subscriber_count(%{account_count: count}) when is_integer(count), do: count
def get_site_subscriber_count(_), do: 0

View file

@ -5,7 +5,7 @@ defmodule ToweropsWeb.ChangelogLive do
@impl true
def mount(_params, _session, socket) do
entries = ToweropsWeb.ChangelogParser.parse()
{:ok, assign(socket, entries: entries, page_title: "What's New", active_page: "changelog")}
{:ok, assign(socket, entries: entries, page_title: "What's New")}
end
@impl true

View file

@ -309,16 +309,12 @@ defmodule ToweropsWeb.CheckLive.FormComponent do
|> assign(assigns)
|> assign(:action, action)
|> assign(:selected_type, selected_type)
|> assign(:form, to_form(changeset))
|> assign_default_config(selected_type)}
|> assign(:form, to_form(changeset))}
end
@impl true
def handle_event("change_type", %{"check" => %{"check_type" => type}}, socket) do
socket =
socket
|> assign(:selected_type, type)
|> assign_default_config(type)
socket = assign(socket, :selected_type, type)
# Auto-fill host from device IP for TCP and SSL checks
socket =
@ -451,20 +447,6 @@ defmodule ToweropsWeb.CheckLive.FormComponent do
def config_to_form_fields(_config, _type), do: %{}
defp assign_default_config(socket, type) do
assign(socket, :default_config, default_config_for_type(socket, type))
end
defp default_config_for_type(_socket, "http"), do: default_config_by_type("http", "")
defp default_config_for_type(socket, "tcp"), do: default_config_by_type("tcp", device_host_from_socket(socket))
defp default_config_for_type(_socket, "dns"), do: default_config_by_type("dns", "")
defp default_config_for_type(socket, "ssl"), do: default_config_by_type("ssl", device_host_from_socket(socket))
defp default_config_for_type(_socket, _type), do: %{}
@doc false
def default_config_by_type("http", _host) do
%{
@ -489,10 +471,6 @@ defmodule ToweropsWeb.CheckLive.FormComponent do
def device_host(%{ip_address: ip}), do: to_string(ip)
def device_host(_), do: ""
defp device_host_from_socket(socket) do
device_host(socket.assigns[:device])
end
@doc false
def merge_config_params(check_params, type) do
check_params

View file

@ -69,9 +69,6 @@ defmodule ToweropsWeb.Live.Components.GlobalSearchComponent do
@impl true
def render(assigns) do
flat = flat_results(assigns.results)
assigns = assign(assigns, flat_results: flat)
~H"""
<div id="global-search" phx-hook="GlobalSearch" phx-target={@myself}>
<%= if @open do %>

View file

@ -29,7 +29,7 @@ defmodule ToweropsWeb.ConfigTimelineLive do
device -> device
end
{:ok, assign(socket, device: device, page_title: "Config Timeline — #{device.name}", active_page: "devices")}
{:ok, assign(socket, device: device, page_title: "Config Timeline — #{device.name}")}
{:error, _} ->
{:ok,

View file

@ -27,7 +27,6 @@ defmodule ToweropsWeb.CoverageLive.Form do
socket
|> assign(:organization, organization)
|> assign(:sites, sites)
|> assign(:antennas, antennas)
|> assign(:antenna_options, antenna_options(antennas))
|> assign(:devices, [])}
end

View file

@ -4,6 +4,7 @@ defmodule ToweropsWeb.DashboardLive do
alias Towerops.ActivityFeed
alias Towerops.Alerts
alias Towerops.ConfigChanges
alias Towerops.Dashboard
alias Towerops.Devices
alias Towerops.Preseem
@ -27,7 +28,6 @@ defmodule ToweropsWeb.DashboardLive do
|> assign(:device_count, 0)
|> assign(:insight_source, nil)
|> assign(:timezone, socket.assigns.current_scope.timezone)
|> assign(:time_format, socket.assigns.current_scope.time_format)
|> assign(:pending_dashboard_reload_ref, nil)}
end
@ -118,13 +118,6 @@ defmodule ToweropsWeb.DashboardLive do
has_subscribers = summary.subscribers.total > 0
site_summaries =
if organization.use_sites do
Dashboard.list_site_summaries(organization_id)
else
[]
end
# Impact data
impact_summary = Dashboard.get_impact_summary(organization_id)
active_incidents = Dashboard.list_active_incidents(organization_id)
@ -174,7 +167,6 @@ defmodule ToweropsWeb.DashboardLive do
|> assign(:device_down, Map.get(status_counts, :down, 0))
|> assign(:device_unknown, Map.get(status_counts, :unknown, 0))
|> assign(:has_subscribers, has_subscribers)
|> assign(:site_summaries, site_summaries)
|> assign(:impact_summary, impact_summary)
|> assign(:active_incidents, active_incidents)
|> assign(:site_impact_summaries, site_impact_summaries)
@ -239,8 +231,6 @@ defmodule ToweropsWeb.DashboardLive do
end
defp load_recent_config_changes(socket, organization_id) do
alias Towerops.ConfigChanges
changes =
ConfigChanges.list_org_changes(organization_id,
limit: 10,

View file

@ -12,6 +12,8 @@ defmodule ToweropsWeb.DeviceLive.Form do
alias Towerops.Workers.DiscoveryWorker
alias ToweropsWeb.Live.Helpers.AccessControl
require Logger
@impl true
def mount(params, _session, socket) do
organization = socket.assigns.current_scope.organization
@ -161,7 +163,6 @@ defmodule ToweropsWeb.DeviceLive.Form do
|> assign(:mikrotik_config_source, mikrotik_config.source)
|> assign(:effective_mikrotik_username, mikrotik_config.username || "(not set)")
|> assign(:is_mikrotik_device, is_mikrotik)
|> assign(:mikrotik_test_result, nil)
|> assign(:monitoring_mode, monitoring_mode)
end
@ -190,8 +191,6 @@ defmodule ToweropsWeb.DeviceLive.Form do
@impl true
def handle_event("validate", %{"device" => device_params}, socket) do
require Logger
device_params = sanitize_device_params(device_params)
changeset =
@ -252,8 +251,6 @@ defmodule ToweropsWeb.DeviceLive.Form do
@impl true
def handle_event("test_snmp", _params, socket) do
require Logger
changeset = socket.assigns.form.source
form_data = Ecto.Changeset.apply_changes(changeset)
@ -645,8 +642,6 @@ defmodule ToweropsWeb.DeviceLive.Form do
@impl true
def handle_info({:credential_test_result, result}, socket) do
require Logger
Logger.info(
"LiveView received credential test result: test_id=#{result.test_id} success=#{result.success} system_description=#{inspect(result.system_description)} error=#{inspect(result.error_message)}"
)
@ -709,8 +704,6 @@ defmodule ToweropsWeb.DeviceLive.Form do
def get_org_agent(_), do: nil
defp send_credential_test_to_agent(agent_token_id, device_map, test_id) do
require Logger
snmp_config = build_snmp_test_config(device_map)
Logger.info("Broadcasting credential test request",

View file

@ -31,7 +31,6 @@ defmodule ToweropsWeb.DeviceLive.Index do
{:ok,
socket
|> assign(:page_title, t_equipment("Devices"))
|> assign(:timezone, socket.assigns.current_scope.timezone)
|> assign(:has_devices, devices != [])
|> assign(:device_rows, build_device_rows(devices))
|> assign(:sites_enabled, organization.use_sites)

View file

@ -2,8 +2,11 @@ defmodule ToweropsWeb.DeviceLive.Show do
@moduledoc false
use ToweropsWeb, :live_view
import ToweropsWeb.Permissions
alias Towerops.Agents
alias Towerops.Devices
alias Towerops.Devices.BackupRequests
alias Towerops.Devices.MikrotikBackups
alias Towerops.Devices.VersionComparator
alias Towerops.Monitoring
@ -15,6 +18,7 @@ defmodule ToweropsWeb.DeviceLive.Show do
alias ToweropsWeb.DeviceLive.Helpers.Formatters
alias ToweropsWeb.DeviceLive.Helpers.SensorClassifiers
alias ToweropsWeb.Live.Helpers.AccessControl
alias ToweropsWeb.TimeHelpers
# SNMP interface type to category mappings
@interface_type_categories %{
@ -413,8 +417,6 @@ defmodule ToweropsWeb.DeviceLive.Show do
end
defp can_view_financials?(socket) do
import ToweropsWeb.Permissions
owner?(socket) || admin?(socket)
end
@ -466,34 +468,15 @@ defmodule ToweropsWeb.DeviceLive.Show do
latency_chart_data = ChartBuilders.load_latency_chart_data(device_id)
processor_chart_data = ChartBuilders.load_processor_chart_data(processors)
memory_chart_data = ChartBuilders.load_sensor_chart_data(snmp_device, ["memory_usage"])
storage_chart_data = ChartBuilders.load_sensor_chart_data(snmp_device, ["disk_usage"])
storage_volume_chart_data = ChartBuilders.load_storage_volume_chart_data(storage)
traffic_chart_data = ChartBuilders.load_overall_traffic_chart_data(snmp_device)
wireless_chart_data =
ChartBuilders.load_sensor_chart_data(snmp_device, [
"frequency",
"power",
"rssi",
"ccq",
"clients",
"distance",
"noise-floor",
"quality",
"rate",
"utilization"
])
# Classify sensors by type
{transceiver_sensors, non_transceiver_sensors} =
SensorClassifiers.split_transceiver_sensors(sensors)
temperature_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.temperature_sensor?/1)
voltage_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.voltage_sensor?/1)
current_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.current_sensor?/1)
power_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.power_sensor?/1)
fan_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.fan_sensor?/1)
load_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.load_sensor?/1)
storage_sensors = Enum.filter(non_transceiver_sensors, &(&1.sensor_type in ["disk_usage"]))
count_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.counter_sensor?/1)
@ -501,34 +484,23 @@ defmodule ToweropsWeb.DeviceLive.Show do
Enum.split_with(count_sensors, &SensorClassifiers.firewall_counter?/1)
wireless_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.wireless_sensor?/1)
signal_sensors = Enum.filter(non_transceiver_sensors, &SensorClassifiers.signal_sensor?/1)
grouped_transceivers = SensorClassifiers.group_transceiver_sensors(transceiver_sensors)
metrics = DataLoaders.calculate_metrics([], device)
available_firmware = DataLoaders.get_available_firmware(snmp_device)
socket
|> assign(:metrics, metrics)
|> assign(:processors, processors)
|> assign(:storage, storage)
|> assign(:latency_chart_data, latency_chart_data)
|> assign(:processor_chart_data, processor_chart_data)
|> assign(:memory_chart_data, memory_chart_data)
|> assign(:storage_chart_data, storage_chart_data)
|> assign(:storage_volume_chart_data, storage_volume_chart_data)
|> assign(:traffic_chart_data, traffic_chart_data)
|> assign(:wireless_chart_data, wireless_chart_data)
|> assign(:temperature_sensors, temperature_sensors)
|> assign(:voltage_sensors, voltage_sensors)
|> assign(:current_sensors, current_sensors)
|> assign(:power_sensors, power_sensors)
|> assign(:fan_sensors, fan_sensors)
|> assign(:load_sensors, load_sensors)
|> assign(:storage_sensors, storage_sensors)
|> assign(:count_sensors, count_sensors)
|> assign(:general_counters, general_counters)
|> assign(:firewall_counters, firewall_counters)
|> assign(:wireless_sensors, wireless_sensors)
|> assign(:signal_sensors, signal_sensors)
|> assign(:grouped_transceivers, grouped_transceivers)
|> assign(:available_firmware, available_firmware)
|> assign(:connected_devices, DataLoaders.load_connected_devices(device_id))
@ -896,8 +868,6 @@ defmodule ToweropsWeb.DeviceLive.Show do
@impl true
def handle_event("delete_backup", %{"id" => backup_id}, socket) do
import ToweropsWeb.Permissions
if owner?(socket) do
do_delete_backup(backup_id, socket)
else
@ -963,8 +933,6 @@ defmodule ToweropsWeb.DeviceLive.Show do
end
defp trigger_manual_backup(socket, device, agent_token_id) do
alias Towerops.Devices.BackupRequests
job_id = "backup:#{device.id}:#{DateTime.to_unix(DateTime.utc_now())}"
case BackupRequests.create_request(device.id, job_id, "manual") do
@ -1151,8 +1119,6 @@ defmodule ToweropsWeb.DeviceLive.Show do
end
defp format_relative_time(datetime) do
alias ToweropsWeb.TimeHelpers
TimeHelpers.format_time_ago(datetime)
end

View file

@ -7,7 +7,7 @@ defmodule ToweropsWeb.EscalationPolicyLive.Form do
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :active_page, "schedules")}
{:ok, socket}
end
@impl true

View file

@ -12,7 +12,6 @@ defmodule ToweropsWeb.EscalationPolicyLive.Index do
{:ok,
socket
|> assign(:page_title, t("Escalation Policies"))
|> assign(:active_page, "schedules")
|> assign(:policies, policies)}
end
end

View file

@ -17,7 +17,6 @@ defmodule ToweropsWeb.EscalationPolicyLive.Show do
{:ok,
socket
|> assign(:page_title, policy.name)
|> assign(:active_page, "schedules")
|> assign(:policy, policy)
|> assign(:org_users, users)
|> assign(:org_schedules, schedules)

View file

@ -118,7 +118,6 @@ defmodule ToweropsWeb.GraphLive.Show do
socket
|> assign(:device, device)
|> assign(:device_id, device.id)
|> assign(:check, check)
|> assign(:check_id, check.id)
|> assign(:range, range)
|> assign(:page_title, "#{device.name} - #{check.name}")
@ -131,7 +130,6 @@ defmodule ToweropsWeb.GraphLive.Show do
|> assign(:max_value, max_value)
|> assign(:min_value, min_value)
|> assign(:is_live_mode, false)
|> assign(:has_agent_assignment, false)
end
defp build_check_chart_data(%{check_type: "dns"} = check, from_time, to_time) do
@ -223,9 +221,6 @@ defmodule ToweropsWeb.GraphLive.Show do
sensor_id = Map.get(params, "sensor_id")
storage_id = Map.get(params, "storage_id")
# Check if device has agent assignment
has_agent = Agents.get_device_assignment(device_id) != nil
socket
|> assign(:device_id, device_id)
|> assign(:sensor_type, sensor_type)
@ -234,7 +229,6 @@ defmodule ToweropsWeb.GraphLive.Show do
|> assign(:sensor_id, sensor_id)
|> assign(:storage_id, storage_id)
|> assign(:is_live_mode, range == "live")
|> assign(:has_agent_assignment, has_agent)
|> maybe_start_live_polling()
|> load_graph_data()
end

View file

@ -36,7 +36,6 @@ defmodule ToweropsWeb.HelpLive.Index do
|> assign(:current_user, current_user)
|> assign(:is_authenticated, is_authenticated)
|> assign(:current_scope, current_scope)
|> assign(:timezone, if(current_user, do: current_user.timezone, else: "UTC"))
|> assign(:generated_password, nil)
|> assign(:password_generating, false)

View file

@ -8,6 +8,8 @@ defmodule ToweropsWeb.InsightsLive.Index do
alias Towerops.Preseem
alias Towerops.Repo
require Logger
@regeneration_workers [
Towerops.Workers.PreseemBaselineWorker,
Towerops.Workers.CapacityInsightWorker,
@ -149,8 +151,6 @@ defmodule ToweropsWeb.InsightsLive.Index do
end
defp insert_regeneration_jobs do
require Logger
Enum.reduce(@regeneration_workers, {0, 0}, fn worker, {ok, fail} ->
case %{} |> worker.new() |> Oban.insert() do
{:ok, _} ->

View file

@ -14,8 +14,7 @@ defmodule ToweropsWeb.MaintenanceLive.Form do
{:ok,
socket
|> assign(:sites, sites)
|> assign(:devices, devices)
|> assign(:timezone, socket.assigns.current_scope.timezone)}
|> assign(:devices, devices)}
end
@impl true

View file

@ -9,6 +9,8 @@ defmodule ToweropsWeb.MobileQRLive do
alias Towerops.MobileSessions
require Logger
@impl true
def mount(_params, _session, socket) do
user = socket.assigns.current_scope.user
@ -20,8 +22,6 @@ defmodule ToweropsWeb.MobileQRLive do
token
{:error, reason} ->
require Logger
Logger.error("Failed to create QR login token: #{inspect(reason)}")
raise "Could not create QR login token"
end

View file

@ -117,7 +117,6 @@ defmodule ToweropsWeb.Org.IntegrationsLive do
|> assign(:page_title, t("Integrations"))
|> assign(:organization, organization)
|> assign(:timezone, socket.assigns.current_scope.timezone)
|> assign(:providers, @providers)
|> assign(:provider_categories, @provider_categories)
|> assign(:integrations, integrations)
|> assign(:configuring, nil)

View file

@ -264,8 +264,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:configuring, nil)
|> assign(:integration_form, nil)
|> assign(:test_result, nil)}
|> assign(:integration_form, nil)}
else
integration = Map.get(socket.assigns.integrations, provider)
@ -286,14 +285,13 @@ defmodule ToweropsWeb.Org.SettingsLive do
if provider == "netbox" do
assign(socket, :netbox_config, load_netbox_config(integration))
else
assign(socket, :integration_credentials, load_integration_credentials(integration, provider))
socket
end
{:noreply,
socket
|> assign(:configuring, provider)
|> assign(:integration_form, form)
|> assign(:test_result, nil)}
|> assign(:integration_form, form)}
end
end
@ -302,8 +300,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:configuring, nil)
|> assign(:integration_form, nil)
|> assign(:test_result, nil)}
|> assign(:integration_form, nil)}
end
@impl true
@ -337,8 +334,8 @@ defmodule ToweropsWeb.Org.SettingsLive do
"tag_filter" => Map.get(params, "tag_filter", "")
})
provider ->
assign(socket, :integration_credentials, extract_credentials_from_params(params, provider))
_provider ->
socket
end
{:noreply, assign(socket, :integration_form, to_form(changeset))}
@ -370,10 +367,8 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:integrations, integrations)
|> assign(:active_billing_provider, active_billing_provider(integrations))
|> assign(:configuring, nil)
|> assign(:integration_form, nil)
|> assign(:test_result, nil)
|> put_flash(:info, t("Integration saved successfully"))}
{:error, changeset} ->
@ -383,8 +378,8 @@ defmodule ToweropsWeb.Org.SettingsLive do
@impl true
def handle_event("test_connection", _params, socket) do
result = do_test_connection(socket.assigns.configuring, socket)
{:noreply, assign(socket, :test_result, result)}
_ = do_test_connection(socket.assigns.configuring, socket)
{:noreply, socket}
end
@impl true
@ -404,7 +399,6 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:integrations, integrations)
|> assign(:active_billing_provider, active_billing_provider(integrations))
|> put_flash(:info, t("Webhook secret saved"))}
{:error, _changeset} ->
@ -429,7 +423,6 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:integrations, integrations)
|> assign(:active_billing_provider, active_billing_provider(integrations))
|> put_flash(:info, t("Gaiia app URL saved"))}
{:error, _changeset} ->
@ -452,7 +445,6 @@ defmodule ToweropsWeb.Org.SettingsLive do
{:noreply,
socket
|> assign(:integrations, integrations)
|> assign(:active_billing_provider, active_billing_provider(integrations))
|> put_flash(:info, t("Integration updated"))}
{:error, _changeset} ->

View file

@ -7,7 +7,7 @@ defmodule ToweropsWeb.ScheduleLive.Form do
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :active_page, "schedules")}
{:ok, socket}
end
@impl true

View file

@ -15,7 +15,6 @@ defmodule ToweropsWeb.ScheduleLive.Index do
{:ok,
socket
|> assign(:page_title, t("Schedules"))
|> assign(:active_page, "schedules")
|> assign(:tab, "schedules")
|> assign(:start_date, default_start_date())
|> assign(:range_days, @default_range)

View file

@ -24,10 +24,8 @@ defmodule ToweropsWeb.ScheduleLive.Show do
{:ok,
socket
|> assign(:page_title, schedule.name)
|> assign(:active_page, "schedules")
|> assign(:schedule, schedule)
|> assign(:on_call, on_call)
|> assign(:timezone, socket.assigns.current_scope.timezone)
|> assign(:org_users, users)
# Pre-open the Add Layer form for freshly-created schedules so the
# new-schedule -> add-layers flow lands in a state ready to act on.

View file

@ -21,8 +21,7 @@ defmodule ToweropsWeb.SiteLive.Index do
|> assign(:page_title, t("Sites"))
|> assign(:sites, sites)
|> assign(:summary_map, summary_map)
|> assign(:insights, insights)
|> assign(:timezone, socket.assigns.current_scope.timezone)}
|> assign(:insights, insights)}
end
@impl true

View file

@ -74,9 +74,7 @@ defmodule ToweropsWeb.TraceLive.Index do
|> assign(:query, "")
|> assign(:results, [])
|> assign(:trace, nil)
|> assign(:trace_type, nil)
|> assign(:trace_id, nil)
|> assign(:searching, false)}
|> assign(:trace_type, nil)}
end
@impl true
@ -92,7 +90,6 @@ defmodule ToweropsWeb.TraceLive.Index do
socket
|> assign(:trace, trace)
|> assign(:trace_type, type_atom)
|> assign(:trace_id, id)
_ ->
socket
@ -117,8 +114,7 @@ defmodule ToweropsWeb.TraceLive.Index do
|> assign(:query, query)
|> assign(:results, results)
|> assign(:trace, nil)
|> assign(:trace_type, nil)
|> assign(:trace_id, nil)}
|> assign(:trace_type, nil)}
end
@impl true
@ -134,7 +130,6 @@ defmodule ToweropsWeb.TraceLive.Index do
|> assign(:results, [])
|> assign(:trace, nil)
|> assign(:trace_type, nil)
|> assign(:trace_id, nil)
|> push_patch(to: ~p"/trace")}
end

View file

@ -28,7 +28,6 @@ defmodule ToweropsWeb.UserRegistrationLive do
|> assign(:invitation_token, invitation_token)
|> assign(:password_breach_count, nil)
|> assign(:detected_timezone, detected_timezone)
|> assign(:trigger_action, false)
|> assign(:privacy_policy_consent, false)
|> assign(:terms_of_service_consent, false)}
end
@ -106,8 +105,7 @@ defmodule ToweropsWeb.UserRegistrationLive do
{:noreply,
socket
|> put_flash(:info, t("Account created! Please check your email to verify your account."))
|> redirect(to: ~p"/users/log-in?email=#{user.email}")
|> assign(:trigger_action, true)}
|> redirect(to: ~p"/users/log-in?email=#{user.email}")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :form, to_form(changeset))}
@ -132,8 +130,7 @@ defmodule ToweropsWeb.UserRegistrationLive do
{:noreply,
socket
|> put_flash(:info, t("Account created! Please check your email to verify your account."))
|> redirect(to: ~p"/users/log-in?email=#{user.email}")
|> assign(:trigger_action, true)}
|> redirect(to: ~p"/users/log-in?email=#{user.email}")}
else
{:error, :invalid_invitation} ->
{:noreply,

View file

@ -22,7 +22,6 @@ defmodule ToweropsWeb.UserResetPasswordLive do
socket
|> assign(:page_title, t("Reset Password"))
|> assign(:form, to_form(changeset))
|> assign(:token, token)
|> assign(:user, user)
|> assign(:password_breach_count, nil)}
end

View file

@ -28,7 +28,6 @@ defmodule ToweropsWeb.UserSettingsLive do
|> assign_mobile_sessions()
|> assign_api_tokens()
|> assign_organizations()
|> assign_default_organization()
|> assign_browser_sessions()
|> assign_login_history()
|> assign_security_alerts()
@ -42,8 +41,6 @@ defmodule ToweropsWeb.UserSettingsLive do
|> assign(:show_add_device_modal, false)
|> assign(:show_device_qr_modal, false)
|> assign(:show_recovery_codes_modal, false)
|> assign(:new_device_id, nil)
|> assign(:new_device_secret, nil)
|> assign(:new_device_qr_code, nil)
|> assign(:generated_recovery_codes, nil)
|> assign(:password_breach_count, nil)
@ -383,21 +380,6 @@ defmodule ToweropsWeb.UserSettingsLive do
assign(socket, :organizations, Towerops.Organizations.list_user_organizations(user.id))
end
defp assign_default_organization(socket) do
user = socket.assigns.current_scope.user
organizations = socket.assigns.organizations
# Get user's preferred default org, or fall back to first org
default_org =
if user.default_organization_id do
Enum.find(organizations, &(&1.id == user.default_organization_id)) || List.first(organizations)
else
List.first(organizations)
end
assign(socket, :default_organization, default_org)
end
defp assign_login_history(socket) do
user = socket.assigns.current_scope.user
page = socket.assigns[:login_history_page] || 1

View file

@ -42,6 +42,8 @@ defmodule ToweropsWeb.Permissions do
alias Towerops.Accounts.Scope
alias Towerops.Organizations.Policy
require Logger
@doc """
Checks if the current user can perform an action on a resource.
@ -145,8 +147,6 @@ defmodule ToweropsWeb.Permissions do
# treats the user as having no role and denies access. That used
# to look indistinguishable from a real denial; surface it so
# missing preloads are noticed and fixed at the call site.
require Logger
Logger.warning(
"Permission check on user=#{user.id} org=#{org.id} without preloaded memberships — denying access by default"
)

View file

@ -13,6 +13,8 @@ defmodule ToweropsWeb.Plugs.WebhookAuth do
import Phoenix.Controller, only: [json: 2]
import Plug.Conn
require Logger
def init(opts), do: opts
def call(conn, _opts) do
@ -33,8 +35,6 @@ defmodule ToweropsWeb.Plugs.WebhookAuth do
cond do
is_nil(secret) or secret == "" ->
require Logger
Logger.error("agent_webhook_secret not configured — rejecting webhook request")
# Log the real reason server-side; don't echo it to the caller —

View file

@ -2,8 +2,11 @@ defmodule ToweropsWeb.Telemetry do
@moduledoc false
use Supervisor
import Ecto.Query
import Telemetry.Metrics
require Logger
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
@ -139,8 +142,6 @@ defmodule ToweropsWeb.Telemetry do
:ok
else
try do
import Ecto.Query
queues = ["default", "discovery", "pollers", "monitors", "maintenance"]
# Query queue sizes
@ -283,8 +284,6 @@ defmodule ToweropsWeb.Telemetry do
# Telemetry handler for router exceptions
def handle_router_exception(_event, _measurements, metadata, _config) do
require Logger
Logger.error(
"Router exception on #{metadata.plug} #{metadata.conn.method} #{metadata.conn.request_path}",
kind: metadata.kind,
@ -296,8 +295,6 @@ defmodule ToweropsWeb.Telemetry do
# Telemetry handler for endpoint stop events (log slow requests and errors)
def handle_endpoint_stop(_event, measurements, metadata, _config) do
require Logger
duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)
# Log slow requests (over 5 seconds)

View file

@ -20,6 +20,7 @@ defmodule ToweropsWeb.UserAuth do
alias Towerops.Accounts
alias Towerops.Accounts.Scope
alias Towerops.Admin.AuditLogger
alias Towerops.Organizations.Policy
alias ToweropsWeb.Helpers.StatusHelpers
# Make the remember me cookie valid for 14 days. This should match
@ -485,8 +486,6 @@ defmodule ToweropsWeb.UserAuth do
membership = Towerops.Organizations.get_membership(organization.id, scope.user.id)
if membership do
alias Towerops.Organizations.Policy
conn
|> assign(:current_scope, Scope.put_organization(scope, organization))
|> assign(:current_membership, membership)
@ -822,8 +821,6 @@ defmodule ToweropsWeb.UserAuth do
halt_with_error(socket, "You don't have access to any organizations.")
mem ->
alias Towerops.Organizations.Policy
scope = socket.assigns.current_scope
{:cont,

View file

@ -11,8 +11,11 @@ defmodule Towerops.Integration.SnmpIntegrationTest do
"""
use ExUnit.Case, async: false
import Towerops.AccountsFixtures
alias Towerops.Snmp.Client
alias Towerops.Snmp.Poller
alias Towerops.Snmp.Profiles.Base
alias Towerops.Workers.DeviceMonitorWorker
@moduletag :real_device
@ -81,8 +84,6 @@ defmodule Towerops.Integration.SnmpIntegrationTest do
describe "Discovery against real device" do
@tag :full_discovery
test "can discover device using Base profile" do
alias Towerops.Snmp.Profiles.Base
# Discover system info using Base profile
{:ok, system_info} = Base.discover_system_info(@test_device_opts)
assert is_binary(system_info.sys_descr)
@ -106,8 +107,6 @@ defmodule Towerops.Integration.SnmpIntegrationTest do
describe "Device Monitor with SNMP" do
test "creates SNMP-specific alert messages when device goes down" do
# This test makes real SNMP calls to an unreachable IP to verify timeout handling
import Towerops.AccountsFixtures
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

View file

@ -285,7 +285,7 @@ defmodule SnmpKit.SnmpLib.CacheTest do
device_id = "192.168.1.1"
oids = ["1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.3.0"]
:ok = Cache.warm_cache(device_id, oids, strategy: :immediate)
assert :ok = Cache.warm_cache(device_id, oids, strategy: :immediate)
# Allow time for async processing
:timer.sleep(10)
@ -294,7 +294,7 @@ defmodule SnmpKit.SnmpLib.CacheTest do
test "performs predictive cache warming" do
device_id = "192.168.1.1"
:ok = Cache.warm_cache(device_id, :auto, strategy: :predictive)
assert :ok = Cache.warm_cache(device_id, :auto, strategy: :predictive)
# Allow time for async processing
:timer.sleep(10)
@ -304,7 +304,7 @@ defmodule SnmpKit.SnmpLib.CacheTest do
device_id = "192.168.1.1"
oids = ["1.3.6.1.2.1.1.1.0"]
:ok = Cache.warm_cache(device_id, oids, strategy: :scheduled)
assert :ok = Cache.warm_cache(device_id, oids, strategy: :scheduled)
# Allow time for async processing
:timer.sleep(10)

View file

@ -168,10 +168,10 @@ defmodule SnmpKit.SnmpLib.ConfigTest do
test_pid = self()
callback = fn _old, _new -> send(test_pid, :config_changed) end
:ok = Config.watch(:snmp, callback)
assert :ok = Config.watch(:snmp, callback)
# Change configuration to trigger watcher
:ok = Config.put(:snmp, :test_value, "changed")
assert :ok = Config.put(:snmp, :test_value, "changed")
# Note: In a real implementation, this would trigger the callback
# For now, we just test that the watcher registration succeeds

View file

@ -74,6 +74,8 @@ defmodule SnmpKit.SnmpLib.DashboardTest do
# Allow time for async processing
:timer.sleep(10)
assert true
end
test "records SNMP operation metrics" do
@ -89,6 +91,8 @@ defmodule SnmpKit.SnmpLib.DashboardTest do
device: "192.168.1.1",
error_type: "timeout"
})
assert true
end
end
@ -116,6 +120,8 @@ defmodule SnmpKit.SnmpLib.DashboardTest do
# Allow time for async processing
:timer.sleep(10)
assert true
end
test "creates device-specific alerts" do
@ -132,6 +138,8 @@ defmodule SnmpKit.SnmpLib.DashboardTest do
avg_response_time: 5000,
threshold: 2000
})
assert true
end
end
@ -279,7 +287,7 @@ defmodule SnmpKit.SnmpLib.DashboardTest do
:timer.sleep(10)
# Acknowledge the alert
:ok = Dashboard.acknowledge_alert(:device_down, "192.168.1.1")
assert :ok = Dashboard.acknowledge_alert(:device_down, "192.168.1.1")
end
end

View file

@ -1,6 +1,7 @@
defmodule Towerops.AccountsTest do
use Towerops.DataCase
import ExUnit.CaptureLog
import Towerops.AccountsFixtures
alias Towerops.Accounts
@ -2079,8 +2080,6 @@ defmodule Towerops.AccountsTest do
describe "verify_totp/2 failure logging" do
test "logs debug info when TOTP verification fails" do
import ExUnit.CaptureLog
secret = Accounts.generate_totp_secret()
# Temporarily enable info logging to exercise the failure log path

View file

@ -2,10 +2,15 @@ defmodule Towerops.AdminTest do
use Towerops.DataCase
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Accounts.User
alias Towerops.Admin
alias Towerops.Admin.AuditLog
alias Towerops.Billing.StripeClient
alias Towerops.Organizations.Organization
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
doctest Admin, except: [delete_user: 3, delete_organization: 3, create_audit_log: 1]
@ -589,7 +594,7 @@ defmodule Towerops.AdminTest do
# Update the old log's timestamp directly to simulate it being older
Towerops.Repo.update_all(
from(l in Towerops.Admin.AuditLog, where: l.id == ^old_log.id),
from(l in AuditLog, where: l.id == ^old_log.id),
set: [inserted_at: old_time]
)
@ -780,13 +785,6 @@ defmodule Towerops.AdminTest do
describe "update_global_pricing/3" do
test "updates settings, creates Stripe price, migrates subscriptions, and audits" do
import Towerops.OrganizationsFixtures
alias Towerops.Admin.AuditLog
alias Towerops.Billing.StripeClient
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
superuser = user_fixture(%{superuser: true})
user = user_fixture()
_org1 = organization_fixture(user.id, %{subscription_status: "active", stripe_subscription_id: "sub_1"})
@ -861,9 +859,6 @@ defmodule Towerops.AdminTest do
end
test "returns ok when price unchanged and no migration needed" do
alias Towerops.Settings
alias Towerops.Settings.ApplicationSetting
superuser = user_fixture(%{superuser: true})
Repo.delete_all(from s in ApplicationSetting, where: s.key in ["default_free_devices", "default_price_per_device"])

View file

@ -5,6 +5,8 @@ defmodule Towerops.Alerts.StormDetectorTest do
use Towerops.DataCase, async: false
use ExUnitProperties
import ExUnit.CaptureLog
alias Towerops.Alerts.StormDetector
defp queue_of(list), do: Enum.reduce(list, :queue.new(), fn x, q -> :queue.in(x, q) end)
@ -121,8 +123,6 @@ defmodule Towerops.Alerts.StormDetectorTest do
end
test "creates a site outage alert when threshold devices go down at the same site" do
import ExUnit.CaptureLog
user = Towerops.AccountsFixtures.user_fixture()
{:ok, org} = Towerops.Organizations.create_organization(%{name: "SD Outage"}, user.id)
{:ok, site} = Towerops.Sites.create_site(%{name: "Tower 7", organization_id: org.id})
@ -172,8 +172,6 @@ defmodule Towerops.Alerts.StormDetectorTest do
end
test "register_device_down enters storm mode when alert rate exceeds threshold" do
import ExUnit.CaptureLog
user = Towerops.AccountsFixtures.user_fixture()
{:ok, org} = Towerops.Organizations.create_organization(%{name: "SD Storm"}, user.id)
{:ok, site} = Towerops.Sites.create_site(%{name: "Storm Site", organization_id: org.id})

View file

@ -1,13 +1,12 @@
defmodule Towerops.AlertsTest do
use Towerops.DataCase
import Towerops.AccountsFixtures
alias Towerops.Alerts
alias Towerops.Alerts.Alert
describe "alerts" do
import Towerops.AccountsFixtures
alias Towerops.Alerts.Alert
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -370,8 +369,6 @@ defmodule Towerops.AlertsTest do
end
describe "GDPR data access - list_alerts_for_organizations/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, org1} = Towerops.Organizations.create_organization(%{name: "Org 1"}, user.id)

View file

@ -3,6 +3,7 @@ defmodule Towerops.Coverages.RasterTest do
alias Towerops.Coverages.Coverage
alias Towerops.Coverages.Raster
alias Towerops.Lidar.Reader
describe "query_rssi/3" do
test "returns :not_ready when raster_path is nil and status is computing" do
@ -67,8 +68,6 @@ defmodule Towerops.Coverages.RasterTest do
end
describe "parse_aaigrid/1 (header parsing — public for testability)" do
alias Towerops.Lidar.Reader
test "parses a small ASCII grid into the expected map shape" do
grid = """
ncols 3

View file

@ -1,7 +1,12 @@
defmodule Towerops.Devices.DeviceFirmwareHistoryTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Devices
alias Towerops.Devices.DeviceFirmwareHistory
alias Towerops.Sites
alias Towerops.Snmp.Device
describe "changeset/2" do
@ -215,12 +220,6 @@ defmodule Towerops.Devices.DeviceFirmwareHistoryTest do
# Helper function to create a test SNMP device
defp insert_snmp_device do
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Devices
alias Towerops.Sites
# Create organization and site
user = user_fixture()
organization = organization_fixture(user.id)

View file

@ -2,17 +2,18 @@ defmodule Towerops.EquipmentTest do
use Towerops.DataCase
use ExUnitProperties
import Towerops.AccountsFixtures
alias Device, as: DeviceSchema
alias Towerops.Alerts.Alert
alias Towerops.Devices
alias Towerops.Devices.Device
alias Towerops.Devices.Event
alias Towerops.Monitoring
doctest Devices
describe "device" do
import Towerops.AccountsFixtures
alias Device, as: DeviceSchema
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -563,8 +564,6 @@ defmodule Towerops.EquipmentTest do
end
describe "SNMP configuration inheritance" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -704,10 +703,6 @@ defmodule Towerops.EquipmentTest do
end
describe "events" do
import Towerops.AccountsFixtures
alias Towerops.Devices.Event
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -801,8 +796,6 @@ defmodule Towerops.EquipmentTest do
end
describe "additional coverage tests" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -997,8 +990,6 @@ defmodule Towerops.EquipmentTest do
end
describe "automatic rediscovery on device updates" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1230,8 +1221,6 @@ defmodule Towerops.EquipmentTest do
end
describe "property-based tests" do
import Towerops.AccountsFixtures
property "creating devices with valid IPs always succeeds" do
# Create fixtures once outside the property check
user = user_fixture()
@ -1261,7 +1250,7 @@ defmodule Towerops.EquipmentTest do
}
# Use bypass_limits since property tests may create > 10 devices
assert {:ok, %Device{} = device} = Devices.create_device(attrs, bypass_limits: true)
assert {:ok, %DeviceSchema{} = device} = Devices.create_device(attrs, bypass_limits: true)
assert device.ip_address == ip_address
assert device.status == :unknown
end
@ -1385,8 +1374,6 @@ defmodule Towerops.EquipmentTest do
end
describe "device creation with subscription limits" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1537,8 +1524,6 @@ defmodule Towerops.EquipmentTest do
end
describe "GDPR data access - list_devices_for_organizations/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, org1} = Towerops.Organizations.create_organization(%{name: "Org 1"}, user.id)
@ -1695,8 +1680,6 @@ defmodule Towerops.EquipmentTest do
end
describe "PubSub broadcasts" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1807,8 +1790,6 @@ defmodule Towerops.EquipmentTest do
end
describe "get_device_by_ip/3" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1857,8 +1838,6 @@ defmodule Towerops.EquipmentTest do
end
describe "get_mikrotik_config/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1925,8 +1904,6 @@ defmodule Towerops.EquipmentTest do
end
describe "get_snmpv3_config/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -1995,8 +1972,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_site_community_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2051,8 +2026,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_organization_community_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
@ -2111,8 +2084,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_site_mikrotik_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2151,8 +2122,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_organization_mikrotik_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2193,8 +2162,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_site_snmpv3_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2243,8 +2210,6 @@ defmodule Towerops.EquipmentTest do
end
describe "propagate_organization_snmpv3_change/2" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2297,8 +2262,6 @@ defmodule Towerops.EquipmentTest do
end
describe "resolve_snmp_community/1" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2360,8 +2323,6 @@ defmodule Towerops.EquipmentTest do
end
describe "list_mikrotik_devices_with_api/0" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
@ -2407,10 +2368,6 @@ defmodule Towerops.EquipmentTest do
end
describe "auto ICMP ping check" do
import Towerops.AccountsFixtures
alias Towerops.Monitoring
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

View file

@ -1,12 +1,12 @@
defmodule Towerops.SitesTest do
use Towerops.DataCase
import Towerops.AccountsFixtures
alias Towerops.Sites
alias Towerops.Sites.Site
describe "sites" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

View file

@ -11,8 +11,13 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
alias Towerops.Repo
alias Towerops.Snmp
alias Towerops.Snmp.Discovery
alias Towerops.Snmp.EntityPhysical
alias Towerops.Snmp.Interface
alias Towerops.Snmp.Mempool
alias Towerops.Snmp.PrinterSupply
alias Towerops.Snmp.Processor
alias Towerops.Snmp.Storage
alias Towerops.Snmp.Transceiver
setup do
user = user_fixture()
@ -122,8 +127,6 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
end
describe "sync_mempools/2" do
alias Towerops.Snmp.Mempool
test "inserts new mempools and removes missing ones", %{snmp_device: snmp_device} do
now = Towerops.Time.now()
@ -168,8 +171,6 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
end
describe "sync_transceivers/2" do
alias Towerops.Snmp.Transceiver
test "inserts, updates, and removes transceivers", %{snmp_device: snmp_device} do
{:ok, [_, _]} =
Discovery.sync_transceivers(snmp_device, [
@ -191,8 +192,6 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
end
describe "sync_printer_supplies/2" do
alias Towerops.Snmp.PrinterSupply
test "inserts, updates, and removes supplies", %{snmp_device: snmp_device} do
Discovery.sync_printer_supplies(snmp_device, [
%{
@ -235,8 +234,6 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
end
describe "sync_entity_physical/2" do
alias Towerops.Snmp.EntityPhysical
test "inserts and removes entities", %{snmp_device: snmp_device} do
Discovery.sync_entity_physical(snmp_device, [
%{
@ -272,8 +269,6 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
end
describe "save_neighbors/2 + save_arp_entries/3" do
alias Towerops.Snmp.Interface
setup %{snmp_device: snmp_device} do
{:ok, iface} =
Repo.insert(%Interface{
@ -326,7 +321,7 @@ defmodule Towerops.Snmp.DiscoverySyncTest do
device = Repo.preload(snmp_device, :device).device
{:ok, _interface} =
Repo.insert(%Towerops.Snmp.Interface{
Repo.insert(%Interface{
snmp_device_id: snmp_device.id,
if_index: 1,
if_name: "eth0",

View file

@ -10,7 +10,9 @@ defmodule Towerops.Snmp.DiscoveryTest do
alias Towerops.Snmp.Device
alias Towerops.Snmp.Discovery
alias Towerops.Snmp.Interface
alias Towerops.Snmp.InterfaceStat
alias Towerops.Snmp.Sensor
alias Towerops.Snmp.SensorReading
alias Towerops.Snmp.SnmpMock
doctest Discovery
@ -521,8 +523,6 @@ defmodule Towerops.Snmp.DiscoveryTest do
organization: organization,
site: site
} do
alias Towerops.Snmp.SensorReading
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Device With Sensors",
@ -673,8 +673,6 @@ defmodule Towerops.Snmp.DiscoveryTest do
organization: organization,
site: site
} do
alias Towerops.Snmp.InterfaceStat
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Device With Interfaces",

View file

@ -117,6 +117,8 @@ defmodule Towerops.Snmp.MibTranslatorTest do
# Cleanup
Application.delete_env(:towerops, :mib_dirs)
assert true
end
test "handles nonexistent directory" do

View file

@ -66,6 +66,7 @@ defmodule Towerops.Snmp.MibValidationTest do
]
validate_oids(mib_file, validations)
assert true
end
test "sensor OIDs match ENTITY-SENSOR-MIB" do
@ -79,6 +80,7 @@ defmodule Towerops.Snmp.MibValidationTest do
]
validate_oids(mib_file, validations)
assert true
end
end
@ -94,6 +96,7 @@ defmodule Towerops.Snmp.MibValidationTest do
]
validate_oids(mib_file, validations)
assert true
end
end
@ -111,6 +114,7 @@ defmodule Towerops.Snmp.MibValidationTest do
]
validate_oids(mib_file, validations)
assert true
end
test "UCD-SNMP OIDs match UCD-SNMP-MIB" do
@ -131,6 +135,7 @@ defmodule Towerops.Snmp.MibValidationTest do
end)
validate_oids(mib_file, validations)
assert true
end
end

View file

@ -80,9 +80,8 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
]
describe "list_vendors/0" do
test "returns all registered vendor modules" do
test "returns vendor modules A-I" do
vendors = Registry.list_vendors()
assert is_list(vendors)
assert Epmp in vendors
assert Airos in vendors
@ -95,6 +94,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Unifi in vendors
assert Ligoos in vendors
assert Cnpilot in vendors
end
test "returns vendor modules J-Z" do
vendors = Registry.list_vendors()
assert Saf in vendors
assert Ruckus in vendors
assert Dragonwave in vendors
@ -106,22 +109,29 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Proxim in vendors
assert Stellar in vendors
assert Xirrus in vendors
assert Ceragon in vendors
assert Cmm in vendors
end
test "returns vendor modules D-F" do
vendors = Registry.list_vendors()
assert Pepwave in vendors
assert Tranzeo in vendors
assert ZyxelWlc in vendors
assert Dlinkap in vendors
assert Fortiap in vendors
assert MoxaAwk in vendors
assert Ceragon in vendors
assert Cmm in vendors
assert Pepwave in vendors
assert Tranzeo in vendors
assert Fortiwlc in vendors
assert Meraki in vendors
end
test "returns vendor modules G-M" do
vendors = Registry.list_vendors()
assert Apc in vendors
assert Arista in vendors
assert Calix in vendors
assert Adtran in vendors
assert AlliedTelesis in vendors
# New auto-generated vendors
assert Cisco in vendors
assert Juniper in vendors
assert Hp in vendors
@ -129,6 +139,11 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Fortinet in vendors
assert Huawei in vendors
assert Nokia in vendors
assert Mikrotik in vendors
end
test "returns vendor modules N-Z" do
vendors = Registry.list_vendors()
assert Omnitron in vendors
assert Paloalto in vendors
assert F5 in vendors
@ -145,17 +160,14 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert Vertiv in vendors
assert Riverbed in vendors
assert Sonicwall in vendors
assert Mikrotik in vendors
assert Adva in vendors
end
end
describe "list_profile_names/0" do
test "returns all profile names from all vendors" do
test "returns profile names A-C" do
names = Registry.list_profile_names()
assert is_list(names)
# Check for some known profile names
assert "epmp" in names
assert "airos" in names
assert "airos-af" in names
@ -171,6 +183,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert "saf" in names
assert "ruckuswireless" in names
assert "horizon-compact" in names
end
test "returns profile names D-M" do
names = Registry.list_profile_names()
assert "ray" in names
assert "extendair" in names
assert "alfo80hd" in names
@ -187,6 +203,10 @@ defmodule Towerops.Snmp.Profiles.Vendors.RegistryTest do
assert "cmm" in names
assert "pepwave" in names
assert "tranzeo" in names
end
test "returns profile names N-Z" do
names = Registry.list_profile_names()
assert "fortiwlc" in names
assert "merakimr" in names
assert "merakims" in names

View file

@ -127,7 +127,7 @@ defmodule Towerops.Snmp.WirelessClientsTest do
# Skipped: WirelessClientReading.device_id references snmp_devices, not devices,
# which makes a minimal fixture awkward. The empty-list path covers the function head we care about.
_ = {device, org_id}
:ok
assert true
end
end

View file

@ -1,6 +1,7 @@
defmodule Towerops.SnmpTest do
use Towerops.DataCase
import Mox
import Towerops.AccountsFixtures
alias Towerops.Snmp
@ -56,8 +57,6 @@ defmodule Towerops.SnmpTest do
end
describe "test_connection/4" do
import Mox
setup :verify_on_exit!
test "delegates to Client.test_connection" do

View file

@ -1,11 +1,15 @@
defmodule Towerops.TopologyTest do
use Towerops.DataCase
import Mox
import Towerops.AccountsFixtures
alias Towerops.Snmp.Device
alias Towerops.Snmp.Interface
alias Towerops.Snmp.IpAddress
alias Towerops.Snmp.Sensor
alias Towerops.Snmp.SnmpMock
alias Towerops.Snmp.WirelessClient
alias Towerops.Topology
alias Towerops.Topology.DeviceLink
@ -1418,8 +1422,6 @@ defmodule Towerops.TopologyTest do
end
describe "compute_wireless_stats/1" do
alias Towerops.Snmp.WirelessClient
test "returns empty map when device_ids list is empty" do
assert Topology.compute_wireless_stats([]) == %{}
end
@ -1505,8 +1507,6 @@ defmodule Towerops.TopologyTest do
end
describe "compute_rf_link_stats/1" do
alias Towerops.Snmp.Sensor
test "returns empty map when device_ids list is empty" do
assert Topology.compute_rf_link_stats([]) == %{}
end
@ -1667,10 +1667,6 @@ defmodule Towerops.TopologyTest do
end
describe "discover_lldp_neighbors/1" do
import Mox
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
test "returns :device_not_found for missing device" do
@ -2223,8 +2219,6 @@ defmodule Towerops.TopologyTest do
end
describe "compute_rf_link_stats/1 - edge enrichment via get_topology_for_map" do
alias Towerops.Snmp.Sensor
test "edges receive worse-of signal_health from source/target SNR readings",
%{router: router, switch: switch, router_iface: iface, router_snmp: router_snmp, organization: org} do
switch_snmp =
@ -2280,10 +2274,6 @@ defmodule Towerops.TopologyTest do
end
describe "discover_lldp_neighbors/1 - error path" do
import Mox
alias Towerops.Snmp.SnmpMock
setup :verify_on_exit!
test "propagates SNMP errors from the LLDP discovery layer", %{router: router} do

View file

@ -3,6 +3,7 @@ defmodule Towerops.Workers.AlertDigestWorkerTest do
use Oban.Testing, repo: Towerops.Repo
import Towerops.AccountsFixtures
import Towerops.DevicesFixtures
alias Towerops.Alerts.NotificationDigest
alias Towerops.Workers.AlertDigestWorker
@ -69,8 +70,6 @@ defmodule Towerops.Workers.AlertDigestWorkerTest do
end
test "delivers digest when real alerts exist for the user" do
import Towerops.DevicesFixtures
user = user_fixture()
device = device_fixture()

View file

@ -153,7 +153,7 @@ defmodule Towerops.Workers.DevicePollerTransceiverTest do
now = DateTime.utc_now()
# Should not crash
:ok = DevicePollerWorker.poll_transceivers(transceivers, client_opts, now)
assert :ok = DevicePollerWorker.poll_transceivers(transceivers, client_opts, now)
# May or may not create a reading with nil values, depending on implementation
# At minimum, should not crash

View file

@ -1,6 +1,7 @@
defmodule Towerops.Workers.DiscoveryWorkerTest do
use Towerops.DataCase, async: true
import ExUnit.CaptureLog
import Mox
import Towerops.AccountsFixtures
@ -186,8 +187,6 @@ defmodule Towerops.Workers.DiscoveryWorkerTest do
end
test "logs error when discovery fails", %{device: device} do
import ExUnit.CaptureLog
stub(SnmpMock, :get, fn _target, _oid, _opts ->
{:error, :network_unreachable}
end)
@ -415,8 +414,6 @@ defmodule Towerops.Workers.DiscoveryWorkerTest do
# Pass a non-string device_id so get_device_with_details raises a
# Postgrex/Ecto error inside the rescue block. The worker should log
# and return :discard rather than re-raising.
import ExUnit.CaptureLog
log =
capture_log(fn ->
assert :discard =

View file

@ -146,6 +146,7 @@ defmodule Towerops.Workers.JobHealthCheckWorkerTest do
test "recovers missing poller jobs (DEPRECATED: per-check jobs now)" do
# This test is deprecated - SNMP polling is now handled by per-check
# CheckExecutorWorker jobs, not device-level DevicePollerWorker jobs
assert true
end
test "does not create duplicate jobs if they already exist" do

View file

@ -10,6 +10,7 @@ defmodule ToweropsWeb.AgentChannelExtraCoverageTest do
"""
use Towerops.DataCase, async: false
import Ecto.Query, only: [from: 2]
import Phoenix.ChannelTest
alias Towerops.AccountsFixtures
@ -476,8 +477,6 @@ defmodule ToweropsWeb.AgentChannelExtraCoverageTest do
describe "device with explicitly disabled assignment" do
test "result for device whose assignment is disabled is treated as reassigned",
%{socket: socket, agent_token: agent_token, device: device} do
import Ecto.Query, only: [from: 2]
device = discover_device!(device)
# Disable the existing assignment (it was created in the parent setup as enabled)

View file

@ -1,6 +1,7 @@
defmodule ToweropsWeb.AgentChannelTest do
use Towerops.DataCase, async: false
import Ecto.Query
import Phoenix.ChannelTest
alias Towerops.AccountsFixtures
@ -318,8 +319,6 @@ defmodule ToweropsWeb.AgentChannelTest do
describe "handle_in monitoring_check" do
test "valid monitoring check creates check record", %{socket: socket, device: device} do
import Ecto.Query
check = build_monitoring_check(device.id, "success")
payload = encode_payload(check)

View file

@ -6,6 +6,8 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
alias Towerops.MobileSessions
alias Towerops.Organizations
alias Towerops.Snmp.Device
alias Towerops.Snmp.Interface
alias ToweropsWeb.Api.MobileController
setup do
user = user_fixture()
@ -87,8 +89,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
end
describe "format_alert_status/1" do
alias ToweropsWeb.Api.MobileController
test "resolved wins over acknowledged" do
alert = %{resolved_at: ~U[2026-01-01 00:00:00Z], acknowledged_at: ~U[2026-01-01 00:00:00Z]}
assert "resolved" == MobileController.format_alert_status(alert)
@ -106,8 +106,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
end
describe "timeticks_to_string/1" do
alias ToweropsWeb.Api.MobileController
test "days when over a day" do
timeticks = (2 * 86_400 + 3 * 3600) * 100
assert "2d 3h" == MobileController.timeticks_to_string(timeticks)
@ -143,8 +141,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
end
test "get_device handler 404s on unknown id (direct call)" do
alias ToweropsWeb.Api.MobileController
user = user_fixture()
conn = Plug.Conn.assign(build_conn(), :current_user, user)
conn = MobileController.get_device(conn, %{"id" => Ecto.UUID.generate()})
@ -152,8 +148,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
end
test "get_device handler returns 403 for cross-org device (direct call)" do
alias ToweropsWeb.Api.MobileController
user = user_fixture()
other_user = user_fixture()
{:ok, other_org} = Organizations.create_organization(%{name: "Other2"}, other_user.id)
@ -174,8 +168,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
test "get_device direct call formats sensors and interfaces from SNMP device",
%{user: user, organization: org} do
alias ToweropsWeb.Api.MobileController
{:ok, site} =
Towerops.Sites.create_site(%{name: "Detail Site", organization_id: org.id})
@ -211,9 +203,6 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
test "get_device direct call maps interfaces when SNMP device has them",
%{user: user, organization: org} do
alias Towerops.Snmp.Interface
alias ToweropsWeb.Api.MobileController
{:ok, site} =
Towerops.Sites.create_site(%{name: "Iface Site", organization_id: org.id})

View file

@ -3,6 +3,7 @@ defmodule ToweropsWeb.Api.V1.AgentReleaseWebhookControllerTest do
use Oban.Testing, repo: Towerops.Repo
alias Towerops.Workers.AgentReleaseWebhookWorker
alias ToweropsWeb.Api.V1.AgentReleaseWebhookController
setup do
conn =
@ -62,8 +63,6 @@ defmodule ToweropsWeb.Api.V1.AgentReleaseWebhookControllerTest do
end
describe "check_timestamp/1" do
alias ToweropsWeb.Api.V1.AgentReleaseWebhookController
test "valid recent timestamp is :ok" do
now = System.system_time(:second)
assert :ok == AgentReleaseWebhookController.check_timestamp(to_string(now))
@ -91,8 +90,6 @@ defmodule ToweropsWeb.Api.V1.AgentReleaseWebhookControllerTest do
end
describe "parse_signature_header/1" do
alias ToweropsWeb.Api.V1.AgentReleaseWebhookController
test "parses standard header" do
header = "t=1234567890,v1=abcdef123"

View file

@ -6,6 +6,7 @@ defmodule ToweropsWeb.GraphQL.Resolvers.CheckTest do
alias Towerops.ApiTokens
alias Towerops.Monitoring
alias ToweropsWeb.GraphQL.Resolvers.Check, as: Resolver
setup do
user = user_fixture()
@ -274,8 +275,6 @@ defmodule ToweropsWeb.GraphQL.Resolvers.CheckTest do
end
describe "resolver fallback clauses (no organization in context)" do
alias ToweropsWeb.GraphQL.Resolvers.Check, as: Resolver
test "list/3 returns auth error" do
assert {:error, _} = Resolver.list(nil, %{}, %{context: %{}})
end

View file

@ -2,6 +2,7 @@ defmodule ToweropsWeb.Admin.OrgLive.IndexTest do
use ToweropsWeb.ConnCase, async: true
import Phoenix.LiveViewTest
import Towerops.OrganizationsFixtures
alias Towerops.Organizations.Organization
@ -204,8 +205,6 @@ defmodule ToweropsWeb.Admin.OrgLive.IndexTest do
end
test "shows confirmation dialog before saving", %{conn: conn, user: user} do
import Towerops.OrganizationsFixtures
_org1 =
organization_fixture(user.id, %{
subscription_status: "active",

View file

@ -5,6 +5,8 @@ defmodule ToweropsWeb.CoverageLive.IndexTest do
import Towerops.CoveragesFixtures
import Towerops.OrganizationsFixtures
alias ToweropsWeb.CoverageLive.Index, as: Idx
setup :register_and_log_in_user
setup %{conn: conn, user: user} do
@ -62,8 +64,6 @@ defmodule ToweropsWeb.CoverageLive.IndexTest do
end
describe "status_badge_class/1" do
alias ToweropsWeb.CoverageLive.Index, as: Idx
test "maps known statuses to themed classes" do
assert Idx.status_badge_class("ready") =~ "bg-green"
assert Idx.status_badge_class("failed") =~ "bg-red"

View file

@ -6,6 +6,7 @@ defmodule ToweropsWeb.CoverageLive.MapTest do
import Towerops.OrganizationsFixtures
alias Towerops.Coverages
alias ToweropsWeb.CoverageLive.Map, as: M
setup :register_and_log_in_user
@ -111,8 +112,6 @@ defmodule ToweropsWeb.CoverageLive.MapTest do
end
describe "format helpers" do
alias ToweropsWeb.CoverageLive.Map, as: M
test "fmt_distance converts to mi or km" do
assert M.fmt_distance(1609.344, "ft") == "1.00 mi"
assert M.fmt_distance(1000, "m") == "1.00 km"

View file

@ -6,6 +6,7 @@ defmodule ToweropsWeb.CoverageLive.ShowTest do
import Towerops.OrganizationsFixtures
alias Towerops.Coverages
alias ToweropsWeb.CoverageLive.Show
setup :register_and_log_in_user
@ -128,8 +129,6 @@ defmodule ToweropsWeb.CoverageLive.ShowTest do
end
describe "coverage_lat / coverage_lon helpers" do
alias ToweropsWeb.CoverageLive.Show
test "coverage_lat returns the coverage's latitude when present", %{org: org, site: site} do
cov = coverage_fixture(org.id, site.id)
valid? = is_number(Show.coverage_lat(cov)) or Show.coverage_lat(cov) == nil
@ -144,8 +143,6 @@ defmodule ToweropsWeb.CoverageLive.ShowTest do
end
describe "format helpers" do
alias ToweropsWeb.CoverageLive.Show
test "format_ft converts metres to feet" do
assert Show.format_ft(30.0) == "98 ft"
assert Show.format_ft(nil) == ""

View file

@ -4,6 +4,7 @@ defmodule ToweropsWeb.DeviceLive.FormTest do
import Phoenix.LiveViewTest
alias Towerops.Snmp.Device
alias Towerops.Snmp.Device, as: SnmpDevice
setup :register_and_log_in_user
@ -77,8 +78,8 @@ defmodule ToweropsWeb.DeviceLive.FormTest do
# Create SNMP device with MikroTik manufacturer (using Towerops.Snmp.Device schema)
_snmp_device =
%Device{}
|> Device.changeset(%{
%SnmpDevice{}
|> SnmpDevice.changeset(%{
device_id: device.id,
manufacturer: "MikroTik",
sys_descr: "RouterOS 7.8",
@ -104,8 +105,6 @@ defmodule ToweropsWeb.DeviceLive.FormTest do
site: site,
organization: organization
} do
alias Device, as: SnmpDevice
# Create a device with non-MikroTik SNMP discovery data
{:ok, device} =
Towerops.Devices.create_device(%{

Some files were not shown because too many files have changed in this diff Show more