Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new assertions #85

Merged
merged 38 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f93a663
add pretty diff
tsv1 May 12, 2024
070c22f
add module loader
tsv1 May 24, 2024
4b4e53a
add tests
tsv1 May 24, 2024
4fa20a1
update plugin
tsv1 May 24, 2024
4779208
fix error message
tsv1 May 24, 2024
5379729
update ScenarioFileLoader
tsv1 May 26, 2024
37810a8
Merge branch 'master' of github.com:vedro-universe/vedro into new-ass…
tsv1 May 26, 2024
5e83f29
add docstring
tsv1 May 26, 2024
3adcb04
add legacy rewriter
tsv1 May 26, 2024
c70f4fa
Merge branch 'pretty-diff' of github.com:vedro-universe/vedro into ne…
tsv1 May 26, 2024
bce0d77
add assert rewriter
tsv1 May 26, 2024
d161419
add filter_traceback
tsv1 May 27, 2024
cb30787
use filter_traceback
tsv1 May 27, 2024
9cbf16b
add tests
tsv1 May 29, 2024
6f58a7b
update method
tsv1 Jun 4, 2024
406425d
add tests
tsv1 Jun 4, 2024
3536205
add tb_supress_modules
tsv1 Jun 4, 2024
f4b65f9
fix tests
tsv1 Jun 4, 2024
5e66655
fix test
tsv1 Jun 4, 2024
e8d3115
add assertion tool
tsv1 Jun 12, 2024
cb9f9c2
rename files
tsv1 Jun 12, 2024
818c2ec
add tests
tsv1 Jun 12, 2024
12059e5
add tests
tsv1 Jun 16, 2024
ad8391c
add tests
tsv1 Jun 16, 2024
3c57588
Merge branch 'master' of github.com:vedro-universe/vedro into new-ass…
tsv1 Jun 16, 2024
a604090
add docstrings
tsv1 Jun 16, 2024
7b75174
fix tests
tsv1 Jun 16, 2024
3c559be
fix types
tsv1 Jun 16, 2024
f2f8f2c
add pretty assertion
tsv1 Jun 16, 2024
ebfc429
fix
tsv1 Jun 16, 2024
66de976
add advanced differ
tsv1 Jul 6, 2024
e6b9ce4
add tests
tsv1 Jul 6, 2024
81a9c25
add tests
tsv1 Jul 6, 2024
b7575e6
add tests
tsv1 Jul 6, 2024
76eeac9
add show full diff
tsv1 Jul 6, 2024
d26eb3e
rename class
tsv1 Jul 7, 2024
871c289
add params
tsv1 Jul 7, 2024
6be9241
add tests
tsv1 Jul 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pytest==7.4.2
pytest-asyncio==0.21.1
pytest-cov==4.1.0
setuptools==68.1.2
dessert==1.4.7
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
cabina>=0.7,<2.0
dessert>=1.4,<2.0
filelock>=3.5,<4.0
niltype>=0.3,<2.0
pyparsing>=3.0,<4.0
Expand Down
3 changes: 1 addition & 2 deletions tests/commands/plugin_command/plugin_manager/_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from os import chdir, linesep
from os import linesep
from pathlib import Path
from time import time
from typing import List, Optional
Expand All @@ -7,7 +7,6 @@


def create_config(tmp_path: Path, config: Optional[List[str]] = None) -> Path:
chdir(tmp_path)
now = int(time() * 1000)
config_file = Path(f"vedro-cfg-{now}.py")
if config is not None:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from os import linesep
from pathlib import Path

Expand All @@ -9,6 +10,16 @@
from ._utils import create_config, read_config


@pytest.fixture()
def tmp_path(tmp_path: Path) -> Path:
cwd = os.getcwd()
try:
os.chdir(tmp_path)
yield tmp_path
finally:
os.chdir(cwd)


async def test_plugin_manager_no_file(tmp_path: Path):
with given:
config_path = create_config(tmp_path)
Expand Down
14 changes: 10 additions & 4 deletions tests/commands/run_command/_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from os import chdir, linesep
import os
from os import linesep
from pathlib import Path

import pytest
Expand All @@ -10,9 +11,14 @@

@pytest.fixture()
def tmp_dir(tmp_path: Path) -> Path:
chdir(tmp_path)
Path("./scenarios").mkdir(exist_ok=True)
yield tmp_path
cwd = os.getcwd()
try:
os.chdir(tmp_path)

Path("./scenarios").mkdir(exist_ok=True)
yield tmp_path
finally:
os.chdir(cwd)


def create_scenario(tmp_dir: Path, scenario_path: str) -> None:
Expand Down
73 changes: 73 additions & 0 deletions tests/core/module_loader/test_module_file_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from pathlib import Path
from types import ModuleType

import pytest
from _pytest.python_api import raises
from baby_steps import given, then, when

from vedro.core import ModuleFileLoader


