central/tests
zvx-echo6 456a744bb4 feat(2-E.5): single-source-of-truth stream registry
Eliminates the duplication that has been hand-bumped through PRs B, C, D, E.
Adding a stream is now one StreamEntry in src/central/streams.py + one
migration row in config.streams. supervisor STREAM_SUBJECTS / archive
STREAMS / gui DASHBOARD_STREAMS all derive at import time. No drift
possible because there is one source.

Pure refactor; no behavior change. Runtime verified: derived structures
are byte-equivalent to the previous literal definitions.

src/central/streams.py (new):
  @dataclass(frozen=True)
  class StreamEntry:
      name: str
      subject_filter: str
      event_bearing: bool = True   # archive consumes from this stream
      dashboard: bool = True       # GUI dashboard surfaces this stream

  STREAMS: list[StreamEntry] = [
      StreamEntry("CENTRAL_WX",       "central.wx.>"),
      StreamEntry("CENTRAL_FIRE",     "central.fire.>"),
      StreamEntry("CENTRAL_QUAKE",    "central.quake.>"),
      StreamEntry("CENTRAL_SPACE",    "central.space.>"),
      StreamEntry("CENTRAL_DISASTER", "central.disaster.>"),
      StreamEntry("CENTRAL_META",     "central.meta.>", event_bearing=False),
  ]

Consumers derive:
  supervisor.STREAM_SUBJECTS = {s.name: [s.subject_filter] for s in STREAMS}
    (includes META: supervisor must create every stream in JetStream)
  archive.STREAMS = [(s.name, s.subject_filter) for s in STREAMS if s.event_bearing]
    (excludes META: status messages, not events)
  gui.DASHBOARD_STREAMS = [s.name for s in STREAMS if s.dashboard]

To resolve the name collision between the registry STREAMS and the
existing archive.STREAMS public symbol, archive.py imports the registry
under an alias: from central.streams import STREAMS as STREAM_REGISTRY.
The archives STREAMS surface (the tuple-list) is unchanged for callers.
Same alias used in supervisor.py and gui/routes.py for symmetry.

Migration files unchanged. config.streams keeps seeding retention/bytes --
operator-tunable ops state, separate SoT from the structural mapping.

Tests:
  Dropped from test_archive_multi_stream.py (7, all tautological vs. registry):
    test_streams_list_has_five_entries (magic-number count)
    test_streams_contains_central_wx / fire / quake / space / disaster
    test_streams_excludes_central_meta
  Dropped from test_dashboard.py:
    `assert len(streams) == 6` line inside test_single_stream_failure_doesnt_crash_card
    (the test itself stays; only the magic-number assertion is removed)
  Added in test_stream_registry.py (8 invariant tests):
    test_stream_names_unique
    test_subject_filters_unique
    test_subject_filter_central_prefix_wildcard
    test_meta_is_only_non_event_bearing
    test_supervisor_stream_subjects_includes_meta
    test_supervisor_stream_subjects_includes_all
    test_archive_streams_excludes_non_event_bearing
    test_dashboard_streams_matches_dashboard_flag

The new tests assert properties (uniqueness, format, derivation correctness),
not literals. Future stream additions need zero new test code -- every
invariant automatically covers them.

Note: test file named tests/test_stream_registry.py (not test_streams.py)
to avoid colliding with the pre-existing tests/test_streams.py, which
covers the GUI streams-management page.

Full suite: 427 passed (was 426 on main: -7 dropped + 8 added).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 07:37:01 +00:00
..
__init__.py scaffold: initial repository structure 2026-05-15 19:16:24 +00:00
conftest.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
README.md chore: normalize line endings to LF 2026-05-16 22:26:12 +00:00
test_adapters.py feat(2-A3b): requires_api_key enforcement in supervisor and GUI 2026-05-19 01:26:35 +00:00
test_api_keys.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_archive_multi_stream.py feat(2-E.5): single-source-of-truth stream registry 2026-05-19 07:37:01 +00:00
test_audit.py feat(gui): add auth core, setup gate, and first-run operator creation 2026-05-17 05:30:49 +00:00
test_auth.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_bootstrap_config.py chore: housekeeping - orphan branch + three stale tests (#22) 2026-05-17 18:14:58 -06:00
test_config_source.py chore: normalize line endings to LF 2026-05-16 22:26:12 +00:00
test_config_store.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_crypto.py chore: normalize line endings to LF 2026-05-16 22:26:12 +00:00
test_csrf_handler.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_csrf_race_condition.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_dashboard.py feat(2-E.5): single-source-of-truth stream registry 2026-05-19 07:37:01 +00:00
test_events_adapter_column.py docs: add test database setup, restore geom to test fixture 2026-05-17 18:26:48 +00:00
test_events_feed.py feat(api): add paginated events feed JSON endpoint (#25) 2026-05-17 22:31:00 -06:00
test_events_feed_frontend.py 1b-9c: Events feed UX iteration — colors, popups, viewport filter, expandable rows (#28) 2026-05-18 14:19:27 -06:00
test_firms.py refactor(adapters): self-describing adapter pattern with auto-discovery 2026-05-18 22:14:12 +00:00
test_form_descriptors.py refactor(wizard): generic adapter handling with Literal types 2026-05-19 00:38:06 +00:00
test_gdacs.py fix(2-E): use canonical removed-event subject pattern 2026-05-19 07:08:15 +00:00
test_gui_scaffold.py fix(tests): update tests for lazy app loading and 302 redirect 2026-05-17 06:14:25 +00:00
test_inciweb.py fix(2-C): wire dedup into poll loop, add conditional fetch 2026-05-19 03:53:10 +00:00
test_models.py refactor(adapters): self-describing adapter pattern with auto-discovery 2026-05-18 22:14:12 +00:00
test_nws_normalization.py refactor(adapters): self-describing adapter pattern with auto-discovery 2026-05-18 22:14:12 +00:00
test_region_picker.py feat(gui): generic adapter edit form 2026-05-18 23:16:37 +00:00
test_requires_api_key.py fix(2-A3b): complete error-render path, fix link, add supervisor tests 2026-05-19 02:17:29 +00:00
test_session_auth.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_setup_gate.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_stream_registry.py feat(2-E.5): single-source-of-truth stream registry 2026-05-19 07:37:01 +00:00
test_streams.py feat(gui): implement first-run setup wizard (1b-8) (#24) 2026-05-17 22:06:22 -06:00
test_supervisor_hotreload.py chore: normalize line endings to LF 2026-05-16 22:26:12 +00:00
test_supervisor_integration.py feat(2-A3b): requires_api_key enforcement in supervisor and GUI 2026-05-19 01:26:35 +00:00
test_swpc.py feat(2-D): add NOAA SWPC space weather adapters (alerts, kindex, protons) 2026-05-19 05:55:29 +00:00
test_usgs_quake.py fix(adapters): complete self-describing adapter attributes 2026-05-18 22:33:19 +00:00
test_wfigs.py fix(2-B): normalize WFIGS field formats 2026-05-19 03:04:27 +00:00
test_wizard.py fix(wizard): eliminate all hardcoded field.name branches 2026-05-19 01:01:56 +00:00

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