|
14 | 14 | # KIND, either express or implied. See the License for the |
15 | 15 | # specific language governing permissions and limitations |
16 | 16 | # under the License. |
| 17 | +import json |
| 18 | + |
17 | 19 | from pyiceberg.catalog.rest.expression import ( |
18 | 20 | AndOrExpression, |
19 | 21 | Expression, |
20 | 22 | LiteralExpression, |
21 | 23 | Term, |
22 | 24 | ) |
23 | 25 | from pyiceberg.catalog.rest.planning_models import ( |
| 26 | + AsyncPlanningResult, |
| 27 | + CancelledPlanningResult, |
| 28 | + CompletedPlanningWithIDResult, |
24 | 29 | DataFile, |
25 | 30 | DeleteFile, |
26 | 31 | EqualityDeleteFile, |
| 32 | + FailedPlanningResult, |
27 | 33 | FileScanTask, |
28 | 34 | PlanTableScanRequest, |
| 35 | + PlanTableScanResult, |
29 | 36 | PositionDeleteFile, |
30 | 37 | ScanTasks, |
31 | 38 | ) |
| 39 | +from pyiceberg.catalog.rest.response import ErrorResponseMessage |
32 | 40 |
|
33 | 41 |
|
34 | 42 | def test_serialize_plan_table_scan_request() -> None: |
@@ -165,3 +173,118 @@ def snapshot_json_for_plan_table_scan_request() -> str: |
165 | 173 |
|
166 | 174 | def snapshot_json_for_scan_tasks() -> str: |
167 | 175 | return """{"delete-files":[{"content":"position-deletes","file-path":"/path/to/delete-a.parquet","file-format":"parquet","spec-id":0,"partition":[],"file-size-in-bytes":256,"record-count":10},{"content":"equality-deletes","file-path":"/path/to/delete-b.parquet","file-format":"parquet","spec-id":0,"partition":[],"file-size-in-bytes":256,"record-count":10,"equality-ids":[1,2]}],"file-scan-tasks":[{"data-file":{"content":"data","file-path":"/path/to/data-a.parquet","file-format":"parquet","spec-id":0,"partition":[],"file-size-in-bytes":1024,"record-count":56},"delete-file-references":[0,1]}]}""" |
| 176 | + |
| 177 | + |
| 178 | +def test_deserialize_async_planning_result() -> None: |
| 179 | + """Test deserializing a dict to an AsyncPlanningResult""" |
| 180 | + result = PlanTableScanResult.model_validate_json(snapshot_json_for_async_planning_result()) |
| 181 | + expected = AsyncPlanningResult(status="submitted", plan_id="plan-123") |
| 182 | + # Assert that deserialized dict == Python object |
| 183 | + assert result.root == expected |
| 184 | + |
| 185 | + |
| 186 | +def test_serialize_async_planning_result() -> None: |
| 187 | + """Test serializing an AsyncPlanningResult to a dict""" |
| 188 | + result = PlanTableScanResult(root=AsyncPlanningResult(status="submitted", plan_id="plan-123")) |
| 189 | + # Assert that JSON matches |
| 190 | + assert json.loads(result.model_dump_json(by_alias=True)) == json.loads(snapshot_json_for_async_planning_result()) |
| 191 | + |
| 192 | + |
| 193 | +def snapshot_json_for_async_planning_result() -> str: |
| 194 | + return """{"status":"submitted","plan-id":"plan-123"}""" |
| 195 | + |
| 196 | + |
| 197 | +def test_deserialize_failed_planning_result() -> None: |
| 198 | + """Test deserializing a dict to a FailedPlanningResult""" |
| 199 | + result = PlanTableScanResult.model_validate_json(snapshot_json_for_failed_planning_result()) |
| 200 | + expected = FailedPlanningResult( |
| 201 | + status="failed", |
| 202 | + error=ErrorResponseMessage( |
| 203 | + message="The plan is invalid", |
| 204 | + type="NoSuchPlanException", |
| 205 | + code=404, |
| 206 | + ), |
| 207 | + ) |
| 208 | + # Assert that deserialized dict == Python object |
| 209 | + assert result.root == expected |
| 210 | + |
| 211 | + |
| 212 | +def test_serialize_failed_planning_result() -> None: |
| 213 | + """Test serializing a FailedPlanningResult to a dict""" |
| 214 | + result = PlanTableScanResult( |
| 215 | + root=FailedPlanningResult( |
| 216 | + status="failed", |
| 217 | + error=ErrorResponseMessage( |
| 218 | + message="The plan is invalid", |
| 219 | + type="NoSuchPlanException", |
| 220 | + code=404, |
| 221 | + ), |
| 222 | + ) |
| 223 | + ) |
| 224 | + # Assert that JSON matches |
| 225 | + assert json.loads(result.model_dump_json(by_alias=True, exclude_none=True)) == json.loads( |
| 226 | + snapshot_json_for_failed_planning_result() |
| 227 | + ) |
| 228 | + |
| 229 | + |
| 230 | +def snapshot_json_for_failed_planning_result() -> str: |
| 231 | + return """{"status":"failed","error":{"message":"The plan is invalid","type":"NoSuchPlanException","code":404}}""" |
| 232 | + |
| 233 | + |
| 234 | +def test_deserialize_cancelled_planning_result() -> None: |
| 235 | + """Test deserializing a dict to an CancelledPlanningResult""" |
| 236 | + result = PlanTableScanResult.model_validate_json(snapshot_json_for_cancelled_planning_result()) |
| 237 | + expected = CancelledPlanningResult(status="cancelled") |
| 238 | + # Assert that deserialized dict == Python object |
| 239 | + assert result.root == expected |
| 240 | + |
| 241 | + |
| 242 | +def test_serialize_cancelled_planning_result() -> None: |
| 243 | + """Test serializing an CancelledPlanningResult to a dict""" |
| 244 | + result = PlanTableScanResult(root=CancelledPlanningResult(status="cancelled")) |
| 245 | + # Assert that JSON matches |
| 246 | + assert json.loads(result.model_dump_json(by_alias=True)) == json.loads(snapshot_json_for_cancelled_planning_result()) |
| 247 | + |
| 248 | + |
| 249 | +def snapshot_json_for_cancelled_planning_result() -> str: |
| 250 | + return """{"status":"cancelled"}""" |
| 251 | + |
| 252 | + |
| 253 | +def test_deserialize_completed_planning_with_id_result() -> None: |
| 254 | + """Test deserializing a dict to a CompletedPlanningWithIDResult""" |
| 255 | + scan_tasks_dict = json.loads(snapshot_json_for_scan_tasks()) |
| 256 | + scan_tasks_dict["status"] = "completed" |
| 257 | + scan_tasks_dict["plan-id"] = "plan-456" |
| 258 | + json_str = json.dumps(scan_tasks_dict) |
| 259 | + |
| 260 | + result = PlanTableScanResult.model_validate_json(json_str) |
| 261 | + expected_scan_tasks = ScanTasks.model_validate_json(snapshot_json_for_scan_tasks()) |
| 262 | + |
| 263 | + expected = CompletedPlanningWithIDResult( |
| 264 | + status="completed", |
| 265 | + plan_id="plan-456", |
| 266 | + file_scan_tasks=expected_scan_tasks.file_scan_tasks, |
| 267 | + delete_files=expected_scan_tasks.delete_files, |
| 268 | + ) |
| 269 | + # Assert that deserialized dict == Python object |
| 270 | + assert result.root == expected |
| 271 | + |
| 272 | + |
| 273 | +def test_serialize_completed_planning_with_id_result() -> None: |
| 274 | + """Test serializing a CompletedPlanningWithIDResult to a dict""" |
| 275 | + expected_scan_tasks = ScanTasks.model_validate_json(snapshot_json_for_scan_tasks()) |
| 276 | + result = PlanTableScanResult( |
| 277 | + root=CompletedPlanningWithIDResult( |
| 278 | + status="completed", |
| 279 | + plan_id="plan-456", |
| 280 | + file_scan_tasks=expected_scan_tasks.file_scan_tasks, |
| 281 | + delete_files=expected_scan_tasks.delete_files, |
| 282 | + ) |
| 283 | + ) |
| 284 | + |
| 285 | + scan_tasks_dict = json.loads(snapshot_json_for_scan_tasks()) |
| 286 | + scan_tasks_dict["status"] = "completed" |
| 287 | + scan_tasks_dict["plan-id"] = "plan-456" |
| 288 | + |
| 289 | + # Assert that JSON matches |
| 290 | + assert json.loads(result.model_dump_json(exclude_none=True, by_alias=True)) == scan_tasks_dict |
0 commit comments