Coverage for tests/test_locations.py: 100%
76 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
1"""Tests for locations endpoints."""
3import pytest
4from httpx import ASGITransport, AsyncClient
6from data_api.api.api import app
8pytestmark = pytest.mark.usefixtures("dataset_runtime")
11class TestLocationsEndpoints:
12 """Tests for locations endpoints."""
14 @pytest.fixture
15 async def client(self):
16 """Create async test client."""
17 async with AsyncClient(
18 transport=ASGITransport(app=app), base_url="http://test", follow_redirects=False
19 ) as client:
20 yield client
22 async def test_get_all_locations(self, client: AsyncClient):
23 """Test getting all locations."""
24 response = await client.get("/locations")
25 assert response.status_code == 200
26 assert [location["name"] for location in response.json()] == [
27 "校門",
28 "綜合",
29 "台積",
30 "台達",
31 ]
32 assert response.headers["X-Data-Commit-Hash"] == "fixture"
34 @pytest.mark.parametrize(
35 "query,expected_names",
36 [
37 ("校門", ["校門"]),
38 ("綜合", ["綜合"]),
39 ("台積", ["台積", "台達"]),
40 ("台達", ["台達", "台積"]),
41 ],
42 )
43 async def test_search_locations_by_name(
44 self, client: AsyncClient, query: str, expected_names: list[str]
45 ):
46 """Test searching locations by name."""
47 response = await client.get("/locations", params={"name": query})
48 assert response.status_code == 200
49 assert response.json() == [
50 {"name": name, "latitude": "24.79", "longitude": "120.99"} for name in expected_names
51 ]
52 assert response.headers["X-Data-Commit-Hash"] == "fixture"
54 @pytest.mark.parametrize("fuzzy", ["true", "false"])
55 async def test_search_locations_no_matches(self, client: AsyncClient, fuzzy: str):
56 response = await client.get("/locations", params={"name": "nonexistent", "fuzzy": fuzzy})
57 assert response.status_code == 200
58 assert response.json() == []
59 assert response.headers["X-Data-Commit-Hash"] == "fixture"
61 @pytest.mark.parametrize("fuzzy", ["true", "false"])
62 @pytest.mark.parametrize("name", [None, ""])
63 async def test_empty_name_returns_all_locations(
64 self, client: AsyncClient, fuzzy: str, name: str | None
65 ):
66 params = {"fuzzy": fuzzy}
67 if name is not None:
68 params["name"] = name
69 response = await client.get("/locations", params=params)
70 unfiltered = await client.get("/locations")
71 assert response.status_code == 200
72 assert response.json() == unfiltered.json()
73 assert response.headers["X-Data-Commit-Hash"] == "fixture"
75 async def test_explicit_fuzzy_search(self, client: AsyncClient):
76 response = await client.get("/locations", params={"name": "台積", "fuzzy": "true"})
77 default = await client.get("/locations", params={"name": "台積"})
78 assert response.status_code == 200
79 assert response.json() == default.json()
81 async def test_exact_name_search(self, client: AsyncClient):
82 response = await client.get("/locations", params={"name": "台積", "fuzzy": "false"})
83 assert response.status_code == 200
84 assert response.json() == [{"name": "台積", "latitude": "24.79", "longitude": "120.99"}]
85 assert response.headers["X-Data-Commit-Hash"] == "fixture"
87 async def test_exact_search_does_not_match_substrings(self, client: AsyncClient):
88 response = await client.get("/locations", params={"name": "台", "fuzzy": "false"})
89 assert response.status_code == 200
90 assert response.json() == []
91 fuzzy = await client.get("/locations", params={"name": "台"})
92 assert [location["name"] for location in fuzzy.json()] == ["台積", "台達"]
94 async def test_invalid_fuzzy_parameter(self, client: AsyncClient):
95 response = await client.get("/locations", params={"name": "台積", "fuzzy": "invalid"})
96 assert response.status_code == 422
97 assert response.json()["detail"][0]["loc"] == ["query", "fuzzy"]
99 async def test_slashless_endpoint_preserves_query(self, client: AsyncClient):
100 response = await client.get("/locations", params={"name": "台積", "fuzzy": "false"})
101 assert response.status_code == 200
102 assert "location" not in response.headers
103 assert response.json() == [{"name": "台積", "latitude": "24.79", "longitude": "120.99"}]
106def test_locations_openapi():
107 paths = app.openapi()["paths"]
108 operation = paths["/locations"]["get"]
109 assert operation["operationId"] == "getLocations"
110 assert not operation.get("deprecated", False)
111 assert "404" not in operation["responses"]
112 assert operation["responses"]["200"]["content"]["application/json"]["schema"]["type"] == "array"
113 parameters = {parameter["name"]: parameter for parameter in operation["parameters"]}
114 assert parameters.keys() == {"name", "fuzzy"}
115 for parameter in parameters.values():
116 assert parameter["in"] == "query"
117 assert not parameter["required"]
118 assert parameters["fuzzy"]["schema"]["type"] == "boolean"
119 assert parameters["fuzzy"]["schema"]["default"] is True