Files
rehearshalhub/api/tests/factories.py
Steffen Schuhmann f7be1b994d Initial commit: RehearsalHub POC
Full-stack self-hosted band rehearsal platform:

Backend (FastAPI + SQLAlchemy 2.0 async):
- Auth with JWT (register, login, /me, settings)
- Band management with Nextcloud folder integration
- Song management with audio version tracking
- Nextcloud scan to auto-import audio files
- Band membership with link-based invite system
- Song comments
- Audio analysis worker (BPM, key, loudness, waveform)
- Nextcloud activity watcher for auto-import
- WebSocket support for real-time annotation updates
- Alembic migrations (0001–0003)
- Repository pattern, Ruff + mypy configured

Frontend (React 18 + Vite + TypeScript strict):
- Login/register page with post-login redirect
- Home page with band list and creation form
- Band page with member panel, invite link, song list, NC scan
- Song page with waveform player, annotations, comment thread
- Settings page for per-user Nextcloud credentials
- Invite acceptance page (/invite/:token)
- ESLint v9 flat config + TypeScript strict mode

Infrastructure:
- Docker Compose: PostgreSQL, Redis, API, worker, watcher, nginx
- nginx reverse proxy for static files + /api/ proxy
- make check runs all linters before docker compose build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:53:03 +01:00

102 lines
2.7 KiB
Python

"""Test data factories for creating model instances in integration tests."""
import uuid
from datetime import datetime, timezone
from rehearsalhub.db.models import Annotation, AudioVersion, Band, BandMember, Member, Song
from rehearsalhub.services.auth import hash_password
async def create_member(
session,
email: str = "test@example.com",
display_name: str = "Test User",
password: str = "testpassword123",
) -> Member:
from rehearsalhub.repositories.member import MemberRepository
repo = MemberRepository(session)
return await repo.create(
email=email,
display_name=display_name,
password_hash=hash_password(password),
)
async def create_band(
session,
name: str = "Test Band",
slug: str | None = None,
creator_id: uuid.UUID | None = None,
) -> Band:
from rehearsalhub.repositories.band import BandRepository
repo = BandRepository(session)
slug = slug or f"test-band-{uuid.uuid4().hex[:6]}"
band = await repo.create(name=name, slug=slug, genre_tags=[])
if creator_id:
await repo.add_member(band.id, creator_id, role="admin")
return band
async def create_song(
session,
band_id: uuid.UUID,
creator_id: uuid.UUID | None = None,
title: str = "Test Song",
status: str = "jam",
) -> Song:
from rehearsalhub.repositories.song import SongRepository
repo = SongRepository(session)
return await repo.create(
band_id=band_id,
title=title,
status=status,
created_by=creator_id,
)
async def create_audio_version(
session,
song_id: uuid.UUID,
uploader_id: uuid.UUID | None = None,
version_number: int = 1,
analysis_status: str = "done",
) -> AudioVersion:
from rehearsalhub.repositories.audio_version import AudioVersionRepository
repo = AudioVersionRepository(session)
return await repo.create(
song_id=song_id,
version_number=version_number,
nc_file_path=f"/bands/test/songs/test/v{version_number}.wav",
nc_file_etag=uuid.uuid4().hex,
analysis_status=analysis_status,
uploaded_by=uploader_id,
)
async def create_annotation(
session,
version_id: uuid.UUID,
author_id: uuid.UUID,
type: str = "point",
timestamp_ms: int = 5000,
range_end_ms: int | None = None,
tags: list[str] | None = None,
) -> Annotation:
from rehearsalhub.repositories.annotation import AnnotationRepository
repo = AnnotationRepository(session)
return await repo.create(
version_id=version_id,
author_id=author_id,
type=type,
timestamp_ms=timestamp_ms,
range_end_ms=range_end_ms if type == "range" else None,
body="Test annotation",
label="Test label",
tags=tags or [],
)