mirror of
https://github.com/zvx-echo6/central.git
synced 2026-05-21 18:14:44 +02:00
Bridge PR for v0.5.0. PR J wired the supervisor with a hardcoded EnrichmentConfig() default; PR K added real backends to the registry but left no operator path to select one. K.5 closes that gap by mirroring the config.adapters storage + LISTEN/NOTIFY hot-reload pattern. config.enrichment (migration 024): single-row table (id BOOLEAN PK CHECK (id = true), mirroring config.system). Columns enricher_class, backend_class, backend_settings JSONB, cache_ttl_s, updated_at. Reuses the existing config.set_updated_at + config.notify_config_change triggers (the NOTIFY function's ELSE branch emits 'enrichment:' for this keyless single-row table). Seeds framework DEFAULTS ONLY — GeocoderEnricher + NoOpBackend, empty backend_settings, 24h TTL. NO URLs/IPs/auth in the seed; a fresh deploy runs NoOp out of the box. Idempotent (CREATE IF NOT EXISTS / DROP TRIGGER IF EXISTS / INSERT ON CONFLICT DO NOTHING). Supervisor: - Reads config.enrichment at startup (start() -> config_source .get_enrichment_config()), overriding the constructor default. - Hot-reloads via _on_config_change(table == "enrichment"): re-reads the row, rebuilds the enricher set, and invalidates the enrichment cache when the enricher/backend/settings changed (a new backend must not keep serving the old backend's cached bundles until TTL). TTL-only changes retain the cache. - build_enrichers now takes an explicit EnrichmentCache (the supervisor owns it so it can invalidate); cache no longer built inside build_enrichers. ConfigStore / ConfigSource: get_enrichment_config() (falls back to defaults if the row is somehow absent) + upsert_enrichment_config(). Mirrors the adapter accessors. cache.py: EnrichmentCache.invalidate(enricher_name=None) — DELETE all or enricher-scoped; returns rows deleted. GUI /enrichment: GET renders the EnrichmentConfig form via the generic describe_fields machinery (no enrichment-specific Jinja); POST validates via Pydantic, writes config.enrichment, and lets the NOTIFY trigger propagate the hot-reload. New enrichment.html + a nav link. backend_settings (a dict field) needed a generic "json" widget in describe_fields + the template — usable by any dict-typed settings field, not enrichment-specific. Necessary deviation (surfaced): PR K shipped a deployment-specific default DEFAULT_BASE_URL = "http://192.168.1.130:8440" in navi.py. Bar (b) forbids deployer IPs in src, and operator-settable base_url is exactly K.5's purpose, so the default is changed to http://localhost:8440 (matching Photon/Nominatim defaults). The live integration smoke (tests/, env-gated, skipped) now reads the endpoint from NAVI_BASE_URL — no IP anywhere in src. Tests (test_enrichment_config_plumbing.py, 10): ConfigStore read / default fallback / upsert-passes-dict; cache invalidate all + scoped; supervisor builds NaviBackend from config; hot-reload rebuilds + invalidates on backend change; no-invalidate on TTL-only change; describe_fields json widget; /enrichment GET render. test_firms updated for the build_enrichers signature change. Hot-reload mechanism mirrored: Postgres LISTEN/NOTIFY on channel 'config_changed' (payload 'table:key'), same path adapters/streams use; the supervisor's existing _on_config_change dispatch gains an "enrichment" branch. Verification: full pytest 535 passed, 1 skipped (was 525; +10). Migration applied cleanly on the live prod schema; SELECT * FROM config.enrichment returns the NoOp default row. grep subject_for_event/_ADAPTER_REGISTRY and grep 100.64.0./192.168.1. in src both empty. Does NOT activate NaviBackend (ships NoOp default; operator action) and does NOT declare enrichment_locations on other adapters (PR L scope). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| fixtures | ||
| __init__.py | ||
| conftest.py | ||
| README.md | ||
| test_adapters.py | ||
| test_api_key_resolver.py | ||
| test_api_keys.py | ||
| test_archive_multi_stream.py | ||
| test_audit.py | ||
| test_auth.py | ||
| test_bootstrap_config.py | ||
| test_config_source.py | ||
| test_config_store.py | ||
| test_consumer_doc.py | ||
| test_crypto.py | ||
| test_csrf_handler.py | ||
| test_csrf_race_condition.py | ||
| test_dashboard.py | ||
| test_enrichment_config_plumbing.py | ||
| test_enrichment_framework.py | ||
| test_eonet.py | ||
| test_events_adapter_column.py | ||
| test_events_feed.py | ||
| test_events_feed_frontend.py | ||
| test_firms.py | ||
| test_form_descriptors.py | ||
| test_gdacs.py | ||
| test_geocoder_enricher.py | ||
| test_gui_scaffold.py | ||
| test_inciweb.py | ||
| test_models.py | ||
| test_navi_backend.py | ||
| test_nominatim_backend.py | ||
| test_nwis.py | ||
| test_nws_normalization.py | ||
| test_photon_backend.py | ||
| test_preview_hook.py | ||
| test_producer_doc.py | ||
| test_region_picker.py | ||
| test_requires_api_key.py | ||
| test_session_auth.py | ||
| test_setup_gate.py | ||
| test_stream_registry.py | ||
| test_streams.py | ||
| test_supervisor_hotreload.py | ||
| test_supervisor_integration.py | ||
| test_swpc.py | ||
| test_usgs_quake.py | ||
| test_wfigs.py | ||
| test_wizard.py | ||
Central Tests
Test Database
Some tests (notably test_config_store.py) require a real PostgreSQL database.
By default, tests connect to:
postgresql://central_test:testpass@localhost/central_test
If your test database uses different credentials, set the CENTRAL_TEST_DB_DSN
environment variable:
export CENTRAL_TEST_DB_DSN="postgresql://myuser:mypass@localhost/mydb"
uv run pytest tests/test_config_store.py