Coverage for tests / unit / ai / test_undo.py: 100%
36 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Tests for the AI undo patch writer."""
3from __future__ import annotations
5from pathlib import Path
7from assertpy import assert_that
9from lintro.ai.models import AIFixSuggestion
10from lintro.ai.undo import UNDO_DIR, UNDO_FILE, save_undo_patch
13def _make_suggestion(
14 file: str = "src/app.py",
15 original: str = "x = 1\n",
16 suggested: str = "x = 2\n",
17) -> AIFixSuggestion:
18 return AIFixSuggestion(
19 file=file,
20 line=1,
21 code="E001",
22 tool_name="ruff",
23 original_code=original,
24 suggested_code=suggested,
25 diff="",
26 explanation="fix",
27 )
30def test_saves_patch_file(tmp_path: Path) -> None:
31 """save_undo_patch creates a patch file on disk."""
32 s = _make_suggestion()
33 result = save_undo_patch([s], workspace_root=tmp_path)
35 expected_path = tmp_path / UNDO_DIR / UNDO_FILE
36 assert_that(expected_path.exists()).is_true()
37 assert_that(result).is_equal_to(expected_path)
40def test_returns_path(tmp_path: Path) -> None:
41 """save_undo_patch returns the path to the patch file."""
42 s = _make_suggestion()
43 result = save_undo_patch([s], workspace_root=tmp_path)
44 assert_that(result).is_not_none()
45 assert_that(str(result)).ends_with(UNDO_FILE)
48def test_reverse_diff_suggested_to_original(tmp_path: Path) -> None:
49 """The patch is a reverse diff (suggested -> original) for undo."""
50 s = _make_suggestion(original="old_line\n", suggested="new_line\n")
51 result = save_undo_patch([s], workspace_root=tmp_path)
52 assert_that(result).is_not_none()
54 content = result.read_text() # type: ignore[union-attr] # assertpy is_not_none narrows this
55 # In a reverse diff, the "from" shows the suggested (new) code
56 # and the "to" shows the original (old) code
57 assert_that(content).contains("-new_line")
58 assert_that(content).contains("+old_line")
61def test_empty_list_returns_none(tmp_path: Path) -> None:
62 """An empty suggestion list returns None."""
63 result = save_undo_patch([], workspace_root=tmp_path)
64 assert_that(result).is_none()
67def test_patch_content_is_valid_unified_diff(tmp_path: Path) -> None:
68 """The patch content contains standard unified diff markers."""
69 s = _make_suggestion(original="alpha\n", suggested="beta\n")
70 result = save_undo_patch([s], workspace_root=tmp_path)
71 assert_that(result).is_not_none()
73 content = result.read_text() # type: ignore[union-attr] # assertpy is_not_none narrows this
74 assert_that(content).contains("---")
75 assert_that(content).contains("+++")
76 assert_that(content).contains("@@")