"""Integration tests for admin feature flag endpoints.""" import pytest from httpx import AsyncClient class TestAdminFeatureFlags: @pytest.mark.asyncio async def test_create_feature_flag( self, client: AsyncClient, admin_auth_headers: dict ): """Create a feature flag.""" response = await client.post( "/api/v1/admin/feature-flags", json={ "flag_key": "test_feature", "display_name": "Test Feature", "description": "A test feature flag", }, headers=admin_auth_headers, ) assert response.status_code == 201 data = response.json() assert data["flag_key"] == "test_feature" assert data["display_name"] == "Test Feature" @pytest.mark.asyncio async def test_list_feature_flags( self, client: AsyncClient, admin_auth_headers: dict ): """List feature flags.""" # Create a flag first await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "list_test", "display_name": "List Test"}, headers=admin_auth_headers, ) response = await client.get("/api/v1/admin/feature-flags", headers=admin_auth_headers) assert response.status_code == 200 flags = response.json() assert len(flags) >= 1 @pytest.mark.asyncio async def test_update_feature_flag( self, client: AsyncClient, admin_auth_headers: dict ): """Update a feature flag.""" create_resp = await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "update_test", "display_name": "Before"}, headers=admin_auth_headers, ) flag_id = create_resp.json()["id"] response = await client.put( f"/api/v1/admin/feature-flags/{flag_id}", json={"display_name": "After"}, headers=admin_auth_headers, ) assert response.status_code == 200 assert response.json()["display_name"] == "After" @pytest.mark.asyncio async def test_update_plan_default( self, client: AsyncClient, admin_auth_headers: dict ): """Update a plan feature default.""" create_resp = await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "plan_default_test", "display_name": "Plan Default Test"}, headers=admin_auth_headers, ) flag_id = create_resp.json()["id"] response = await client.put( "/api/v1/admin/feature-flags/plan-defaults", json={"plan": "free", "flag_id": flag_id, "enabled": True}, headers=admin_auth_headers, ) assert response.status_code == 200 # Verify in list list_resp = await client.get("/api/v1/admin/feature-flags", headers=admin_auth_headers) flag = next(f for f in list_resp.json() if f["id"] == flag_id) assert any(d["plan"] == "free" and d["enabled"] for d in flag["plan_defaults"]) @pytest.mark.asyncio async def test_delete_feature_flag_cascades( self, client: AsyncClient, admin_auth_headers: dict ): """Delete a feature flag cascades to plan defaults.""" create_resp = await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "delete_test", "display_name": "Delete Test"}, headers=admin_auth_headers, ) flag_id = create_resp.json()["id"] # Add a plan default await client.put( "/api/v1/admin/feature-flags/plan-defaults", json={"plan": "pro", "flag_id": flag_id, "enabled": True}, headers=admin_auth_headers, ) # Delete the flag response = await client.delete( f"/api/v1/admin/feature-flags/{flag_id}", headers=admin_auth_headers, ) assert response.status_code == 204 # Verify gone list_resp = await client.get("/api/v1/admin/feature-flags", headers=admin_auth_headers) assert not any(f["id"] == flag_id for f in list_resp.json()) @pytest.mark.asyncio async def test_duplicate_flag_key_fails( self, client: AsyncClient, admin_auth_headers: dict ): """Duplicate flag_key returns 409.""" await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "dupe_test", "display_name": "First"}, headers=admin_auth_headers, ) response = await client.post( "/api/v1/admin/feature-flags", json={"flag_key": "dupe_test", "display_name": "Second"}, headers=admin_auth_headers, ) assert response.status_code == 409 @pytest.mark.asyncio async def test_non_admin_cannot_access( self, client: AsyncClient, auth_headers: dict ): """Non-admin gets 403.""" response = await client.get("/api/v1/admin/feature-flags", headers=auth_headers) assert response.status_code == 403