def make_module(tmp_path: Path, name: str, content: str) -> Path:
module_path = tmp_path / name
module_path.write_text(content)
return module_path


async def test_load_module(tmp_path: Path):
with given:
loader = ModuleFileLoader()
module_path = make_module(tmp_path, "test_module.py", "x = 42")

with when:
module = await loader.load(module_path)

with then:
assert isinstance(module, ModuleType)
assert module.x == 42


async def test_load_module_with_valid_name(tmp_path: Path):
with given:
loader = ModuleFileLoader(validate_module_names=True)
rel_path = tmp_path.relative_to(Path.cwd())
module_path = make_module(rel_path, "test_module.py", "x = 42")

with when:
module = await loader.load(module_path)

with then:
assert isinstance(module, ModuleType)
assert module.x == 42


@pytest.mark.parametrize("name", ["123name", "import"])
async def test_load_module_with_invalid_name(tmp_path: Path, name: str):
with given:
loader = ModuleFileLoader(validate_module_names=True)
rel_path = tmp_path.relative_to(Path.cwd())
module_path = make_module(rel_path, f"{name}.py", "x = 42")

with when, raises(BaseException) as exc:
await loader.load(module_path)

with then:
assert exc.type is ValueError
assert str(exc.value) == (
f"The module name derived from the path '{module_path}' is invalid due to the segment "
f"'{name}'. A valid module name should start with a letter or underscore, contain "
"only letters, digits, or underscores, and not be a Python keyword."
)


async def test_load_module_with_non_existent_path():
with given:
loader = ModuleFileLoader()
non_existent_path = Path("non_existent_module.py")

with when, raises(BaseException) as exc:
await loader.load(non_existent_path)

with then:
assert exc.type is FileNotFoundError
assert "[Errno 2] No such file or directory" in str(exc.value)
13 changes: 13 additions & 0 deletions tests/core/module_loader/test_module_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from baby_steps import given, when
from pytest import raises

from vedro.core import ModuleLoader


def test_abstract_module_loader_instantiation():
with when, raises(BaseException) as exc_info:
ModuleLoader()

with given:
assert exc_info.type is TypeError
assert "Can't instantiate abstract class ModuleLoader" in str(exc_info.value)
46 changes: 1 addition & 45 deletions tests/core/scenario_loader/test_scenario_file_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,50 +88,6 @@ def __init__(self, user):
assert len(scenarios) == 2


async def test_scenario_file_loader_with_invalid_module_name(tmp_scn_dir: Path):
with given:
path = tmp_scn_dir / "scenario with space.py"
path.write_text(dedent('''
import vedro
class Scenario(vedro.Scenario):
pass
'''))
loader = ScenarioFileLoader()
loader._validate_module_names = True

with when, raises(BaseException) as exc:
await loader.load(path)

with then:
assert exc.type is ValueError
assert str(exc.value) == (
"The module name derived from the path 'scenarios/scenario with space.py' "
"is invalid due to the segment 'scenario with space'. "
"A valid module name should start with a letter or underscore, contain "
"only letters, digits, or underscores, and not be a Python keyword."
)


async def test_scenario_file_loader_with_invalid_class_name(tmp_scn_dir: Path):
with given:
path = tmp_scn_dir / "scenario.py"
path.write_text(dedent('''
import vedro
class Scn(vedro.Scenario):
pass
'''))
loader = ScenarioFileLoader()

with when, raises(BaseException) as exc:
await loader.load(path)

with then:
assert exc.type is ValueError
assert str(exc.value) == (
"'scenarios.scenario.Scn' must have a name that starts or ends with 'Scenario'"
)


async def test_scenario_file_loader_without_inheriting_scenario(tmp_scn_dir: Path):
with given:
path = tmp_scn_dir / "scenario.py"
Expand Down Expand Up @@ -161,7 +117,7 @@ async def test_scenario_file_loader_without_scenarios(tmp_scn_dir: Path):
with then:
assert exc.type is ValueError
assert str(exc.value) == (
"No valid Vedro scenarios found in the module at 'scenarios/scenario.py'. "
"No Vedro scenarios found in the module at 'scenarios/scenario.py'. "
"Ensure the module contains at least one subclass of 'vedro.Scenario'"
)

Expand Down
61 changes: 61 additions & 0 deletions tests/plugins/assert_rewriter/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import os
from argparse import ArgumentParser, Namespace
from pathlib import Path

import pytest

from vedro.core import Config, ConfigType, Dispatcher, Factory, ModuleFileLoader, ModuleLoader
from vedro.events import ArgParsedEvent, ArgParseEvent, ConfigLoadedEvent
from vedro.plugins.assert_rewriter import AssertRewriter, AssertRewriterPlugin

__all__ = ("tmp_scn_dir", "dispatcher", "assert_rewriter_plugin", "make_config",
"fire_config_loaded_event", "fire_arg_parsed_event",)


