Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 5 additions & 6 deletions .github/workflows/pytest-and-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ name: CI - Pytest & Coverage
on:
push:
paths:
- ".github/**"
- "pyproject.toml"
- "tests/**"
- "prich/**"
branches: [ main ]
pull_request:
paths:
- ".github/**"
- "pyproject.toml"
- "tests/**"
- "prich/**"
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

# read repo and publish checks
permissions:
Expand All @@ -26,6 +27,8 @@ jobs:
strategy:
matrix:
python-version: ["3.10", "3.11"]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}

steps:
- name: Checkout repository
Expand All @@ -48,11 +51,7 @@ jobs:

- name: Run tests with coverage + JUnit report
run: |
pytest \
-n auto \
--junitxml=pytest-results.xml \
--cov-report=xml \
--cov-report=html
pytest -n auto --junitxml=pytest-results.xml --cov-report=xml --cov-report=html

# Upload artifacts only for Python 3.11 to avoid duplicates
- name: Upload coverage reports (only on 3.11)
Expand Down
6 changes: 3 additions & 3 deletions prich/core/steps/step_run_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def run_command_step(template: TemplateModel, step: PythonStep | CommandStep, va
if isinstance(step, PythonStep) and step.type == "python":
method_path = template_dir / "scripts" / method
if not method_path.exists():
raise click.ClickException(f"Python script not found: {method_path}")
raise click.ClickException(f"Python script not found: {str(method_path)}")
if not method.endswith(".py"):
raise click.ClickException(f"Python script file should end with .py: {method_path}")
raise click.ClickException(f"Python script file should end with .py: {str(method_path)}")

if template.venv in ["shared", "isolated"]:
if template.venv == "shared":
Expand All @@ -33,7 +33,7 @@ def run_command_step(template: TemplateModel, step: PythonStep | CommandStep, va
venv_path = template_dir / "scripts" / "venv"
python_path = venv_path / "bin" / "python"
if not python_path.exists():
raise click.ClickException(f"{template.venv.capitalize()} venv python not found: {python_path}")
raise click.ClickException(f"{template.venv.capitalize()} venv python not found: {str(python_path)}")
cmd = [str(python_path), str(method_path)]
elif template.venv is None:
cmd = ["python", str(method_path)]
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ classifiers = [
[project.optional-dependencies]
openai = ["openai>=1.0.0,<2.0.0"]
mlx = ["mlx_lm>=0.24.1,<1.0.0"]
dev = ["openai", "mlx_lm", "pytest", "coverage", "pytest-cov", "pytest-xdist", "twine", "build", "faker"]
dev = ["openai", "pytest", "coverage", "pytest-cov", "pytest-xdist", "twine", "build", "faker"]

[project.urls]
Homepage = "https://github.com/oleks-dev/prich"
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def mock_paths(tmp_path, monkeypatch):
local_templates=local_prich_templates_dir, #cwd_dir / ".prich" / "templates"
)
)
if "/pytest-" in str(tmp_path):
if "pytest-" in str(tmp_path):
shutil.rmtree(tmp_path)
else:
raise RuntimeError(f"Failed to check folder before removing! {tmp_path}")
Expand Down
9 changes: 5 additions & 4 deletions tests/test_engine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import tempfile
from subprocess import CompletedProcess

Expand Down Expand Up @@ -414,14 +415,14 @@ def test_validate_step_output(mock_paths, basic_config, case):
"step": PythonStep(name="test", type="python", call="test.py"),
"mock_output": CompletedProcess(args=["python", "test.py"], returncode=0, stdout="hello"),
"expected_exception": click.ClickException,
"expected_exception_message": "Python script not found: test/templates/test-template/scripts/test.py"
"expected_exception_message": r"Python script not found: test[\/|\\]templates[\/|\\]test-template[\/|\\]scripts[\/|\\]test.py"
},
{"id": "python_step_file_not_found_isolated_venv",
"template": generate_template(template_id="test-template", isolated_venv=True),
"step": PythonStep(name="test", type="python", call="test.py"),
"mock_output": CompletedProcess(args=["python", "test.py"], returncode=0, stdout="hello"),
"expected_exception": click.ClickException,
"expected_exception_message": "Python script not found: test/templates/test-template/scripts/test.py"
"expected_exception_message": r"Python script not found: test[\/|\\]templates[\/|\\]test-template[\/|\\]scripts[\/|\\]test.py"
},
{"id": "command_step",
"template": generate_template(template_id="test-template"),
Expand All @@ -441,7 +442,7 @@ def test_validate_step_output(mock_paths, basic_config, case):
"template": generate_template(template_id="test-template"),
"step": CommandStep(name="test", type="command", call="notexistingcommand", args=["hello"]),
"expected_exception": click.ClickException,
"expected_exception_message": "Unexpected error in notexistingcommand: [Errno 2] No such file or directory: 'notexistingcommand'"
"expected_exception_message": r"Unexpected error in notexistingcommand: [\[Errno 2\] No such file or directory: 'notexistingcommand'|\[WinErrno 2\] The system cannot find the file specified]"
},
]
@pytest.mark.parametrize("case", get_run_command_step_CASES, ids=[c["id"] for c in get_run_command_step_CASES])
Expand All @@ -455,7 +456,7 @@ def test_run_command_step(case, monkeypatch):
with pytest.raises(case.get("expected_exception")) as e:
run_command_step(case.get("template"), case.get("step"), case.get("variables", {}))
if case.get("expected_exception_message"):
assert str(e.value) in case.get("expected_exception_message")
assert re.search(case.get("expected_exception_message"), str(e.value))
else:
actual, actual_exitcode = run_command_step(case.get("template"), case.get("step"), case.get("variables", {}))
if case.get("expected_result") is not None:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_init_cmd(mock_paths, monkeypatch, case):
else:
assert False, "Wrong init_folder param specified"

