Coverage for tests/test_buses_graph.py: 100%

118 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-11-13 19:28 +0000

1"""Tests for bus graph module.""" 

2 

3import pytest 

4 

5from data_api.domain.buses import graph, models 

6 

7 

8class TestStopsData: 

9 """Tests for stop definitions.""" 

10 

11 async def test_stops_data_exists(self): 

12 """Test that stops data is defined.""" 

13 assert len(graph.STOPS_DATA) > 0 

14 

15 async def test_stop_has_required_fields(self): 

16 """Test that stops have required fields.""" 

17 for stop_id, stop in graph.STOPS_DATA.items(): 

18 assert isinstance(stop, models.Stop) 

19 assert stop.id == stop_id 

20 assert stop.name 

21 assert stop.name_en 

22 assert stop.latitude 

23 assert stop.longitude 

24 

25 

26class TestCreateRoute: 

27 """Tests for route creation.""" 

28 

29 async def test_create_route(self): 

30 """Test creating a route.""" 

31 route = graph.create_route("test_route", ["M1", "M2", "M3"], [0, 1, 2]) 

32 assert route.id == "test_route" 

33 assert len(route.stops) == 3 

34 assert len(route.time_offsets) == 3 

35 assert route.time_offsets == [0, 1, 3] 

36 

37 async def test_create_route_offsets_cumulative(self): 

38 """Test that time offsets are cumulative.""" 

39 route = graph.create_route("test_route", ["M1", "M2", "M3", "M4"], [0, 2, 3, 1]) 

40 assert route.time_offsets == [0, 2, 5, 6] 

41 

42 

43class TestRouteDefinitions: 

44 """Tests for route definitions.""" 

45 

46 async def test_red_routes_exist(self): 

47 """Test red routes are defined.""" 

48 assert graph.red_M1_M5 is not None 

49 assert graph.red_M2_M5 is not None 

50 assert graph.red_M5_M1 is not None 

51 assert graph.red_M5_M2 is not None 

52 

53 async def test_green_routes_exist(self): 

54 """Test green routes are defined.""" 

55 assert graph.green_M1_M5 is not None 

56 assert graph.green_M2_M5 is not None 

57 assert graph.green_M5_M1 is not None 

58 assert graph.green_M5_M2 is not None 

59 

60 async def test_nanda_routes_exist(self): 

61 """Test Nanda routes are defined.""" 

62 assert graph.nanda_M1_S1_r1 is not None 

63 assert graph.nanda_M1_S1_r2 is not None 

64 assert graph.nanda_S1_M1_r1 is not None 

65 assert graph.nanda_S1_M1_r2 is not None 

66 

67 

68class TestRouteResolver: 

69 """Tests for RouteResolver.""" 

70 

71 async def test_resolve_main_campus_red_uphill(self): 

72 """Test resolving red line uphill route.""" 

73 route = graph.RouteResolver.resolve_main_campus_route("red", "校門") 

74 assert route == graph.red_M1_M5 

75 

76 async def test_resolve_main_campus_red_downhill(self): 

77 """Test resolving red line downhill route.""" 

78 route = graph.RouteResolver.resolve_main_campus_route("red", "台積館") 

79 assert route == graph.red_M5_M1 

80 

81 async def test_resolve_main_campus_red_downhill_from_gen2(self): 

82 """Test resolving red line downhill route ending at Gen2.""" 

83 route = graph.RouteResolver.resolve_main_campus_route("red", "台積館", is_from_gen2=True) 

84 assert route == graph.red_M5_M2 

85 

86 async def test_resolve_main_campus_red_from_gen2(self): 

87 """Test resolving red line from Gen2.""" 

88 route = graph.RouteResolver.resolve_main_campus_route("red", "綜二") 

89 assert route == graph.red_M2_M5 

90 

91 async def test_resolve_main_campus_green_uphill(self): 

92 """Test resolving green line uphill route.""" 

93 route = graph.RouteResolver.resolve_main_campus_route("green", "北校門") 

94 assert route == graph.green_M1_M5 

95 

96 async def test_resolve_main_campus_green_downhill(self): 

97 """Test resolving green line downhill route.""" 

98 route = graph.RouteResolver.resolve_main_campus_route("green", "台積") 

99 assert route == graph.green_M5_M1 

100 

101 async def test_resolve_main_campus_green_from_gen2(self): 

102 """Test resolving green line from Gen2.""" 

103 route = graph.RouteResolver.resolve_main_campus_route("green", "綜二") 

104 assert route == graph.green_M2_M5 

105 

106 async def test_resolve_main_campus_unknown_stop(self): 

107 """Test resolving unknown stop returns None.""" 

108 route = graph.RouteResolver.resolve_main_campus_route("red", "未知站") 

109 assert route is None 

110 

