You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
1 week ago | |
|---|---|---|
| .. | ||
| application | 1 week ago | |
| domain | 1 week ago | |
| echo | 3 months ago | |
| fakes | 3 months ago | |
| fixtures | 1 week ago | |
| harness | 1 week ago | |
| hbp | 2 months ago | |
| infrastructure | 1 week ago | |
| obp | 4 months ago | |
| replay | 3 months ago | |
| routing | 1 week ago | |
| schemas | 3 months ago | |
| smoke | 3 months ago | |
| support | 3 months ago | |
| talker_alias | 2 months ago | |
| voice | 3 months ago | |
| README.md | 3 months ago | |
| __init__.py | 3 months ago | |
| conftest.py | 3 months ago | |
| test_version.py | 3 months ago | |
README.md
Test layout
One topic per file — run only what you need while developing or validating a change.
# Install into the pyenv site-packages (not ~/.local); Cursor/VS Code pytest sets PYTHONNOUSERSITE=1
python3 -m pip install --no-user -e ".[dev]"
python3 -m pytest tests/<path>/test_<name>.py -q # single file
python3 -m pytest tests/<path>/test_<name>.py::test_foo -q # single test
python3 -m pytest tests/routing/ -q # whole domain
python3 -m pytest tests/ -q # full suite
python3 -m pytest tests/ -q -m "not mqtt" # skip MQTT-heavy tests
Use the project interpreter, e.g. /opt/.pyenv/versions/3.11.8/bin/python3.
Directories
| Directory | What it covers |
|---|---|
routing/ |
Static TG, startup subscriptions, unit data, CRC dedup, echo reset, private voice, config reload |
hbp/ |
HBP ingress, loop control, rate limit, timeout/collision, master maintenance |
obp/ |
OpenBridge loop, rate limit, unit-data loop |
voice/ |
Announcements, TTS schedule, broadcast queue, disconnected voice, in-band signalling |
talker_alias/ |
Encode/decode, passthrough, MMDVM wire, routing inject (DeterministicScenario) |
echo/ |
Recording timers, playback loop, seq preservation, ingress path |
replay/ |
JSONL session replay |
schemas/ |
Report v2 JSON Schema validation (jsonschema dev dep) |
application/ |
Report payloads, monitor topology, proxy use cases, subscription store/router |
fakes/ |
Re-exports for application tests (InMemorySubscriptionStore shim) — not run as tests |
infrastructure/ |
Logging reload, ACL router, HBP REPEAT + proxy fan-in integration, MQTT |
smoke/ |
Quick routing smoke |
support/ |
Shared stacks (hbp_repeat_stack, monitor sim) — not run as tests |
harness/ |
Shared fakes (DeterministicScenario, assertions) — not run as tests |
Integration vs harness
Most routing/voice tests inject packets via DeterministicScenario (routing.dmrd_received on fakes). That is fast but skips udp_hbp REPEAT rewrite and proxy UDP fan-in.
For regressions on those paths, use:
| File | Topic |
|---|---|
infrastructure/test_hbp_repeat_talker_alias.py |
Real HBPProtocol REPEAT + embedded TA |
infrastructure/test_proxy_repeat_e2e.py |
Proxy fan-in → REPEAT downlink |
infrastructure/test_proxy_reload.py |
Hot reload keeps UDP listener |
Mark new stack tests with @pytest.mark.integration.
Files by domain
routing/
| File | Tests | Topic |
|---|---|---|
test_config_reload.py |
1 | Merge system config on reload |
test_crc_dedup.py |
3 | HBP/OBP CRC dedup, seq=0 |
test_echo_subscription_reset.py |
5 | Echo leg after subscription reset / OPTIONS |
test_options_config_loop.py |
3 | OPTIONS paths (RPTO/startup; no 26s loop) |
test_peer_options_override.py |
— | RPTO SINGLE/TIMER override, inject proxy |
test_private_voice.py |
3 | Private call routing |
test_startup_subscriptions.py |
4 | Startup subscriptions + voice E2E |
test_static_tg_options.py |
4 | Static TG from peer OPTIONS |
test_subscription_router_dmrd.py |
— | dmrd_received via SubscriptionRouter |
test_unit_data_ingress.py |
4 | Unit headers, CSBK, reports |
test_unit_data_routing.py |
5 | SUB_MAP, hotspot, gateway, OBP fanout |
hbp/
| File | Tests | Topic |
|---|---|---|
test_hbp_loop_control.py |
2 | HBP loop winner/loser |
test_hbp_rate_limit.py |
2 | Ingress rate drop + stall |
test_ingress.py |
3 | RX start, rate drop, OBP loop loser |
test_master_maintenance.py |
2 | Peer timeout / shared PEERS dict |
test_timeout_collision.py |
3 | 180s timeout, collision, rekey |
obp/
| File | Tests | Topic |
|---|---|---|
test_loop_control.py |
4 | OBP loop, VTERM, BCSQ |
test_obp_rate_limit.py |
1 | Rate limit epoch |
test_unit_data_loop.py |
2 | Unit data loop loser |
voice/
| File | Tests | Topic |
|---|---|---|
test_announcement_anticollision.py |
3 | Busy slot skip / abort |
test_broadcast_queue.py |
2 | Same-TG broadcast queue |
test_disconnected_voice.py |
3 | Not-linked / reflector prompts |
test_in_band_signalling.py |
5 | Reflector / single-mode VTERM |
test_play_file_on_request.py |
3 | On-demand file playback |
test_scheduled_announcement.py |
4 | File announcements (AMBE) |
test_scheduled_tts.py |
7 | TTS schedule + conversion |
test_voice_config_reload.py |
3 | Hot reload announcement/TTS loops |
talker_alias/
| File | Tests | Topic |
|---|---|---|
test_routing_inject.py |
3 | TA inject on routing VHEAD (harness) |
test_embed_ta.py |
4 | Embedded LC modes |
test_encode_decode.py |
8 | Domain encode/decode |
test_format.py |
2 | Format from subscriber profile |
test_mmdvm_wire.py |
10 | MMDVM wire blocks |
test_passthrough.py |
9 | Passthrough / both modes |
echo/
| File | Tests | Topic |
|---|---|---|
test_playback_ingress.py |
2 | ingress_pkt_time, record→playback |
test_playback_logging.py |
1 | Duration log format |
test_playback_send_loop.py |
4 | Send loop, max duration, interval |
test_recording_timers.py |
2 | Idle timeout, VTERM commit |
test_rekey_playback.py |
4 | Seq preservation past 255 / 30s |
infrastructure (integration highlights)
| File | Topic |
|---|---|
test_hbp_repeat_talker_alias.py |
REPEAT embed TA through real HBP |
test_proxy_repeat_e2e.py |
Proxy → REPEAT E2E |
test_proxy_reload.py |
Proxy hot reload |
test_udp_fanin.py |
UDP fan-in routing |
test_report_server_wire.py |
Report server wire opcodes |
test_logging_reload.py |
Log level reload |
test_acl_router.py |
ACL range checks (acl_check parity) |
smoke/ · application/
| File | Topic |
|---|---|
smoke/test_routing.py |
Static TG forward smoke |
application/test_monitor_topology.py |
Inject-only proxy monitor remap |
application/test_runtime_context.py |
RuntimeContext holder, SIGHUP swap prep |
application/test_subscription_router.py |
SubscriptionRouter vs legacy scan |
application/test_routing_table_export.py |
Monitor export shim from store |
Harness API (post-rename)
| Symbol | Role |
|---|---|
DeterministicScenario |
Wires RoutingUseCases + InMemorySubscriptionStore + InMemoryAclRouter |
scenario.routing |
Use-case facade (dmrd_received, timers, OPTIONS) |
scenario.seed_routing_table() |
Seed store from legacy monitor-shaped dict |
active_routing_table() |
Build minimal ACTIVE routing table for harness |
patch_routing_wall_time() |
Patches wall clock on OBP/unit paths |
Session replay JSONL meta accepts routing_table (preferred) or legacy bridges; apply_startup_subscriptions or legacy apply_startup_bridges.
Examples (copy-paste)
# After changing unit-data routing
python3 -m pytest tests/routing/test_unit_data_routing.py -q
# After echo seq fix
python3 -m pytest tests/echo/test_rekey_playback.py -q
# Talker Alias REPEAT (real stack)
python3 -m pytest tests/infrastructure/test_hbp_repeat_talker_alias.py -q
# HBP loop + rate (common RF regressions)
python3 -m pytest tests/hbp/test_hbp_loop_control.py tests/hbp/test_hbp_rate_limit.py -q
# One test by name
python3 -m pytest tests/routing/test_startup_subscriptions.py::test_startup_bridge_routes_voice_after_apply -q
Policy
- New tests: add a new file (or extend the smallest existing file for the same topic). Avoid large multi-topic modules.
- Harness: shared code lives in
harness/andsupport/only. - Stack regressions: prefer
infrastructure/test_*_e2e.pywith real adapters over duplicating inDeterministicScenario. - Full audit:
docs-priv/en/test-audit.md(maintainer checkout).