Marco Ricci commited on 2025-01-25 23:29:34
Zeige 1 geänderte Dateien mit 173 Einfügungen und 36 Löschungen.
Add hypothesis tests for vault key discovery and for registring vault configuration data export handlers, each via their own parameter object and corresponding hypothesis strategy, and convert all existing explicit parametrized tests to hypothesis examples.
... | ... |
@@ -4,12 +4,17 @@ |
4 | 4 |
|
5 | 5 |
from __future__ import annotations |
6 | 6 |
|
7 |
+import contextlib |
|
8 |
+import operator |
|
7 | 9 |
import os |
8 | 10 |
import pathlib |
9 |
-from typing import TYPE_CHECKING, Any |
|
11 |
+import string |
|
12 |
+from typing import TYPE_CHECKING, Any, NamedTuple |
|
10 | 13 |
|
11 | 14 |
import click.testing |
15 |
+import hypothesis |
|
12 | 16 |
import pytest |
17 |
+from hypothesis import strategies |
|
13 | 18 |
|
14 | 19 |
import tests |
15 | 20 |
from derivepassphrase import cli, exporter |
... | ... |
@@ -21,33 +26,143 @@ if TYPE_CHECKING: |
21 | 26 |
class Test001ExporterUtils: |
22 | 27 |
"""Test the utility functions in the `exporter` subpackage.""" |
23 | 28 |
|
24 |
- @pytest.mark.parametrize( |
|
25 |
- ['expected', 'vault_key', 'logname', 'user', 'username'], |
|
26 |
- [ |
|
27 |
- ('4username', None, None, None, '4username'), |
|
28 |
- ('3user', None, None, '3user', None), |
|
29 |
- ('3user', None, None, '3user', '4username'), |
|
30 |
- ('2logname', None, '2logname', None, None), |
|
31 |
- ('2logname', None, '2logname', None, '4username'), |
|
32 |
- ('2logname', None, '2logname', '3user', None), |
|
33 |
- ('2logname', None, '2logname', '3user', '4username'), |
|
34 |
- ('1vault_key', '1vault_key', None, None, None), |
|
35 |
- ('1vault_key', '1vault_key', None, None, '4username'), |
|
36 |
- ('1vault_key', '1vault_key', None, '3user', None), |
|
37 |
- ('1vault_key', '1vault_key', None, '3user', '4username'), |
|
38 |
- ('1vault_key', '1vault_key', '2logname', None, None), |
|
39 |
- ('1vault_key', '1vault_key', '2logname', None, '4username'), |
|
40 |
- ('1vault_key', '1vault_key', '2logname', '3user', None), |
|
41 |
- ('1vault_key', '1vault_key', '2logname', '3user', '4username'), |
|
42 |
- ], |
|
29 |
+ class VaultKeyEnvironment(NamedTuple): |
|
30 |
+ """An environment configuration for vault key determination. |
|
31 |
+ |
|
32 |
+ Attributes: |
|
33 |
+ expected: |
|
34 |
+ The correct vault key value. |
|
35 |
+ vault_key: |
|
36 |
+ The value for the `VAULT_KEY` environment variable. |
|
37 |
+ logname: |
|
38 |
+ The value for the `LOGNAME` environment variable. |
|
39 |
+ user: |
|
40 |
+ The value for the `USER` environment variable. |
|
41 |
+ username: |
|
42 |
+ The value for the `USERNAME` environment variable. |
|
43 |
+ |
|
44 |
+ """ |
|
45 |
+ |
|
46 |
+ expected: str | None |
|
47 |
+ """""" |
|
48 |
+ vault_key: str | None |
|
49 |
+ """""" |
|
50 |
+ logname: str | None |
|
51 |
+ """""" |
|
52 |
+ user: str | None |
|
53 |
+ """""" |
|
54 |
+ username: str | None |
|
55 |
+ """""" |
|
56 |
+ |
|
57 |
+ @strategies.composite |
|
58 |
+ @staticmethod |
|
59 |
+ def strategy( |
|
60 |
+ draw: strategies.DrawFn, |
|
61 |
+ allow_missing: bool = False, |
|
62 |
+ ) -> Test001ExporterUtils.VaultKeyEnvironment: |
|
63 |
+ """Return a vault key environment configuration.""" |
|
64 |
+ text_strategy = strategies.text( |
|
65 |
+ strategies.characters(min_codepoint=32, max_codepoint=127), |
|
66 |
+ min_size=1, |
|
67 |
+ max_size=24, |
|
68 |
+ ) |
|
69 |
+ env_var_strategy = strategies.one_of( |
|
70 |
+ strategies.none(), |
|
71 |
+ text_strategy, |
|
72 |
+ ) |
|
73 |
+ num_fields = sum( |
|
74 |
+ 1 |
|
75 |
+ for f in Test001ExporterUtils.VaultKeyEnvironment._fields |
|
76 |
+ if f != 'expected' |
|
77 |
+ ) |
|
78 |
+ env_vars: list[str | None] = draw( |
|
79 |
+ strategies.builds( |
|
80 |
+ operator.add, |
|
81 |
+ strategies.lists( |
|
82 |
+ env_var_strategy, |
|
83 |
+ min_size=num_fields - 1, |
|
84 |
+ max_size=num_fields - 1, |
|
85 |
+ ), |
|
86 |
+ strategies.lists( |
|
87 |
+ text_strategy |
|
88 |
+ if not allow_missing |
|
89 |
+ else env_var_strategy, |
|
90 |
+ min_size=1, |
|
91 |
+ max_size=1, |
|
92 |
+ ), |
|
93 |
+ ) |
|
94 |
+ ) |
|
95 |
+ expected: str | None = None |
|
96 |
+ for value in reversed(env_vars): |
|
97 |
+ if value is not None: |
|
98 |
+ expected = value |
|
99 |
+ return Test001ExporterUtils.VaultKeyEnvironment( |
|
100 |
+ expected, *env_vars |
|
101 |
+ ) |
|
102 |
+ |
|
103 |
+ @hypothesis.example( |
|
104 |
+ VaultKeyEnvironment('4username', None, None, None, '4username') |
|
105 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
106 |
+ @hypothesis.example( |
|
107 |
+ VaultKeyEnvironment('3user', None, None, '3user', None) |
|
108 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
109 |
+ @hypothesis.example( |
|
110 |
+ VaultKeyEnvironment('3user', None, None, '3user', '4username') |
|
111 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
112 |
+ @hypothesis.example( |
|
113 |
+ VaultKeyEnvironment('2logname', None, '2logname', None, None) |
|
114 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
115 |
+ @hypothesis.example( |
|
116 |
+ VaultKeyEnvironment('2logname', None, '2logname', None, '4username') |
|
117 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
118 |
+ @hypothesis.example( |
|
119 |
+ VaultKeyEnvironment('2logname', None, '2logname', '3user', None) |
|
120 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
121 |
+ @hypothesis.example( |
|
122 |
+ VaultKeyEnvironment('2logname', None, '2logname', '3user', '4username') |
|
123 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
124 |
+ @hypothesis.example( |
|
125 |
+ VaultKeyEnvironment('1vault_key', '1vault_key', None, None, None) |
|
126 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
127 |
+ @hypothesis.example( |
|
128 |
+ VaultKeyEnvironment( |
|
129 |
+ '1vault_key', '1vault_key', None, None, '4username' |
|
130 |
+ ) |
|
131 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
132 |
+ @hypothesis.example( |
|
133 |
+ VaultKeyEnvironment('1vault_key', '1vault_key', None, '3user', None) |
|
134 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
135 |
+ @hypothesis.example( |
|
136 |
+ VaultKeyEnvironment( |
|
137 |
+ '1vault_key', '1vault_key', None, '3user', '4username' |
|
138 |
+ ) |
|
139 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
140 |
+ @hypothesis.example( |
|
141 |
+ VaultKeyEnvironment('1vault_key', '1vault_key', '2logname', None, None) |
|
142 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
143 |
+ @hypothesis.example( |
|
144 |
+ VaultKeyEnvironment( |
|
145 |
+ '1vault_key', '1vault_key', '2logname', None, '4username' |
|
146 |
+ ) |
|
147 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
148 |
+ @hypothesis.example( |
|
149 |
+ VaultKeyEnvironment( |
|
150 |
+ '1vault_key', '1vault_key', '2logname', '3user', None |
|
151 |
+ ) |
|
152 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
153 |
+ @hypothesis.example( |
|
154 |
+ VaultKeyEnvironment( |
|
155 |
+ '1vault_key', '1vault_key', '2logname', '3user', '4username' |
|
156 |
+ ) |
|
157 |
+ ).via('manual, pre-hypothesis parametrization value') |
|
158 |
+ @hypothesis.given( |
|
159 |
+ vault_key_env=VaultKeyEnvironment.strategy().filter( |
|
160 |
+ lambda env: bool(env.expected) |
|
161 |
+ ), |
|
43 | 162 |
) |
44 | 163 |
def test_200_get_vault_key( |
45 | 164 |
self, |
46 |
- expected: str, |
|
47 |
- vault_key: str | None, |
|
48 |
- logname: str | None, |
|
49 |
- user: str | None, |
|
50 |
- username: str | None, |
|
165 |
+ vault_key_env: VaultKeyEnvironment, |
|
51 | 166 |
) -> None: |
52 | 167 |
"""Look up the vault key in `VAULT_KEY`/`LOGNAME`/`USER`/`USERNAME`. |
53 | 168 |
|
... | ... |
@@ -55,6 +170,8 @@ class Test001ExporterUtils: |
55 | 170 |
their relative priorities. |
56 | 171 |
|
57 | 172 |
""" |
173 |
+ expected, vault_key, logname, user, username = vault_key_env |
|
174 |
+ assert expected is not None |
|
58 | 175 |
priority_list = [ |
59 | 176 |
('VAULT_KEY', vault_key), |
60 | 177 |
('LOGNAME', logname), |
... | ... |
@@ -115,7 +232,32 @@ class Test001ExporterUtils: |
115 | 232 |
== expected.expanduser().resolve() |
116 | 233 |
) |
117 | 234 |
|
118 |
- def test_220_register_export_vault_config_data_handler(self) -> None: |
|
235 |
+ @hypothesis.given( |
|
236 |
+ name_data=strategies.lists( |
|
237 |
+ strategies.integers(min_value=1, max_value=3), |
|
238 |
+ min_size=2, |
|
239 |
+ max_size=2, |
|
240 |
+ ).flatmap( |
|
241 |
+ lambda nm: strategies.lists( |
|
242 |
+ strategies.builds( |
|
243 |
+ operator.add, |
|
244 |
+ strategies.sampled_from(string.ascii_letters), |
|
245 |
+ strategies.text( |
|
246 |
+ string.ascii_letters + string.digits + '_-', |
|
247 |
+ max_size=23, |
|
248 |
+ ), |
|
249 |
+ ), |
|
250 |
+ min_size=sum(nm), |
|
251 |
+ max_size=sum(nm), |
|
252 |
+ unique=True, |
|
253 |
+ ).flatmap( |
|
254 |
+ lambda list_: strategies.just((list_[nm[0] :], list_[: nm[0]])) |
|
255 |
+ ) |
|
256 |
+ ), |
|
257 |
+ ) |
|
258 |
+ def test_220_register_export_vault_config_data_handler( |
|
259 |
+ self, name_data: tuple[list[str], list[str]] |
|
260 |
+ ) -> None: |
|
119 | 261 |
"""Register vault config data export handlers.""" |
120 | 262 |
|
121 | 263 |
def handler( # pragma: no cover |
... | ... |
@@ -127,21 +269,16 @@ class Test001ExporterUtils: |
127 | 269 |
del path, key |
128 | 270 |
raise ValueError(format) |
129 | 271 |
|
272 |
+ names1, names2 = name_data |
|
273 |
+ |
|
130 | 274 |
with pytest.MonkeyPatch.context() as monkeypatch: |
131 |
- registry = {'dummy': handler} |
|
275 |
+ registry = dict.fromkeys(names1, handler) |
|
132 | 276 |
monkeypatch.setattr( |
133 | 277 |
exporter, '_export_vault_config_data_registry', registry |
134 | 278 |
) |
135 |
- dec = exporter.register_export_vault_config_data_handler( |
|
136 |
- 'name1', |
|
137 |
- 'name2', |
|
138 |
- ) |
|
279 |
+ dec = exporter.register_export_vault_config_data_handler(*names2) |
|
139 | 280 |
assert dec(handler) == handler |
140 |
- assert registry == { |
|
141 |
- 'dummy': handler, |
|
142 |
- 'name1': handler, |
|
143 |
- 'name2': handler, |
|
144 |
- } |
|
281 |
+ assert registry == dict.fromkeys(names1 + names2, handler) |
|
145 | 282 |
|
146 | 283 |
def test_300_get_vault_key_without_envs(self) -> None: |
147 | 284 |
"""Fail to look up the vault key in the empty environment.""" |
148 | 285 |