111 async def test_resolve_nanda_route_up_route1(self): 

112 """Test resolving Nanda uphill route 1.""" 

113 route = graph.RouteResolver.resolve_nanda_route("up", "一般路線") 

114 assert route == graph.nanda_M1_S1_r1 

115 

116 async def test_resolve_nanda_route_up_route2(self): 

117 """Test resolving Nanda uphill route 2.""" 

118 route = graph.RouteResolver.resolve_nanda_route("up", "路線二經過教育學院") 

119 assert route == graph.nanda_M1_S1_r2 

120 

121 async def test_resolve_nanda_route_down_route1(self): 

122 """Test resolving Nanda downhill route 1.""" 

123 route = graph.RouteResolver.resolve_nanda_route("down", "") 

124 assert route == graph.nanda_S1_M1_r1 

125 

126 async def test_resolve_nanda_route_down_route2(self): 

127 """Test resolving Nanda downhill route 2.""" 

128 route = graph.RouteResolver.resolve_nanda_route("down", "教育學院") 

129 assert route == graph.nanda_S1_M1_r2 

130 

131 async def test_get_nanda_line_route1(self): 

132 """Test get_nanda_line returns route_1 for regular description.""" 

133 line = graph.RouteResolver.get_nanda_line("一般路線") 

134 assert line == "route_1" 

135 

136 async def test_get_nanda_line_route2(self): 

137 """Test get_nanda_line returns route_2 for route 2 description.""" 

138 line = graph.RouteResolver.get_nanda_line("路線二") 

139 assert line == "route_2" 

140 

141 async def test_get_nanda_line_education_building(self): 

142 """Test get_nanda_line returns route_2 for education building.""" 

143 line = graph.RouteResolver.get_nanda_line("經過教育學院") 

144 assert line == "route_2" 

145 

146 async def test_get_nanda_line_none(self): 

147 """Test get_nanda_line handles None description.""" 

148 line = graph.RouteResolver.get_nanda_line(None) 

149 assert line == "route_1" 

150 

151 

152@pytest.mark.parametrize( 

153 "line,description,expected", 

154 [ 

155 ("route1", "路線二", "route_1"), 

156 ("route2", "", "route_2"), 

157 ("route_1", "教育學院", "route_1"), 

158 ("route_2", "", "route_2"), 

159 ("nanda_route_1", "路線二", "route_1"), 

160 ("nanda_route_2", "", "route_2"), 

161 ("", "路線二", "route_2"), 

162 ("", "", "route_1"), 

163 ], 

164) 

165@pytest.mark.parametrize("direction", ["up", "down"]) 

166def test_published_nanda_line_selects_matching_graph(line, description, expected, direction): 

167 assert graph.RouteResolver.get_nanda_line(description, line) == expected 

168 route = graph.RouteResolver.resolve_nanda_route(direction, description, line) 

169 assert ("M7" in [stop.id for stop in route.stops]) == (expected == "route_2") 

170 assert route.stops[0].id == ("M1" if direction == "up" else "S1") 

171 

172 

173def test_unknown_published_nanda_line_is_rejected(): 

174 with pytest.raises(ValueError, match="Invalid Nanda bus line"): 

175 graph.RouteResolver.get_nanda_line("", "route3") 

176 

177 

178class TestModels: 

179 """Tests for bus models.""" 

180 

181 async def test_stop_model(self): 

182 """Test Stop model.""" 

183 stop = models.Stop("T1", "測試站", "Test Stop", "24.0", "120.0") 

184 assert stop.id == "T1" 

185 assert stop.name == "測試站" 

186 assert stop.name_en == "Test Stop" 

187 

188 async def test_route_model(self): 

189 """Test Route model.""" 

190 stop = models.Stop("T1", "測試站", "Test Stop", "24.0", "120.0") 

191 route = models.Route("test_route", [stop], [0]) 

192 assert route.id == "test_route" 

193 assert len(route.stops) == 1 

194 assert route.time_offsets == [0] 

195 

196 async def test_bus_schedule_model(self): 

197 """Test BusSchedule model.""" 

198 schedule = models.BusSchedule( 

199 time="08:00", 

200 description="往台積", 

201 bus_type="main", 

202 line="red", 

203 dep_stop="校門", 

204 ) 

205 assert schedule.time == "08:00" 

206 assert schedule.bus_type == "main" 

207 

208 async def test_bus_detailed_schedule_model(self): 

209 """Test BusDetailedSchedule model.""" 

210 schedule = models.BusDetailedSchedule( 

211 dep_info={"time": "08:00", "description": "往台積"}, 

212 arr_info=[{"stop": "綜二", "arrive_time": "08:01"}], 

213 bus_type="main", 

214 ) 

215 assert schedule.dep_info["time"] == "08:00" 

216 assert len(schedule.arr_info) == 1