Adds a "Radio location" section to the coverage form with an embedded
Leaflet map. The marker starts at the parent site's coordinates and
the user can drag it to the actual radio location. A "Use site
location" button clears the override.
- New CoverageLocationPicker hook in assets/js/app.ts: creates the
map, draggable marker, and pushes "location_picked" {lat, lon} on
dragend. Re-creates the marker when the user picks a site that
initially had no coords.
- form.html.heex: location section with map div (phx-hook), readout
("lat, lon - override" if set), and reset button. Hidden inputs for
latitude_override / longitude_override so the form's params always
carry the chosen coordinates.
- form.ex: handle_event "location_picked" updates the override fields
in the changeset; "use_site_location" clears them. Helper functions
radio_marker_lat/lon walk the form values, falling back to the
selected site.
CI test gate fix:
- Worker test now requires GDAL CLI tools to write GeoTIFF + PNG.
test_helper.exs detects whether gdal_translate / gdaldem are on
PATH; when missing (CI's lean test image), :requires_gdal tagged
tests are excluded. Locally with brew install gdal they still run.
96 lines
3 KiB
Elixir
96 lines
3 KiB
Elixir
# Configure MIB directories BEFORE any NIF loading
|
|
# This must happen before ExUnit.start() to ensure ToweropsNative NIF
|
|
# can find vendor MIBs when it initializes
|
|
mib_dir = Application.app_dir(:towerops, "priv/mibs")
|
|
|
|
if File.dir?(mib_dir) do
|
|
# Get main directory plus all subdirectories for vendor MIBs
|
|
subdirs =
|
|
mib_dir
|
|
|> File.ls!()
|
|
|> Enum.map(&Path.join(mib_dir, &1))
|
|
|> Enum.filter(&File.dir?/1)
|
|
|
|
mib_dirs = [mib_dir | subdirs]
|
|
mibdirs_env = Enum.join(mib_dirs, ":")
|
|
|
|
# Set environment variables BEFORE initializing the NIF
|
|
System.put_env("MIBDIRS", mibdirs_env)
|
|
System.put_env("MIBS", "ALL")
|
|
|
|
# Add each directory to net-snmp's search path
|
|
Enum.each(mib_dirs, fn dir ->
|
|
case ToweropsNative.load_mib_directory(dir) do
|
|
:ok -> :ok
|
|
# Ignore errors in test environment
|
|
{:error, _reason} -> :ok
|
|
end
|
|
end)
|
|
|
|
# Explicitly initialize the MIB library with correct environment variables
|
|
case ToweropsNative.init_mib_library() do
|
|
result when result in ["initialized", "already_initialized"] ->
|
|
IO.puts("Test MIB library initialized: #{result}")
|
|
|
|
other ->
|
|
IO.puts("Warning: Unexpected MIB library init result: #{inspect(other)}")
|
|
end
|
|
end
|
|
|
|
# Load antenna pattern registry for tests that exercise the Coverages
|
|
# context (the schema validates antenna_slug against this in-memory registry).
|
|
Towerops.Coverages.Antenna.load_registry()
|
|
|
|
ExUnit.start()
|
|
|
|
# Skip the coverage-compute integration test when the GDAL CLI tools
|
|
# aren't installed (e.g. CI test gate runs in a lean image without
|
|
# gdal-bin). Locally we install GDAL via `brew install gdal` per
|
|
# CLAUDE.md, so the test runs there.
|
|
extra_excludes =
|
|
if System.find_executable("gdal_translate") && System.find_executable("gdaldem") do
|
|
[]
|
|
else
|
|
[:requires_gdal]
|
|
end
|
|
|
|
# Exclude tests by default
|
|
# Run specific tests with: mix test --only <tag>
|
|
ExUnit.configure(
|
|
exclude:
|
|
[
|
|
:integration,
|
|
# Tests making real network calls (ping, DNS, SSL)
|
|
:network,
|
|
# SnmpKit-specific exclusions
|
|
:performance,
|
|
:snmpv3,
|
|
:memory,
|
|
:shell_integration,
|
|
:optional,
|
|
:needs_simulator,
|
|
:manual,
|
|
:real_device,
|
|
:yecc_required
|
|
] ++ extra_excludes
|
|
)
|
|
|
|
# Define mocks for testing
|
|
Mox.defmock(Towerops.Monitoring.PingMock, for: Towerops.Monitoring.PingBehaviour)
|
|
Mox.defmock(Towerops.Snmp.PollerMock, for: Towerops.Snmp.PollerBehaviour)
|
|
Mox.defmock(Towerops.Snmp.SnmpMock, for: Towerops.Snmp.SnmpBehaviour)
|
|
Mox.defmock(Towerops.RateLimit.RedisClientMock, for: Towerops.RateLimit.RedisClient)
|
|
|
|
# Define stub module for SNMP mock (returns errors for all calls)
|
|
defmodule Towerops.Snmp.SnmpMockStub do
|
|
@moduledoc false
|
|
@behaviour Towerops.Snmp.SnmpBehaviour
|
|
|
|
def get(_target, _oid, _opts), do: {:error, :timeout}
|
|
def get_next(_target, _oid, _opts), do: {:error, :timeout}
|
|
def walk(_target, _oid, _opts), do: {:error, :timeout}
|
|
def get_bulk(_target, _oid, _opts), do: {:error, :timeout}
|
|
def get_multiple(_target, _oids, _opts), do: {:error, :timeout}
|
|
end
|
|
|
|
Ecto.Adapters.SQL.Sandbox.mode(Towerops.Repo, :manual)
|