Skip to content

Add pytest test suite and CI workflow - #13

Merged
diese-tech merged 1 commit into
mainfrom
claude/yaphub-pytest-ci-suite
Jul 19, 2026
Merged

Add pytest test suite and CI workflow#13
diese-tech merged 1 commit into
mainfrom
claude/yaphub-pytest-ci-suite

Conversation

@diese-tech

Copy link
Copy Markdown
Owner

Summary

YapHub has never had an automated test suite — every change so far was verified with one-off python3 -c "..." mock scripts that got thrown away after each PR. This adds a permanent pytest suite plus a GitHub Actions workflow, without touching any existing source file.

Scope discipline: this PR only adds new files (tests/, .github/workflows/ci.yml, pytest.ini, requirements-dev.txt). No file under commands/, services/, storage.py, bot.py, config.py, or schema.sql is modified, to avoid conflicting with the independent feature branch developing against those same files in parallel.

What's covered (82 tests)

  • storage.py (tests/test_storage.py) — full CRUD round-trips for profiles, active temp channels, and permits; the guarded _migrate() path exercised against a hand-built pre-migration database (missing default_user_limit/temp_name_template/panel_message_id) with existing rows, confirming columns are added and data survives; idempotent double-initialize(); panel_message_id set/get and that it doesn't leak across a delete+recreate of the same channel id. Uses a real SQLite file via tempfile.TemporaryDirectory(), matching this repo's established manual-verification style (no DB mocking layer).
  • services/ownership.py (tests/test_ownership.py) — the _authorize_channel matrix: owner absent → allowed; non-owner+admin+present → allowed; non-owner+admin+absent → denied with the "must be connected" message; non-owner+non-admin → denied with the "owner or admin" message, even if present; untracked channel / wrong-guild record → denied. Also covers the guard clauses in resolve_owned_temp_channel / resolve_owned_temp_channel_by_id.
  • services/panel.py (tests/test_panel.py) — Rename/Limit buttons don't open their modal when _resolve denies (and do when it allows); refresh_panel_message edits the message when found, no-ops when panel_message_id is None or the record is missing, and swallows discord.NotFound without raising.
  • services/room_actions.py (tests/test_room_actions.py) — apply_transfer/apply_claim guard clauses (bot target, not-in-room, already-owns-another-room, owner-still-present for claim), and that both call refresh_panel_message exactly once, after (not before) the ephemeral response. apply_kick removing a permit and revoking overwrites on success, and leaving both untouched when the move fails.
  • services/temp_channels.py (tests/test_temp_channels.py) — reconcile_active_temp_channels's stale-guild / stale-channel / empty-room cleanup, and the panel_message_id backfill matrix (already set / owner present / owner left the guild); the refcounted user_creation_locks dict is empty after 5 concurrent asyncio.gather calls to create_temp_room for the same user.
  • Command tree sanity (tests/test_command_tree.py) — builds a bare discord.ext.commands.Bot, wires up YapGroup, and asserts the full expected /yap ... command surface exists with no name collisions.
  • Circular-import regression (tests/test_circular_import.py) — runs import services.room_actions; import services.panel and the reverse order each in a fresh subprocess, to catch a regression in the deliberate lazy-import structure that normal pytest collection order could mask.
  • Syntax sanity (tests/test_py_compile.py) — py_compile's every tracked .py file.

Running locally

pip install -r requirements-dev.txt
pytest

pytest.ini sets asyncio_mode = auto so async tests need no per-test marker. Mocking follows this repo's existing conventions (see tests/conftest.py docstring): Mock(spec=discord.X) / AsyncMock() for discord.py objects, types.SimpleNamespace for lightweight bot/storage stand-ins, unittest.mock.patch for the lazily-imported refresh_panel_message.

CI

.github/workflows/ci.yml runs on every push and pull request: checkout, Python 3.11, pip install -r requirements-dev.txt, pytest. No PR template exists in the repo (checked both pull_request_template.md and .github/PULL_REQUEST_TEMPLATE/).

Test plan

  • pytest passes locally: 82 passed
  • Confirmed via git diff --stat origin/main -- commands/ services/ storage.py bot.py config.py schema.sql that no existing source file is touched
  • CI workflow YAML is valid and mirrors the local run command

🤖 Generated with Claude Code

https://claude.ai/code/session_01V9sZm6RFgrRL6z8Vy1HTLE


Generated by Claude Code

Adds a permanent, real pytest-based test suite for the previously
untested YapHub codebase, replacing the throwaway python3 -c "..." mock
scripts that had been used for one-off verification since the project
started. Covers storage.py (full CRUD round-trips, the guarded
_migrate() column-add path against a simulated pre-migration DB,
idempotent double-initialize, panel_message_id lifecycle),
services/ownership.py (the owner/admin/presence authorization matrix
in _authorize_channel, including the exact deny-message differences),
services/panel.py (Rename/Limit buttons refusing to open their modal
on denial, refresh_panel_message's found/None/missing-record/NotFound
paths), services/room_actions.py (apply_transfer/apply_claim calling
refresh_panel_message exactly once after their ephemeral response,
apply_claim's presence/owner-still-here/already-owns-another-room
guards, apply_kick revoking a permit), services/temp_channels.py
(reconcile_active_temp_channels' stale-guild/stale-channel/empty-room
cleanup and panel_message_id backfill matrix, and the refcounted
user_creation_locks eviction under concurrent asyncio.gather calls),
a command-tree sanity check for the full /yap command surface, and a
subprocess-based regression test for the services.panel /
services.room_actions circular-import hazard that normal pytest
collection order could otherwise mask.

Also adds .github/workflows/ci.yml to run the suite on every push and
pull_request, and requirements-dev.txt (pytest, pytest-asyncio, plus
requirements.txt) for local runs.

Only new files are added -- no existing commands/, services/,
storage.py, bot.py, config.py, or schema.sql file is touched, to avoid
conflicting with the independent feature branch developing against
those same files in parallel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9sZm6RFgrRL6z8Vy1HTLE
@diese-tech
diese-tech merged commit dcf989a into main Jul 19, 2026
2 checks passed
@diese-tech
diese-tech deleted the claude/yaphub-pytest-ci-suite branch July 19, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants