[AZ-777] Phase 1 hotfix (z/x/y) + Phase 2 Derkachi seed + ops

Phase 1 hotfix:
- C11 HttpTileDownloader adapted to satellite-provider v2.0.0
  z/x/y inventory contract (bulk POST keyed by slippy-map coords).
- Unit tests rewritten to exercise the new inventory schema.
- E2E smoke test updated to match the v2.0.0 wire.

Phase 2 (Derkachi seed + smoke-validated on Jetson):
- tests/fixtures/derkachi_c6/{README,bbox.yaml,seed_region.py}
  drives POST /api/satellite/region against satellite-provider
  with Google Maps as the imagery source. Smoke run produced
  4 regions, 175 tiles, inventory 32/32.
- scripts/mint_dev_jwt.py + run-tests-jetson.sh auto-mint and
  export SATELLITE_PROVIDER_API_KEY using JWT_SECRET / JWT_ISSUER
  / JWT_AUDIENCE env vars (no host port mappings; e2e-runner
  reaches SP via internal docker network only).

Spec amendment: AZ-777 todo spec updated to record the
Google Maps imagery source decision and STOP-gate state.

AZ-777 Phase 3+ work is superseded by Epic AZ-835 (see next
commit).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-22 17:39:21 +03:00
parent 811b04e605
commit b15454b9a9
9 changed files with 924 additions and 182 deletions
+10 -18
View File
@@ -49,7 +49,6 @@ from gps_denied_onboard.components.c11_tile_manager import (
)
from gps_denied_onboard.helpers.wgs_converter import WgsConverter
# ----------------------------------------------------------------------
# Skip gates
# ----------------------------------------------------------------------
@@ -113,10 +112,7 @@ def _mint_bearer_token_or_skip() -> str:
if not value
]
if missing:
pytest.skip(
"Cannot mint a fallback JWT — missing env: "
+ ", ".join(missing)
)
pytest.skip("Cannot mint a fallback JWT — missing env: " + ", ".join(missing))
now = datetime.now(timezone.utc)
payload = {
"sub": "gps-denied-onboard-smoke",
@@ -174,9 +170,9 @@ def test_smoke_satellite_provider_inventory_contract() -> None:
body = {
"tiles": [
{
"tileZoom": _DERKACHI_TILE_ZOOM,
"tileX": tile_x,
"tileY": tile_y,
"z": _DERKACHI_TILE_ZOOM,
"x": tile_x,
"y": tile_y,
}
]
}
@@ -191,8 +187,7 @@ def test_smoke_satellite_provider_inventory_contract() -> None:
# Assert — contract invariants from `tile-inventory.md` v1.0.0
assert response.status_code == 200, (
f"satellite-provider inventory POST returned "
f"{response.status_code}: {response.text!r}"
f"satellite-provider inventory POST returned {response.status_code}: {response.text!r}"
)
decoded = response.json()
assert isinstance(decoded, dict), f"expected JSON object, got {type(decoded)}"
@@ -201,13 +196,12 @@ def test_smoke_satellite_provider_inventory_contract() -> None:
assert isinstance(results, list)
assert len(results) == len(body["tiles"]), (
# Inv-2: response order/length matches request order/length.
f"inventory response length {len(results)} != request length "
f"{len(body['tiles'])}"
f"inventory response length {len(results)} != request length {len(body['tiles'])}"
)
entry = results[0]
assert entry["tileZoom"] == _DERKACHI_TILE_ZOOM
assert entry["tileX"] == tile_x
assert entry["tileY"] == tile_y
assert entry["z"] == _DERKACHI_TILE_ZOOM
assert entry["x"] == tile_x
assert entry["y"] == tile_y
assert "present" in entry, f"missing 'present' in entry {entry!r}"
assert isinstance(entry["present"], bool)
if entry["present"]:
@@ -259,9 +253,7 @@ class _InMemoryC6Adapter:
)
return "fresh"
def tile_already_present(
self, *, zoom_level: int, lat: float, lon: float
) -> bool:
def tile_already_present(self, *, zoom_level: int, lat: float, lon: float) -> bool:
self.exists_calls.append((zoom_level, lat, lon))
return False