Coverage for utils_aiosqlite/tests/unit/test_task_manager_str.py: 26%

179 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-30 14:44 +0800

1"""Unit tests for TaskManager with string task IDs.""" 

2 

3import tempfile 

4from collections.abc import AsyncGenerator 

5from pathlib import Path 

6from unittest.mock import patch 

7 

8import orjson 

9import pandas as pd 

10import pytest 

11 

12from utils_aiosqlite import TaskManager 

13 

14 

15class TestTaskManagerStr: 

16 """Test TaskManager with string task IDs.""" 

17 

18 @pytest.fixture 

19 async def task_manager(self) -> AsyncGenerator[TaskManager[str], None]: 

20 """Create a TaskManager instance with str task IDs for testing.""" 

21 with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp: 

22 db_path = Path(tmp.name) 

23 

24 manager = await TaskManager.async_init(db_path, column_type="str") 

25 yield manager 

26 

27 # Cleanup 

28 if db_path.exists(): 

29 db_path.unlink() 

30 

31 @pytest.mark.asyncio 

32 async def test_async_init_str_default(self): 

33 """Test TaskManager initialization with default str column type.""" 

34 with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp: 

35 db_path = Path(tmp.name) 

36 

37 try: 

38 # Default should be "str" 

39 manager = await TaskManager.async_init(db_path) 

40 assert manager.pth_tasks_db == db_path 

41 assert manager.column_type == "str" 

42 assert manager.default_timeout == 5 

43 finally: 

44 if db_path.exists(): 

45 db_path.unlink() 

46 

47 @pytest.mark.asyncio 

48 async def test_async_init_str_explicit(self): 

49 """Test TaskManager initialization with explicit str column type.""" 

50 with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp: 

51 db_path = Path(tmp.name) 

52 

53 try: 

54 manager = await TaskManager.async_init(db_path, column_type="str") 

55 assert manager.pth_tasks_db == db_path 

56 assert manager.column_type == "str" 

57 assert manager.default_timeout == 5 

58 finally: 

59 if db_path.exists(): 

60 db_path.unlink() 

61 

62 @pytest.mark.asyncio 

63 async def test_setup_tasks_str(self, task_manager: TaskManager[str]): 

64 """Test setting up tasks with string IDs.""" 

65 task_ids = [("task1",), ("task2",), ("task3",)] 

66 await task_manager.setup_tasks(task_ids) 

67 

68 all_tasks = await task_manager.get_all_tasks() 

69 assert sorted(all_tasks) == ["task1", "task2", "task3"] 

70 

71 @pytest.mark.asyncio 

72 async def test_setup_tasks_with_special_chars(self, task_manager: TaskManager[str]): 

73 """Test setting up tasks with string IDs containing special characters.""" 

74 task_ids = [("task-1",), ("task_2",), ("task.3",), ("task@4",)] 

75 await task_manager.setup_tasks(task_ids) 

76 

77 all_tasks = await task_manager.get_all_tasks() 

78 assert sorted(all_tasks) == ["task-1", "task.3", "task@4", "task_2"] 

79 

80 @pytest.mark.asyncio 

81 async def test_update_outputs_str(self, task_manager: TaskManager[str]): 

82 """Test updating outputs with string task IDs.""" 

83 await task_manager.setup_tasks([("task1",), ("task2",)]) 

84 

85 outputs = [{"result": "success", "value": 42}, {"result": "error", "message": "failed"}] 

86 await task_manager.update_outputs(["task1", "task2"], outputs) 

87 

88 output_1 = await task_manager.get_output("task1") 

89 output_2 = await task_manager.get_output("task2") 

90 

91 assert output_1 == {"result": "success", "value": 42} 

92 assert output_2 == {"result": "error", "message": "failed"} 

93 

94 @pytest.mark.asyncio 

95 async def test_update_outputs_n_metadatas_str(self, task_manager: TaskManager[str]): 

96 """Test updating outputs and metadata with string task IDs.""" 

97 await task_manager.setup_tasks([("task1",), ("task2",)]) 

98 

99 outputs = [{"result": "success"}, {"result": "error"}] 

100 metadatas = [{"duration": 1.5}, {"error_code": 500}] 

101 

102 await task_manager.update_outputs_n_metadatas(["task1", "task2"], outputs, metadatas) 

103 

104 output_1 = await task_manager.get_output("task1") 

105 metadata_1 = await task_manager.get_metadata("task1") 

106 output_2 = await task_manager.get_output("task2") 

