defmodule Microwaveprop.Buildings.MsFootprintsTest do use ExUnit.Case, async: false use ExUnitProperties alias Microwaveprop.Buildings.MsFootprints describe "quadkey/3" do test "returns a string of length equal to zoom" do assert String.length(MsFootprints.quadkey(33.0, -97.0, 9)) == 9 assert String.length(MsFootprints.quadkey(33.0, -97.0, 5)) == 5 end test "returns the same quadkey for the same coordinates" do assert MsFootprints.quadkey(33.0, -97.0, 9) == MsFootprints.quadkey(33.0, -97.0, 9) end test "coordinates far apart produce different quadkeys" do q1 = MsFootprints.quadkey(33.0, -97.0, 9) q2 = MsFootprints.quadkey(40.0, -80.0, 9) refute q1 == q2 end end describe "quadkeys_for_bbox/2" do test "returns a list of quadkey strings" do bbox = %{"south" => 32.0, "north" => 34.0, "west" => -98.0, "east" => -96.0} keys = MsFootprints.quadkeys_for_bbox(bbox, 9) assert is_list(keys) assert keys != [] assert Enum.all?(keys, &is_binary/1) end test "each quadkey has the expected length" do bbox = %{"south" => 32.0, "north" => 34.0, "west" => -98.0, "east" => -96.0} keys = MsFootprints.quadkeys_for_bbox(bbox, 9) Enum.each(keys, fn k -> assert String.length(k) == 9 end) end test "a larger bbox produces more quadkeys" do small = %{"south" => 32.5, "north" => 33.0, "west" => -97.5, "east" => -96.5} large = %{"south" => 32.0, "north" => 34.0, "west" => -98.0, "east" => -96.0} assert length(MsFootprints.quadkeys_for_bbox(large, 9)) > length(MsFootprints.quadkeys_for_bbox(small, 9)) end end describe "cache_dir/0" do test "returns a string" do assert is_binary(MsFootprints.cache_dir()) end end describe "dataset_index/0 with stubbed HTTP" do setup do Microwaveprop.Cache.invalidate(:ms_footprints_index) prev = Application.get_env(:microwaveprop, :ms_footprints_http_get) on_exit(fn -> if prev, do: Application.put_env(:microwaveprop, :ms_footprints_http_get, prev), else: Application.delete_env(:microwaveprop, :ms_footprints_http_get) end) :ok end test "parses dataset-links CSV into quadkey -> URL map" do Application.put_env(:microwaveprop, :ms_footprints_http_get, fn _url, _opts -> {:ok, %{ status: 200, body: "RegionName,QuadKey,Url,Size,DatasetDate\nUnitedStates,023112330,https://example.com/tile1.csv.gz,1234,2026-01-01\nUnitedStates,023112331,https://example.com/tile2.csv.gz,5678,2026-01-01\nCanada,023112332,https://example.com/ca-tile.csv.gz,9999,2026-01-01\n" }} end) index = MsFootprints.dataset_index() assert index["023112330"] == "https://example.com/tile1.csv.gz" assert index["023112331"] == "https://example.com/tile2.csv.gz" refute Map.has_key?(index, "023112332") assert map_size(index) == 2 end test "handles empty CSV gracefully" do Application.put_env(:microwaveprop, :ms_footprints_http_get, fn _url, _opts -> {:ok, %{status: 200, body: "RegionName,QuadKey,Url,Size,DatasetDate\n"}} end) assert MsFootprints.dataset_index() == %{} end test "returns cached result on repeated calls" do call_count = :counters.new(1, [:atomics]) Application.put_env(:microwaveprop, :ms_footprints_http_get, fn _url, _opts -> :counters.add(call_count, 1, 1) {:ok, %{ status: 200, body: "RegionName,QuadKey,Url,Size,DatasetDate\nUnitedStates,qk,http://example.com/t,1,2026-01-01\n" }} end) MsFootprints.dataset_index() MsFootprints.dataset_index() assert :counters.get(call_count, 1) == 1 end end describe "download_tile/1 with stubbed HTTP" do setup do prev = Application.get_env(:microwaveprop, :buildings_cache_dir) tmp = Path.join(System.tmp_dir!(), "ms_footprints_#{System.unique_integer([:positive])}") Application.put_env(:microwaveprop, :buildings_cache_dir, tmp) Microwaveprop.Cache.invalidate(:ms_footprints_index) prev_http = Application.get_env(:microwaveprop, :ms_footprints_http_get) on_exit(fn -> File.rm_rf(tmp) if prev, do: Application.put_env(:microwaveprop, :buildings_cache_dir, prev), else: Application.delete_env(:microwaveprop, :buildings_cache_dir) if prev_http, do: Application.put_env(:microwaveprop, :ms_footprints_http_get, prev_http), else: Application.delete_env(:microwaveprop, :ms_footprints_http_get) end) :ok end test "downloads tile when quadkey exists in index" do Application.put_env(:microwaveprop, :ms_footprints_http_get, fn url, _opts -> if String.contains?(url, "dataset-links.csv") do {:ok, %{ status: 200, body: "RegionName,QuadKey,Url,Size,DatasetDate\nUnitedStates,023112330,http://example.com/tile,1234,2026-01-01\n" }} else {:ok, %{status: 200, body: "fake-gzip-data"}} end end) assert {:ok, path} = MsFootprints.download_tile("023112330") assert File.exists?(path) assert File.read!(path) == "fake-gzip-data" end test "returns error for unknown quadkey" do Application.put_env(:microwaveprop, :ms_footprints_http_get, fn _url, _opts -> {:ok, %{status: 200, body: "RegionName,QuadKey,Url,Size,DatasetDate\n"}} end) assert {:error, :unknown_quadkey} = MsFootprints.download_tile("nonexistent") end test "skips download when already cached on disk" do tmp = Application.get_env(:microwaveprop, :buildings_cache_dir) File.mkdir_p!(tmp) File.write!(Path.join(tmp, "023112330.csv.gz"), "existing-data") assert {:ok, path} = MsFootprints.download_tile("023112330") assert File.read!(path) == "existing-data" end end end