import tempfile import unittest from pathlib import Path from subprocess import CompletedProcess from unittest.mock import Mock, patch from PIL import Image from copienator.commands import annotating class AnnotationLatexErrorTests(unittest.TestCase): def test_failure_preserves_source_and_log_and_explains_recovery(self): with tempfile.TemporaryDirectory() as directory: diagnostics = Path(directory) / "diagnostics" diagnostics.mkdir() def compile_latex(command, **kwargs): Path(kwargs["cwd"], "text.log").write_bytes( b"preamble\n! Missing \\endcsname inserted.\nl.12 \\end{document}\n\xff" ) return CompletedProcess(command, 1, stdout=b"compiler output\xff") # Patch only the retained diagnostics directory, not TemporaryDirectory. original_mkdtemp = tempfile.mkdtemp def make_directory(*args, **kwargs): if kwargs.get("prefix") == "copienator-latex-error-": return str(diagnostics) return original_mkdtemp(*args, **kwargs) with patch.object(annotating.subprocess, "run", side_effect=compile_latex), patch.object( annotating.tempfile, "mkdtemp", side_effect=make_directory ): with self.assertRaises(annotating.AnnotationLatexError) as caught: annotating.render_real_latex_text(r"Antislash \.", 400) message = str(caught.exception) for expected in ("code 1", r"Antislash \.", "Missing", "correction.json", "relancez cette étape", str(diagnostics)): self.assertIn(expected, message) self.assertIn(r"Antislash \.", (diagnostics / "text.tex").read_text()) self.assertTrue((diagnostics / "text.log").exists()) self.assertTrue((diagnostics / "compiler-output.txt").exists()) def test_comment_error_identifies_copy_label_and_correction_file(self): for box in (None, [0, 0, 20, 20]): with self.subTest(box=box), patch.object( annotating, "render_score_text", return_value=Image.new("RGBA", (200, 40)) ): with self.assertRaises(annotating.AnnotationLatexError) as caught: annotating.compose_label_image( Image.new("RGB", (800, 200)), "I)7)", {"feedback": [{"text": "Bad comment", "box_2d": box}]}, 0, render_fn=Mock(side_effect=annotating.AnnotationLatexError("Bad comment")), source_pdf=Path("/exam/Copies/Copie39/I)7).pdf"), ) message = str(caught.exception) for expected in ("Copie39", "I)7)", "/exam/correction.json", "Bad comment"): self.assertIn(expected, message)