sendmsg/TESTING.md
2026-07-15 11:41:58 -07:00

3.7 KiB

Testing sendmsg

The test suite lives in tests/test_sendmsg.py and covers phone/group detection, path expansion, attachment validation, Signal payload construction, CSV parsing and dispatch (BOM handling, comma repair, blank rows, skips, dry run, --json output), and CLI argument validation.

Requirements

  • Python 3.10 or newer
  • pytest (the only test dependency — sendmsg itself is stdlib-only)
pip install pytest
# or on a system where pip refuses to touch system packages (Homebrew, Debian):
pip install pytest --break-system-packages
# or keep it isolated:
python3 -m venv .venv && source .venv/bin/activate && pip install pytest

Running the tests

Run from the repository root (the tests locate the sendmsg script relative to their own path, so the working directory doesn't actually matter — but the root is the natural place):

python3 -m pytest tests/

Expected output looks like:

.....................................................................    [100%]
69 passed in 0.14s

Useful variations

python3 -m pytest tests/ -v                      # one line per test, with names
python3 -m pytest tests/ -q                      # terse summary only
python3 -m pytest tests/ -k csv                  # only tests matching "csv"
python3 -m pytest tests/ -k "bom or comma"       # match multiple keywords
python3 -m pytest tests/test_sendmsg.py::TestPhoneNumbers   # one test class
python3 -m pytest tests/test_sendmsg.py::TestCsv::test_quoted_commas_in_message  # one test
python3 -m pytest tests/ -x                      # stop at the first failure
python3 -m pytest tests/ --lf                    # re-run only what failed last time

What the tests do NOT need

  • No running signal-cli-rest-api container. All network calls (signal_rest_post, signal_rest_get) are replaced with fakes; the suite asserts on the payloads sendmsg would send.
  • No macOS / no imsg. SMS sends are stubbed the same way, so the suite runs identically on Linux and macOS.
  • No real config. The suite pins SIGNAL_REST_URL and SIGNAL_ACCOUNT environment variables before importing the script, so your real ~/.sendmsg.conf and shell environment can't leak into (or be touched by) test runs. Nothing is ever actually sent.

How the script gets imported

sendmsg has no .py extension, so the suite loads it with importlib's SourceFileLoader (see the top of tests/test_sendmsg.py). The loaded module is a normal Python module — tests call its functions (normalize_account, send_one_signal, cmd_csv via main(), ...) directly and monkeypatch its globals.

Two consequences worth knowing:

  1. Module-level code (config loading) runs at import time — that's why the env vars are pinned first.
  2. If you rename or move the sendmsg script, update SCRIPT_PATH at the top of the test file.

Continuous integration

.github/workflows/ci.yml runs on every push and pull request: a compile check (python -m py_compile sendmsg) followed by the full suite on Python 3.10, 3.11, 3.12, and 3.13. A green run on your branch means the same command that CI uses passed:

python -m py_compile sendmsg && python3 -m pytest tests/ -v

Adding tests

  • Pure helpers (parsing, normalization, validation) get direct parametrized tests — see TestPhoneNumbers for the pattern.
  • Anything that would send goes through the stubbing helpers: run_csv() for CSV behavior (returns exit code, stdout, stderr, and the captured Signal/SMS calls) and the captured_posts fixture for payload-level assertions.
  • When fixing a bug, add a test that fails on the old behavior first — most tests in the suite carry a comment naming the bug they pin down.