import json import tempfile import unittest from pathlib import Path from unittest.mock import Mock, patch from copienator import EvaluationWorkspace, atomic_write_json from copienator.commands import correction class CorrectionIdTests(unittest.TestCase): def test_mismatched_ids_are_rejected_before_any_side_effects(self): for ids, diagnostic in ( (["37", "03", "05"], "unexpected IDs: ['03']"), (["37"], "missing IDs: ['05']"), (["37", "05", "05"], "duplicate IDs: ['05']"), ): with self.subTest(ids=ids), tempfile.TemporaryDirectory() as directory: root = Path(directory) group = root / "Par label" / "Ex 1" / "Group_1.jpg" group.parent.mkdir(parents=True) group.touch() atomic_write_json(group.with_suffix(".json"), [ ["37", 0, 500, 1], ["05", 550, 1000, 1], ]) pdf = root / "Copies" / "Copie37" / "Ex 1.pdf" pdf.parent.mkdir(parents=True) pdf.write_bytes(b"original") workspace = EvaluationWorkspace(root) prior = {"Other": [[{"id": "22", "result": {"score": 4}}]]} atomic_write_json(workspace.correction_file, prior) before = workspace.correction_file.read_bytes() args = correction.build_parser().parse_args([str(root)]) correction.configure_runtime(workspace, [(str(group), "Ex 1")], args, api_client=Mock()) response = [{"id": pid, "result": { "score": 0, "error": "empty-answer", "feedback": [], }} for pid in ids] with patch.object(correction.prompting, "generate_request", return_value=([], {})), patch.object( correction, "handle_label_errors" ) as handle: correction.process_single_task((str(group), "Ex 1"), json.dumps(response)) handle.assert_not_called() self.assertEqual(pdf.read_bytes(), b"original") self.assertFalse(pdf.with_name("Ex 1_old.pdf").exists()) self.assertEqual(workspace.correction_file.read_bytes(), before) self.assertEqual(correction.completed_tasks, []) self.assertEqual(correction.results, prior) self.assertIn(diagnostic, correction.errors_summary[0][0]) def test_valid_ids_can_be_returned_in_different_order(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) group = root / "Par label" / "Ex 1" / "Group_1.jpg" group.parent.mkdir(parents=True) group.touch() atomic_write_json(group.with_suffix(".json"), [ ["37", 0, 500, 1], ["05", 550, 1000, 1], ]) workspace = EvaluationWorkspace(root) args = correction.build_parser().parse_args([str(root)]) correction.configure_runtime(workspace, [(str(group), "Ex 1")], args, api_client=Mock()) response = [{"id": pid, "result": { "score": 4, "error": "", "feedback": [], }} for pid in ["05", "37"]] with patch.object(correction.prompting, "generate_request", return_value=([], {})): correction.process_single_task((str(group), "Ex 1"), json.dumps(response)) self.assertEqual(correction.errors_summary, []) self.assertEqual(correction.results["Ex 1"], [response]) self.assertEqual(correction.completed_tasks, [(str(group), "Ex 1")])