if "/pytest-" in str(prich_dir) and prich_dir.exists():
if "pytest-" in str(prich_dir) and prich_dir.exists():
shutil.rmtree(prich_dir)
else:
raise RuntimeError(f"Failed to check folder before removing! {prich_dir}")
Expand Down
7 changes: 6 additions & 1 deletion tests/test_providers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import json
import re

import click
import pytest
from dataclasses import dataclass
Expand Down Expand Up @@ -60,7 +62,7 @@ class OpenAIStream:
provider_type="stdin_consumer", name="echo", call="catt", args=[], mode="flat"
),
"expected_exception": click.ClickException,
"expected_exception_messages": ["STDIN consumer provider error: [Errno 2] No such file or director"],
"expected_exception_messages_regex": [r"STDIN consumer provider error: [\[Errno 2\] No such file or directory|\[WinErrno 2\] The system cannot find the file specified]"],
},

# OpenAI
Expand Down Expand Up @@ -450,6 +452,9 @@ def fake_response_stream(self, **kwargs):
if case.get("expected_exception_messages") is not None:
for message in case.get("expected_exception_messages"):
assert message in str(e.value)
if case.get("expected_exception_messages_regex") is not None:
for message in case.get("expected_exception_messages_regex"):
assert re.search(message, str(e.value))
else:
result, output = capture_stdout(provider.send_prompt, prompt=prompt, instructions=instructions, input_=input_)
result_repeat, output_repeat = capture_stdout(provider.send_prompt, prompt=prompt, instructions=instructions, input_=input_)
Expand Down
8 changes: 5 additions & 3 deletions tests/test_run_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@
call="echo1",
args=["test"],
validate=ValidateStepOutput(
match="No such file or directory: 'echo1'",
match="No such file or directory: 'echo1'|The system cannot find the file specified",
not_match="test",
match_exit_code=1,
on_fail="error"
Expand All @@ -358,7 +358,7 @@
# ),
},
"expected_exception": click.ClickException,
"expected_exception_message": "No such file or directory: 'echo1'",
"expected_exception_message_regex": "No such file or directory: 'echo1'|The system cannot find the file specified",
},
{"id": "run_cmd_and_validate_fail_exitcode_format", "template":
# TemplateModel(
Expand All @@ -372,7 +372,7 @@
call="echo",
args=["test"],
validate=ValidateStepOutput(
match="No such file or directory: 'echo1'",
match="No such file or directory: 'echo1'|The system cannot find the file specified",
not_match="test",
match_exit_code="hello",
on_fail="error"
Expand Down Expand Up @@ -907,6 +907,8 @@ def test_run_template(case, monkeypatch, basic_config):
run_template(test_template.id)
if case.get("expected_exception_message") is not None:
assert case.get("expected_exception_message") in str(e.value)
if case.get("expected_exception_message_regex") is not None:
assert re.search(case.get("expected_exception_message_regex"), str(e.value))
else:
result, out = capture_stdout(run_template, test_template.id)
if case.get("expected_output"):
Expand Down
14 changes: 8 additions & 6 deletions tests/test_template.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

import pytest
from click.testing import CliRunner
from prich.models.file_scope import FileScope
Expand Down Expand Up @@ -222,30 +224,30 @@ def test_show_template(mock_paths, template, case):
{"id": "create_template_no_template_id",
"iterations": [
{"args": [],
"expected_exception_messages": ["Usage: create [OPTIONS] TEMPLATE_ID"],
"expected_exception_messages": [r"Usage: create \[OPTIONS\] TEMPLATE_ID"],
"expected_exit_code": 2},
]
},
{"id": "create_template_local",
"iterations": [
{"args": ["test-tpl"],
"expected_exception_messages": ["Template test-tpl created in", "local/.prich/templates/test-tpl/test-tpl"],
"expected_exception_messages": [r"Template test-tpl created in", r"local[\/|\\]\.prich[\/|\\]templates[\/|\\]test-tpl[\/|\\]test-tpl"],
"expected_exit_code": 0,
"check_file": ""},
{"args": ["test-tpl"],
"expected_exception_messages": ["Error: Template test-tpl already exists."],
"expected_exception_messages": [r"Error: Template test-tpl already exists\."],
"expected_exit_code": 1,
"check_file": ""},
]
},
{"id": "create_template_global",
"iterations": [
{"args": ["test-tpl", "-g"],
"expected_exception_message": ["Template test-tpl created in", "global/.prich/templates/test-tpl/test-tpl"],
"expected_exception_message": [r"Template test-tpl created in", r"global[\/|\\]\.prich[\/|\\]templates[\/|\\]test-tpl[\/|\\]test-tpl"],
"expected_exit_code": 0,
"check_file": ""},
{"args": ["test-tpl", "-g"],
"expected_exception_message": ["Error: Template test-tpl already exists."],
"expected_exception_message": [r"Error: Template test-tpl already exists\."],
"expected_exit_code": 1,
"check_file": ""},
]
Expand All @@ -261,7 +263,7 @@ def test_create_template(mock_paths, case):
result = runner.invoke(create_template, iteration.get("args"))
if iteration.get("expected_exception_messages") is not None:
for message in iteration.get("expected_exception_messages"):
assert message in result.output.replace("\n", "")
assert re.search(message, result.output.replace("\n", "")), f"'{message}' not found in " + result.output.replace("\n", "")
if iteration.get("expected_exit_code") is not None:
assert result.exit_code == iteration.get("expected_exit_code")

Expand Down
Loading
Loading