Skip to main content

End to end telephony scenarios

This page gives the state of the 31 end to end scenarios of the Navin telephony (ADR-0173, task t-211ee738). The tests run with the real engines, without root or containers: Asterisk 20.6.0, Kamailio 5.7.4 (file also checked by Kamailio 6.0.8), RTPengine, SIPp 3.7.2, PostgreSQL (pgserver), SeaweedFS, and the real Control Plane code: compilation, publication through AMI to Asterisk and through JSONRPC to Kamailio, routes under /v1/telephony.

Statuses: executed (the test passes here), blocked (impossible here, with the reason).

Test bench​

The Navin Manager renders the asterisk, kamailio and rtpengine services of its catalog (tests/telephony/render_stack.py, run with the Manager interpreter). tests/telephony/e2e_stack.py starts the engines, speaks AMI, ARI and JSONRPC, runs SIPp and provides a Python caller (PCMU RTP and RFC 4733 DTMF, which SIPp cannot send without privileges) and a SIP client over secure WebSocket. Loopback addresses: Kamailio 127.0.0.21:5060, Asterisk 127.0.0.1, phones of tenant A 127.0.0.6, of tenant B 127.0.0.7, carriers 127.0.0.5 (primary trunk) and 127.0.0.15 (backup trunk).

One time preparation: ln -s en_US_f_Allison /tmp/toolchains/asterisk/usr/share/asterisk/sounds/en (the Debian sound package creates this link in its postinst, which an extraction skips; without it the IVR, voicemail and conference PIN prompts fail). The test fails with this command when the link is missing.

Commands​

services/control-plane/.venv/bin/python -m pytest services/control-plane/tests/test_telephony_e2e_edge.py -q -p no:cacheprovider
services/control-plane/.venv/bin/python -m pytest services/control-plane/tests/test_telephony_e2e.py -q -p no:cacheprovider
services/control-plane/.venv/bin/python -m pytest services/control-plane/tests/test_telephony_e2e_dedicated.py -q -p no:cacheprovider
services/control-plane/.venv/bin/python -m pytest services/control-plane/tests/test_telephony_ha_real.py -q -p no:cacheprovider

Results of 3 November 2026, after the fix of findings 3, 7, 8 and 9: edge 6 passed; e2e 21 passed; dedicated 2 passed; ha_real 7 passed (tests of tel-ha). 29 scenarios executed, 2 blocked (S30, S31).

Matrix​

No.ScenarioStatusProof or reason
S01Asterisk single rendered by the Manager, modules loadedexecutedtest_telephony_e2e_edge.py::test_s01_*, test_telephony_e2e.py::test_s01_*
S02Asterisk pool of 2 nodesexecuted (tel-ha)test_telephony_ha_real.py::test_1_both_asterisk_nodes_are_probed_up, test_2_calls_of_one_tenant_land_on_the_same_node
S03Kamailio checked by the real binaryexecutedtest_telephony_e2e_edge.py::test_s03_* (5.7.4 and 6.0.8, JSONRPC over HTTPS with the test CA)
S04RTPengine starts and answers ng pingexecutedtest_telephony_e2e_edge.py::test_s04_*
S05Shared model: two tenants published and read backexecutedtest_telephony_e2e.py::test_s05_* (GetConfig, endpoints, Kamailio tables)
S06Dedicated modelexecutedtest_telephony_e2e_dedicated.py::test_s06_* (one tenant alone on its pool, call between two extensions)
S07Registration through Kamailio, Path keptexecutedtest_telephony_e2e.py::test_s07_* (Path of the edge read through AMI PJSIPShowContacts, wrong password refused)
S08Internal call between two extensionsexecutedtest_telephony_e2e.py::test_s08_*
S09Tenant isolationexecutedtest_telephony_e2e.py::test_s09_* (no context of B names A, call of B to A refused, credentials of B refused on the domain of A)
S10Inbound DID from an allowed carrierexecutedtest_telephony_e2e.py::test_s10_*
S11Unknown DID (404), host outside trunk_hosts refusedexecutedtest_telephony_e2e.py::test_s11_*, test_telephony_e2e_edge.py::test_s11_*
S12IVR: DTMF to a queue, timeout, invalid choiceexecutedtest_telephony_e2e.py::test_s12_* (option 1 to the queue, invalid choice to 1001); timeout exit proven by test_s14_*
S13Queue answered by a memberexecutedtest_telephony_e2e.py::test_s13_*
S14Voicemail on no answer, AMI countexecutedtest_telephony_e2e.py::test_s14_* (MailboxCount, msg0000 file in INBOX)
S15Conference, PINexecutedtest_telephony_e2e.py::test_s15_* (two callers admitted, wrong PIN refused)
S16Recording, object storage, signed URLexecutedtest_telephony_e2e.py::test_s16_s17_*: MixMonitor file listed by ARI, stored in SeaweedFS (indexed by media.sweep, see finding 8), signed URL of 300 s, forged signature refused, other tenant refused
S17CDR and history from Cdr and CELexecutedtest_telephony_e2e.py::test_s16_s17_* (AMI stream of tel-calls: inbound call, ANSWERED, extension 1002)
S18National call through a trunk, failoverexecutedtest_telephony_e2e.py::test_s18_* (caller id of the trunk, 503 of the primary then backup trunk)
S19International refused without permissionexecutedtest_telephony_e2e.py::test_s19_*
S20Premium prefix blocked by the quotaexecutedtest_telephony_e2e.py::test_s20_*
S21Call duration limitexecutedtest_telephony_e2e.py::test_s21_* (BYE of Asterisk after 60 s)
S22CPS limit per tenant (pipelimit)executedtest_telephony_e2e.py::test_s22_* (a burst above the limit of the tenant refused with 503, after the fix of finding 7)
S23Concurrent calls per tenantexecutedtest_telephony_e2e.py::test_s23_* (486 "Tenant call limit")
S24Anti-flood (pike)executedtest_telephony_e2e_edge.py::test_s24_*
S25Anti-bruteforce (ipban)executedtest_telephony_e2e_edge.py::test_s25_*
S26Versions: v2 then back to v1executedtest_telephony_e2e_dedicated.py::test_s26_* (files of the node equal to those of v1)
S27Node failure and drainexecuted (tel-ha)test_telephony_ha_real.py::test_3_4_5_loss_of_a_node_and_its_return, test_6_drain_of_a_node, test_6b_drain_with_the_disabled_state_holds; the drain of Navin Enterprise sends the state d (disabled, neither selected nor probed), because the state i was set active again by the OPTIONS probes after 15 s
S28WebRTC: ephemeral credentials, REGISTER over WSSexecutedtest_telephony_e2e.py::test_s28_* (credentials of the Control Plane accepted over WSS, wrong credentials refused, after the fix of finding 9; media not tested)
S29call.ended webhook and fraud blockexecutedcall.ended proven by test_s16_s17_*; test_s29_*: block written into tenant_blocks and the call of the blocked extension refused, after the fix of finding 3
S30LiveKit SIP call to a voice agentblockedlivekit/sip ships no binary (empty GitHub releases, tags up to v1.22.0), only a container image; building it needs libopus, libopusfile and libsoxr (system packages, root); no container runtime here. Proven elsewhere: services/voice-agent/tests/test_registration_real.py, test_telephony_calls.py::test_livekit_webhook_and_voice_worker_contract (fake Twirp server)
S31Transfer of an AI call to a humanblockedsame reason as S30; TransferSIPParticipant proven against a fake Twirp server in test_telephony_calls.py

