Coverage for tests / integration / test_actionlint_integration.py: 90%

49 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Integration tests for actionlint tool.""" 

2 

3from __future__ import annotations 

4 

5import subprocess 

6from pathlib import Path 

7 

8import pytest 

9from assertpy import assert_that 

10from loguru import logger 

11 

12from lintro.plugins import ToolRegistry 

13 

14logger.remove() 

15logger.add(lambda msg: print(msg, end=""), level="INFO") 

16 

17 

18def actionlint_available() -> bool: 

19 """Return True if the `actionlint` binary is available on PATH. 

20 

21 Returns: 

22 bool: True when `actionlint -version` succeeds, False otherwise. 

23 """ 

24 try: 

25 proc = subprocess.run( 

26 ["actionlint", "-version"], 

27 capture_output=True, 

28 text=True, 

29 ) 

30 return proc.returncode == 0 

31 except FileNotFoundError: 

32 return False 

33 

34 

35@pytest.mark.actionlint 

36def test_actionlint_available() -> None: 

37 """Skip the suite if actionlint is not present locally. 

38 

39 Ensures local runs behave like CI (which always has actionlint in Docker), 

40 but do not fail when developers don't have actionlint installed. 

41 """ 

42 if not actionlint_available(): 

43 pytest.skip("actionlint not available") 

44 

45 

46SAMPLE_BAD = Path("test_samples/tools/config/github_actions/actionlint_violations.yml") 

47 

48 

49@pytest.mark.actionlint 

50def test_actionlint_reports_violations(tmp_path: Path) -> None: 

51 """Assert that Lintro detects violations reported by actionlint. 

52 

53 Args: 

54 tmp_path: Temporary directory provided by pytest. 

55 """ 

56 if not actionlint_available(): 

57 pytest.skip("actionlint not available") 

58 wf_dir = tmp_path / ".github" / "workflows" 

59 wf_dir.mkdir(parents=True, exist_ok=True) 

60 wf = wf_dir / "workflow_bad.yml" 

61 wf.write_text(SAMPLE_BAD.read_text()) 

62 proc = subprocess.run(["actionlint", str(wf)], capture_output=True, text=True) 

63 direct_out = proc.stdout + proc.stderr 

64 logger.info(f"[LOG] actionlint stdout+stderr:\n{direct_out}") 

65 

66 assert_that(proc.returncode).is_not_equal_to(0) 

67 tool = ToolRegistry.get("actionlint") 

68 assert_that(tool).is_not_none() 

69 result = tool.check([str(tmp_path)], {}) 

70 logger.info(f"[LOG] lintro actionlint issues: {result.issues_count}") 

71 assert_that(result.issues_count > 0).is_true() 

72 assert_that(result.success).is_false() 

73 

74 

75@pytest.mark.actionlint 

76def test_actionlint_no_files(tmp_path: Path) -> None: 

77 """Assert that Lintro succeeds when no workflow files are present. 

78 

79 Args: 

80 tmp_path: Temporary directory provided by pytest. 

81 """ 

82 if not actionlint_available(): 

83 pytest.skip("actionlint not available") 

84 empty = tmp_path / "empty" 

85 empty.mkdir() 

86 tool = ToolRegistry.get("actionlint") 

87 assert_that(tool).is_not_none() 

88 result = tool.check([str(empty)], {}) 

89 assert_that(result.success).is_true() 

90 assert_that(result.issues_count).is_equal_to(0)