Trim redundant websocket phase tests
Keep room/subscription, parity, and state-reflection coverage. Drop bare receipt checks and duplicated HTTP smoke tests.
This commit is contained in:
parent
a0f4eea9df
commit
279000435b
5 changed files with 6 additions and 499 deletions
|
|
@ -35,34 +35,6 @@ class TestInfrastructure:
|
|||
assert client.is_connected()
|
||||
client.disconnect()
|
||||
|
||||
def test_http_status_still_works(self, flask_client):
|
||||
"""GET /status returns 200 with expected keys."""
|
||||
resp = flask_client.get('/status')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'spotify' in data
|
||||
assert 'media_server' in data
|
||||
assert 'soulseek' in data
|
||||
assert 'active_media_server' in data
|
||||
|
||||
def test_http_watchlist_count_still_works(self, flask_client):
|
||||
"""GET /api/watchlist/count returns 200 with expected keys."""
|
||||
resp = flask_client.get('/api/watchlist/count')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['success'] is True
|
||||
assert 'count' in data
|
||||
assert 'next_run_in_seconds' in data
|
||||
|
||||
def test_http_download_batch_still_works(self, flask_client):
|
||||
"""GET /api/download_status/batch returns 200 with expected structure."""
|
||||
resp = flask_client.get('/api/download_status/batch')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'batches' in data
|
||||
assert 'metadata' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group B — Service Status Parity
|
||||
# =========================================================================
|
||||
|
|
@ -70,16 +42,6 @@ class TestInfrastructure:
|
|||
class TestServiceStatus:
|
||||
"""status:update socket events match GET /status HTTP responses."""
|
||||
|
||||
def test_status_update_received(self, test_app, shared_state):
|
||||
"""Client receives a status:update event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_status_payload']
|
||||
socketio.emit('status:update', build())
|
||||
received = client.get_received()
|
||||
status_events = [e for e in received if e['name'] == 'status:update']
|
||||
assert len(status_events) >= 1
|
||||
|
||||
def test_status_update_shape(self, test_app, shared_state):
|
||||
"""status:update event data has the expected keys."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -139,16 +101,6 @@ class TestServiceStatus:
|
|||
class TestWatchlistCount:
|
||||
"""watchlist:count socket events match GET /api/watchlist/count."""
|
||||
|
||||
def test_watchlist_count_received(self, test_app, shared_state):
|
||||
"""Client receives a watchlist:count event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_watchlist_count_payload']
|
||||
socketio.emit('watchlist:count', build())
|
||||
received = client.get_received()
|
||||
wl_events = [e for e in received if e['name'] == 'watchlist:count']
|
||||
assert len(wl_events) >= 1
|
||||
|
||||
def test_watchlist_count_shape(self, test_app, shared_state):
|
||||
"""watchlist:count event data has expected keys."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -222,12 +174,6 @@ class TestDownloadBatch:
|
|||
with lock:
|
||||
batches[batch_id] = defaults
|
||||
|
||||
def test_download_subscribe(self, test_app):
|
||||
"""Client can subscribe to a batch room."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
client.emit('downloads:subscribe', {'batch_ids': ['batch_abc']})
|
||||
|
||||
def test_download_receives_updates(self, test_app, shared_state):
|
||||
"""After subscribing, client receives batch_update for that batch."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -338,47 +284,6 @@ class TestDownloadBatch:
|
|||
assert 'active_count' in data
|
||||
assert 'max_concurrent' in data
|
||||
|
||||
def test_download_http_batch_still_works(self, test_app, shared_state):
|
||||
"""HTTP batch endpoint works alongside WebSocket rooms."""
|
||||
app, socketio = test_app
|
||||
flask_client = app.test_client()
|
||||
|
||||
self._add_batch(shared_state, 'batch_http')
|
||||
|
||||
resp = flask_client.get('/api/download_status/batch?batch_ids=batch_http')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'batch_http' in data['batches']
|
||||
assert data['batches']['batch_http']['phase'] == 'downloading'
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group E — Fallback Behavior
|
||||
# =========================================================================
|
||||
|
||||
class TestFallback:
|
||||
"""HTTP endpoints work when no WebSocket is connected."""
|
||||
|
||||
def test_http_works_without_websocket(self, flask_client):
|
||||
"""All three HTTP endpoints work without any WebSocket connection."""
|
||||
# Status
|
||||
resp = flask_client.get('/status')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['spotify']['connected'] is True
|
||||
|
||||
# Watchlist
|
||||
resp = flask_client.get('/api/watchlist/count')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['count'] == 7
|
||||
|
||||
# Download batch (empty — no active batches)
|
||||
resp = flask_client.get('/api/download_status/batch')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['batches'] == {}
|
||||
|
||||
def test_multiple_clients_get_updates(self, test_app, shared_state):
|
||||
"""Multiple WebSocket clients each receive broadcast events."""
|
||||
app, socketio = test_app
|
||||
|
|
|
|||
|
|
@ -21,16 +21,6 @@ import time
|
|||
class TestSystemStats:
|
||||
"""dashboard:stats socket events match GET /api/system/stats."""
|
||||
|
||||
def test_stats_event_received(self, test_app, shared_state):
|
||||
"""Client receives a dashboard:stats event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_system_stats']
|
||||
socketio.emit('dashboard:stats', build())
|
||||
received = client.get_received()
|
||||
stats_events = [e for e in received if e['name'] == 'dashboard:stats']
|
||||
assert len(stats_events) >= 1
|
||||
|
||||
def test_stats_shape(self, test_app, shared_state):
|
||||
"""dashboard:stats event data has expected keys."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -70,16 +60,6 @@ class TestSystemStats:
|
|||
assert ws_data['uptime'] == http_data['uptime']
|
||||
assert ws_data['memory_usage'] == http_data['memory_usage']
|
||||
|
||||
def test_http_stats_still_works(self, flask_client):
|
||||
"""GET /api/system/stats returns 200 with expected keys."""
|
||||
resp = flask_client.get('/api/system/stats')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'active_downloads' in data
|
||||
assert 'finished_downloads' in data
|
||||
assert 'download_speed' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group B — Activity Feed
|
||||
# =========================================================================
|
||||
|
|
@ -87,16 +67,6 @@ class TestSystemStats:
|
|||
class TestActivityFeed:
|
||||
"""dashboard:activity socket events match GET /api/activity/feed."""
|
||||
|
||||
def test_activity_event_received(self, test_app, shared_state):
|
||||
"""Client receives a dashboard:activity event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_activity_feed_payload']
|
||||
socketio.emit('dashboard:activity', build())
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'dashboard:activity']
|
||||
assert len(events) >= 1
|
||||
|
||||
def test_activity_shape(self, test_app, shared_state):
|
||||
"""dashboard:activity event data has activities array."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -136,14 +106,6 @@ class TestActivityFeed:
|
|||
if ws_data['activities']:
|
||||
assert ws_data['activities'][0]['title'] == http_data['activities'][0]['title']
|
||||
|
||||
def test_http_activity_still_works(self, flask_client):
|
||||
"""GET /api/activity/feed returns 200 with expected structure."""
|
||||
resp = flask_client.get('/api/activity/feed')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'activities' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group C — Toasts (instant push)
|
||||
# =========================================================================
|
||||
|
|
@ -201,18 +163,6 @@ class TestToasts:
|
|||
assert 'show_toast' in data
|
||||
assert data['show_toast'] is True
|
||||
|
||||
def test_http_toasts_still_works(self, flask_client, shared_state):
|
||||
"""GET /api/activity/toasts returns 200 with expected structure."""
|
||||
# Add a toast-worthy activity first
|
||||
add_item = shared_state['add_activity_item']
|
||||
add_item('', 'Test', 'Sub', show_toast=True)
|
||||
|
||||
resp = flask_client.get('/api/activity/toasts')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'toasts' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group D — DB Stats
|
||||
# =========================================================================
|
||||
|
|
@ -220,16 +170,6 @@ class TestToasts:
|
|||
class TestDbStats:
|
||||
"""dashboard:db_stats socket events match GET /api/database/stats."""
|
||||
|
||||
def test_db_stats_event_received(self, test_app, shared_state):
|
||||
"""Client receives a dashboard:db_stats event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_db_stats']
|
||||
socketio.emit('dashboard:db_stats', build())
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'dashboard:db_stats']
|
||||
assert len(events) >= 1
|
||||
|
||||
def test_db_stats_shape(self, test_app, shared_state):
|
||||
"""dashboard:db_stats event data has expected keys."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -266,16 +206,6 @@ class TestDbStats:
|
|||
assert ws_data['database_size_mb'] == http_data['database_size_mb']
|
||||
assert ws_data['server_source'] == http_data['server_source']
|
||||
|
||||
def test_http_db_stats_still_works(self, flask_client):
|
||||
"""GET /api/database/stats returns 200 with expected keys."""
|
||||
resp = flask_client.get('/api/database/stats')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'artists' in data
|
||||
assert 'albums' in data
|
||||
assert 'tracks' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group E — Wishlist Count
|
||||
# =========================================================================
|
||||
|
|
@ -283,16 +213,6 @@ class TestDbStats:
|
|||
class TestWishlistCount:
|
||||
"""dashboard:wishlist_count socket events match GET /api/wishlist/count."""
|
||||
|
||||
def test_wishlist_count_received(self, test_app, shared_state):
|
||||
"""Client receives a dashboard:wishlist_count event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_wishlist_count_payload_ws']
|
||||
socketio.emit('dashboard:wishlist_count', build())
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'dashboard:wishlist_count']
|
||||
assert len(events) >= 1
|
||||
|
||||
def test_wishlist_count_shape(self, test_app, shared_state):
|
||||
"""dashboard:wishlist_count event data has count key."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -322,54 +242,6 @@ class TestWishlistCount:
|
|||
|
||||
assert ws_data['count'] == http_data['count']
|
||||
|
||||
def test_http_wishlist_count_still_works(self, flask_client):
|
||||
"""GET /api/wishlist/count returns 200 with count."""
|
||||
resp = flask_client.get('/api/wishlist/count')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'count' in data
|
||||
assert isinstance(data['count'], int)
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group F — Backward Compatibility
|
||||
# =========================================================================
|
||||
|
||||
class TestBackwardCompat:
|
||||
"""HTTP endpoints work when no WebSocket is connected."""
|
||||
|
||||
def test_all_http_endpoints_work_without_socket(self, flask_client):
|
||||
"""All 5 Phase 2 HTTP endpoints work without any WebSocket connection."""
|
||||
# System stats
|
||||
resp = flask_client.get('/api/system/stats')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['active_downloads'] == 2
|
||||
|
||||
# Activity feed
|
||||
resp = flask_client.get('/api/activity/feed')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'activities' in data
|
||||
|
||||
# Toasts
|
||||
resp = flask_client.get('/api/activity/toasts')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'toasts' in data
|
||||
|
||||
# DB stats
|
||||
resp = flask_client.get('/api/database/stats')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['artists'] == 350
|
||||
|
||||
# Wishlist count
|
||||
resp = flask_client.get('/api/wishlist/count')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['count'] == 5
|
||||
|
||||
def test_multiple_clients_get_dashboard_updates(self, test_app, shared_state):
|
||||
"""Multiple WebSocket clients each receive dashboard broadcast events."""
|
||||
app, socketio = test_app
|
||||
|
|
|
|||
|
|
@ -39,19 +39,6 @@ ENDPOINTS = {
|
|||
class TestEnrichmentEventDelivery:
|
||||
"""enrichment:<worker> socket events are received by the client."""
|
||||
|
||||
@pytest.mark.parametrize('worker', WORKERS)
|
||||
def test_enrichment_event_received(self, test_app, shared_state, worker):
|
||||
"""Client receives an enrichment:<worker> event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_enrichment_status']
|
||||
|
||||
socketio.emit(f'enrichment:{worker}', build(worker))
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == f'enrichment:{worker}']
|
||||
assert len(events) >= 1
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group B — Data Shape (parameterized)
|
||||
# =========================================================================
|
||||
|
|
@ -160,38 +147,11 @@ class TestEnrichmentHttpParity:
|
|||
|
||||
|
||||
# =========================================================================
|
||||
# Group D — HTTP Still Works (parameterized)
|
||||
# =========================================================================
|
||||
|
||||
class TestEnrichmentHttpStillWorks:
|
||||
"""HTTP endpoints return 200 with expected structure."""
|
||||
|
||||
@pytest.mark.parametrize('worker', WORKERS)
|
||||
def test_http_enrichment_still_works(self, flask_client, worker):
|
||||
"""GET /api/<worker>/status returns 200."""
|
||||
endpoint = ENDPOINTS[worker]
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'running' in data
|
||||
assert 'paused' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group E — Backward Compatibility
|
||||
# Group D — Backward Compatibility
|
||||
# =========================================================================
|
||||
|
||||
class TestEnrichmentBackwardCompat:
|
||||
"""HTTP endpoints work when no WebSocket is connected."""
|
||||
|
||||
def test_all_http_endpoints_work_without_socket(self, flask_client):
|
||||
"""All 7 enrichment HTTP endpoints work without any WebSocket connection."""
|
||||
for worker in WORKERS:
|
||||
endpoint = ENDPOINTS[worker]
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['running'] is True
|
||||
"""WebSocket clients still receive broadcast enrichment updates."""
|
||||
|
||||
def test_multiple_clients_get_enrichment_updates(self, test_app, shared_state):
|
||||
"""Multiple WebSocket clients each receive enrichment events."""
|
||||
|
|
|
|||
|
|
@ -38,19 +38,6 @@ ENDPOINTS = {
|
|||
class TestToolEventDelivery:
|
||||
"""tool:<name> socket events are received by the client."""
|
||||
|
||||
@pytest.mark.parametrize('tool', TOOLS)
|
||||
def test_tool_event_received(self, test_app, shared_state, tool):
|
||||
"""Client receives a tool:<name> event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_tool_status']
|
||||
|
||||
socketio.emit(f'tool:{tool}', build(tool))
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == f'tool:{tool}']
|
||||
assert len(events) >= 1
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group B — Data Shape (individual per tool)
|
||||
# =========================================================================
|
||||
|
|
@ -255,41 +242,11 @@ class TestToolHttpParity:
|
|||
|
||||
|
||||
# =========================================================================
|
||||
# Group D — HTTP Still Works (parameterized)
|
||||
# =========================================================================
|
||||
|
||||
class TestToolHttpStillWorks:
|
||||
"""HTTP endpoints return 200 with expected structure."""
|
||||
|
||||
@pytest.mark.parametrize('tool', TOOLS)
|
||||
def test_http_tool_still_works(self, flask_client, tool):
|
||||
"""GET /api/<tool>/status returns 200."""
|
||||
endpoint = ENDPOINTS[tool]
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
if tool == 'logs':
|
||||
assert 'logs' in data
|
||||
elif tool == 'metadata':
|
||||
assert 'success' in data
|
||||
assert 'status' in data
|
||||
else:
|
||||
assert 'status' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group E — Backward Compatibility
|
||||
# Group D — Backward Compatibility
|
||||
# =========================================================================
|
||||
|
||||
class TestToolBackwardCompat:
|
||||
"""HTTP endpoints work when no WebSocket is connected."""
|
||||
|
||||
def test_all_http_endpoints_work_without_socket(self, flask_client):
|
||||
"""All 7 tool HTTP endpoints work without any WebSocket connection."""
|
||||
for tool in TOOLS:
|
||||
endpoint = ENDPOINTS[tool]
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
"""WebSocket clients still receive broadcast tool updates."""
|
||||
|
||||
def test_multiple_clients_get_tool_updates(self, test_app, shared_state):
|
||||
"""Multiple WebSocket clients each receive tool events."""
|
||||
|
|
|
|||
|
|
@ -29,28 +29,6 @@ SYNC_PLAYLIST_IDS = ['test-playlist-1']
|
|||
|
||||
class TestSyncEventDelivery:
|
||||
"""sync:progress socket events are received by subscribed clients."""
|
||||
|
||||
def test_sync_event_received(self, test_app, shared_state):
|
||||
"""Client subscribes and receives sync:progress event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_sync_status']
|
||||
|
||||
# Subscribe to sync room
|
||||
client.emit('sync:subscribe', {'playlist_ids': ['test-playlist-1']})
|
||||
|
||||
# Server emits to the room
|
||||
socketio.emit('sync:progress', {
|
||||
'playlist_id': 'test-playlist-1',
|
||||
**build('test-playlist-1')
|
||||
}, room='sync:test-playlist-1')
|
||||
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'sync:progress']
|
||||
assert len(events) >= 1
|
||||
|
||||
client.disconnect()
|
||||
|
||||
def test_sync_only_subscribed(self, test_app, shared_state):
|
||||
"""Unsubscribed client does NOT receive sync:progress events."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -155,30 +133,6 @@ class TestSyncDataShape:
|
|||
|
||||
class TestDiscoveryEventDelivery:
|
||||
"""discovery:progress socket events are received by subscribed clients."""
|
||||
|
||||
@pytest.mark.parametrize('platform,pid', [
|
||||
('tidal', 'test-tidal-1'),
|
||||
('youtube', 'test-yt-hash'),
|
||||
])
|
||||
def test_discovery_event_received(self, test_app, shared_state, platform, pid):
|
||||
"""Client subscribes and receives discovery:progress event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_discovery_status']
|
||||
|
||||
client.emit('discovery:subscribe', {'ids': [pid]})
|
||||
|
||||
payload = build(platform, pid)
|
||||
payload['platform'] = platform
|
||||
payload['id'] = pid
|
||||
socketio.emit('discovery:progress', payload, room=f'discovery:{pid}')
|
||||
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'discovery:progress']
|
||||
assert len(events) >= 1
|
||||
|
||||
client.disconnect()
|
||||
|
||||
def test_discovery_only_subscribed(self, test_app, shared_state):
|
||||
"""Unsubscribed client does NOT receive discovery:progress events."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -281,24 +235,6 @@ class TestDiscoveryDataShape:
|
|||
|
||||
class TestScanEventDelivery:
|
||||
"""Broadcast scan events are received by all connected clients."""
|
||||
|
||||
def test_watchlist_scan_received(self, test_app, shared_state):
|
||||
"""All clients receive scan:watchlist event."""
|
||||
app, socketio = test_app
|
||||
client1 = socketio.test_client(app)
|
||||
client2 = socketio.test_client(app)
|
||||
build = shared_state['build_watchlist_scan_status']
|
||||
|
||||
socketio.emit('scan:watchlist', build())
|
||||
|
||||
for client in [client1, client2]:
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'scan:watchlist']
|
||||
assert len(events) >= 1
|
||||
|
||||
client1.disconnect()
|
||||
client2.disconnect()
|
||||
|
||||
def test_watchlist_scan_shape(self, test_app, shared_state):
|
||||
"""scan:watchlist data has success, status, current_artist_name."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -321,23 +257,6 @@ class TestScanEventDelivery:
|
|||
|
||||
client.disconnect()
|
||||
|
||||
def test_media_scan_received(self, test_app, shared_state):
|
||||
"""All clients receive scan:media event."""
|
||||
app, socketio = test_app
|
||||
client1 = socketio.test_client(app)
|
||||
client2 = socketio.test_client(app)
|
||||
build = shared_state['build_media_scan_status']
|
||||
|
||||
socketio.emit('scan:media', build())
|
||||
|
||||
for client in [client1, client2]:
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'scan:media']
|
||||
assert len(events) >= 1
|
||||
|
||||
client1.disconnect()
|
||||
client2.disconnect()
|
||||
|
||||
def test_media_scan_shape(self, test_app, shared_state):
|
||||
"""scan:media data has success and status with is_scanning."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -491,33 +410,11 @@ class TestScanHttpParity:
|
|||
|
||||
class TestHttpStillWorks:
|
||||
"""HTTP endpoints return 200 with expected structure."""
|
||||
|
||||
def test_sync_http_works(self, flask_client):
|
||||
"""GET /api/sync/status/:id returns 200."""
|
||||
resp = flask_client.get('/api/sync/status/test-playlist-1')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'status' in data
|
||||
assert 'progress' in data
|
||||
|
||||
def test_sync_http_404_unknown(self, flask_client):
|
||||
"""GET /api/sync/status/:id returns 404 for unknown playlist."""
|
||||
resp = flask_client.get('/api/sync/status/nonexistent')
|
||||
assert resp.status_code == 404
|
||||
|
||||
@pytest.mark.parametrize('platform,endpoint', [
|
||||
('tidal', '/api/tidal/discovery/status/test-tidal-1'),
|
||||
('youtube', '/api/youtube/discovery/status/test-yt-hash'),
|
||||
])
|
||||
def test_discovery_http_works(self, flask_client, platform, endpoint):
|
||||
"""GET /api/<platform>/discovery/status/:id returns 200."""
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'phase' in data
|
||||
assert 'status' in data
|
||||
assert 'results' in data
|
||||
|
||||
def test_discovery_http_not_found(self, flask_client):
|
||||
"""GET /api/tidal/discovery/status/:id returns error for unknown ID."""
|
||||
resp = flask_client.get('/api/tidal/discovery/status/nonexistent')
|
||||
|
|
@ -525,42 +422,12 @@ class TestHttpStillWorks:
|
|||
data = resp.get_json()
|
||||
assert 'error' in data
|
||||
|
||||
def test_watchlist_scan_http_works(self, flask_client):
|
||||
"""GET /api/watchlist/scan/status returns 200."""
|
||||
resp = flask_client.get('/api/watchlist/scan/status')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['success'] is True
|
||||
assert 'status' in data
|
||||
|
||||
def test_media_scan_http_works(self, flask_client):
|
||||
"""GET /api/scan/status returns 200."""
|
||||
resp = flask_client.get('/api/scan/status')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['success'] is True
|
||||
assert 'status' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group H — Backward Compatibility
|
||||
# Group H — Broadcast Updates
|
||||
# =========================================================================
|
||||
|
||||
class TestBackwardCompat:
|
||||
"""HTTP endpoints work when no WebSocket is connected."""
|
||||
|
||||
def test_all_http_endpoints_work_without_socket(self, flask_client):
|
||||
"""All Phase 5 HTTP endpoints work without any WebSocket connection."""
|
||||
endpoints = [
|
||||
'/api/sync/status/test-playlist-1',
|
||||
'/api/tidal/discovery/status/test-tidal-1',
|
||||
'/api/youtube/discovery/status/test-yt-hash',
|
||||
'/api/watchlist/scan/status',
|
||||
'/api/scan/status',
|
||||
]
|
||||
for endpoint in endpoints:
|
||||
resp = flask_client.get(endpoint)
|
||||
assert resp.status_code == 200
|
||||
"""Broadcast scan events still reach multiple clients."""
|
||||
|
||||
def test_multiple_clients_get_scan_updates(self, test_app, shared_state):
|
||||
"""Multiple WebSocket clients each receive scan events."""
|
||||
|
|
@ -668,29 +535,6 @@ PLATFORM_SYNC_IDS = [
|
|||
|
||||
class TestPlatformSyncEventDelivery:
|
||||
"""Platform sync pollers receive sync:progress via WS rooms."""
|
||||
|
||||
@pytest.mark.parametrize('platform,sync_id', PLATFORM_SYNC_IDS)
|
||||
def test_platform_sync_event_received(self, test_app, shared_state, platform, sync_id):
|
||||
"""Client subscribes to platform sync_playlist_id and receives sync:progress."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_sync_status']
|
||||
|
||||
client.emit('sync:subscribe', {'playlist_ids': [sync_id]})
|
||||
|
||||
socketio.emit('sync:progress', {
|
||||
'playlist_id': sync_id, **build(sync_id)
|
||||
}, room=f'sync:{sync_id}')
|
||||
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'sync:progress']
|
||||
assert len(events) >= 1
|
||||
data = events[0]['args'][0]
|
||||
assert data['playlist_id'] == sync_id
|
||||
assert data['status'] == 'syncing'
|
||||
|
||||
client.disconnect()
|
||||
|
||||
@pytest.mark.parametrize('platform,sync_id', PLATFORM_SYNC_IDS)
|
||||
def test_platform_sync_not_received_when_unsubscribed(
|
||||
self, test_app, shared_state, platform, sync_id):
|
||||
|
|
@ -796,35 +640,12 @@ class TestPlatformSyncEventDelivery:
|
|||
|
||||
client.disconnect()
|
||||
|
||||
@pytest.mark.parametrize('platform,sync_id', PLATFORM_SYNC_IDS)
|
||||
def test_platform_sync_http_still_works(self, flask_client, platform, sync_id):
|
||||
"""GET /api/sync/status/<platform_sync_id> returns 200."""
|
||||
resp = flask_client.get(f'/api/sync/status/{sync_id}')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'status' in data
|
||||
assert 'progress' in data
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Group H — Wishlist Stats (broadcast)
|
||||
# =========================================================================
|
||||
|
||||
class TestWishlistStatsEventDelivery:
|
||||
"""wishlist:stats broadcast events are received by the client."""
|
||||
|
||||
def test_wishlist_stats_event_received(self, test_app, shared_state):
|
||||
"""Client receives a wishlist:stats event."""
|
||||
app, socketio = test_app
|
||||
client = socketio.test_client(app)
|
||||
build = shared_state['build_wishlist_stats']
|
||||
|
||||
socketio.emit('wishlist:stats', build())
|
||||
received = client.get_received()
|
||||
events = [e for e in received if e['name'] == 'wishlist:stats']
|
||||
assert len(events) >= 1
|
||||
client.disconnect()
|
||||
|
||||
def test_wishlist_stats_data_shape(self, test_app, shared_state):
|
||||
"""wishlist:stats has is_auto_processing and next_run_in_seconds."""
|
||||
app, socketio = test_app
|
||||
|
|
@ -843,14 +664,6 @@ class TestWishlistStatsEventDelivery:
|
|||
assert isinstance(data['next_run_in_seconds'], (int, float))
|
||||
client.disconnect()
|
||||
|
||||
def test_wishlist_stats_http_still_works(self, flask_client):
|
||||
"""GET /api/wishlist/stats returns 200 with expected keys."""
|
||||
resp = flask_client.get('/api/wishlist/stats')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert 'is_auto_processing' in data
|
||||
assert 'next_run_in_seconds' in data
|
||||
|
||||
def test_wishlist_stats_auto_processing_detection(self, test_app, shared_state):
|
||||
"""When is_auto_processing is True, client detects it."""
|
||||
app, socketio = test_app
|
||||
|
|
|
|||
Loading…
Reference in a new issue