Track what was actually built, and flag when it's stale
The .pmtiles files themselves carry no record of what upstream snapshot they came from — only a local mtime. Each successful build now writes a small sidecar (offline.pmtiles.meta.json: the Protomaps build date used; terrain.pmtiles.meta.json: the Mapterhorn source names/md5sums) recording that, surfaced in the panel next to the existing size/updated line. New GET .../check-update routes compare the recorded build against the live upstream index/manifest — true/false/null, null meaning "don't know" (no local build yet, or the check itself failed) rather than a false "no". Kept separate from the plain status endpoints (which map.js's own topbar dot hits on every page load) since this one reaches an external service; only called once when the Offline data panel is actually opened. Also: flag emoji next to each country in the picker (derived from the ISO code via Unicode regional indicators, no image assets needed).
This commit is contained in:
1 parent
53c9a82af2
commit
4a3862189f
7 files changed
+261
-15
No files matched your search
@@ -387,6 +387,11 @@ class AppTest < Minitest::Test
|
||||
assert_equal({ "state" => "idle" }, json_body(request.get("/api/terrain-tiles/build-status")))
|
||||
end
|
||||
|
||||
def test_check_update_is_null_before_any_build
|
||||
assert_nil json_body(request.get("/api/offline-tiles/check-update"))["update_available"]
|
||||
assert_nil json_body(request.get("/api/terrain-tiles/check-update"))["update_available"]
|
||||
end
|
||||
|
||||
def test_offline_tiles_build_is_409_with_no_selection
|
||||
response = request.post("/api/offline-tiles/build", **logged_in_env)
|
||||
assert_equal 409, response.status
|
||||
|
||||
@@ -69,11 +69,11 @@ class PmtilesBuilderTest < Minitest::Test
|
||||
|
||||
# chunk-a covers Slovakia's real extent, chunk-b covers Iceland's —
|
||||
# Spain falls in neither, for the "no chunk intersects" case.
|
||||
def fake_manifest
|
||||
def fake_manifest(chunk_a_md5: "md5-a")
|
||||
[
|
||||
{ "name" => "planet.pmtiles", "url" => "https://example.com/planet.pmtiles", "min_lon" => -180.0, "min_lat" => -85.0, "max_lon" => 180.0, "max_lat" => 85.0, "min_zoom" => 0, "max_zoom" => 12 },
|
||||
{ "name" => "chunk-a.pmtiles", "url" => "https://example.com/chunk-a.pmtiles", "min_lon" => 16.0, "min_lat" => 47.0, "max_lon" => 23.0, "max_lat" => 50.0, "min_zoom" => 13, "max_zoom" => 15 },
|
||||
{ "name" => "chunk-b.pmtiles", "url" => "https://example.com/chunk-b.pmtiles", "min_lon" => -25.0, "min_lat" => 63.0, "max_lon" => -13.0, "max_lat" => 67.0, "min_zoom" => 13, "max_zoom" => 15 },
|
||||
{ "name" => "planet.pmtiles", "url" => "https://example.com/planet.pmtiles", "md5sum" => "md5-planet", "min_lon" => -180.0, "min_lat" => -85.0, "max_lon" => 180.0, "max_lat" => 85.0, "min_zoom" => 0, "max_zoom" => 12 },
|
||||
{ "name" => "chunk-a.pmtiles", "url" => "https://example.com/chunk-a.pmtiles", "md5sum" => chunk_a_md5, "min_lon" => 16.0, "min_lat" => 47.0, "max_lon" => 23.0, "max_lat" => 50.0, "min_zoom" => 13, "max_zoom" => 15 },
|
||||
{ "name" => "chunk-b.pmtiles", "url" => "https://example.com/chunk-b.pmtiles", "md5sum" => "md5-b", "min_lon" => -25.0, "min_lat" => 63.0, "max_lon" => -13.0, "max_lat" => 67.0, "min_zoom" => 13, "max_zoom" => 15 },
|
||||
]
|
||||
end
|
||||
|
||||
@@ -121,6 +121,40 @@ class PmtilesBuilderTest < Minitest::Test
|
||||
refute_path_exists File.join(@data_dir, "offline.pmtiles.tmp")
|
||||
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
|
||||
assert_equal ["extract https://example.com/fake.pmtiles"], log_lines
|
||||
assert_equal({ "source" => "fake.pmtiles" }, Maptool::PmtilesBuilder.offline_meta(@data_dir))
|
||||
end
|
||||
|
||||
def test_offline_meta_is_nil_before_any_build
|
||||
assert_nil Maptool::PmtilesBuilder.offline_meta(@data_dir)
|
||||
end
|
||||
|
||||
def test_offline_update_available_is_nil_before_any_build
|
||||
assert_nil Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_build_url: -> { "https://example.com/fake.pmtiles" })
|
||||
end
|
||||
|
||||
def test_offline_update_available_is_false_when_unchanged
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, fetch_build_url: -> { "https://example.com/fake.pmtiles" }).join
|
||||
|
||||
refute Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_build_url: -> { "https://example.com/fake.pmtiles" })
|
||||
end
|
||||
|
||||
def test_offline_update_available_is_true_when_a_newer_build_exists
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, fetch_build_url: -> { "https://example.com/old.pmtiles" }).join
|
||||
|
||||
assert Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_build_url: -> { "https://example.com/new.pmtiles" })
|
||||
end
|
||||
|
||||
def test_offline_update_available_is_nil_when_the_index_is_unreachable
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, fetch_build_url: -> { "https://example.com/fake.pmtiles" }).join
|
||||
|
||||
fetch_build_url = -> { raise Maptool::PmtilesBuilder::BuildError, "could not reach index" }
|
||||
assert_nil Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_build_url: fetch_build_url)
|
||||
end
|
||||
|
||||
def test_build_offline_reports_an_error_status_on_non_zero_exit
|
||||
@@ -198,6 +232,43 @@ class PmtilesBuilderTest < Minitest::Test
|
||||
merged = log_lines.select { |line| line.start_with?("merge ") }
|
||||
assert_equal ["extract https://example.com/planet.pmtiles", "extract https://example.com/chunk-a.pmtiles"], extracted
|
||||
assert_equal 1, merged.size
|
||||
assert_equal(
|
||||
{ "sources" => [{ "name" => "planet.pmtiles", "md5sum" => "md5-planet" }, { "name" => "chunk-a.pmtiles", "md5sum" => "md5-a" }] },
|
||||
Maptool::PmtilesBuilder.terrain_meta(@data_dir),
|
||||
)
|
||||
end
|
||||
|
||||
def test_terrain_meta_is_nil_before_any_build
|
||||
assert_nil Maptool::PmtilesBuilder.terrain_meta(@data_dir)
|
||||
end
|
||||
|
||||
def test_terrain_update_available_is_false_when_unchanged
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
|
||||
|
||||
refute Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest })
|
||||
end
|
||||
|
||||
# Same selection, but chunk-a's md5sum has since changed upstream.
|
||||
def test_terrain_update_available_is_true_when_a_source_changed
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
|
||||
|
||||
assert Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest(chunk_a_md5: "md5-a-v2") })
|
||||
end
|
||||
|
||||
# Same recorded sources, but the selection has since grown to also cover
|
||||
# chunk-b — recorded sources no longer match what the current selection
|
||||
# would need.
|
||||
def test_terrain_update_available_is_true_when_the_selection_grew
|
||||
select_regions("SK")
|
||||
ENV["PMTILES_BIN"] = @pmtiles_ok
|
||||
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
|
||||
|
||||
select_regions("SK", "IS")
|
||||
assert Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest })
|
||||
end
|
||||
|
||||
# Slovakia + Iceland together intersect both fake chunks — planet + both
|
||||
|
||||
Reference in new issue
Block a user