Findings​

  1. AMI publication impossible on a real Asterisk (UpdateConfig adds nothing to a template category). Fixed by tel-sipfix: AOR without template.
  2. Phones identified as navin-kamailio: REGISTER refused with 403. Fixed by tel-sipfix: X-Navin-Endpoint header, endpoint_identifier_order = header,ip,username,anonymous.
  3. No tenant_blocks table in the rendered Kamailio: htable.sets answers "HTTP 500", a fraud block never reaches the edge. Fixed: table declared (replicated by DMQ in ha) and checked in route[FROM_PHONE], 403 for a blocked tenant or extension; route[FRAUD_BLOCK] also refuses with 403 the calls of the carriers to a blocked tenant and those of its PBX to a carrier (UUID of X-Navin-Tenant turned into the tid), before any relay; deleting the key restores the calls (navin-manager/server/tests/test_kamailio_real.py::test_fraud_blocks_refuse_the_calls_of_the_tenant_until_lifted, Kamailio 5.7.4 and 6.0.8).
  4. rewrite_contact = yes behind Kamailio: the contact became the address of the edge. Fixed by tel-sipfix.
  5. Preloaded Route to the edge (Path) refused by route[FROM_INTERNAL] (loose_route is false for an initial request). Fixed by tel-sipfix.
  6. Duplicate navin-reject between the Manager and the compiler. Fixed by tel-sipfix.
  7. pl_check(...) < 0 in route[LIMITS] is never true: the CPS limit refuses nothing (8 INVITEs in one burst for a limit of 3, all relayed). Fixed: if (!pl_check(...)), a negative code being false.
  8. apps = all in cel.conf.j2: Asterisk 20 no longer accepts all, no CEL APP_START event is emitted, the recording is not announced to the ingestion. Fixed: explicit list of applications (mixmonitor, queue, dial, playback, background, voicemail, confbridge, stasis). Test S16 still goes through media.sweep; the announcement through APP_START is not proven by a test yet.
  9. WebRTC password signed with HMAC-SHA1 by the Control Plane while the Manager sets sha_algorithm to SHA256 by default: REGISTER refused. Fixed: setting EAI_TELEPHONY_EPHEMERAL_ALGORITHM (sha256 by default), to keep equal to the ephemeral_algorithm parameter of the kamailio service.
  10. The TLS listeners of Kamailio refuse handshakes for the first seconds: the bench waits for a JSONRPC answer before it starts.