test_musehub_sessions_service.py
python
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠ breaking
1 day ago
| 1 | """Unit tests for musehub/services/musehub_sessions.py. |
| 2 | |
| 3 | Tests the service layer directly (no HTTP) to validate session creation, |
| 4 | listing, retrieval, and ordering semantics. |
| 5 | """ |
| 6 | from __future__ import annotations |
| 7 | |
| 8 | from datetime import datetime, timedelta, timezone |
| 9 | |
| 10 | import pytest |
| 11 | from sqlalchemy.ext.asyncio import AsyncSession |
| 12 | |
| 13 | from musehub.models.musehub import SessionCreate |
| 14 | from musehub.services import musehub_sessions |
| 15 | from tests.factories import create_repo |
| 16 | |
| 17 | |
| 18 | def _session_create(**kwargs: str | int | list[str]) -> SessionCreate: |
| 19 | defaults = dict( |
| 20 | participants=["alice"], |
| 21 | intent="Recording session", |
| 22 | location="Studio A", |
| 23 | ) |
| 24 | defaults.update(kwargs) |
| 25 | return SessionCreate(**defaults) |
| 26 | |
| 27 | |
| 28 | async def test_upsert_session_creates_record(db_session: AsyncSession) -> None: |
| 29 | """upsert_session returns a SessionResponse with the expected fields.""" |
| 30 | repo = await create_repo(db_session, visibility="public") |
| 31 | data = _session_create(participants=["bob", "alice"]) |
| 32 | |
| 33 | result = await musehub_sessions.upsert_session(db_session, str(repo.repo_id), data) |
| 34 | |
| 35 | assert result.session_id is not None |
| 36 | assert set(result.participants) == {"bob", "alice"} |
| 37 | assert result.intent == "Recording session" |
| 38 | assert result.location == "Studio A" |
| 39 | assert result.is_active is True |
| 40 | |
| 41 | |
| 42 | async def test_upsert_session_uses_provided_started_at(db_session: AsyncSession) -> None: |
| 43 | """started_at from the request is preserved in the session record.""" |
| 44 | repo = await create_repo(db_session) |
| 45 | t = datetime(2026, 1, 15, 10, 0, 0, tzinfo=timezone.utc) |
| 46 | data = _session_create(started_at=t) |
| 47 | |
| 48 | result = await musehub_sessions.upsert_session(db_session, str(repo.repo_id), data) |
| 49 | await db_session.commit() |
| 50 | |
| 51 | assert result.started_at is not None |
| 52 | |
| 53 | |
| 54 | async def test_list_sessions_empty_for_new_repo(db_session: AsyncSession) -> None: |
| 55 | """A new repo with no sessions returns an empty list and total=0.""" |
| 56 | repo = await create_repo(db_session) |
| 57 | sessions, total, _ = await musehub_sessions.list_sessions(db_session, str(repo.repo_id)) |
| 58 | assert sessions == [] |
| 59 | assert total == 0 |
| 60 | |
| 61 | |
| 62 | async def test_list_sessions_returns_all_sessions(db_session: AsyncSession) -> None: |
| 63 | """list_sessions returns all sessions belonging to a repo.""" |
| 64 | repo = await create_repo(db_session) |
| 65 | rid = str(repo.repo_id) |
| 66 | |
| 67 | for i in range(3): |
| 68 | await musehub_sessions.upsert_session( |
| 69 | db_session, rid, _session_create(intent=f"Session {i}") |
| 70 | ) |
| 71 | await db_session.commit() |
| 72 | |
| 73 | sessions, total, _ = await musehub_sessions.list_sessions(db_session, rid) |
| 74 | assert total == 3 |
| 75 | assert len(sessions) == 3 |
| 76 | |
| 77 | |
| 78 | async def test_list_sessions_ordered_newest_first(db_session: AsyncSession) -> None: |
| 79 | """list_sessions returns sessions sorted by started_at descending.""" |
| 80 | repo = await create_repo(db_session) |
| 81 | rid = str(repo.repo_id) |
| 82 | now = datetime.now(tz=timezone.utc) |
| 83 | |
| 84 | for i in range(3): |
| 85 | t = now - timedelta(hours=i) |
| 86 | await musehub_sessions.upsert_session( |
| 87 | db_session, rid, _session_create(started_at=t, intent=f"Session {i}") |
| 88 | ) |
| 89 | await db_session.commit() |
| 90 | |
| 91 | sessions, _, _nc = await musehub_sessions.list_sessions(db_session, rid) |
| 92 | timestamps = [s.started_at for s in sessions] |
| 93 | assert timestamps == sorted(timestamps, reverse=True) |
| 94 | |
| 95 | |
| 96 | async def test_list_sessions_isolates_by_repo(db_session: AsyncSession) -> None: |
| 97 | """Sessions from one repo do not appear when listing another repo's sessions.""" |
| 98 | repo_a = await create_repo(db_session, slug="repo-a-sess") |
| 99 | repo_b = await create_repo(db_session, slug="repo-b-sess") |
| 100 | |
| 101 | await musehub_sessions.upsert_session( |
| 102 | db_session, str(repo_a.repo_id), _session_create() |
| 103 | ) |
| 104 | await db_session.commit() |
| 105 | |
| 106 | sessions, total, _ = await musehub_sessions.list_sessions(db_session, str(repo_b.repo_id)) |
| 107 | assert total == 0 |
| 108 | assert sessions == [] |
| 109 | |
| 110 | |
| 111 | async def test_get_session_by_id(db_session: AsyncSession) -> None: |
| 112 | """get_session returns the correct session given its ID.""" |
| 113 | repo = await create_repo(db_session) |
| 114 | rid = str(repo.repo_id) |
| 115 | |
| 116 | created = await musehub_sessions.upsert_session( |
| 117 | db_session, rid, _session_create(intent="Find me") |
| 118 | ) |
| 119 | await db_session.commit() |
| 120 | |
| 121 | found = await musehub_sessions.get_session(db_session, rid, created.session_id) |
| 122 | assert found is not None |
| 123 | assert found.session_id == created.session_id |
| 124 | assert found.intent == "Find me" |
| 125 | |
| 126 | |
| 127 | async def test_get_session_nonexistent_returns_none(db_session: AsyncSession) -> None: |
| 128 | """get_session returns None for an unknown session ID.""" |
| 129 | repo = await create_repo(db_session) |
| 130 | result = await musehub_sessions.get_session( |
| 131 | db_session, str(repo.repo_id), "00000000-0000-0000-0000-000000000000" |
| 132 | ) |
| 133 | assert result is None |
| 134 | |
| 135 | |
| 136 | async def test_list_sessions_limit_respected(db_session: AsyncSession) -> None: |
| 137 | """list_sessions respects the limit parameter.""" |
| 138 | repo = await create_repo(db_session) |
| 139 | rid = str(repo.repo_id) |
| 140 | |
| 141 | for i in range(5): |
| 142 | await musehub_sessions.upsert_session(db_session, rid, _session_create()) |
| 143 | await db_session.commit() |
| 144 | |
| 145 | sessions, total, _ = await musehub_sessions.list_sessions(db_session, rid, limit=2) |
| 146 | assert total == 5 |
| 147 | assert len(sessions) == 2 |
File History
1 commit
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠
1 day ago