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

1"""Tests for the AI undo patch writer.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6 

7from assertpy import assert_that 

8 

9from lintro.ai.models import AIFixSuggestion 

10from lintro.ai.undo import UNDO_DIR, UNDO_FILE, save_undo_patch 

11 

12 

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 ) 

28 

29 

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) 

34 

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) 

38 

39 

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) 

46 

47 

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() 

53 

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") 

59 

60 

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() 

65 

66 

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() 

72 

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("@@")