afterhours-shift-manager/tests/slack_bot/conftest.py
Adam Moussa a16bf6d370 Add pytest suite and wire it into CI
Stands up the first automated tests for the repo (151 tests) and turns on
the CI test step.

- Lift slack-bot handlers out of create_app() closures to module level so
  they're unit-testable; create_app is now a thin Bolt-wiring layer. No
  behavior change (handler entrypoints and create_app signature unchanged).
- tests/ mirrors src/: shared layer (schedule, blocks, 3CX client,
  ring_scheduler, secrets) + all four Lambdas (pay math, drop/swap/pick/
  admin/register/rate, pickup button, roster sync, queue scheduler).
- All boundaries mocked: DynamoDB/SES/Secrets via moto, 3CX HTTP via
  responses, Slack via fakes, time via freezegun. No real network/AWS.
- pyproject.toml pytest config (pythonpath=src/shared, importlib mode);
  per-package conftest loads each app.py under a unique name to avoid the
  four-app.py collision. tests/requirements.txt for test-only deps.
- ci.yaml: run-tests: true (reusable workflow auto-installs deps) and lint
  the tests dir too.
- README Testing section.

Closes #85
2026-06-01 18:50:27 -04:00

53 lines
1.2 KiB
Python

"""Load src/slack-bot/app.py under a unique module name so it doesn't collide
with the other Lambdas' app.py modules in sys.modules, plus common test doubles."""
import importlib.util
import pathlib
import sys
from unittest.mock import MagicMock
import pytest
_ROOT = pathlib.Path(__file__).resolve().parents[2]
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, _ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
sys.modules[name] = mod
spec.loader.exec_module(mod)
return mod
@pytest.fixture
def slackbot_app():
return _load("slackbot_app", "src/slack-bot/app.py")
@pytest.fixture
def respond():
return MagicMock(name="respond")
@pytest.fixture
def client():
return MagicMock(name="client")
@pytest.fixture
def routing_spy(slackbot_app, monkeypatch):
"""Replace the 3CX routing call with a spy so tests can assert on it
without touching the network."""
spy = MagicMock(name="_update_3cx_routing")
monkeypatch.setattr(slackbot_app, "_update_3cx_routing", spy)
return spy
@pytest.fixture
def text_of():
"""Helper: pull the `text=` kwarg from a mock's most recent call."""
def _get(mock):
return mock.call_args.kwargs.get("text", "")
return _get