central/tests/test_nws_normalization.py

399 lines
12 KiB
Python
Raw Normal View History

"""Tests for NWS adapter normalization."""
from datetime import datetime, timezone
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from central.adapters.nws import (
NWSAdapter,
_snake_case,
_parse_datetime,
_extract_states_from_codes,
_build_regions,
_compute_centroid,
_compute_bbox,
SEVERITY_MAP,
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
from central.config_models import AdapterConfig
from central.models import subject_for_event
# Sample NWS GeoJSON features for testing
# SAME codes: 6 digits, first 2 are state FIPS (ID=16, OR=41, CA=06, WA=53)
SAMPLE_FEATURE_ID = {
"id": "urn:oid:2.49.0.1.840.0.a1b2c3d4e5f6",
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-116.5, 43.5],
[-116.0, 43.5],
[-116.0, 44.0],
[-116.5, 44.0],
[-116.5, 43.5],
]]
},
"properties": {
"id": "urn:oid:2.49.0.1.840.0.a1b2c3d4e5f6",
"event": "Severe Thunderstorm Warning",
"sent": "2026-05-15T12:00:00-06:00",
"expires": "2026-05-15T14:00:00-06:00",
"severity": "Severe",
"geocode": {
"SAME": ["160001"], # Idaho state FIPS 16
"UGC": ["IDC001", "IDZ033"],
},
},
}
SAMPLE_FEATURE_OR = {
"id": "urn:oid:2.49.0.1.840.0.x1y2z3w4",
"type": "Feature",
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
"geometry": {"type": "Point", "coordinates": [-122.7, 45.5]}, # Portland, OR
"properties": {
"id": "urn:oid:2.49.0.1.840.0.x1y2z3w4",
"event": "Winter Storm Warning",
"sent": "2026-05-15T08:00:00Z",
"expires": "2026-05-16T08:00:00Z",
"severity": "Moderate",
"geocode": {
"SAME": ["410051"], # Oregon state FIPS 41
"UGC": ["ORC051"],
},
},
}
SAMPLE_FEATURE_CA = {
"id": "urn:oid:2.49.0.1.840.0.ca1234",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-118.25, 34.05],
},
"properties": {
"id": "urn:oid:2.49.0.1.840.0.ca1234",
"event": "Fire Weather Watch",
"sent": "2026-05-15T10:00:00-07:00",
"expires": "2026-05-16T18:00:00-07:00",
"severity": "Minor",
"geocode": {
"SAME": ["060037"], # California state FIPS 06
"UGC": ["CAZ568"],
},
},
}
SAMPLE_FEATURE_UNKNOWN_SEVERITY = {
"id": "urn:oid:2.49.0.1.840.0.unk123",
"type": "Feature",
"geometry": None,
"properties": {
"id": "urn:oid:2.49.0.1.840.0.unk123",
"event": "Test Alert",
"sent": "2026-05-15T12:00:00Z",
"expires": None,
"severity": "Unknown",
"geocode": {
"SAME": ["530033"], # Washington state FIPS 53
"UGC": ["WAC033"],
},
},
}
class TestSnakeCase:
"""Tests for snake_case conversion."""
def test_spaces_to_underscores(self) -> None:
assert _snake_case("Severe Thunderstorm Warning") == "severe_thunderstorm_warning"
def test_removes_special_chars(self) -> None:
assert _snake_case("Fire Weather (Red Flag)") == "fire_weather_red_flag"
def test_lowercase(self) -> None:
assert _snake_case("TORNADO WARNING") == "tornado_warning"
class TestParseDatetime:
"""Tests for datetime parsing."""
def test_iso_with_offset(self) -> None:
result = _parse_datetime("2026-05-15T12:00:00-06:00")
assert result is not None
assert result.tzinfo == timezone.utc
assert result.hour == 18 # 12:00 MDT = 18:00 UTC
def test_iso_with_z(self) -> None:
result = _parse_datetime("2026-05-15T12:00:00Z")
assert result is not None
assert result.hour == 12
def test_none_input(self) -> None:
assert _parse_datetime(None) is None
def test_invalid_input(self) -> None:
assert _parse_datetime("not a date") is None
class TestExtractStates:
"""Tests for state extraction from geocodes."""
def test_same_codes(self) -> None:
# Idaho FIPS is 16
states = _extract_states_from_codes(["160001", "160003"], [])
assert states == {"ID"}
def test_ugc_codes(self) -> None:
states = _extract_states_from_codes([], ["IDC001", "ORC051"])
assert states == {"ID", "OR"}
def test_combined(self) -> None:
# Idaho FIPS is 16
states = _extract_states_from_codes(["160001"], ["WAC033"])
assert states == {"ID", "WA"}
def test_empty(self) -> None:
states = _extract_states_from_codes([], [])
assert states == set()
class TestBuildRegions:
"""Tests for region string building."""
def test_same_to_fips_region(self) -> None:
# Idaho FIPS is 16
regions = _build_regions(["160001"], [])
assert "US-ID-FIPS160001" in regions
def test_ugc_county(self) -> None:
regions = _build_regions([], ["IDC001"])
assert "US-ID-C001" in regions
def test_ugc_zone(self) -> None:
regions = _build_regions([], ["IDZ033"])
assert "US-ID-Z033" in regions
def test_sorted_alphabetically(self) -> None:
regions = _build_regions(["160001"], ["IDC001", "IDZ033"])
assert regions == sorted(regions)
class TestStateFilter:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
"""Tests for region-based filtering."""
@pytest.fixture
def adapter(self, tmp_path: Path) -> NWSAdapter:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
"""Create adapter with Pacific Northwest region (excludes CA)."""
config = AdapterConfig(
name="nws",
enabled=True,
cadence_s=60,
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
settings={
"contact_email": "test@example.com",
# Pacific NW region: WA/OR/ID - excludes CA (LA at 34N, region starts at 42N)
"region": {"north": 49.0, "south": 42.0, "east": -104.0, "west": -125.0},
},
updated_at=datetime.now(timezone.utc),
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
mock_config_store = MagicMock()
return NWSAdapter(config, mock_config_store, tmp_path / "test.db")
def test_accepts_id_feature(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_ID)
assert event is not None
assert event.id == SAMPLE_FEATURE_ID["id"]
def test_accepts_or_feature(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_OR)
assert event is not None
assert event.id == SAMPLE_FEATURE_OR["id"]
def test_rejects_ca_feature(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_CA)
assert event is None
class TestSeverityMapping:
"""Tests for severity mapping."""
def test_extreme(self) -> None:
assert SEVERITY_MAP["Extreme"] == 4
def test_severe(self) -> None:
assert SEVERITY_MAP["Severe"] == 3
def test_moderate(self) -> None:
assert SEVERITY_MAP["Moderate"] == 2
def test_minor(self) -> None:
assert SEVERITY_MAP["Minor"] == 1
def test_unknown(self) -> None:
assert SEVERITY_MAP["Unknown"] is None
def test_unknown_severity_in_feature(self, tmp_path: Path) -> None:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
config = AdapterConfig(
name="nws",
enabled=True,
cadence_s=60,
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
settings={
"contact_email": "test@example.com",
# No region = accept all features
},
updated_at=datetime.now(timezone.utc),
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
mock_config_store = MagicMock()
adapter = NWSAdapter(config, mock_config_store, tmp_path / "test.db")
event = adapter._normalize_feature(SAMPLE_FEATURE_UNKNOWN_SEVERITY)
assert event is not None
assert event.severity is None
class TestSubjectDerivation:
"""Tests for NATS subject derivation."""
@pytest.fixture
def adapter(self, tmp_path: Path) -> NWSAdapter:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
config = AdapterConfig(
name="nws",
enabled=True,
cadence_s=60,
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
settings={
"contact_email": "test@example.com",
# No region = accept all features
},
updated_at=datetime.now(timezone.utc),
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
mock_config_store = MagicMock()
return NWSAdapter(config, mock_config_store, tmp_path / "test.db")
def test_county_subject(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_ID)
assert event is not None
subject = subject_for_event(event)
# Primary region should be alphabetically first
# Could be county or zone depending on sort order
assert subject.startswith("central.wx.alert.us.id.")
def test_zone_subject(self, adapter: NWSAdapter) -> None:
# Create feature with only zone codes
feature = {
"id": "urn:test:zone",
"geometry": None,
"properties": {
"event": "Test Alert",
"sent": "2026-05-15T12:00:00Z",
"severity": "Minor",
"geocode": {
"SAME": [],
"UGC": ["IDZ033"],
},
},
}
event = adapter._normalize_feature(feature)
assert event is not None
subject = subject_for_event(event)
assert "zone" in subject
class TestRegionsSorted:
"""Tests for regions list sorting."""
@pytest.fixture
def adapter(self, tmp_path: Path) -> NWSAdapter:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
config = AdapterConfig(
name="nws",
enabled=True,
cadence_s=60,
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
settings={
"contact_email": "test@example.com",
# No region = accept all features
},
updated_at=datetime.now(timezone.utc),
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
mock_config_store = MagicMock()
return NWSAdapter(config, mock_config_store, tmp_path / "test.db")
def test_regions_alphabetically_sorted(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_ID)
assert event is not None
assert event.geo.regions == sorted(event.geo.regions)
def test_primary_region_is_first(self, adapter: NWSAdapter) -> None:
event = adapter._normalize_feature(SAMPLE_FEATURE_ID)
assert event is not None
assert len(event.geo.regions) > 0
assert event.geo.primary_region == event.geo.regions[0]
class TestDeduplication:
"""Tests for event deduplication."""
@pytest.fixture
def adapter(self, tmp_path: Path) -> NWSAdapter:
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
config = AdapterConfig(
name="nws",
enabled=True,
cadence_s=60,
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
settings={
"contact_email": "test@example.com",
# No region = accept all features
},
updated_at=datetime.now(timezone.utc),
)
chore: housekeeping - orphan branch + three stale tests (#22) * test(bootstrap): isolate env vars in test_reads_from_env_file The test was failing on CT104 because live CENTRAL_DB_DSN environment variable overrode the test .env file content. Fix: use monkeypatch.delenv to clear all CENTRAL_* env vars before creating the Settings object, ensuring the test env file is the only source of configuration values. Also add CENTRAL_CSRF_SECRET to test env file since it's now a required field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(models): remove stale test_custom_prefix test The test called subject_for_event(event, prefix="myapp.events") but the prefix parameter was removed from the API. The prefix functionality was intentionally removed - subjects now always use the "central." prefix hardcoded in the function. Delete the test rather than re-add the parameter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(nws): update fixtures for new adapter signature and region filtering NWSAdapter.__init__ signature changed from (config, cursor_db_path) to (config, config_store, cursor_db_path) with config now being AdapterConfig with a settings dict instead of NWSAdapterConfig. Also adapts tests to region-based bbox filtering: - TestStateFilter now uses region bbox to accept PNW, reject CA - Add geometry to SAMPLE_FEATURE_OR so it passes region filter - Other test fixtures use region=None to skip filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-17 18:14:58 -06:00
mock_config_store = MagicMock()
return NWSAdapter(config, mock_config_store, tmp_path / "test.db")
def test_same_feature_same_id(self, adapter: NWSAdapter) -> None:
"""Normalizing the same feature twice returns same Event.id."""
event1 = adapter._normalize_feature(SAMPLE_FEATURE_ID)
event2 = adapter._normalize_feature(SAMPLE_FEATURE_ID)
assert event1 is not None
assert event2 is not None
assert event1.id == event2.id
class TestGeometry:
"""Tests for geometry computation."""
def test_centroid_polygon(self) -> None:
geom = {
"type": "Polygon",
"coordinates": [[
[-116.5, 43.5],
[-116.0, 43.5],
[-116.0, 44.0],
[-116.5, 44.0],
[-116.5, 43.5],
]]
}
centroid = _compute_centroid(geom)
assert centroid is not None
# Average of 5 vertices (including closing point)
# lon: (-116.5 + -116.0 + -116.0 + -116.5 + -116.5) / 5 = -116.3
# lat: (43.5 + 43.5 + 44.0 + 44.0 + 43.5) / 5 = 43.7
assert -116.4 < centroid[0] < -116.2
assert 43.6 < centroid[1] < 43.8
def test_bbox_polygon(self) -> None:
geom = {
"type": "Polygon",
"coordinates": [[
[-116.5, 43.5],
[-116.0, 43.5],
[-116.0, 44.0],
[-116.5, 44.0],
[-116.5, 43.5],
]]
}
bbox = _compute_bbox(geom)
assert bbox is not None
assert bbox == (-116.5, 43.5, -116.0, 44.0)
def test_centroid_none_geometry(self) -> None:
assert _compute_centroid(None) is None
def test_bbox_none_geometry(self) -> None:
assert _compute_bbox(None) is None