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

1"""Tests for locations endpoints.""" 

2 

3import pytest 

4from httpx import ASGITransport, AsyncClient 

5 

6from data_api.api.api import app 

7 

8pytestmark = pytest.mark.usefixtures("dataset_runtime") 

9 

10 

11class TestLocationsEndpoints: 

12 """Tests for locations endpoints.""" 

13 

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 

21 

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" 

33 

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" 

53 

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" 

60 

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" 

74 

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() 

80 

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" 

86 

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()] == ["台積", "台達"] 

93 

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"] 

98 

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"}] 

104 

105 

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