Coverage for tests\test_derivepassphrase_types.py: 100.000%

60 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-23 12:17 +0200

1# SPDX-FileCopyrightText: 2025 Marco Ricci <software@the13thletter.info> 

2# 

3# SPDX-License-Identifier: Zlib 

4 

5from __future__ import annotations 

6 

7import copy 

8import math 

9import types 

10 

11import hypothesis 

12import pytest 

13from hypothesis import strategies 

14from typing_extensions import Any 

15 

16import tests 

17from derivepassphrase import _types 

18 

19 

20@strategies.composite 

21def js_atoms_strategy( 

22 draw: strategies.DrawFn, 

23) -> int | float | str | bytes | bool | None: 

24 """Yield a JS atom.""" 

25 return draw( 1b

26 strategies.one_of( 

27 strategies.integers(), 

28 strategies.floats(allow_nan=False, allow_infinity=False), 

29 strategies.text(max_size=100), 

30 strategies.binary(max_size=100), 

31 strategies.booleans(), 

32 strategies.none(), 

33 ), 

34 ) 

35 

36 

37@strategies.composite 

38def js_nested_strategy(draw: strategies.DrawFn) -> Any: 

39 """Yield an arbitrary and perhaps nested JS value.""" 

40 return draw( 1b

41 strategies.one_of( 

42 js_atoms_strategy(), 

43 strategies.builds(tuple), 

44 strategies.builds(list), 

45 strategies.builds(dict), 

46 strategies.builds(set), 

47 strategies.builds(frozenset), 

48 strategies.recursive( 

49 js_atoms_strategy(), 

50 lambda s: strategies.one_of( 

51 strategies.frozensets(s, max_size=100), 

52 strategies.builds( 

53 tuple, strategies.frozensets(s, max_size=100) 

54 ), 

55 ), 

56 max_leaves=8, 

57 ), 

58 strategies.recursive( 

59 js_atoms_strategy(), 

60 lambda s: strategies.one_of( 

61 strategies.lists(s, max_size=100), 

62 strategies.dictionaries(strategies.text(max_size=100), s), 

63 ), 

64 max_leaves=25, 

65 ), 

66 ), 

67 ) 

68 

69 

70class Parametrize(types.SimpleNamespace): 

71 VALID_VAULT_TEST_CONFIGS = pytest.mark.parametrize( 

72 'test_config', 

73 [ 

74 conf 

75 for conf in tests.TEST_CONFIGS 

76 if conf.validation_settings in {None, (True,)} 

77 ], 

78 ids=tests._test_config_ids, 

79 ) 

80 VAULT_TEST_CONFIGS = pytest.mark.parametrize( 

81 'test_config', tests.TEST_CONFIGS, ids=tests._test_config_ids 

82 ) 

83 

84 

85@hypothesis.given(value=js_nested_strategy()) 

86@hypothesis.example(float('nan')) 1ab

87def test_100_js_truthiness(value: Any) -> None: 

88 """Determine the truthiness of a value according to JavaScript. 

89 

90 Use hypothesis to generate test values. 

91 

92 """ 

93 expected = ( 1be

94 value is not None # noqa: PLR1714 

95 and value != False # noqa: E712 

96 and value != 0 

97 and value != 0.0 

98 and value != '' 

99 and not (isinstance(value, float) and math.isnan(value)) 

100 ) 

101 assert _types.js_truthiness(value) == expected 1be

102 

103 

104@Parametrize.VALID_VAULT_TEST_CONFIGS 

105def test_200_is_vault_config(test_config: tests.VaultTestConfig) -> None: 

106 """Is this vault configuration recognized as valid/invalid? 

107 

108 Check all test configurations that do not need custom validation 

109 settings. 

110 

111 This primarily tests the [`_types.is_vault_config`][] and 

112 [`_types.clean_up_falsy_vault_config_values`][] functions. 

113 

114 """ 

115 obj, comment, _ = test_config 1d

116 obj = copy.deepcopy(obj) 1d

117 _types.clean_up_falsy_vault_config_values(obj) 1d

118 assert _types.is_vault_config(obj) == (not comment), ( 1d

119 'failed to complain about: ' + comment 

120 if comment 

121 else 'failed on valid example' 

122 ) 

