Coverage for tests / unit / tools / core / test_tool_options_spec.py: 100%

37 statements  

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

1"""Tests for ToolOptionsSpec in lintro.tools.core.option_spec module.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6from assertpy import assert_that 

7 

8from lintro.tools.core.option_spec import ( 

9 OptionSpec, 

10 OptionType, 

11 ToolOptionsSpec, 

12 bool_option, 

13 int_option, 

14 str_option, 

15) 

16 

17 

18def test_add_returns_self_for_chaining() -> None: 

19 """Add method returns self for method chaining.""" 

20 spec = ToolOptionsSpec() 

21 result = spec.add(bool_option("preview", "--preview")) 

22 assert_that(result).is_same_as(spec) 

23 

24 

25def test_add_multiple_options_via_chaining() -> None: 

26 """Multiple options can be added via chaining.""" 

27 spec = ( 

28 ToolOptionsSpec() 

29 .add(bool_option("preview", "--preview")) 

30 .add(int_option("line_length", "--line-length", default=88)) 

31 .add(str_option("target", "--target")) 

32 ) 

33 assert_that(spec.options).contains_key("preview", "line_length", "target") 

34 

35 

36def test_validate_all_valid_values() -> None: 

37 """Validate all passes for valid values.""" 

38 spec = ( 

39 ToolOptionsSpec() 

40 .add(bool_option("preview", "--preview")) 

41 .add(int_option("line_length", "--line-length")) 

42 ) 

43 # Should not raise 

44 spec.validate_all({"preview": True, "line_length": 88}) 

45 

46 

47def test_validate_all_rejects_invalid_value() -> None: 

48 """Validate all raises for invalid value.""" 

49 spec = ToolOptionsSpec().add( 

50 int_option("line_length", "--line-length", min_value=1), 

51 ) 

52 with pytest.raises(ValueError, match="line_length"): 

53 spec.validate_all({"line_length": 0}) 

54 

55 

56def test_validate_all_checks_required() -> None: 

57 """Validate all checks required options.""" 

58 spec = ToolOptionsSpec().add( 

59 OptionSpec( 

60 name="required_opt", 

61 cli_flag="--required", 

62 option_type=OptionType.STR, 

63 required=True, 

64 ), 

65 ) 

66 with pytest.raises(ValueError, match="required_opt is required"): 

67 spec.validate_all({}) 

68 

69 

70def test_to_cli_args() -> None: 

71 """Convert all values to CLI args.""" 

72 spec = ( 

73 ToolOptionsSpec() 

74 .add(bool_option("preview", "--preview")) 

75 .add(int_option("line_length", "--line-length")) 

76 ) 

77 result = spec.to_cli_args({"preview": True, "line_length": 88}) 

78 assert_that(result).contains("--preview", "--line-length", "88") 

79 

80 

81def test_to_cli_args_skips_false_bools() -> None: 

82 """CLI args skips False boolean values.""" 

83 spec = ToolOptionsSpec().add(bool_option("preview", "--preview")) 

84 result = spec.to_cli_args({"preview": False}) 

85 assert_that(result).is_empty() 

86 

87 

88def test_get_defaults() -> None: 

89 """Get defaults returns default values.""" 

90 spec = ( 

91 ToolOptionsSpec() 

92 .add(bool_option("preview", "--preview", default=False)) 

93 .add(int_option("line_length", "--line-length", default=88)) 

94 .add(str_option("target", "--target")) 

95 ) 

96 defaults = spec.get_defaults() 

97 assert_that(defaults).is_equal_to({"preview": False, "line_length": 88}) 

98 

99 

100def test_get_defaults_empty_when_no_defaults() -> None: 

101 """Get defaults returns empty dict when no defaults.""" 

102 spec = ( 

103 ToolOptionsSpec() 

104 .add(bool_option("preview", "--preview")) 

105 .add(str_option("target", "--target")) 

106 ) 

107 assert_that(spec.get_defaults()).is_empty()