From c9874f7961209482dac6fffa3bae02196a6a70b5 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 3 Jan 2024 11:03:40 -0600 Subject: [PATCH] Add `inspect` command replacing `list --output-format json` (#53) --- src/packse/cli.py | 43 ++- src/packse/inspect.py | 47 +++ src/packse/list.py | 18 +- src/packse/view.py | 2 +- tests/__snapshots__/test_inspect.ambr | 528 ++++++++++++++++++++++++++ tests/__snapshots__/test_list.ambr | 68 ---- tests/__snapshots__/test_root.ambr | 21 +- tests/test_inspect.py | 42 ++ tests/test_list.py | 7 - 9 files changed, 670 insertions(+), 106 deletions(-) create mode 100644 src/packse/inspect.py create mode 100644 tests/__snapshots__/test_inspect.ambr create mode 100644 tests/test_inspect.py diff --git a/src/packse/cli.py b/src/packse/cli.py index 2afbe5a4..ba2364ca 100644 --- a/src/packse/cli.py +++ b/src/packse/cli.py @@ -13,6 +13,7 @@ UserError, ) from packse.index import index_down, index_up +from packse.inspect import inspect from packse.list import list from packse.publish import publish from packse.serve import serve @@ -126,10 +127,26 @@ def _call_list(args): args.no_versions, skip_invalid, args.no_sources, - args.output_format, ) +def _call_inspect(args): + skip_invalid = args.skip_invalid + if not args.targets: + skip_invalid = True + targets = Path.cwd().glob("**/*.json") + else: + targets = [] + for target in args.targets: + # Expand any directories to json files within + if target.is_dir(): + targets.extend(target.glob("**/*.json")) + else: + targets.append(target) + + inspect(targets, skip_invalid) + + def _root_parser(): parser = argparse.ArgumentParser( description="Utilities for working example packaging scenarios", @@ -295,7 +312,7 @@ def _add_index_parser(subparsers): def _add_list_parser(subparsers): - parser = subparsers.add_parser("list", help="List scenarios") + parser = subparsers.add_parser("list", help="List scenarios at the given paths") parser.set_defaults(call=_call_list) parser.add_argument( "targets", @@ -318,11 +335,24 @@ def _add_list_parser(subparsers): action="store_true", help="Do not show the source file for each scenario.", ) + _add_shared_arguments(parser) + + +def _add_inspect_parser(subparsers): + parser = subparsers.add_parser( + "inspect", help="Inspect scenarios at the given paths" + ) + parser.set_defaults(call=_call_inspect) + parser.add_argument( + "targets", + type=Path, + nargs="*", + help="The scenario files to load", + ) parser.add_argument( - "--output-format", - choices=["pretty", "json"], - default="pretty", - help="The output format.", + "--skip-invalid", + action="store_true", + help="Skip invalid scenario files instead of failing.", ) _add_shared_arguments(parser) @@ -350,6 +380,7 @@ def get_parser() -> argparse.ArgumentParser: _add_view_parser(subparsers) _add_publish_parser(subparsers) _add_list_parser(subparsers) + _add_inspect_parser(subparsers) _add_serve_parser(subparsers) _add_index_parser(subparsers) diff --git a/src/packse/inspect.py b/src/packse/inspect.py new file mode 100644 index 00000000..e5cc9a8c --- /dev/null +++ b/src/packse/inspect.py @@ -0,0 +1,47 @@ +""" +Get details for all scenarios. +""" +import json +import logging +from pathlib import Path +from typing import cast + +from packse.error import FileNotFound, InvalidScenario +from packse.scenario import ( + Scenario, + load_scenarios, + scenario_prefix, +) +from packse.view import dependency_tree + +logger = logging.getLogger(__name__) + + +def inspect(targets: list[Path], skip_invalid: bool = False): + scenarios_by_path: dict[Path, list[Scenario]] = {} + + # Validate and collect all targets first + for target in sorted(targets): + if not target.exists(): + raise FileNotFound(target) + + try: + logger.debug("Loading %s", target) + scenarios_by_path[target] = load_scenarios(target) + except Exception as exc: + if not skip_invalid: + raise InvalidScenario(target, reason=str(exc)) from exc + + # Collect a JSON-compatible representation for each scenario + result = {"scenarios": []} + for source, scenarios in scenarios_by_path.items(): + for scenario in scenarios: + scenario = cast(Scenario, scenario) + + raw = scenario.dict() + raw["source"] = str(source) + raw["prefix"] = scenario_prefix(scenario) + raw["tree"] = dependency_tree(scenario) + result["scenarios"].append(raw) + + print(json.dumps(result, indent=2)) diff --git a/src/packse/list.py b/src/packse/list.py index 7fa8232c..ee2a0984 100644 --- a/src/packse/list.py +++ b/src/packse/list.py @@ -1,10 +1,9 @@ """ List all scenarios. """ -import json import logging from pathlib import Path -from typing import Literal, cast +from typing import cast from packse.error import FileNotFound, InvalidScenario from packse.scenario import ( @@ -21,7 +20,6 @@ def list( no_versions: bool = False, skip_invalid: bool = False, no_sources: bool = False, - format: Literal["json", "pretty"] = "pretty", ): scenarios_by_path: dict[Path, list[Scenario]] = {} @@ -38,30 +36,20 @@ def list( raise InvalidScenario(target, reason=str(exc)) from exc # Then list each one - result = {"scenarios": []} for source, scenarios in scenarios_by_path.items(): prefix = "" if no_sources else " " * 4 - if not no_sources and format == "pretty": + if not no_sources: print(to_display_path(source)) for scenario in scenarios: scenario = cast(Scenario, scenario) - raw = scenario.dict() - raw["source"] = str(source) - raw["prefix"] = scenario_prefix(scenario) - result["scenarios"].append(raw) - if no_versions: name = scenario.name else: name = scenario_prefix(scenario) - if format == "pretty": - print(prefix + name) - - if format == "json": - print(json.dumps(result, indent=2)) + print(prefix + name) def to_display_path(path: Path | str, relative_to: Path | str | None = None) -> str: diff --git a/src/packse/view.py b/src/packse/view.py index d407233b..7ffdd43a 100644 --- a/src/packse/view.py +++ b/src/packse/view.py @@ -61,7 +61,7 @@ def view_scenario(scenario: Scenario): print(dependency_tree(scenario)) -def dependency_tree(scenario: Scenario): +def dependency_tree(scenario: Scenario) -> str: """ Generate a dependency tree for a scenario """ diff --git a/tests/__snapshots__/test_inspect.ambr b/tests/__snapshots__/test_inspect.ambr new file mode 100644 index 00000000..17f21ade --- /dev/null +++ b/tests/__snapshots__/test_inspect.ambr @@ -0,0 +1,528 @@ +# serializer version: 1 +# name: test_inspect + dict({ + 'exit_code': 0, + 'stderr': '', + 'stdout': ''' + { + "scenarios": [ + { + "name": "example", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "b>1.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "b": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [ + "c" + ], + "sdist": true, + "wheel": true, + "description": "" + }, + "3.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a" + ] + }, + "template": "simple", + "description": "This is an example scenario, in which the user depends on a single package `a` which requires `b`", + "source": "[PWD]/scenarios/example.json", + "prefix": "example-89cac9f1", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a\n\u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u251c\u2500\u2500 a\n\u2502 \u2514\u2500\u2500 a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b>1.0.0\n\u2502 \u251c\u2500\u2500 satisfied by b-2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by b-3.0.0\n\u2514\u2500\u2500 b\n \u251c\u2500\u2500 b-1.0.0\n \u251c\u2500\u2500 b-2.0.0\n \u2502 \u2514\u2500\u2500 requires c\n \u2502 \u2514\u2500\u2500 unsatisfied: no versions for package\n \u2514\u2500\u2500 b-3.0.0\n" + } + ] + } + + ''', + }) +# --- +# name: test_inspect_invalid_target + dict({ + 'exit_code': 1, + 'stderr': ''' + File at '[PWD]/test.json' is not a valid scenario: Input data was truncated. + + ''', + 'stdout': '', + }) +# --- +# name: test_inspect_invalid_target_skip_invalid + dict({ + 'exit_code': 0, + 'stderr': '', + 'stdout': ''' + { + "scenarios": [ + { + "name": "example", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "b>1.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "b": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [ + "c" + ], + "sdist": true, + "wheel": true, + "description": "" + }, + "3.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a" + ] + }, + "template": "simple", + "description": "This is an example scenario, in which the user depends on a single package `a` which requires `b`", + "source": "[PROJECT_ROOT]/scenarios/example.json", + "prefix": "example-89cac9f1", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a\n\u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u251c\u2500\u2500 a\n\u2502 \u2514\u2500\u2500 a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b>1.0.0\n\u2502 \u251c\u2500\u2500 satisfied by b-2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by b-3.0.0\n\u2514\u2500\u2500 b\n \u251c\u2500\u2500 b-1.0.0\n \u251c\u2500\u2500 b-2.0.0\n \u2502 \u2514\u2500\u2500 requires c\n \u2502 \u2514\u2500\u2500 unsatisfied: no versions for package\n \u2514\u2500\u2500 b-3.0.0\n" + } + ] + } + + ''', + }) +# --- +# name: test_inspect_no_target_finds_all_valid_scenarios + dict({ + 'exit_code': 0, + 'stderr': '', + 'stdout': ''' + { + "scenarios": [ + { + "name": "example", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "b>1.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "b": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [ + "c" + ], + "sdist": true, + "wheel": true, + "description": "" + }, + "3.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a" + ] + }, + "template": "simple", + "description": "This is an example scenario, in which the user depends on a single package `a` which requires `b`", + "source": "[PWD]/scenarios/example.json", + "prefix": "example-89cac9f1", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a\n\u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u251c\u2500\u2500 a\n\u2502 \u2514\u2500\u2500 a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b>1.0.0\n\u2502 \u251c\u2500\u2500 satisfied by b-2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by b-3.0.0\n\u2514\u2500\u2500 b\n \u251c\u2500\u2500 b-1.0.0\n \u251c\u2500\u2500 b-2.0.0\n \u2502 \u2514\u2500\u2500 requires c\n \u2502 \u2514\u2500\u2500 unsatisfied: no versions for package\n \u2514\u2500\u2500 b-3.0.0\n" + }, + { + "name": "requires-package-does-not-exist", + "packages": {}, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a" + ] + }, + "template": "simple", + "description": "The user requires any version of package `a` which does not exist.", + "source": "[PWD]/scenarios/requires-does-not-exist.json", + "prefix": "requires-package-does-not-exist-59108293", + "tree": "\u2514\u2500\u2500 root\n \u2514\u2500\u2500 requires a\n \u2514\u2500\u2500 unsatisfied: no versions for package\n" + }, + { + "name": "requires-exact-version-does-not-exist", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a==2.0.0" + ] + }, + "template": "simple", + "description": "The user requires an exact version of package `a` but only other versions exist", + "source": "[PWD]/scenarios/requires-does-not-exist.json", + "prefix": "requires-exact-version-does-not-exist-bc5f5f6d", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a==2.0.0\n\u2502 \u2514\u2500\u2500 unsatisfied: no matching version\n\u2514\u2500\u2500 a\n \u2514\u2500\u2500 a-1.0.0\n" + }, + { + "name": "requires-greater-version-does-not-exist", + "packages": { + "a": { + "versions": { + "0.1.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a>1.0.0" + ] + }, + "template": "simple", + "description": "The user requires a version of `a` greater than `1.0.0` but only smaller or equal versions exist", + "source": "[PWD]/scenarios/requires-does-not-exist.json", + "prefix": "requires-greater-version-does-not-exist-670431f9", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a>1.0.0\n\u2502 \u2514\u2500\u2500 unsatisfied: no matching version\n\u2514\u2500\u2500 a\n \u251c\u2500\u2500 a-0.1.0\n \u2514\u2500\u2500 a-1.0.0\n" + }, + { + "name": "requires-less-version-does-not-exist", + "packages": { + "a": { + "versions": { + "2.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "3.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "4.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a<2.0.0" + ] + }, + "template": "simple", + "description": "The user requires a version of `a` less than `1.0.0` but only larger versions exist", + "source": "[PWD]/scenarios/requires-does-not-exist.json", + "prefix": "requires-less-version-does-not-exist-9a75991b", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a<2.0.0\n\u2502 \u2514\u2500\u2500 unsatisfied: no matching version\n\u2514\u2500\u2500 a\n \u251c\u2500\u2500 a-2.0.0\n \u251c\u2500\u2500 a-3.0.0\n \u2514\u2500\u2500 a-4.0.0\n" + }, + { + "name": "transitive-requires-package-does-not-exist", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "b" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a" + ] + }, + "template": "simple", + "description": "The user requires package `a` but `a` requires package `b` which does not exist", + "source": "[PWD]/scenarios/requires-does-not-exist.json", + "prefix": "transitive-requires-package-does-not-exist-ca79eaa2", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u2514\u2500\u2500 requires a\n\u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u2514\u2500\u2500 a\n \u2514\u2500\u2500 a-1.0.0\n \u2514\u2500\u2500 requires b\n \u2514\u2500\u2500 unsatisfied: no versions for package\n" + }, + { + "name": "requires-direct-incompatible-versions", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a==1.0.0", + "a==2.0.0" + ] + }, + "template": "simple", + "description": "The user requires two incompatible, existing versions of package `a`", + "source": "[PWD]/scenarios/requires-incompatible-versions.json", + "prefix": "requires-direct-incompatible-versions-350bd4b0", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u251c\u2500\u2500 requires a==1.0.0\n\u2502 \u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u2502 \u2514\u2500\u2500 requires a==2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by a-2.0.0\n\u2514\u2500\u2500 a\n \u251c\u2500\u2500 a-1.0.0\n \u2514\u2500\u2500 a-2.0.0\n" + }, + { + "name": "requires-transitive-incompatible-with-root-version", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "b==2.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "b": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a", + "b==1.0.0" + ] + }, + "template": "simple", + "description": "The user requires packages `a` and `b` but `a` requires a different version of `b`", + "source": "[PWD]/scenarios/requires-incompatible-versions.json", + "prefix": "requires-transitive-incompatible-with-root-version-3240dab1", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u251c\u2500\u2500 requires a\n\u2502 \u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b==1.0.0\n\u2502 \u2514\u2500\u2500 satisfied by b-1.0.0\n\u251c\u2500\u2500 a\n\u2502 \u2514\u2500\u2500 a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b==2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by b-2.0.0\n\u2514\u2500\u2500 b\n \u251c\u2500\u2500 b-1.0.0\n \u2514\u2500\u2500 b-2.0.0\n" + }, + { + "name": "requires-transitive-incompatible-with-transitive", + "packages": { + "a": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "c==1.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "b": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [ + "c==2.0.0" + ], + "sdist": true, + "wheel": true, + "description": "" + } + } + }, + "c": { + "versions": { + "1.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + }, + "2.0.0": { + "requires_python": ">=3.7", + "requires": [], + "sdist": true, + "wheel": true, + "description": "" + } + } + } + }, + "root": { + "requires_python": ">=3.7", + "requires": [ + "a", + "b" + ] + }, + "template": "simple", + "description": "The user requires package `a` and `b`; `a` and `b` require different versions of `c`", + "source": "[PWD]/scenarios/requires-incompatible-versions.json", + "prefix": "requires-transitive-incompatible-with-transitive-8329cfc0", + "tree": "\u251c\u2500\u2500 root\n\u2502 \u251c\u2500\u2500 requires a\n\u2502 \u2502 \u2514\u2500\u2500 satisfied by a-1.0.0\n\u2502 \u2514\u2500\u2500 requires b\n\u2502 \u2514\u2500\u2500 satisfied by b-1.0.0\n\u251c\u2500\u2500 a\n\u2502 \u2514\u2500\u2500 a-1.0.0\n\u2502 \u2514\u2500\u2500 requires c==1.0.0\n\u2502 \u2514\u2500\u2500 satisfied by c-1.0.0\n\u251c\u2500\u2500 b\n\u2502 \u2514\u2500\u2500 b-1.0.0\n\u2502 \u2514\u2500\u2500 requires c==2.0.0\n\u2502 \u2514\u2500\u2500 satisfied by c-2.0.0\n\u2514\u2500\u2500 c\n \u251c\u2500\u2500 c-1.0.0\n \u2514\u2500\u2500 c-2.0.0\n" + } + ] + } + + ''', + }) +# --- +# name: test_inspect_one_target_does_not_exist + dict({ + 'exit_code': 1, + 'stderr': ''' + File 'foo' not found. + + ''', + 'stdout': '', + }) +# --- +# name: test_inspect_target_does_not_exist + dict({ + 'exit_code': 1, + 'stderr': ''' + File 'foo' not found. + + ''', + 'stdout': '', + }) +# --- diff --git a/tests/__snapshots__/test_list.ambr b/tests/__snapshots__/test_list.ambr index 98bd6263..349dc7c3 100644 --- a/tests/__snapshots__/test_list.ambr +++ b/tests/__snapshots__/test_list.ambr @@ -101,74 +101,6 @@ 'stdout': '', }) # --- -# name: test_list_output_format - dict({ - 'exit_code': 0, - 'stderr': '', - 'stdout': ''' - { - "scenarios": [ - { - "name": "example", - "packages": { - "a": { - "versions": { - "1.0.0": { - "requires_python": ">=3.7", - "requires": [ - "b>1.0.0" - ], - "sdist": true, - "wheel": true, - "description": "" - } - } - }, - "b": { - "versions": { - "1.0.0": { - "requires_python": ">=3.7", - "requires": [], - "sdist": true, - "wheel": true, - "description": "" - }, - "2.0.0": { - "requires_python": ">=3.7", - "requires": [ - "c" - ], - "sdist": true, - "wheel": true, - "description": "" - }, - "3.0.0": { - "requires_python": ">=3.7", - "requires": [], - "sdist": true, - "wheel": true, - "description": "" - } - } - } - }, - "root": { - "requires_python": ">=3.7", - "requires": [ - "a" - ] - }, - "template": "simple", - "description": "This is an example scenario, in which the user depends on a single package `a` which requires `b`", - "source": "[PWD]/scenarios/example.json", - "prefix": "example-89cac9f1" - } - ] - } - - ''', - }) -# --- # name: test_list_target_does_not_exist dict({ 'exit_code': 1, diff --git a/tests/__snapshots__/test_root.ambr b/tests/__snapshots__/test_root.ambr index 77be4992..2caf975d 100644 --- a/tests/__snapshots__/test_root.ambr +++ b/tests/__snapshots__/test_root.ambr @@ -4,7 +4,7 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - usage: packse [-h] [-v] [-q] {build,view,publish,list,serve,index} ... + usage: packse [-h] [-v] [-q] {build,view,publish,list,inspect,serve,index} ... Utilities for working example packaging scenarios @@ -14,11 +14,12 @@ -q, --quiet Disable logging commands: - {build,view,publish,list,serve,index} + {build,view,publish,list,inspect,serve,index} build Build packages for a scenario view View a scenario publish Publish packages for a scenario - list List scenarios + list List scenarios at the given paths + inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index index Run a local package index @@ -30,7 +31,7 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - usage: packse [-h] [-v] [-q] {build,view,publish,list,serve,index} ... + usage: packse [-h] [-v] [-q] {build,view,publish,list,inspect,serve,index} ... Utilities for working example packaging scenarios @@ -40,11 +41,12 @@ -q, --quiet Disable logging commands: - {build,view,publish,list,serve,index} + {build,view,publish,list,inspect,serve,index} build Build packages for a scenario view View a scenario publish Publish packages for a scenario - list List scenarios + list List scenarios at the given paths + inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index index Run a local package index @@ -56,7 +58,7 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - usage: packse [-h] [-v] [-q] {build,view,publish,list,serve,index} ... + usage: packse [-h] [-v] [-q] {build,view,publish,list,inspect,serve,index} ... Utilities for working example packaging scenarios @@ -66,11 +68,12 @@ -q, --quiet Disable logging commands: - {build,view,publish,list,serve,index} + {build,view,publish,list,inspect,serve,index} build Build packages for a scenario view View a scenario publish Publish packages for a scenario - list List scenarios + list List scenarios at the given paths + inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index index Run a local package index diff --git a/tests/test_inspect.py b/tests/test_inspect.py new file mode 100644 index 00000000..aff81637 --- /dev/null +++ b/tests/test_inspect.py @@ -0,0 +1,42 @@ +import pytest +from packse import __development_base_path__ + +from .common import snapshot_command + + +def test_inspect_no_target_finds_all_valid_scenarios(snapshot): + assert snapshot_command(["inspect"]) == snapshot + + +def test_inspect_target_does_not_exist(snapshot): + assert snapshot_command(["inspect", "foo"]) == snapshot + + +@pytest.mark.usefixtures("tmpcwd") +def test_inspect_one_target_does_not_exist(snapshot): + target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["inspect", str(target), "foo"]) == snapshot + + +def test_inspect_invalid_target(snapshot, tmpcwd): + bad_target = tmpcwd / "test.json" + bad_target.touch() + good_target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["inspect", str(bad_target), str(good_target)]) == snapshot + + +def test_inspect_invalid_target_skip_invalid(snapshot, tmpcwd): + bad_target = tmpcwd / "test.json" + bad_target.touch() + good_target = __development_base_path__ / "scenarios" / "example.json" + assert ( + snapshot_command( + ["inspect", str(bad_target), str(good_target), "--skip-invalid"] + ) + == snapshot + ) + + +def test_inspect(snapshot): + target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["inspect", str(target)]) == snapshot diff --git a/tests/test_list.py b/tests/test_list.py index 2be774d4..6fba914f 100644 --- a/tests/test_list.py +++ b/tests/test_list.py @@ -46,10 +46,3 @@ def test_list_invalid_target_skip_invalid(snapshot, tmpcwd): def test_list_example(snapshot): target = __development_base_path__ / "scenarios" / "example.json" assert snapshot_command(["list", str(target)]) == snapshot - - -def test_list_output_format(snapshot): - target = __development_base_path__ / "scenarios" / "example.json" - assert ( - snapshot_command(["list", str(target), "--output-format", "json"]) == snapshot - )