Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/elections/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ def _raise_if_bad_election_data(

@router.get(
"",
description="Return a list of all elections, their statuses and nominees (if requested)",
description="Return a list of all elections, their statuses and nominees (if requested), or an empty list if none exist",
response_model=list[ElectionResponse],
responses={status.HTTP_404_NOT_FOUND: {"description": "No election found", "model": DetailModel}},
operation_id="get_all_elections",
)
async def list_elections(
Expand All @@ -91,15 +90,11 @@ async def list_elections(
election_responses = await elections.crud.get_all_elections_with_nominees(
db_session, current_time, has_permission
)
if not election_responses:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="no election found")
election_metadata_list = [
election.model_dump(mode="json", exclude_none=True) for election in election_responses
]
else:
election_list = await elections.crud.get_all_elections(db_session)
if election_list is None or len(election_list) == 0:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="no election found")
election_metadata_list = []
for election in election_list:
if has_permission:
Expand Down
71 changes: 71 additions & 0 deletions tests/unit/test_elections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from unittest.mock import AsyncMock

import pytest
import pytest_asyncio
from fastapi import FastAPI, status
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

import elections.crud
from database import get_db_session
from elections.urls import router

pytestmark = pytest.mark.unit


@pytest_asyncio.fixture
async def election_client():
app = FastAPI()
app.include_router(router)

async def override_get_db_session():
yield AsyncMock(spec=AsyncSession)

app.dependency_overrides[get_db_session] = override_get_db_session
async with AsyncClient(transport=ASGITransport(app), base_url="http://test") as client:
yield client


@pytest.mark.parametrize(
("query", "with_nominees"),
[("", False), ("?with_nominees=false", False), ("?with_nominees=true", True)],
)
async def test__empty_election_list_returns_ok(
election_client: AsyncClient, monkeypatch: pytest.MonkeyPatch, query, with_nominees
):
get_all = AsyncMock(return_value=[])
get_with_nominees = AsyncMock(return_value=[])
monkeypatch.setattr(elections.crud, "get_all_elections", get_all)
monkeypatch.setattr(elections.crud, "get_all_elections_with_nominees", get_with_nominees)

response = await election_client.get(f"/election{query}")

assert response.status_code == status.HTTP_200_OK
assert response.json() == []
if with_nominees:
get_with_nominees.assert_awaited_once()
get_all.assert_not_awaited()
else:
get_all.assert_awaited_once()
get_with_nominees.assert_not_awaited()


async def test__missing_election_still_returns_not_found(election_client: AsyncClient, monkeypatch: pytest.MonkeyPatch):
get_election = AsyncMock(return_value=None)
monkeypatch.setattr(elections.crud, "get_election", get_election)

response = await election_client.get("/election/missing")

assert response.status_code == status.HTTP_404_NOT_FOUND
assert response.json() == {"detail": "election with slug missing does not exist"}
get_election.assert_awaited_once()


async def test__election_list_schema_does_not_advertise_not_found(election_client: AsyncClient):
response = await election_client.get("/openapi.json")
assert response.status_code == status.HTTP_200_OK
paths = response.json()["paths"]

assert "200" in paths["/election"]["get"]["responses"]
assert "404" not in paths["/election"]["get"]["responses"]
assert "404" in paths["/election/{election_name}"]["get"]["responses"]