@pytest.fixture()
def tmp_scn_dir(tmp_path: Path) -> Path:
cwd = os.getcwd()
try:
os.chdir(tmp_path)

scn_dir = tmp_path / "scenarios/"
scn_dir.mkdir(exist_ok=True)
yield scn_dir.relative_to(tmp_path)
finally:
os.chdir(cwd)


@pytest.fixture()
def dispatcher() -> Dispatcher:
return Dispatcher()


@pytest.fixture()
def assert_rewriter_plugin(dispatcher: Dispatcher) -> AssertRewriterPlugin:
plugin = AssertRewriterPlugin(AssertRewriter)
plugin.subscribe(dispatcher)
return plugin


def make_config() -> ConfigType:
class TestConfig(Config):
class Registry(Config.Registry):
ModuleLoader = Factory[ModuleLoader](ModuleFileLoader)

return TestConfig


async def fire_config_loaded_event(dispatcher: Dispatcher, config: ConfigType) -> None:
config_loaded_event = ConfigLoadedEvent(Path(), config)
await dispatcher.fire(config_loaded_event)


async def fire_arg_parsed_event(
dispatcher: Dispatcher, *,
legacy_assertions: bool = AssertRewriter.legacy_assertions
) -> None:
arg_parse_event = ArgParseEvent(ArgumentParser())
await dispatcher.fire(arg_parse_event)

arg_parsed_event = ArgParsedEvent(Namespace(legacy_assertions=legacy_assertions))
await dispatcher.fire(arg_parsed_event)
66 changes: 66 additions & 0 deletions tests/plugins/assert_rewriter/test_assert_rewriter_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from pathlib import Path
from textwrap import dedent

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

from vedro.plugins.assert_rewriter import AssertRewriterLoader, CompareOperator, assert_

from ._utils import tmp_scn_dir

__all__ = ("tmp_scn_dir",) # pytest fixtures


async def test_load_assertion_failure(tmp_scn_dir: Path):
with given:
path = tmp_scn_dir / "scenario.py"
path.write_text(dedent('''
import vedro
class Scenario(vedro.Scenario):
def step(self):
assert 1 == 2
'''))

loader = AssertRewriterLoader()
module = await loader.load(path)
scenario = module.Scenario()

with when, raises(BaseException) as exc:
scenario.step()

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

assert assert_.get_left(exc.value) == 1
assert assert_.get_right(exc.value) == 2
assert assert_.get_operator(exc.value) == CompareOperator.EQUAL
assert assert_.get_message(exc.value) == Nil


async def test_load_assertion_failure_with_message(tmp_scn_dir: Path):
with given:
path = tmp_scn_dir / "scenario.py"
path.write_text(dedent('''
import vedro
class Scenario(vedro.Scenario):
def step(self):
assert 1 == 2, "assertion failed"
'''))

loader = AssertRewriterLoader()
module = await loader.load(path)
scenario = module.Scenario()

with when, raises(BaseException) as exc:
scenario.step()

with then:
assert exc.type is AssertionError
assert str(exc.value) == "assertion failed"

assert assert_.get_left(exc.value) == 1
assert assert_.get_right(exc.value) == 2
assert assert_.get_operator(exc.value) == CompareOperator.EQUAL
assert assert_.get_message(exc.value) == "assertion failed"
41 changes: 35 additions & 6 deletions tests/plugins/assert_rewriter/test_assert_rewriter_plugin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
from baby_steps import then, when
import pytest
from baby_steps import given, then, when

from vedro.core import Plugin
from vedro.plugins.assert_rewriter import AssertRewriter, AssertRewriterPlugin
from vedro.core import Dispatcher
from vedro.plugins.assert_rewriter import AssertRewriterLoader, LegacyAssertRewriterLoader

from ._utils import (
assert_rewriter_plugin,
dispatcher,
fire_arg_parsed_event,
fire_config_loaded_event,
make_config,
)

__all__ = ("dispatcher", "assert_rewriter_plugin",) # pytest fixtures


@pytest.mark.usefixtures(assert_rewriter_plugin.__name__)
async def test_registers_default_assert_rewriter_loader(*, dispatcher: Dispatcher):
with given:
config = make_config()
await fire_config_loaded_event(dispatcher, config)

with when:
await fire_arg_parsed_event(dispatcher)

with then:
assert isinstance(config.Registry.ModuleLoader(), AssertRewriterLoader)


@pytest.mark.usefixtures(assert_rewriter_plugin.__name__)
async def test_registers_legacy_assert_rewriter_loader(*, dispatcher: Dispatcher):
with given:
config = make_config()
await fire_config_loaded_event(dispatcher, config)

def test_plugin():
with when:
plugin = AssertRewriterPlugin(AssertRewriter)
await fire_arg_parsed_event(dispatcher, legacy_assertions=True)

with then:
assert isinstance(plugin, Plugin)
assert isinstance(config.Registry.ModuleLoader(), LegacyAssertRewriterLoader)
Loading
Loading