Coverage for utils_aiosqlite/tests/unit/test_task_manager_int.py: 27%

164 statements  

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

1"""Unit tests for TaskManager with integer 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 TestTaskManagerInt: 

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

17 

18 @pytest.fixture 

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

20 """Create a TaskManager instance with int 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="int") 

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_int(self): 

33 """Test TaskManager initialization with int column type.""" 

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

35 db_path = Path(tmp.name) 

36 

37 try: 

38 manager = await TaskManager.async_init(db_path, column_type="int") 

39 assert manager.pth_tasks_db == db_path 

40 assert manager.column_type == "int" 

41 assert manager.default_timeout == 5 

42 finally: 

43 if db_path.exists(): 

44 db_path.unlink() 

45 

46 @pytest.mark.asyncio 

47 async def test_async_init_invalid_column_type(self): 

48 """Test TaskManager initialization with invalid column type.""" 

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

50 db_path = Path(tmp.name) 

51 

52 try: 

53 with pytest.raises(ValueError, match="column_type must be either 'int' or 'str'"): 

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

55 finally: 

56 if db_path.exists(): 

57 db_path.unlink() 

58 

59 @pytest.mark.asyncio 

60 async def test_setup_tasks_int(self, task_manager: TaskManager[int]): 

61 """Test setting up tasks with integer IDs.""" 

62 task_ids = [(1,), (2,), (3,)] 

63 await task_manager.setup_tasks(task_ids) 

64 

65 all_tasks = await task_manager.get_all_tasks() 

66 assert sorted(all_tasks) == [1, 2, 3] 

67 

68 @pytest.mark.asyncio 

69 async def test_setup_tasks_empty_list(self, task_manager: TaskManager[int]): 

70 """Test setup_tasks with empty list raises ValueError.""" 

71 with pytest.raises(ValueError, match="task_id_tpls cannot be empty"): 

72 await task_manager.setup_tasks([]) 

73 

74 @pytest.mark.asyncio 

75 async def test_update_outputs_int(self, task_manager: TaskManager[int]): 

76 """Test updating outputs with integer task IDs.""" 

77 await task_manager.setup_tasks([(1,), (2,)]) 

78 

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

80 await task_manager.update_outputs([1, 2], outputs) 

81 

82 output_1 = await task_manager.get_output(1) 

83 output_2 = await task_manager.get_output(2) 

84 

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

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

87 

88 @pytest.mark.asyncio 

89 async def test_update_outputs_n_metadatas_int(self, task_manager: TaskManager[int]): 

90 """Test updating outputs and metadata with integer task IDs.""" 

91 await task_manager.setup_tasks([(1,), (2,)]) 

92 

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

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

95 

96 await task_manager.update_outputs_n_metadatas([1, 2], outputs, metadatas) 

97 

98 output_1 = await task_manager.get_output(1) 

99 metadata_1 = await task_manager.get_metadata(1) 

100 output_2 = await task_manager.get_output(2) 

101 metadata_2 = await task_manager.get_metadata(2) 

102 

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

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

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

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

107 

108 @pytest.mark.asyncio 

109 async def test_get_output_nonexistent_int(self, task_manager: TaskManager[int]): 

110 """Test getting output for non-existent integer task ID.""" 

111 result = await task_manager.get_output(999) 

112 assert result is None 

113 

114 @pytest.mark.asyncio 

115 async def test_get_metadata_nonexistent_int(self, task_manager: TaskManager[int]): 

116 """Test getting metadata for non-existent integer task ID.""" 

117 result = await task_manager.get_metadata(999) 

118 assert result is None 

119 

120 @pytest.mark.asyncio 

121 async def test_get_all_output_int(self, task_manager: TaskManager[int]): 

122 """Test getting all outputs with integer task IDs.""" 

123 await task_manager.setup_tasks([(1,), (2,)]) 

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

125 await task_manager.update_outputs([1, 2], outputs) 

126 

127 df = await task_manager.get_all_output() 

128 assert len(df) == 2 

129 assert set(df["task_id"]) == {1, 2} 

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

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

132 

133 @pytest.mark.asyncio 

134 async def test_get_all_output_flatten_int(self, task_manager: TaskManager[int]): 

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

136 await task_manager.setup_tasks([(1,), (2,)]) 

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

138 await task_manager.update_outputs([1, 2], outputs) 

