Skip to content

Commit

Permalink
Add custom scenarios dir (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsv1 authored Jan 5, 2025
1 parent 86bfcf8 commit 3c033b7
Show file tree
Hide file tree
Showing 13 changed files with 428 additions and 59 deletions.
80 changes: 80 additions & 0 deletions tests/commands/run_command/test_run_command.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pathlib import Path

import pytest
from baby_steps import given, then, when
from pytest import raises

Expand All @@ -23,6 +24,71 @@ class Terminator(vedro.Config.Plugins.Terminator):
pass


async def test_run_command_with_invalid_type_scenario_dir(arg_parser: ArgumentParser):
with given:
class CustomScenarioDir(CustomConfig):
default_scenarios_dir = None
command = RunCommand(CustomScenarioDir, arg_parser)

with when, raises(BaseException) as exc:
await command.run()

with then:
assert exc.type is TypeError
assert "default_scenarios_dir" in str(exc.value)
assert "to be a Path" in str(exc.value)


async def test_run_command_with_nonexistent_scenario_dir(arg_parser: ArgumentParser):
with given:
class CustomScenarioDir(CustomConfig):
default_scenarios_dir = "nonexisting/"
command = RunCommand(CustomScenarioDir, arg_parser)

with when, raises(BaseException) as exc:
await command.run()

with then:
assert exc.type is FileNotFoundError
assert "default_scenarios_dir" in str(exc.value)
assert "does not exist" in str(exc.value)


async def test_run_command_with_non_directory_scenario_dir(tmp_dir: Path,
arg_parser: ArgumentParser):
with given:
existing_file = tmp_dir / "scenario.py"
existing_file.touch()

class CustomScenarioDir(CustomConfig):
default_scenarios_dir = existing_file
command = RunCommand(CustomScenarioDir, arg_parser)

with when, raises(BaseException) as exc:
await command.run()

with then:
assert exc.type is NotADirectoryError
assert "default_scenarios_dir" in str(exc.value)
assert "is not a directory" in str(exc.value)


@pytest.mark.usefixtures(tmp_dir.__name__)
async def test_run_command_with_scenario_dir_outside_project_dir(arg_parser: ArgumentParser):
with given:
class CustomScenarioDir(CustomConfig):
default_scenarios_dir = "/tmp"
command = RunCommand(CustomScenarioDir, arg_parser)

with when, raises(BaseException) as exc:
await command.run()

with then:
assert exc.type is ValueError
assert "default_scenarios_dir" in str(exc.value)
assert "must be inside project directory" in str(exc.value)


async def test_run_command_without_scenarios(arg_parser: ArgumentParser):
with given:
command = RunCommand(CustomConfig, arg_parser)
Expand All @@ -35,6 +101,19 @@ async def test_run_command_without_scenarios(arg_parser: ArgumentParser):
assert str(exc.value) == "1"


@pytest.mark.usefixtures(tmp_dir.__name__)
async def test_run_command_with_no_scenarios(arg_parser: ArgumentParser):
with given:
command = RunCommand(CustomConfig, arg_parser)

with when, raises(BaseException) as exc:
await command.run()

with then:
assert exc.type is SystemExit
assert str(exc.value) == "1"


async def test_run_command_with_scenarios(tmp_dir: Path, arg_parser: ArgumentParser):
with given:
command = RunCommand(CustomConfig, arg_parser)
Expand Down Expand Up @@ -68,6 +147,7 @@ class Terminator(vedro.Config.Plugins.Terminator):
assert str(exc.value) == "0"


@pytest.mark.usefixtures(tmp_dir.__name__)
async def test_run_command_validate_plugin_error(arg_parser: ArgumentParser):
with given:
class InvalidConfig(CustomConfig):
Expand Down
14 changes: 11 additions & 3 deletions tests/plugins/skipper/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from niltype import Nil, Nilable

from vedro import Scenario
from vedro.core import Dispatcher, VirtualScenario
from vedro.events import ArgParsedEvent, ArgParseEvent
from vedro.core import Config, Dispatcher, VirtualScenario
from vedro.events import ArgParsedEvent, ArgParseEvent, ConfigLoadedEvent
from vedro.plugins.skipper import Skipper, SkipperPlugin
from vedro.plugins.skipper import only as only_scenario
from vedro.plugins.skipper import skip as skip_scenario
Expand Down Expand Up @@ -41,7 +41,15 @@ def tmp_dir(tmp_path: Path) -> Path:
async def fire_arg_parsed_event(dispatcher: Dispatcher, *,
file_or_dir: Optional[List[str]] = None,
ignore: Optional[List[str]] = None,
subject: Optional[str] = None) -> None:
subject: Optional[str] = None,
project_dir: Optional[Path] = None) -> None:
project_dir_ = project_dir if project_dir else Config.project_dir

class CustomConfig(Config):
project_dir = project_dir_

await dispatcher.fire(ConfigLoadedEvent(Path(), CustomConfig))

arg_parse_event = ArgParseEvent(ArgumentParser())
await dispatcher.fire(arg_parse_event)

Expand Down
2 changes: 1 addition & 1 deletion tests/plugins/skipper/test_skipper_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ async def test_select_rel_dir(dirname: str, *, dispatcher: Dispatcher, tmp_dir:
]

rel_path = tmp_dir.relative_to(getcwd())
await fire_arg_parsed_event(dispatcher, file_or_dir=[
await fire_arg_parsed_event(dispatcher, project_dir=tmp_dir, file_or_dir=[
str(rel_path / dirname)
])

Expand Down
16 changes: 16 additions & 0 deletions vedro/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@


async def main() -> None:
"""
Execute the main logic of the Vedro command-line interface.
This function handles the parsing of command-line arguments, dynamically loads
configuration files, and invokes the appropriate command based on the user's input.
Steps include:
- Parsing the project directory argument.
- Validating the existence and type of the specified project directory.
- Dynamically loading the configuration file.
- Parsing the main command (run, version, plugin, etc.).
- Executing the corresponding command logic.
:raises FileNotFoundError: If the specified project directory does not exist.
:raises NotADirectoryError: If the specified project directory path is not a directory.
"""
shadow_parser = ArgumentParser(add_help=False, allow_abbrev=False)
shadow_parser.add_argument("--project-dir", type=Path, default=Path.cwd())
shadow_args, _ = shadow_parser.parse_known_args()
Expand Down
25 changes: 25 additions & 0 deletions vedro/commands/_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,36 @@


class Command(ABC):
"""
Serves as an abstract base class for defining commands.
Commands are operations that can be executed with a specific configuration
and argument parser. Subclasses must implement the `run` method to define
the behavior of the command.
:param config: The global configuration instance for the command.
:param arg_parser: The argument parser for parsing command-line options.
:param kwargs: Additional keyword arguments for customization.
"""

def __init__(self, config: Type[Config],
arg_parser: CommandArgumentParser, **kwargs: Any) -> None:
"""
Initialize the Command instance with a configuration and argument parser.
:param config: The global configuration instance.
:param arg_parser: The argument parser for parsing command-line options.
:param kwargs: Additional keyword arguments for customization.
"""
self._config = config
self._arg_parser = arg_parser

@abstractmethod
async def run(self) -> None:
"""
Execute the command's logic.
Subclasses must implement this method to define the specific behavior
of the command when executed.
"""
pass
Loading

0 comments on commit 3c033b7

Please sign in to comment.