107 metadata_2 = await task_manager.get_metadata("task2") 

108 

109 assert output_1 == {"result": "success"} 

110 assert metadata_1 == {"duration": 1.5} 

111 assert output_2 == {"result": "error"} 

112 assert metadata_2 == {"error_code": 500} 

113 

114 @pytest.mark.asyncio 

115 async def test_get_output_nonexistent_str(self, task_manager: TaskManager[str]): 

116 """Test getting output for non-existent string task ID.""" 

117 result = await task_manager.get_output("nonexistent") 

118 assert result is None 

119 

120 @pytest.mark.asyncio 

121 async def test_get_metadata_nonexistent_str(self, task_manager: TaskManager[str]): 

122 """Test getting metadata for non-existent string task ID.""" 

123 result = await task_manager.get_metadata("nonexistent") 

124 assert result is None 

125 

126 @pytest.mark.asyncio 

127 async def test_get_all_output_str(self, task_manager: TaskManager[str]): 

128 """Test getting all outputs with string task IDs.""" 

129 await task_manager.setup_tasks([("task1",), ("task2",)]) 

130 outputs = [{"result": "success"}, {"result": "error"}] 

131 await task_manager.update_outputs(["task1", "task2"], outputs) 

132 

133 df = await task_manager.get_all_output() 

134 assert len(df) == 2 

135 assert set(df["task_id"]) == {"task1", "task2"} 

136 assert df[df["task_id"] == "task1"]["output"].iloc[0] == {"result": "success"} 

137 assert df[df["task_id"] == "task2"]["output"].iloc[0] == {"result": "error"} 

138 

139 @pytest.mark.asyncio 

140 async def test_get_all_output_flatten_str(self, task_manager: TaskManager[str]): 

141 """Test getting all outputs with flattening enabled.""" 

142 await task_manager.setup_tasks([("task1",), ("task2",)]) 

143 outputs = [{"result": "success", "count": 5}, {"result": "error", "count": 0}] 

144 await task_manager.update_outputs(["task1", "task2"], outputs) 

145 

146 df = await task_manager.get_all_output(flatten=True) 

147 assert len(df) == 2 

148 assert "result" in df.columns 

149 assert "count" in df.columns 

150 assert set(df["task_id"]) == {"task1", "task2"} 

151 

152 @pytest.mark.asyncio 

153 async def test_get_all_metadata_str(self, task_manager: TaskManager[str]): 

154 """Test getting all metadata with string task IDs.""" 

155 await task_manager.setup_tasks([("task1",), ("task2",)]) 

156 outputs = [{"result": "success"}, {"result": "error"}] 

157 metadatas = [{"duration": 1.5}, {"error_code": 500}] 

158 await task_manager.update_outputs_n_metadatas(["task1", "task2"], outputs, metadatas) 

159 

160 df = await task_manager.get_all_metadata() 

161 assert len(df) == 2 

162 assert set(df["task_id"]) == {"task1", "task2"} 

163 assert df[df["task_id"] == "task1"]["metadata"].iloc[0] == {"duration": 1.5} 

164 assert df[df["task_id"] == "task2"]["metadata"].iloc[0] == {"error_code": 500} 

165 

166 @pytest.mark.asyncio 

167 async def test_get_pending_tasks_str(self, task_manager: TaskManager[str]): 

168 """Test getting pending tasks with string IDs.""" 

169 await task_manager.setup_tasks([("task1",), ("task2",), ("task3",)]) 

170 await task_manager.update_outputs(["task1"], [{"result": "success"}]) 

171 

172 pending = await task_manager.get_pending_tasks() 

173 assert sorted(pending) == ["task2", "task3"] 

174 

175 @pytest.mark.asyncio 

176 async def test_get_noresult_tasks_str(self, task_manager: TaskManager[str]): 

177 """Test getting no-result tasks with string IDs.""" 

178 await task_manager.setup_tasks([("task1",), ("task2",), ("task3",)]) 

179 await task_manager.update_outputs(["task1", "task2"], [{"result": "success"}, {}]) 

180 

181 noresult = await task_manager.get_noresult_tasks() 

182 assert noresult == ["task2"] 

183 

184 @pytest.mark.asyncio 

185 async def test_mark_noresult_tasks_as_pending_str(self, task_manager: TaskManager[str]): 

186 """Test marking no-result tasks as pending.""" 

187 await task_manager.setup_tasks([("task1",), ("task2",)]) 

