""" API contract tests - verify all endpoints match expected schemas. """ import pytest class TestAuthContracts: """Contract tests for authentication endpoints.""" @pytest.mark.contract def test_login_endpoint_exists(self, client): """Login endpoint should exist at /auth/login.""" response = client.post("/auth/login", json={}) # Should not be 404 assert response.status_code != 404 @pytest.mark.contract def test_bootstrap_status_endpoint_exists(self, client): """Bootstrap status endpoint should exist.""" response = client.get("/auth/bootstrap/status") assert response.status_code != 404 @pytest.mark.contract def test_bootstrap_owner_endpoint_exists(self, client): """Bootstrap owner endpoint should exist.""" response = client.post("/auth/bootstrap/owner", json={}) assert response.status_code != 404 @pytest.mark.contract def test_pair_code_endpoint_exists(self, client): """Pair code endpoint should exist.""" response = client.get("/auth/getpaircode") # Should require auth, not 404 assert response.status_code != 404 @pytest.mark.contract def test_pair_endpoint_exists(self, client): """Pair endpoint should exist.""" response = client.get("/auth/pair") assert response.status_code != 404 @pytest.mark.contract def test_refresh_endpoint_exists(self, client): """Refresh endpoint should exist.""" response = client.post("/auth/refresh") assert response.status_code != 404 @pytest.mark.contract def test_invite_create_endpoint_exists(self, client): """Invite create endpoint should exist.""" response = client.post("/auth/invite/create", json={}) assert response.status_code != 404 @pytest.mark.contract def test_invite_accept_endpoint_exists(self, client): """Invite accept endpoint should exist.""" response = client.post("/auth/invite/accept", json={}) assert response.status_code != 404 class TestDownloadContracts: """Contract tests for download endpoints.""" @pytest.mark.contract def test_jobs_endpoint_exists(self, client): """Jobs endpoint should exist.""" response = client.get("/api/downloads/jobs") assert response.status_code != 404 @pytest.mark.contract def test_create_job_endpoint_exists(self, client): """Create job endpoint should exist.""" response = client.post("/api/downloads/jobs", json={}) assert response.status_code != 404 @pytest.mark.contract def test_queue_endpoint_exists(self, client): """Queue endpoint should exist.""" response = client.get("/api/downloads/queue") assert response.status_code != 404 @pytest.mark.contract def test_status_endpoint_exists(self, client): """Status endpoint should exist.""" response = client.get("/api/downloads/status") assert response.status_code != 404 @pytest.mark.contract def test_history_endpoint_exists(self, client): """History endpoint should exist.""" response = client.get("/api/downloads/history") assert response.status_code != 404 @pytest.mark.contract def test_import_candidates_endpoint_exists(self, client): """Import candidates endpoint should exist.""" response = client.post("/api/downloads/imports/candidates", json={}) assert response.status_code != 404 @pytest.mark.contract def test_import_confirm_endpoint_exists(self, client): """Import confirm endpoint should exist.""" response = client.post("/api/downloads/imports/confirm", json={}) assert response.status_code != 404 class TestCatalogContracts: """Contract tests for catalog/search endpoints.""" @pytest.mark.contract def test_search_endpoint_exists(self, client): """Search endpoint should exist.""" response = client.get("/api/catalog/search") assert response.status_code != 404 @pytest.mark.contract def test_tracks_endpoint_exists(self, client): """Tracks endpoint should exist.""" response = client.get("/api/catalog/tracks") assert response.status_code != 404 @pytest.mark.contract def test_albums_endpoint_exists(self, client): """Albums endpoint should exist.""" response = client.get("/api/catalog/albums") assert response.status_code != 404 @pytest.mark.contract def test_artists_endpoint_exists(self, client): """Artists endpoint should exist.""" response = client.get("/api/catalog/artists") assert response.status_code != 404 @pytest.mark.contract def test_folders_endpoint_exists(self, client): """Folders endpoint should exist.""" response = client.get("/api/catalog/folders") assert response.status_code != 404 class TestFavoritesContracts: """Contract tests for favorites endpoints.""" @pytest.mark.contract def test_favorite_tracks_endpoint_exists(self, client): """Favorite tracks endpoint should exist.""" response = client.get("/api/favorites/tracks") assert response.status_code != 404 @pytest.mark.contract def test_favorite_albums_endpoint_exists(self, client): """Favorite albums endpoint should exist.""" response = client.get("/api/favorites/albums") assert response.status_code != 404 @pytest.mark.contract def test_favorite_artists_endpoint_exists(self, client): """Favorite artists endpoint should exist.""" response = client.get("/api/favorites/artists") assert response.status_code != 404 class TestPlaylistContracts: """Contract tests for playlist endpoints.""" @pytest.mark.contract def test_playlists_endpoint_exists(self, client): """Playlists endpoint should exist.""" response = client.get("/api/playlists") assert response.status_code != 404 @pytest.mark.contract def test_create_playlist_endpoint_exists(self, client): """Create playlist endpoint should exist.""" response = client.post("/api/playlists", json={}) assert response.status_code != 404 class TestQueueContracts: """Contract tests for queue endpoints.""" @pytest.mark.contract def test_queue_endpoint_exists(self, client): """Queue endpoint should exist.""" response = client.get("/api/queue") assert response.status_code != 404 @pytest.mark.contract def test_add_to_queue_endpoint_exists(self, client): """Add to queue endpoint should exist.""" response = client.post("/api/queue/add", json={}) assert response.status_code != 404 @pytest.mark.contract def test_clear_queue_endpoint_exists(self, client): """Clear queue endpoint should exist.""" response = client.delete("/api/queue/clear") assert response.status_code != 404 class TestSettingsContracts: """Contract tests for settings endpoints.""" @pytest.mark.contract def test_settings_endpoint_exists(self, client): """Settings endpoint should exist.""" response = client.get("/api/settings") assert response.status_code != 404 @pytest.mark.contract def test_user_preferences_endpoint_exists(self, client): """User preferences endpoint should exist.""" response = client.get("/api/user/preferences") assert response.status_code != 404 class TestLoggerContracts: """Contract tests for logger endpoints (used by mobile).""" @pytest.mark.contract def test_track_log_endpoint_exists(self, client): """Track log endpoint should exist for mobile playback tracking.""" response = client.post("/logger/track/log", json={}) assert response.status_code != 404 class TestMobileOfflineContracts: """Contract tests for mobile offline endpoints.""" @pytest.mark.contract def test_device_register_endpoint_exists(self, client): """Device register endpoint should exist.""" response = client.post("/api/mobile-offline/devices/register", json={}) assert response.status_code != 404 @pytest.mark.contract def test_devices_list_endpoint_exists(self, client): """Devices list endpoint should exist.""" response = client.get("/api/mobile-offline/devices") assert response.status_code != 404 class TestResponseFormatContracts: """Contract tests for response formats.""" @pytest.mark.contract def test_healthz_returns_json(self, client): """Health endpoint should return JSON.""" response = client.get("/healthz") assert response.content_type.startswith("application/json") @pytest.mark.contract def test_bootstrap_status_returns_json(self, client): """Bootstrap status should return JSON.""" response = client.get("/auth/bootstrap/status") assert response.content_type.startswith("application/json") @pytest.mark.contract def test_error_responses_are_json(self, client): """Error responses should be JSON.""" response = client.post("/auth/login", json={ "username": "nonexistent", "password": "wrong", }) assert response.content_type.startswith("application/json") class TestCORSContracts: """Contract tests for CORS headers.""" @pytest.mark.contract def test_cors_headers_on_health(self, client): """Health endpoint should have CORS headers.""" response = client.get("/healthz") # CORS headers should be present for cross-origin requests # At minimum, the response should succeed assert response.status_code == 200 @pytest.mark.contract def test_options_request_supported(self, client): """OPTIONS requests should be supported for CORS preflight.""" response = client.options("/auth/login") # Should not be 404 or 405 assert response.status_code in [200, 204, 400] class TestHTTPMethodContracts: """Contract tests for HTTP methods.""" @pytest.mark.contract def test_login_accepts_post(self, client): """Login should accept POST.""" response = client.post("/auth/login", json={}) assert response.status_code != 405 @pytest.mark.contract def test_healthz_accepts_get(self, client): """Health should accept GET.""" response = client.get("/healthz") assert response.status_code != 405 @pytest.mark.contract def test_jobs_accepts_get(self, client): """Jobs should accept GET.""" response = client.get("/api/downloads/jobs") assert response.status_code != 405 @pytest.mark.contract def test_create_job_accepts_post(self, client): """Create job should accept POST.""" response = client.post("/api/downloads/jobs", json={}) assert response.status_code != 405