139 

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

141 assert len(df) == 2 

142 assert "result" in df.columns 

143 assert "count" in df.columns 

144 assert set(df["task_id"]) == {1, 2} 

145 

146 @pytest.mark.asyncio 

147 async def test_get_all_metadata_int(self, task_manager: TaskManager[int]): 

148 """Test getting all metadata with integer task IDs.""" 

149 await task_manager.setup_tasks([(1,), (2,)]) 

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

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

152 await task_manager.update_outputs_n_metadatas([1, 2], outputs, metadatas) 

153 

154 df = await task_manager.get_all_metadata() 

155 assert len(df) == 2 

156 assert set(df["task_id"]) == {1, 2} 

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

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

159 

160 @pytest.mark.asyncio 

161 async def test_get_pending_tasks_int(self, task_manager: TaskManager[int]): 

162 """Test getting pending tasks with integer IDs.""" 

163 await task_manager.setup_tasks([(1,), (2,), (3,)]) 

164 await task_manager.update_outputs([1], [{"result": "success"}]) 

165 

166 pending = await task_manager.get_pending_tasks() 

167 assert sorted(pending) == [2, 3] 

168 

169 @pytest.mark.asyncio 

170 async def test_get_noresult_tasks_int(self, task_manager: TaskManager[int]): 

171 """Test getting no-result tasks with integer IDs.""" 

172 await task_manager.setup_tasks([(1,), (2,), (3,)]) 

173 await task_manager.update_outputs([1, 2], [{"result": "success"}, {}]) 

174 

175 noresult = await task_manager.get_noresult_tasks() 

176 assert noresult == [2] 

177 

178 @pytest.mark.asyncio 

179 async def test_mark_noresult_tasks_as_pending_int(self, task_manager: TaskManager[int]): 

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

181 await task_manager.setup_tasks([(1,), (2,)]) 

182 await task_manager.update_outputs_n_metadatas( 

183 [1, 2], [{"result": "success"}, {}], [{"meta": "data"}, {"meta": "data2"}] 

184 ) 

185 

186 # Verify task 2 has empty output but metadata 

187 output_2 = await task_manager.get_output(2) 

188 metadata_2 = await task_manager.get_metadata(2) 

189 assert output_2 == {} 

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

191 

192 await task_manager.mark_noresult_tasks_as_pending() 

193 

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

195 output_2_after = await task_manager.get_output(2) 

196 metadata_2_after = await task_manager.get_metadata(2) 

197 assert output_2_after is None 

198 assert metadata_2_after is None 

199 

200 pending = await task_manager.get_pending_tasks() 

201 assert 2 in pending 

202 

203 @pytest.mark.asyncio 

204 async def test_query_int(self, task_manager: TaskManager[int]): 

205 """Test custom query execution.""" 

206 await task_manager.setup_tasks([(1,), (2,), (3,)]) 

207 

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

209 assert len(df) == 1 

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

211 

212 @pytest.mark.asyncio 

213 async def test_static_methods(self): 

214 """Test static JSON serialization methods.""" 

215 test_dict = {"key": "value", "number": 42} 

216 

217 # Test _dumps 

218 blob = TaskManager._dumps(test_dict) 

219 assert isinstance(blob, bytes) 

220 

221 # Test _loads 

222 result = TaskManager._loads(blob) 

223 assert result == test_dict 

224 

225 # Test _loads with invalid data 

226 invalid_result = TaskManager._loads(b"invalid json") 

227 assert invalid_result is None 

228 

229 # Test _dumps with unserializable data (should return None) 

230 class UnserializableClass: 

231 pass 

232 

233 unserializable = {"obj": UnserializableClass()} 

234 result = TaskManager._dumps(unserializable) 

235 assert result is None 

236 

237 @pytest.mark.asyncio 

238 async def test_executemany_int(self, task_manager: TaskManager[int]): 

239 """Test executemany method.""" 

240 # Create some initial tasks 

241 await task_manager.setup_tasks([(1,), (2,), (3,)]) 

242 

243 # Use executemany to update multiple tasks at once 

244 data = [(orjson.dumps({"result": f"batch_{i}"}), i) for i in [1, 2, 3]] 

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

246 

247 # Verify updates 

248 for i in [1, 2, 3]: 

249 output = await task_manager.get_output(i) 

250 assert output == {"result": f"batch_{i}"}