prop/test/microwaveprop/terrain/elevation_client_test.exs

159 lines
4.8 KiB
Elixir

defmodule Microwaveprop.Terrain.ElevationClientTest do
use ExUnit.Case, async: true
alias Microwaveprop.Terrain.ElevationClient
describe "sample_path/5" do
test "generates N+1 evenly spaced points" do
points = ElevationClient.sample_path(32.9, -97.0, 30.3, -97.7, 4)
assert length(points) == 5
first = hd(points)
last = List.last(points)
assert_in_delta first.lat, 32.9, 0.001
assert_in_delta first.lon, -97.0, 0.001
assert_in_delta first.d, 0.0, 0.001
assert_in_delta last.lat, 30.3, 0.001
assert_in_delta last.lon, -97.7, 0.001
assert_in_delta last.d, 1.0, 0.001
end
test "midpoint is correct" do
points = ElevationClient.sample_path(0.0, 0.0, 10.0, 10.0, 2)
mid = Enum.at(points, 1)
assert_in_delta mid.lat, 5.0, 0.001
assert_in_delta mid.lon, 5.0, 0.001
assert_in_delta mid.d, 0.5, 0.001
end
end
describe "fetch_elevation_profile/5" do
test "returns profile with elevations from Open-Meteo" do
Req.Test.stub(ElevationClient, fn conn ->
Req.Test.json(conn, %{"elevation" => [200.0, 250.0, 180.0]})
end)
assert {:ok, profile} =
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
assert length(profile) == 3
first = hd(profile)
assert Map.has_key?(first, :lat)
assert Map.has_key?(first, :lon)
assert Map.has_key?(first, :elev)
assert Map.has_key?(first, :dist_km)
assert first.elev == 200.0
assert_in_delta first.dist_km, 0.0, 0.001
end
test "batches requests when more than 100 points" do
call_count = :counters.new(1, [:atomics])
Req.Test.stub(ElevationClient, fn conn ->
:counters.add(call_count, 1, 1)
# Return 101 elevations for first batch, or however many requested
params = Plug.Conn.fetch_query_params(conn).query_params
lat_count = params["latitude"] |> String.split(",") |> length()
elevations = List.duplicate(100.0, lat_count)
Req.Test.json(conn, %{"elevation" => elevations})
end)
assert {:ok, profile} =
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 150)
# 151 points → 2 batches (100 + 51)
assert length(profile) == 151
assert :counters.get(call_count, 1) == 2
end
test "falls back to Open-Topo-Data on Open-Meteo failure" do
call_count = :counters.new(1, [:atomics])
Req.Test.stub(ElevationClient, fn conn ->
:counters.add(call_count, 1, 1)
current = :counters.get(call_count, 1)
if current == 1 do
# First call (Open-Meteo) fails
Plug.Conn.send_resp(conn, 500, "error")
else
# Second call (Open-Topo-Data) succeeds
Req.Test.json(conn, %{
"status" => "OK",
"results" => [
%{"elevation" => 100.0},
%{"elevation" => 150.0},
%{"elevation" => 120.0}
]
})
end
end)
assert {:ok, profile} =
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
assert length(profile) == 3
assert hd(profile).elev == 100.0
end
test "returns error when both APIs fail" do
Req.Test.stub(ElevationClient, fn conn ->
Plug.Conn.send_resp(conn, 500, "error")
end)
assert {:error, _reason} =
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
end
end
end
defmodule Microwaveprop.Terrain.ElevationClientSrtmTest do
use ExUnit.Case, async: false
alias Microwaveprop.Terrain.ElevationClient
@tiles_dir Path.expand("~/srtm/tiles")
@has_tiles File.exists?(Path.join(@tiles_dir, "N32W097.hgt"))
setup do
prev = Application.get_env(:microwaveprop, :srtm_tiles_dir)
on_exit(fn ->
if prev,
do: Application.put_env(:microwaveprop, :srtm_tiles_dir, prev),
else: Application.delete_env(:microwaveprop, :srtm_tiles_dir)
end)
:ok
end
describe "SRTM integration" do
@tag :srtm
@tag skip: !@has_tiles && "SRTM tiles not available"
test "uses local SRTM tiles when configured" do
Application.put_env(:microwaveprop, :srtm_tiles_dir, @tiles_dir)
assert {:ok, profile} =
ElevationClient.fetch_elevation_profile(32.78, -96.8, 32.5, -96.5, 4)
assert length(profile) == 5
assert hd(profile).elev > 0
end
@tag :srtm
test "returns zero elevation when SRTM tiles missing (no API fallback)" do
Application.put_env(:microwaveprop, :srtm_tiles_dir, "/nonexistent/tiles")
assert {:ok, profile} =
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
assert length(profile) == 3
assert Enum.all?(profile, fn p -> p.elev == 0 end)
end
end
end