"""Tests for API endpoints""" import pytest from uuid import uuid4 import json @pytest.mark.asyncio async def test_health_endpoint(test_client): """Test health check endpoint""" response = await test_client.get("/api/health") assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.asyncio async def test_collection_summary_empty(test_client, test_db_pool): """Test collection summary with no data""" response = await test_client.get("/api/collection/summary") assert response.status_code == 200 assert response.json() == {} @pytest.mark.asyncio async def test_collection_summary_with_data(test_client, test_db_pool): """Test collection summary with media items""" # Insert test data async with test_db_pool.connection() as conn: async with conn.cursor() as cur: # Insert media item await cur.execute(""" INSERT INTO moviemap.media_item (source_kind, source_item_id, title, year, media_type, arr_raw) VALUES ('radarr', 1, 'Test Movie', 2020, 'movie', '{}'::jsonb) RETURNING id """) media_id = (await cur.fetchone())[0] # Insert country association await cur.execute(""" INSERT INTO moviemap.media_country (media_item_id, country_code) VALUES (%s, 'US') """, (media_id,)) await conn.commit() response = await test_client.get("/api/collection/summary") assert response.status_code == 200 data = response.json() assert "US" in data assert data["US"]["movie"] == 1 @pytest.mark.asyncio async def test_collection_summary_filtered(test_client, test_db_pool): """Test collection summary with type filters""" # Insert test data async with test_db_pool.connection() as conn: async with conn.cursor() as cur: # Insert movie await cur.execute(""" INSERT INTO moviemap.media_item (source_kind, source_item_id, title, year, media_type, arr_raw) VALUES ('radarr', 1, 'Test Movie', 2020, 'movie', '{}'::jsonb) RETURNING id """) movie_id = (await cur.fetchone())[0] await cur.execute(""" INSERT INTO moviemap.media_country (media_item_id, country_code) VALUES (%s, 'US') """, (movie_id,)) # Insert show await cur.execute(""" INSERT INTO moviemap.media_item (source_kind, source_item_id, title, year, media_type, arr_raw) VALUES ('sonarr', 1, 'Test Show', 2020, 'show', '{}'::jsonb) RETURNING id """) show_id = (await cur.fetchone())[0] await cur.execute(""" INSERT INTO moviemap.media_country (media_item_id, country_code) VALUES (%s, 'US') """, (show_id,)) await conn.commit() # Test with movie filter response = await test_client.get("/api/collection/summary?types=movie") assert response.status_code == 200 data = response.json() assert "US" in data assert data["US"]["movie"] == 1 assert "show" not in data["US"] @pytest.mark.asyncio async def test_watched_list_empty(test_client, test_db_pool): """Test watched items list with no data""" response = await test_client.get("/api/watched") assert response.status_code == 200 assert response.json() == [] @pytest.mark.asyncio async def test_watched_create(test_client, test_db_pool): """Test creating a watched item""" response = await test_client.post( "/api/watched", json={ "media_type": "movie", "title": "Test Movie", "year": 2020, "country_code": "US", "notes": "Test notes" } ) assert response.status_code == 200 data = response.json() assert "id" in data assert data["status"] == "created" @pytest.mark.asyncio async def test_watched_list_with_data(test_client, test_db_pool): """Test watched items list with data""" # Create watched item create_response = await test_client.post( "/api/watched", json={ "media_type": "movie", "title": "Test Movie", "country_code": "US" } ) assert create_response.status_code == 200 # List watched items response = await test_client.get("/api/watched") assert response.status_code == 200 data = response.json() assert len(data) == 1 assert data[0]["title"] == "Test Movie" assert data[0]["country_code"] == "US" @pytest.mark.asyncio async def test_watched_delete(test_client, test_db_pool): """Test deleting a watched item""" # Create watched item create_response = await test_client.post( "/api/watched", json={ "media_type": "movie", "title": "Test Movie", "country_code": "US" } ) item_id = create_response.json()["id"] # Delete it response = await test_client.delete(f"/api/watched/{item_id}") assert response.status_code == 200 assert response.json()["status"] == "deleted" # Verify it's gone list_response = await test_client.get("/api/watched") assert len(list_response.json()) == 0 @pytest.mark.asyncio async def test_watched_summary(test_client, test_db_pool): """Test watched items summary""" # Create watched items await test_client.post( "/api/watched", json={ "media_type": "movie", "title": "Test Movie", "country_code": "US", "watched_at": "2024-01-01T00:00:00Z" } ) await test_client.post( "/api/watched", json={ "media_type": "show", "title": "Test Show", "country_code": "US", "watched_at": "2024-01-01T00:00:00Z" } ) response = await test_client.get("/api/watched/summary") assert response.status_code == 200 data = response.json() assert "US" in data assert data["US"]["movie"] == 1 assert data["US"]["show"] == 1 @pytest.mark.asyncio async def test_pins_list_empty(test_client, test_db_pool): """Test pins list with no data""" response = await test_client.get("/api/pins") assert response.status_code == 200 assert response.json() == [] @pytest.mark.asyncio async def test_pins_create(test_client, test_db_pool): """Test creating a pin""" response = await test_client.post( "/api/pins", json={ "country_code": "US", "label": "Test Pin" } ) assert response.status_code == 200 data = response.json() assert "id" in data assert data["status"] == "created" @pytest.mark.asyncio async def test_pins_delete(test_client, test_db_pool): """Test deleting a pin""" # Create pin create_response = await test_client.post( "/api/pins", json={"country_code": "US"} ) pin_id = create_response.json()["id"] # Delete it response = await test_client.delete(f"/api/pins/{pin_id}") assert response.status_code == 200 assert response.json()["status"] == "deleted" @pytest.mark.asyncio async def test_admin_sync_no_auth(test_client, test_db_pool): """Test admin sync without auth (should work if no token configured)""" # This will fail if admin token is required, but should work if not # We'll mock the sync function to avoid actual API calls response = await test_client.post("/admin/sync") # Either 200 (no auth required) or 401 (auth required) assert response.status_code in [200, 401, 500] # 500 if sync fails due to missing *arr @pytest.mark.asyncio async def test_missing_countries_empty(test_client, test_db_pool): """Test missing countries endpoint with no data""" response = await test_client.get("/admin/missing-countries") # May require auth, but if not, should return empty if response.status_code == 200: data = response.json() assert data["total"] == 0 assert data["returned"] == 0 assert data["items"] == []