188 await task_manager.update_outputs_n_metadatas( 

189 ["task1", "task2"], [{"result": "success"}, {}], [{"meta": "data"}, {"meta": "data2"}] 

190 ) 

191 

192 # Verify task2 has empty output but metadata 

193 output_2 = await task_manager.get_output("task2") 

194 metadata_2 = await task_manager.get_metadata("task2") 

195 assert output_2 == {} 

196 assert metadata_2 == {"meta": "data2"} 

197 

198 await task_manager.mark_noresult_tasks_as_pending() 

199 

200 # After marking as pending, both output and metadata should be None 

201 output_2_after = await task_manager.get_output("task2") 

202 metadata_2_after = await task_manager.get_metadata("task2") 

203 assert output_2_after is None 

204 assert metadata_2_after is None 

205 

206 pending = await task_manager.get_pending_tasks() 

207 assert "task2" in pending 

208 

209 @pytest.mark.asyncio 

210 async def test_query_str(self, task_manager: TaskManager[str]): 

211 """Test custom query execution with string task IDs.""" 

212 await task_manager.setup_tasks([("task1",), ("task2",), ("task3",)]) 

213 

214 df = await task_manager.query("SELECT COUNT(*) as count FROM tasks") 

215 assert len(df) == 1 

216 assert df["count"].iloc[0] == 3 

217 

218 # Test query with WHERE clause on string task_id 

219 df = await task_manager.query("SELECT task_id FROM tasks WHERE task_id LIKE 'task%'") 

220 assert len(df) == 3 

221 assert set(df["task_id"]) == {"task1", "task2", "task3"} 

222 

223 @pytest.mark.asyncio 

224 async def test_executemany_str(self, task_manager: TaskManager[str]): 

225 """Test executemany method with string task IDs.""" 

226 # Create some initial tasks 

227 await task_manager.setup_tasks([("task1",), ("task2",), ("task3",)]) 

228 

229 # Use executemany to update multiple tasks at once 

230 data = [(orjson.dumps({"result": f"batch_{task_id}"}), task_id) for task_id in ["task1", "task2", "task3"]] 

231 await task_manager.executemany("UPDATE tasks SET output = ? WHERE task_id = ?", data) 

232 

233 # Verify updates 

234 for task_id in ["task1", "task2", "task3"]: 

235 output = await task_manager.get_output(task_id) 

236 assert output == {"result": f"batch_{task_id}"} 

237 

238 @pytest.mark.asyncio 

239 async def test_mixed_string_formats(self, task_manager: TaskManager[str]): 

240 """Test TaskManager with various string formats.""" 

241 # Test with different string formats 

242 task_ids = [ 

243 ("uuid-123-456",), 

244 ("task_with_underscores",), 

245 ("task-with-dashes",), 

246 ("task.with.dots",), 

247 ("UPPERCASE_TASK",), 

248 ("MixedCase_Task",), 

249 ("123_numeric_prefix",), 

250 ("task_with_numbers_123",), 

251 ] 

252 

253 await task_manager.setup_tasks(task_ids) 

254 

255 all_tasks = await task_manager.get_all_tasks() 

256 expected_tasks = [tid[0] for tid in task_ids] 

257 assert sorted(all_tasks) == sorted(expected_tasks) 

258 

259 # Test updating some of these tasks 

260 test_outputs = [{"status": "completed", "type": "uuid"}, {"status": "failed", "type": "underscore"}] 

261 

262 await task_manager.update_outputs(["uuid-123-456", "task_with_underscores"], test_outputs) 

263 

264 # Verify the updates 

265 output_uuid = await task_manager.get_output("uuid-123-456") 

266 output_underscore = await task_manager.get_output("task_with_underscores") 

267 

268 assert output_uuid == {"status": "completed", "type": "uuid"} 

269 assert output_underscore == {"status": "failed", "type": "underscore"} 

270 

271 @pytest.mark.asyncio 

272 async def test_long_string_task_ids(self, task_manager: TaskManager[str]): 

273 """Test TaskManager with very long string task IDs.""" 

274 long_task_id = "very_long_task_id_" + "x" * 200 # 218 characters total 

275 

276 await task_manager.setup_tasks([(long_task_id,)]) 

277 

278 # Test that we can work with long task IDs 

279 await task_manager.update_outputs([long_task_id], [{"result": "success_with_long_id"}]) 

280 

281 output = await task_manager.get_output(long_task_id) 

282 assert output == {"result": "success_with_long_id"} 

283 

284 all_tasks = await task_manager.get_all_tasks() 

285 assert long_task_id in all_tasks