Fix 403 for invited members streaming audio and 500 on invite listing
Invited members have no Nextcloud credentials of their own — stream and waveform endpoints now use the file uploader's NC credentials instead of the current member's. Falls back to the current member if uploaded_by is null. The invite listing/info endpoints were comparing timezone-aware expires_at values against naive datetime.now(), causing a TypeError (500). Fixed by using datetime.now(timezone.utc) throughout bands.py and invites.py. Also removes leftover debug logging from versions.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
"""Integration tests for version streaming endpoints."""
|
||||
|
||||
import pytest
|
||||
import uuid
|
||||
from unittest.mock import AsyncMock, patch, MagicMock
|
||||
import httpx
|
||||
|
||||
from rehearsalhub.routers.versions import stream_version, get_waveform
|
||||
from rehearsalhub.db.models import Member, AudioVersion
|
||||
from rehearsalhub.db.models import Member, AudioVersion, Song
|
||||
from rehearsalhub.schemas.audio_version import AudioVersionRead
|
||||
|
||||
|
||||
@@ -15,24 +16,27 @@ async def test_stream_version_connection_error():
|
||||
"""Test stream_version endpoint handles connection errors gracefully."""
|
||||
# Mock dependencies
|
||||
mock_session = MagicMock()
|
||||
mock_member = Member(id=1, name="Test User")
|
||||
mock_member = Member(id=uuid.uuid4())
|
||||
|
||||
# Mock version with nc_file_path
|
||||
# Mock song and version
|
||||
mock_song = Song(id=uuid.uuid4(), band_id=uuid.uuid4())
|
||||
mock_version = AudioVersion(
|
||||
id="test-version-id",
|
||||
song_id=mock_song.id,
|
||||
nc_file_path="test/path/file.mp3",
|
||||
waveform_url="test/path/waveform.json"
|
||||
waveform_url="test/path/waveform.json",
|
||||
version_number=1
|
||||
)
|
||||
|
||||
# Mock the storage client to raise connection error
|
||||
with patch("rehearsalhub.routers.versions.NextcloudClient") as mock_client_class:
|
||||
mock_client = MagicMock()
|
||||
mock_client.download = AsyncMock(side_effect=httpx.ConnectError("Connection failed"))
|
||||
mock_client_class.return_value = mock_client
|
||||
mock_client_class.for_member.return_value = mock_client
|
||||
|
||||
# Mock the membership check
|
||||
with patch("rehearsalhub.routers.versions._get_version_and_assert_band_membership",
|
||||
return_value=(mock_version, None)):
|
||||
return_value=(mock_version, mock_song)):
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -45,7 +49,7 @@ async def test_stream_version_connection_error():
|
||||
|
||||
# Should return 503 Service Unavailable
|
||||
assert exc_info.value.status_code == 503
|
||||
assert "Failed to connect to storage" in str(exc_info.value.detail)
|
||||
assert "Storage service unavailable" in str(exc_info.value.detail)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -54,13 +58,16 @@ async def test_stream_version_file_not_found():
|
||||
"""Test stream_version endpoint handles 404 errors gracefully."""
|
||||
# Mock dependencies
|
||||
mock_session = MagicMock()
|
||||
mock_member = Member(id=1, name="Test User")
|
||||
mock_member = Member(id=uuid.uuid4())
|
||||
|
||||
# Mock version with nc_file_path
|
||||
# Mock song and version
|
||||
mock_song = Song(id=uuid.uuid4(), band_id=uuid.uuid4())
|
||||
mock_version = AudioVersion(
|
||||
id="test-version-id",
|
||||
song_id=mock_song.id,
|
||||
nc_file_path="test/path/file.mp3",
|
||||
waveform_url="test/path/waveform.json"
|
||||
waveform_url="test/path/waveform.json",
|
||||
version_number=1
|
||||
)
|
||||
|
||||
# Mock the storage client to raise 404 error
|
||||
@@ -75,11 +82,11 @@ async def test_stream_version_file_not_found():
|
||||
mock_client.download = AsyncMock(
|
||||
side_effect=httpx.HTTPStatusError("Not found", request=MagicMock(), response=mock_response)
|
||||
)
|
||||
mock_client_class.return_value = mock_client
|
||||
mock_client_class.for_member.return_value = mock_client
|
||||
|
||||
# Mock the membership check
|
||||
with patch("rehearsalhub.routers.versions._get_version_and_assert_band_membership",
|
||||
return_value=(mock_version, None)):
|
||||
return_value=(mock_version, mock_song)):
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -101,24 +108,27 @@ async def test_get_waveform_connection_error():
|
||||
"""Test get_waveform endpoint handles connection errors gracefully."""
|
||||
# Mock dependencies
|
||||
mock_session = MagicMock()
|
||||
mock_member = Member(id=1, name="Test User")
|
||||
mock_member = Member(id=uuid.uuid4())
|
||||
|
||||
# Mock version with waveform_url
|
||||
# Mock song and version
|
||||
mock_song = Song(id=uuid.uuid4(), band_id=uuid.uuid4())
|
||||
mock_version = AudioVersion(
|
||||
id="test-version-id",
|
||||
song_id=mock_song.id,
|
||||
nc_file_path="test/path/file.mp3",
|
||||
waveform_url="test/path/waveform.json"
|
||||
waveform_url="test/path/waveform.json",
|
||||
version_number=1
|
||||
)
|
||||
|
||||
# Mock the storage client to raise connection error
|
||||
with patch("rehearsalhub.routers.versions.NextcloudClient") as mock_client_class:
|
||||
mock_client = MagicMock()
|
||||
mock_client.download = AsyncMock(side_effect=httpx.ConnectError("Connection failed"))
|
||||
mock_client_class.return_value = mock_client
|
||||
mock_client_class.for_member.return_value = mock_client
|
||||
|
||||
# Mock the membership check
|
||||
with patch("rehearsalhub.routers.versions._get_version_and_assert_band_membership",
|
||||
return_value=(mock_version, None)):
|
||||
return_value=(mock_version, mock_song)):
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -131,7 +141,7 @@ async def test_get_waveform_connection_error():
|
||||
|
||||
# Should return 503 Service Unavailable
|
||||
assert exc_info.value.status_code == 503
|
||||
assert "Failed to connect to storage" in str(exc_info.value.detail)
|
||||
assert "Storage service unavailable" in str(exc_info.value.detail)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -140,24 +150,27 @@ async def test_stream_version_success():
|
||||
"""Test successful streaming when connection works."""
|
||||
# Mock dependencies
|
||||
mock_session = MagicMock()
|
||||
mock_member = Member(id=1, name="Test User")
|
||||
mock_member = Member(id=uuid.uuid4())
|
||||
|
||||
# Mock version with nc_file_path
|
||||
# Mock song and version
|
||||
mock_song = Song(id=uuid.uuid4(), band_id=uuid.uuid4())
|
||||
mock_version = AudioVersion(
|
||||
id="test-version-id",
|
||||
song_id=mock_song.id,
|
||||
nc_file_path="test/path/file.mp3",
|
||||
waveform_url="test/path/waveform.json"
|
||||
waveform_url="test/path/waveform.json",
|
||||
version_number=1
|
||||
)
|
||||
|
||||
# Mock the storage client to return success
|
||||
with patch("rehearsalhub.routers.versions.NextcloudClient") as mock_client_class:
|
||||
mock_client = MagicMock()
|
||||
mock_client.download = AsyncMock(return_value=b"audio_data")
|
||||
mock_client_class.return_value = mock_client
|
||||
mock_client_class.for_member.return_value = mock_client
|
||||
|
||||
# Mock the membership check
|
||||
with patch("rehearsalhub.routers.versions._get_version_and_assert_band_membership",
|
||||
return_value=(mock_version, None)):
|
||||
return_value=(mock_version, mock_song)):
|
||||
|
||||
result = await stream_version(
|
||||
version_id="test-version-id",
|
||||
|
||||
Reference in New Issue
Block a user