woolly 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {woolly-0.3.0 → woolly-0.4.0}/PKG-INFO +3 -1
- woolly-0.4.0/examples/template/example_template.md +16 -0
- {woolly-0.3.0 → woolly-0.4.0}/pyproject.toml +5 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/functional/test_cli.py +83 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_commands/test_check.py +120 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_optional_dependencies.py +3 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/test_json.py +51 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/test_markdown.py +65 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/test_stdout.py +29 -0
- woolly-0.4.0/tests/unit/test_reporters/test_template.py +358 -0
- {woolly-0.3.0 → woolly-0.4.0}/uv.lock +104 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/commands/check.py +69 -1
- {woolly-0.3.0 → woolly-0.4.0}/woolly/reporters/__init__.py +15 -1
- {woolly-0.3.0 → woolly-0.4.0}/woolly/reporters/base.py +3 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/reporters/json.py +6 -1
- {woolly-0.3.0 → woolly-0.4.0}/woolly/reporters/markdown.py +12 -9
- {woolly-0.3.0 → woolly-0.4.0}/woolly/reporters/stdout.py +5 -4
- woolly-0.4.0/woolly/reporters/template.py +215 -0
- {woolly-0.3.0 → woolly-0.4.0}/.coverage +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/.github/workflows/lint.yml +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/.github/workflows/publish.yml +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/.github/workflows/tests.yml +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/.gitignore +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/.python-version +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/LICENSE +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/README.md +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/conftest.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/functional/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/functional/test_optional_flag.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_cache.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_commands/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_commands/test_other_commands.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_debug.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_languages/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_languages/test_base.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_languages/test_python.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_languages/test_registry.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_languages/test_rust.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_progress.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/test_base.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/tests/unit/test_reporters/test_registry.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/__main__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/cache.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/commands/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/commands/clear_cache.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/commands/list_formats.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/commands/list_languages.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/debug.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/http.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/languages/__init__.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/languages/base.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/languages/python.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/languages/rust.py +0 -0
- {woolly-0.3.0 → woolly-0.4.0}/woolly/progress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: woolly
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Check if package dependencies are available in Fedora. Supports Rust, Python, and more.
|
|
5
5
|
Author-email: Rodolfo Olivieri <rodolfo.olivieri3@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -26,6 +26,8 @@ Requires-Dist: cyclopts>=4.3.0
|
|
|
26
26
|
Requires-Dist: httpx>=0.28.0
|
|
27
27
|
Requires-Dist: pydantic>=2.10.0
|
|
28
28
|
Requires-Dist: rich>=14.2.0
|
|
29
|
+
Provides-Extra: template
|
|
30
|
+
Requires-Dist: jinja2>=3.1.0; extra == 'template'
|
|
29
31
|
Description-Content-Type: text/markdown
|
|
30
32
|
|
|
31
33
|
# 🐑 Woolly
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Report for {{ root_package }}
|
|
2
|
+
|
|
3
|
+
Generated: {{ timestamp }}
|
|
4
|
+
Language: {{ language }}
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
- Total: {{ total_dependencies }}
|
|
8
|
+
- Packaged: {{ packaged_count }}
|
|
9
|
+
- Missing: {{ missing_count }}
|
|
10
|
+
|
|
11
|
+
{% if missing_packages %}
|
|
12
|
+
## Missing Packages
|
|
13
|
+
{% for pkg in missing_packages %}
|
|
14
|
+
- {{ pkg }}
|
|
15
|
+
{% endfor %}
|
|
16
|
+
{% endif %}
|
|
@@ -37,6 +37,11 @@ classifiers = [
|
|
|
37
37
|
"Topic :: Utilities",
|
|
38
38
|
]
|
|
39
39
|
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
template = [
|
|
42
|
+
"jinja2>=3.1.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
40
45
|
[project.url]
|
|
41
46
|
"Source code" = "https://github.com/r0x0d/woolly"
|
|
42
47
|
"Bug Tracker" = "https://github.com/r0x0d/woolly/issues"
|
|
@@ -34,6 +34,7 @@ class TestCliHelp:
|
|
|
34
34
|
assert result.returncode == 0
|
|
35
35
|
assert "package" in result.stdout.lower()
|
|
36
36
|
assert "--lang" in result.stdout
|
|
37
|
+
assert "--missing-only" in result.stdout
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
class TestListLanguages:
|
|
@@ -263,6 +264,32 @@ class TestCheckCommandPython:
|
|
|
263
264
|
class TestCheckCommandOptions:
|
|
264
265
|
"""Tests for check command options."""
|
|
265
266
|
|
|
267
|
+
@pytest.mark.functional
|
|
268
|
+
@pytest.mark.slow
|
|
269
|
+
def test_missing_only_option(self, cli_runner, tmp_path, monkeypatch):
|
|
270
|
+
"""Good path: missing-only option is accepted."""
|
|
271
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
272
|
+
|
|
273
|
+
result = cli_runner(
|
|
274
|
+
"check", "cfg-if", "--lang", "rust", "--missing-only", "--no-progress"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
assert result.returncode == 0
|
|
278
|
+
# Should not show the dependency tree when --missing-only is used
|
|
279
|
+
assert "Dependency Tree:" not in result.stdout
|
|
280
|
+
|
|
281
|
+
@pytest.mark.functional
|
|
282
|
+
@pytest.mark.slow
|
|
283
|
+
def test_missing_only_short_option(self, cli_runner, tmp_path, monkeypatch):
|
|
284
|
+
"""Good path: missing-only short option (-m) is accepted."""
|
|
285
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
286
|
+
|
|
287
|
+
result = cli_runner("check", "cfg-if", "--lang", "rust", "-m", "--no-progress")
|
|
288
|
+
|
|
289
|
+
assert result.returncode == 0
|
|
290
|
+
# Should not show the dependency tree when -m is used
|
|
291
|
+
assert "Dependency Tree:" not in result.stdout
|
|
292
|
+
|
|
266
293
|
@pytest.mark.functional
|
|
267
294
|
@pytest.mark.slow
|
|
268
295
|
def test_max_depth_option(self, cli_runner, tmp_path, monkeypatch):
|
|
@@ -275,6 +302,62 @@ class TestCheckCommandOptions:
|
|
|
275
302
|
|
|
276
303
|
assert result.returncode == 0
|
|
277
304
|
|
|
305
|
+
@pytest.mark.functional
|
|
306
|
+
@pytest.mark.slow
|
|
307
|
+
def test_exclude_option(self, cli_runner, tmp_path, monkeypatch):
|
|
308
|
+
"""Good path: exclude option is accepted and displayed."""
|
|
309
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
310
|
+
|
|
311
|
+
result = cli_runner(
|
|
312
|
+
"check",
|
|
313
|
+
"cfg-if",
|
|
314
|
+
"--lang",
|
|
315
|
+
"rust",
|
|
316
|
+
"--exclude",
|
|
317
|
+
"windows*",
|
|
318
|
+
"--no-progress",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
assert result.returncode == 0
|
|
322
|
+
assert "Excluding dependencies matching" in result.stdout
|
|
323
|
+
assert "windows*" in result.stdout
|
|
324
|
+
|
|
325
|
+
@pytest.mark.functional
|
|
326
|
+
@pytest.mark.slow
|
|
327
|
+
def test_exclude_multiple_patterns(self, cli_runner, tmp_path, monkeypatch):
|
|
328
|
+
"""Good path: multiple exclude patterns are accepted."""
|
|
329
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
330
|
+
|
|
331
|
+
result = cli_runner(
|
|
332
|
+
"check",
|
|
333
|
+
"cfg-if",
|
|
334
|
+
"--lang",
|
|
335
|
+
"rust",
|
|
336
|
+
"--exclude",
|
|
337
|
+
"windows*",
|
|
338
|
+
"--exclude",
|
|
339
|
+
"*-sys",
|
|
340
|
+
"--no-progress",
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
assert result.returncode == 0
|
|
344
|
+
assert "Excluding dependencies matching" in result.stdout
|
|
345
|
+
assert "windows*" in result.stdout
|
|
346
|
+
assert "*-sys" in result.stdout
|
|
347
|
+
|
|
348
|
+
@pytest.mark.functional
|
|
349
|
+
@pytest.mark.slow
|
|
350
|
+
def test_exclude_short_option(self, cli_runner, tmp_path, monkeypatch):
|
|
351
|
+
"""Good path: -e short option works."""
|
|
352
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
353
|
+
|
|
354
|
+
result = cli_runner(
|
|
355
|
+
"check", "cfg-if", "--lang", "rust", "-e", "win*", "--no-progress"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
assert result.returncode == 0
|
|
359
|
+
assert "Excluding dependencies matching" in result.stdout
|
|
360
|
+
|
|
278
361
|
@pytest.mark.functional
|
|
279
362
|
@pytest.mark.slow
|
|
280
363
|
def test_debug_option(self, cli_runner, tmp_path, monkeypatch):
|
|
@@ -170,6 +170,126 @@ class TestBuildTree:
|
|
|
170
170
|
|
|
171
171
|
tracker.update.assert_called()
|
|
172
172
|
|
|
173
|
+
@pytest.mark.unit
|
|
174
|
+
def test_excludes_dependencies_matching_pattern(self, provider):
|
|
175
|
+
"""Good path: excludes dependencies matching glob patterns."""
|
|
176
|
+
# Set up package with dependencies
|
|
177
|
+
provider.packages["parent"] = PackageInfo(name="parent", latest_version="1.0.0")
|
|
178
|
+
provider.packages["child"] = PackageInfo(name="child", latest_version="2.0.0")
|
|
179
|
+
provider.packages["windows-sys"] = PackageInfo(
|
|
180
|
+
name="windows-sys", latest_version="0.52.0"
|
|
181
|
+
)
|
|
182
|
+
provider.dependencies["parent:1.0.0"] = [
|
|
183
|
+
Dependency(name="child", version_requirement="^2.0", kind="normal"),
|
|
184
|
+
Dependency(name="windows-sys", version_requirement="^0.52", kind="normal"),
|
|
185
|
+
]
|
|
186
|
+
provider.fedora_status["parent"] = FedoraPackageStatus(
|
|
187
|
+
is_packaged=True, versions=["1.0.0"]
|
|
188
|
+
)
|
|
189
|
+
provider.fedora_status["child"] = FedoraPackageStatus(
|
|
190
|
+
is_packaged=True, versions=["2.0.0"]
|
|
191
|
+
)
|
|
192
|
+
provider.fedora_status["windows-sys"] = FedoraPackageStatus(is_packaged=False)
|
|
193
|
+
|
|
194
|
+
tree = build_tree(provider, "parent", exclude_patterns=["windows*"])
|
|
195
|
+
|
|
196
|
+
# Should only have child, not windows-sys
|
|
197
|
+
assert isinstance(tree, Tree)
|
|
198
|
+
assert len(tree.children) == 1
|
|
199
|
+
child_label = str(tree.children[0].label)
|
|
200
|
+
assert "child" in child_label
|
|
201
|
+
# windows-sys should be excluded
|
|
202
|
+
for child in tree.children:
|
|
203
|
+
label = str(child.label) if hasattr(child, "label") else str(child)
|
|
204
|
+
assert "windows" not in label
|
|
205
|
+
|
|
206
|
+
@pytest.mark.unit
|
|
207
|
+
def test_excludes_multiple_patterns(self, provider):
|
|
208
|
+
"""Good path: excludes dependencies matching multiple glob patterns."""
|
|
209
|
+
provider.packages["parent"] = PackageInfo(name="parent", latest_version="1.0.0")
|
|
210
|
+
provider.packages["good-dep"] = PackageInfo(
|
|
211
|
+
name="good-dep", latest_version="1.0.0"
|
|
212
|
+
)
|
|
213
|
+
provider.packages["win-dep"] = PackageInfo(
|
|
214
|
+
name="win-dep", latest_version="1.0.0"
|
|
215
|
+
)
|
|
216
|
+
provider.packages["macos-dep"] = PackageInfo(
|
|
217
|
+
name="macos-dep", latest_version="1.0.0"
|
|
218
|
+
)
|
|
219
|
+
provider.dependencies["parent:1.0.0"] = [
|
|
220
|
+
Dependency(name="good-dep", version_requirement="^1.0", kind="normal"),
|
|
221
|
+
Dependency(name="win-dep", version_requirement="^1.0", kind="normal"),
|
|
222
|
+
Dependency(name="macos-dep", version_requirement="^1.0", kind="normal"),
|
|
223
|
+
]
|
|
224
|
+
provider.fedora_status["parent"] = FedoraPackageStatus(
|
|
225
|
+
is_packaged=True, versions=["1.0.0"]
|
|
226
|
+
)
|
|
227
|
+
provider.fedora_status["good-dep"] = FedoraPackageStatus(
|
|
228
|
+
is_packaged=True, versions=["1.0.0"]
|
|
229
|
+
)
|
|
230
|
+
provider.fedora_status["win-dep"] = FedoraPackageStatus(is_packaged=False)
|
|
231
|
+
provider.fedora_status["macos-dep"] = FedoraPackageStatus(is_packaged=False)
|
|
232
|
+
|
|
233
|
+
tree = build_tree(provider, "parent", exclude_patterns=["win*", "macos*"])
|
|
234
|
+
|
|
235
|
+
# Should only have good-dep
|
|
236
|
+
assert isinstance(tree, Tree)
|
|
237
|
+
assert len(tree.children) == 1
|
|
238
|
+
child_label = str(tree.children[0].label)
|
|
239
|
+
assert "good-dep" in child_label
|
|
240
|
+
|
|
241
|
+
@pytest.mark.unit
|
|
242
|
+
def test_exclude_patterns_applied_recursively(self, provider):
|
|
243
|
+
"""Good path: exclude patterns are applied at all depth levels."""
|
|
244
|
+
provider.packages["root"] = PackageInfo(name="root", latest_version="1.0.0")
|
|
245
|
+
provider.packages["child"] = PackageInfo(name="child", latest_version="1.0.0")
|
|
246
|
+
provider.packages["windows-inner"] = PackageInfo(
|
|
247
|
+
name="windows-inner", latest_version="1.0.0"
|
|
248
|
+
)
|
|
249
|
+
provider.dependencies["root:1.0.0"] = [
|
|
250
|
+
Dependency(name="child", version_requirement="^1.0", kind="normal"),
|
|
251
|
+
]
|
|
252
|
+
provider.dependencies["child:1.0.0"] = [
|
|
253
|
+
Dependency(name="windows-inner", version_requirement="^1.0", kind="normal"),
|
|
254
|
+
]
|
|
255
|
+
provider.fedora_status["root"] = FedoraPackageStatus(
|
|
256
|
+
is_packaged=True, versions=["1.0.0"]
|
|
257
|
+
)
|
|
258
|
+
provider.fedora_status["child"] = FedoraPackageStatus(
|
|
259
|
+
is_packaged=True, versions=["1.0.0"]
|
|
260
|
+
)
|
|
261
|
+
provider.fedora_status["windows-inner"] = FedoraPackageStatus(is_packaged=False)
|
|
262
|
+
|
|
263
|
+
tree = build_tree(provider, "root", exclude_patterns=["windows*"])
|
|
264
|
+
|
|
265
|
+
# root -> child (no windows-inner)
|
|
266
|
+
assert isinstance(tree, Tree)
|
|
267
|
+
assert len(tree.children) == 1
|
|
268
|
+
child_tree = tree.children[0]
|
|
269
|
+
assert isinstance(child_tree, Tree)
|
|
270
|
+
# child should have no children (windows-inner was filtered)
|
|
271
|
+
assert len(child_tree.children) == 0
|
|
272
|
+
|
|
273
|
+
@pytest.mark.unit
|
|
274
|
+
def test_no_exclusion_when_patterns_none(self, provider):
|
|
275
|
+
"""Good path: no exclusion when patterns is None."""
|
|
276
|
+
provider.packages["parent"] = PackageInfo(name="parent", latest_version="1.0.0")
|
|
277
|
+
provider.packages["child"] = PackageInfo(name="child", latest_version="1.0.0")
|
|
278
|
+
provider.dependencies["parent:1.0.0"] = [
|
|
279
|
+
Dependency(name="child", version_requirement="^1.0", kind="normal"),
|
|
280
|
+
]
|
|
281
|
+
provider.fedora_status["parent"] = FedoraPackageStatus(
|
|
282
|
+
is_packaged=True, versions=["1.0.0"]
|
|
283
|
+
)
|
|
284
|
+
provider.fedora_status["child"] = FedoraPackageStatus(
|
|
285
|
+
is_packaged=True, versions=["1.0.0"]
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
tree = build_tree(provider, "parent", exclude_patterns=None)
|
|
289
|
+
|
|
290
|
+
assert isinstance(tree, Tree)
|
|
291
|
+
assert len(tree.children) == 1
|
|
292
|
+
|
|
173
293
|
|
|
174
294
|
class TestCollectStats:
|
|
175
295
|
"""Tests for collect_stats function."""
|
|
@@ -229,6 +229,7 @@ class TestBuildTreeOptional:
|
|
|
229
229
|
"""Good path: optional dependencies are marked in the tree."""
|
|
230
230
|
tree = build_tree(provider, "parent", include_optional=True)
|
|
231
231
|
|
|
232
|
+
assert isinstance(tree, Tree)
|
|
232
233
|
# Find optional children
|
|
233
234
|
optional_count = 0
|
|
234
235
|
for child in tree.children:
|
|
@@ -246,6 +247,7 @@ class TestBuildTreeOptional:
|
|
|
246
247
|
"""Good path: required dependencies don't have optional marker."""
|
|
247
248
|
tree = build_tree(provider, "parent", include_optional=True)
|
|
248
249
|
|
|
250
|
+
assert isinstance(tree, Tree)
|
|
249
251
|
# The root should not have optional marker
|
|
250
252
|
label = str(tree.label)
|
|
251
253
|
assert "(optional)" not in label
|
|
@@ -255,6 +257,7 @@ class TestBuildTreeOptional:
|
|
|
255
257
|
"""Critical path: is_optional_dep parameter adds marker."""
|
|
256
258
|
tree = build_tree(provider, "required-child", is_optional_dep=True)
|
|
257
259
|
|
|
260
|
+
assert isinstance(tree, Tree)
|
|
258
261
|
label = str(tree.label)
|
|
259
262
|
assert "(optional)" in label
|
|
260
263
|
|
|
@@ -179,3 +179,54 @@ class TestJsonReporterParseLabel:
|
|
|
179
179
|
|
|
180
180
|
assert "[" not in result.raw
|
|
181
181
|
assert "]" not in result.raw
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class TestJsonReporterMissingOnly:
|
|
185
|
+
"""Tests for JsonReporter with missing_only flag."""
|
|
186
|
+
|
|
187
|
+
@pytest.mark.unit
|
|
188
|
+
def test_missing_only_metadata_included(self, sample_report_data):
|
|
189
|
+
"""Good path: missing_only is included in metadata."""
|
|
190
|
+
sample_report_data.missing_only = True
|
|
191
|
+
reporter = JsonReporter()
|
|
192
|
+
|
|
193
|
+
result = reporter.generate(sample_report_data)
|
|
194
|
+
parsed = json.loads(result)
|
|
195
|
+
|
|
196
|
+
assert "missing_only" in parsed["metadata"]
|
|
197
|
+
assert parsed["metadata"]["missing_only"] is True
|
|
198
|
+
|
|
199
|
+
@pytest.mark.unit
|
|
200
|
+
def test_missing_only_excludes_packaged_packages(self, sample_report_data):
|
|
201
|
+
"""Good path: excludes packaged packages when missing_only is True."""
|
|
202
|
+
sample_report_data.missing_only = True
|
|
203
|
+
reporter = JsonReporter()
|
|
204
|
+
|
|
205
|
+
result = reporter.generate(sample_report_data)
|
|
206
|
+
parsed = json.loads(result)
|
|
207
|
+
|
|
208
|
+
assert parsed["packaged_packages"] == []
|
|
209
|
+
|
|
210
|
+
@pytest.mark.unit
|
|
211
|
+
def test_missing_only_false_includes_packaged_packages(self, sample_report_data):
|
|
212
|
+
"""Good path: includes packaged packages when missing_only is False."""
|
|
213
|
+
sample_report_data.missing_only = False
|
|
214
|
+
reporter = JsonReporter()
|
|
215
|
+
|
|
216
|
+
result = reporter.generate(sample_report_data)
|
|
217
|
+
parsed = json.loads(result)
|
|
218
|
+
|
|
219
|
+
assert len(parsed["packaged_packages"]) > 0
|
|
220
|
+
assert "packaged-a" in parsed["packaged_packages"]
|
|
221
|
+
|
|
222
|
+
@pytest.mark.unit
|
|
223
|
+
def test_missing_only_still_includes_missing_packages(self, sample_report_data):
|
|
224
|
+
"""Good path: missing packages are still included when missing_only is True."""
|
|
225
|
+
sample_report_data.missing_only = True
|
|
226
|
+
reporter = JsonReporter()
|
|
227
|
+
|
|
228
|
+
result = reporter.generate(sample_report_data)
|
|
229
|
+
parsed = json.loads(result)
|
|
230
|
+
|
|
231
|
+
assert len(parsed["missing_packages"]) > 0
|
|
232
|
+
assert "missing-a" in parsed["missing_packages"]
|
|
@@ -133,3 +133,68 @@ class TestMarkdownReporterTreeToText:
|
|
|
133
133
|
|
|
134
134
|
# Should contain tree structure characters
|
|
135
135
|
assert "├──" in result or "└──" in result
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TestMarkdownReporterMissingOnly:
|
|
139
|
+
"""Tests for MarkdownReporter with missing_only flag."""
|
|
140
|
+
|
|
141
|
+
@pytest.mark.unit
|
|
142
|
+
def test_missing_only_metadata_included(self, sample_report_data):
|
|
143
|
+
"""Good path: shows missing_only indicator in metadata."""
|
|
144
|
+
sample_report_data.missing_only = True
|
|
145
|
+
reporter = MarkdownReporter()
|
|
146
|
+
|
|
147
|
+
result = reporter.generate(sample_report_data)
|
|
148
|
+
|
|
149
|
+
assert "**Missing only:** Yes" in result
|
|
150
|
+
|
|
151
|
+
@pytest.mark.unit
|
|
152
|
+
def test_missing_only_excludes_packaged_section(self, sample_report_data):
|
|
153
|
+
"""Good path: excludes packaged packages section when missing_only is True."""
|
|
154
|
+
sample_report_data.missing_only = True
|
|
155
|
+
reporter = MarkdownReporter()
|
|
156
|
+
|
|
157
|
+
result = reporter.generate(sample_report_data)
|
|
158
|
+
|
|
159
|
+
assert "## Packaged Packages" not in result
|
|
160
|
+
|
|
161
|
+
@pytest.mark.unit
|
|
162
|
+
def test_missing_only_excludes_dependency_tree(self, sample_report_data):
|
|
163
|
+
"""Good path: excludes dependency tree when missing_only is True."""
|
|
164
|
+
sample_report_data.missing_only = True
|
|
165
|
+
reporter = MarkdownReporter()
|
|
166
|
+
|
|
167
|
+
result = reporter.generate(sample_report_data)
|
|
168
|
+
|
|
169
|
+
assert "## Dependency Tree" not in result
|
|
170
|
+
|
|
171
|
+
@pytest.mark.unit
|
|
172
|
+
def test_missing_only_false_includes_packaged_section(self, sample_report_data):
|
|
173
|
+
"""Good path: includes packaged section when missing_only is False."""
|
|
174
|
+
sample_report_data.missing_only = False
|
|
175
|
+
reporter = MarkdownReporter()
|
|
176
|
+
|
|
177
|
+
result = reporter.generate(sample_report_data)
|
|
178
|
+
|
|
179
|
+
assert "## Packaged Packages" in result
|
|
180
|
+
|
|
181
|
+
@pytest.mark.unit
|
|
182
|
+
def test_missing_only_false_includes_dependency_tree(self, sample_report_data):
|
|
183
|
+
"""Good path: includes dependency tree when missing_only is False."""
|
|
184
|
+
sample_report_data.missing_only = False
|
|
185
|
+
reporter = MarkdownReporter()
|
|
186
|
+
|
|
187
|
+
result = reporter.generate(sample_report_data)
|
|
188
|
+
|
|
189
|
+
assert "## Dependency Tree" in result
|
|
190
|
+
|
|
191
|
+
@pytest.mark.unit
|
|
192
|
+
def test_missing_only_still_includes_missing_packages(self, sample_report_data):
|
|
193
|
+
"""Good path: missing packages are still shown when missing_only is True."""
|
|
194
|
+
sample_report_data.missing_only = True
|
|
195
|
+
reporter = MarkdownReporter()
|
|
196
|
+
|
|
197
|
+
result = reporter.generate(sample_report_data)
|
|
198
|
+
|
|
199
|
+
assert "## Missing Packages" in result
|
|
200
|
+
assert "- `missing-a`" in result
|
|
@@ -102,3 +102,32 @@ class TestStdoutReporterGenerate:
|
|
|
102
102
|
for arg in args:
|
|
103
103
|
if isinstance(arg, str):
|
|
104
104
|
assert "Missing packages that need packaging" not in arg
|
|
105
|
+
|
|
106
|
+
@pytest.mark.unit
|
|
107
|
+
def test_missing_only_skips_dependency_tree(self, sample_report_data, mock_console):
|
|
108
|
+
"""Good path: skips dependency tree when missing_only is True."""
|
|
109
|
+
sample_report_data.missing_only = True
|
|
110
|
+
reporter = StdoutReporter(console=mock_console)
|
|
111
|
+
|
|
112
|
+
reporter.generate(sample_report_data)
|
|
113
|
+
|
|
114
|
+
# Should not print "Dependency Tree:"
|
|
115
|
+
for call_obj in mock_console.print.call_args_list:
|
|
116
|
+
args = call_obj[0] if call_obj[0] else []
|
|
117
|
+
for arg in args:
|
|
118
|
+
if isinstance(arg, str):
|
|
119
|
+
assert "Dependency Tree:" not in arg
|
|
120
|
+
|
|
121
|
+
@pytest.mark.unit
|
|
122
|
+
def test_missing_only_false_shows_dependency_tree(
|
|
123
|
+
self, sample_report_data, mock_console
|
|
124
|
+
):
|
|
125
|
+
"""Good path: shows dependency tree when missing_only is False."""
|
|
126
|
+
sample_report_data.missing_only = False
|
|
127
|
+
reporter = StdoutReporter(console=mock_console)
|
|
128
|
+
|
|
129
|
+
reporter.generate(sample_report_data)
|
|
130
|
+
|
|
131
|
+
# Should print "Dependency Tree:"
|
|
132
|
+
calls_str = str(mock_console.print.call_args_list)
|
|
133
|
+
assert "Dependency Tree" in calls_str
|