Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions services/api/api/resources/grids/fbfm40/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
domain_id comes from the URL path parameter, not the request body.
"""

from lib.landfire import LANDFIRE_VERSIONS

EXAMPLE_FBFM40_MINIMAL = {}

EXAMPLE_FBFM40_WITH_METADATA = {
Expand Down Expand Up @@ -39,8 +41,8 @@

EXAMPLE_FBFM40_SEASONAL = {
"name": "FBFM40 spring seasonal fuels",
"version": "2025",
"season": "SP",
"version": LANDFIRE_VERSIONS["fbfm40"]["lfps_available"][0],
"season": "ES",
}

CREATE_LANDFIRE_FBFM40_OPENAPI_EXAMPLES = {
Expand Down Expand Up @@ -112,11 +114,15 @@
},
}

ALL_FBFM40_EXAMPLE_VALUES = [
STAGED_FBFM40_EXAMPLE_VALUES = [
("minimal", EXAMPLE_FBFM40_MINIMAL),
("with_metadata", EXAMPLE_FBFM40_WITH_METADATA),
("remove_non_burnable", EXAMPLE_FBFM40_REMOVE_NON_BURNABLE),
("with_buffer", EXAMPLE_FBFM40_WITH_BUFFER),
("domain_aligned_2m", EXAMPLE_FBFM40_DOMAIN_2M),
("native_anchor", EXAMPLE_FBFM40_NATIVE),
]

LFPS_FBFM40_EXAMPLE_VALUES = [
("seasonal", EXAMPLE_FBFM40_SEASONAL),
]
23 changes: 22 additions & 1 deletion services/api/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@
DOMAINS_COLLECTION,
KEYS_COLLECTION,
)
from lib.testing import SHARED_TEST_DOMAINS_DIR
from tests import fixtures
from tests.fixtures import make_application_data, make_domain_data, make_key_data
from tests.fixtures import (
load_domain_from_file,
make_application_data,
make_domain_data,
make_key_data,
)

TEST_URL = os.getenv("TEST_API_URL", "http://127.0.0.1:8080")
TEST_API_KEY = os.environ.get("TEST_API_KEY", "")
Expand Down Expand Up @@ -146,6 +152,21 @@ def second_domain(firestore_client, test_owner_id):
doc_ref.delete()


@pytest.fixture(scope="session")
def lfps_covered_domain(firestore_client, test_owner_id):
"""A domain with known LANDFIRE Product Service SW coverage (Kingman, AZ)."""
domain_data = load_domain_from_file(
SHARED_TEST_DOMAINS_DIR / "threedep_ept_seam.json",
owner_id=test_owner_id,
)
doc_ref = firestore_client.collection(DOMAINS_COLLECTION).document(
domain_data["id"]
)
doc_ref.set(domain_data)
yield domain_data
doc_ref.delete()


@pytest.fixture(scope="session")
def application_for_testing(firestore_client, test_owner_id):
"""An application owned by test-owner, available for any test that needs one."""
Expand Down
21 changes: 21 additions & 0 deletions services/api/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ def make_domain_data(
}


def load_domain_from_file(path, owner_id: str | None = None) -> dict:
"""Factory function to load a domain fixture file as Firestore-ready data.

Domain fixture files under lib.testing.SHARED_TEST_DOMAINS_DIR store
coordinates as plain lists and carry their own id/timestamps; this
stringifies coordinates for Firestore and assigns a fresh id/owner/
timestamps the same way make_domain_data does.
"""
with open(path) as f:
domain_data = json.load(f)
domain_data["id"] = f"test-{uuid.uuid4().hex}"
domain_data["owner_id"] = owner_id or DEFAULT_OWNER_ID
domain_data["created_on"] = datetime.now()
domain_data["modified_on"] = datetime.now()
for feature in domain_data.get("features", []):
coords = feature.get("geometry", {}).get("coordinates")
if isinstance(coords, list):
feature["geometry"]["coordinates"] = json.dumps(coords)
return domain_data


def make_grid_data(
domain_id: str,
owner_id: str | None = None,
Expand Down
20 changes: 18 additions & 2 deletions services/api/tests/resources/grids/fbfm40/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import pytest
from api.resources.grids.fbfm40.examples import (
ALL_FBFM40_EXAMPLE_VALUES,
LFPS_FBFM40_EXAMPLE_VALUES,
STAGED_FBFM40_EXAMPLE_VALUES,
)


Expand Down Expand Up @@ -91,7 +92,7 @@ def test_response_excludes_owner_id(self, client, domain_for_testing):
data = response.json()
assert "owner_id" not in data

@pytest.mark.parametrize("example_name,example_value", ALL_FBFM40_EXAMPLE_VALUES)
@pytest.mark.parametrize("example_name,example_value", STAGED_FBFM40_EXAMPLE_VALUES)
def test_documented_example_creates_grid(
self, client, domain_for_testing, example_name, example_value
):
Expand Down Expand Up @@ -180,3 +181,18 @@ def test_season_omitted_persists_none_and_annual_year(
source = response.json()["source"]
assert source["season"] is None
assert source["year"] == 2024 # default version

@pytest.mark.parametrize("example_name,example_value", LFPS_FBFM40_EXAMPLE_VALUES)
def test_lfps_documented_example_creates_grid(
self, client, lfps_covered_domain, example_name, example_value
):
"""Each LFPS-sourced example should successfully create a grid
against a domain known to have LFPS coverage. A failure here after
LANDFIRE rotates its live season means the example needs updating."""
response = client.post(
self.route(lfps_covered_domain["id"]), json=example_value
)
assert response.status_code == 201, (
f"Example '{example_name}' failed with status {response.status_code}: "
f"{response.json()}"
)
17 changes: 17 additions & 0 deletions services/griddle/tests/integration/test_landfire_lfps.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ def test_seasonal_fbfm40(roi):
assert fbfm_valid.max() <= 204 # matches test_landfire.py's FBFM40 range check


def test_configured_seasonal_version_still_live():
"""Fails once LANDFIRE moves seasonal fuels past our configured
version -- signals LANDFIRE_VERSIONS["fbfm40"]["lfps_available"]
needs updating, rather than silently going stale."""
version = LANDFIRE_VERSIONS["fbfm40"]["lfps_available"][0]
live = any(
p.acronym.upper() == "FBFM40"
and p.season is not None
and p.version == f"LF{version}"
for p in list_products()
)
assert live, (
f"No live seasonal FBFM40 product for version {version} -- LANDFIRE may "
'have moved on; update LANDFIRE_VERSIONS["fbfm40"]["lfps_available"].'
)


@pytest.mark.parametrize(
("product", "fetch_fn", "band"),
[
Expand Down
Loading