123 

124 

125@hypothesis.given( 

126 test_config=tests.smudged_vault_test_config( 

127 config=strategies.sampled_from([ 

128 conf 

129 for conf in tests.TEST_CONFIGS 

130 if tests.is_valid_test_config(conf) 

131 ]) 

132 ) 

133) 

134def test_200a_is_vault_config_smudged( 

135 test_config: tests.VaultTestConfig, 

136) -> None: 

137 """Is this vault configuration recognized as valid/invalid? 

138 

139 Generate test data via hypothesis by smudging all valid test 

140 configurations. 

141 

142 This primarily tests the [`_types.is_vault_config`][] and 

143 [`_types.clean_up_falsy_vault_config_values`][] functions. 

144 

145 """ 

146 obj_, comment, _ = test_config 1b

147 obj = copy.deepcopy(obj_) 1b

148 did_cleanup = _types.clean_up_falsy_vault_config_values(obj) 1b

149 assert _types.is_vault_config(obj) == (not comment), ( 1b

150 'failed to complain about: ' + comment 

151 if comment 

152 else 'failed on valid example' 

153 ) 

154 assert did_cleanup is None or bool(did_cleanup) == (obj != obj_), ( 1b

155 'mismatched report on cleanup work' 

156 ) 

157 

158 

159@Parametrize.VAULT_TEST_CONFIGS 

160def test_400_validate_vault_config(test_config: tests.VaultTestConfig) -> None: 

161 """Validate this vault configuration. 

162 

163 Check all test configurations, including those with non-standard 

164 validation settings. 

165 

166 This primarily tests the [`_types.validate_vault_config`][] and 

167 [`_types.clean_up_falsy_vault_config_values`][] functions. 

168 

169 """ 

170 obj, comment, validation_settings = test_config 1c

171 (allow_unknown_settings,) = validation_settings or (True,) 1c

172 obj = copy.deepcopy(obj) 1c

173 _types.clean_up_falsy_vault_config_values(obj) 1c

174 if comment: 1c

175 with pytest.raises((TypeError, ValueError)): 1c

176 _types.validate_vault_config( 1c

177 obj, 

178 allow_unknown_settings=allow_unknown_settings, 

179 ) 

180 else: 

181 try: 1c

182 _types.validate_vault_config( 1c

183 obj, 

184 allow_unknown_settings=allow_unknown_settings, 

185 ) 

186 except (TypeError, ValueError) as exc: # pragma: no cover 

187 assert not exc, 'failed to validate valid example' # noqa: PT017 

188 

189 

190@hypothesis.given( 

191 test_config=tests.smudged_vault_test_config( 

192 config=strategies.sampled_from([ 

193 conf 

194 for conf in tests.TEST_CONFIGS 

195 if tests.is_smudgable_vault_test_config(conf) 

196 ]) 

197 ) 

198) 

199def test_400a_validate_vault_config_smudged( 

200 test_config: tests.VaultTestConfig, 

201) -> None: 

202 """Validate this vault configuration. 

203 

204 Generate test data via hypothesis by smudging all smudgable test 

205 configurations. 

206 

207 This primarily tests the [`_types.validate_vault_config`][] and 

208 [`_types.clean_up_falsy_vault_config_values`][] functions. 

209 

210 """ 

211 obj_, comment, validation_settings = test_config 1b

212 (allow_unknown_settings,) = validation_settings or (True,) 1b

213 obj = copy.deepcopy(obj_) 1b

214 did_cleanup = _types.clean_up_falsy_vault_config_values(obj) 1b

215 if comment: 1b

216 with pytest.raises((TypeError, ValueError)): 1b

217 _types.validate_vault_config( 1b

218 obj, 

219 allow_unknown_settings=allow_unknown_settings, 

220 ) 

221 else: 

222 try: 1b

223 _types.validate_vault_config( 1b

224 obj, 

225 allow_unknown_settings=allow_unknown_settings, 

226 ) 

227 except (TypeError, ValueError) as exc: # pragma: no cover 

228 assert not exc, 'failed to validate valid example' # noqa: PT017 

229 assert did_cleanup is None or bool(did_cleanup) == (obj != obj_), ( 1b

230 'mismatched report on cleanup work' 

231 )