# Testing How to verify each part of the system. Do the mailcmd tests **as the `asterisk`** user, not as yourself — that's where permission bugs hide. --- ## 0. Pre-checks ```bash df -h /var # must not be full (Postfix 452) grep '^mailcmd' /etc/asterisk/voicemail.conf sudo systemctl is-active vm-api vm-portal curl -s http://127.0.0.1:8098/api/healthz curl -s http://127.0.0.1:8099/healthz ``` --- ## 1. Unit tests (no sudo, no asterisk) ```bash cd /home/jp/Work/asterisk-voicemail ./venv/bin/python tests/test_telegram.py # routing, caption clipping, opus transcode ./venv/bin/python tests/test_contacts.py # vCard/CSV parse, digit-normalised match ``` `test_telegram.py` asserts: per-mailbox → one chat; comma list → N chats; group id negative; unmapped → default; `enabled=no` → no route; caption clipped to 1017 chars on a word boundary when over 1024; a 374 KB wav → 64 KB `OggS` opus. `test_contacts.py` asserts: `+447****0123`, `07700900123`, `447700900123` all match one contact (last-9-digit key); multi-TEL cards; Google CSV `:::` split. --- ## 2. Build a fake voicemail and run the real pipeline ```bash # needs ffmpeg; uses test_vm.wav shipped in the repo (or any wav) python3 tests/make_test_mail.py test_vm.wav you@example.com > /tmp/t.eml sudo chmod 644 /tmp/t.eml sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_mailcmd.py < /tmp/t.eml sudo tail -5 /var/log/asterisk/vm_mailcmd.log ``` Expect `transcribed … chars` then `sent enriched notification to you@example.com`. Then confirm real delivery: ```bash sudo grep "to=" /var/log/mail.log | tail -1 # status=sent ``` ### Fail-safe: a message with no audio ```bash printf 'From: a@b\nTo: you@example.com\nSubject: no audio\n\nbody\n' > /tmp/n.eml sudo chmod 644 /tmp/n.eml sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_mailcmd.py < /tmp/n.eml # log must show: "no audio attachment; relaying original" ``` ### Fail-safe: a thrown error relays the original Temporarily point `VM_DB` at an unreadable path (or MySQL offline); the script should fall back to relaying the original Asterisk message and log `relaying original, mailcmd error`. --- ## 3. Telegram (needs a real token) ```bash sudo /opt/vm-transcribe/venv/bin/python3 /opt/vm-transcribe/vm_tg_setup.py ids # each recipient must have messaged the bot once (/start) to appear sudo /opt/vm-transcribe/venv/bin/python3 /opt/vm-transcribe/vm_tg_setup.py test 1001 # sends a real voice note + summary; exits non-zero on any failure ``` Offline, the routing logic is covered by `tests/test_telegram.py` so you don't need a token to verify the config parser. --- ## 4. Contacts (MySQL) ```bash # resolve a caller ID against the MySQL contacts table sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_contacts.py '<07700900123>' # prints (name, email) or the raw caller id # load an export sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_import_contacts.py /path/to/contacts.vcf ``` --- ## 5. JSON API — auth, authz, playback, delete Run the API locally (or against the live service) and exercise it with curl. Use `VM_INSECURE_COOKIE=1` only for plain-HTTP local tests so the `Secure` cookie can be set. ```bash B=http://127.0.0.1:8098 # login ok curl -s -c /tmp/j -o /dev/null -w '%{redirect_url}\n' -d 'mailbox=7940&pin=5159' $B/api/login # wrong pin rejected curl -s -o /dev/null -w '%{http_code}\n' -d 'mailbox=7940&pin=1111' $B/api/login # authenticated message list curl -s -b /tmp/j $B/api/messages | head -c 400 # contacts CRUD curl -s -b /tmp/j $B/api/contacts | head -c 400 ``` ### Authorization (IDOR) — must 404 A session for mailbox A must never reach mailbox B's data. With a cookie for mailbox 7940, hitting `/api/messages/{id-owned-by-1001}` (or its audio/delete variants) must return **404**, never serve or delete the other mailbox's message. Add this assertion whenever you change `vm_store` or `vm_api`. --- ## 6. Portal over the real URL (TLS + proxy) ```bash R="--resolve vm.txt3.net:443:ORIGIN.IP" curl -s $R https://vm.txt3.net/api/healthz curl -s $R -o /dev/null -w '%{http_code}\n' https://vm.txt3.net/login curl -s $R -D- -o /dev/null https://vm.txt3.net/login | grep -iE 'strict-transport|content-security|x-frame' # http -> https redirect curl -s $R -o /dev/null -w '%{redirect_url}\n' http://vm.txt3.net/ ``` The React SPA is served as static files; all data flows through `/api/`. Confirm the SPA fallback returns `index.html` for arbitrary client-side routes: ```bash curl -s $R -o /dev/null -w '%{http_code}\n' https://vm.txt3.net/some/client/route ``` Behind a CDN, `--resolve` to the **origin** IP; otherwise you are testing the CDN, not your server. --- ## 7. Backfill ```bash sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_import.py --dry-run --limit 5 # preview: lists 5 messages with caller + date, no writes sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 \ /opt/vm-transcribe/vm_import.py --limit 3 # real: 3 imported, others skipped; re-running is a no-op (idempotent) ``` Verify they appear via the API/messages endpoint and are playable (§5). Expect a `no_speech` count — 44-byte WAVs are hung-up calls, not failures. CDR history import: ```bash sudo -u asterisk /opt/vm-transcribe/venv/bin/python3 /opt/vm-transcribe/vm_backfill_cdr.py ```