Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install dependencies
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync
uv sync
- name: Check ruff formatter
run: |
uv run ruff check
Expand All @@ -36,4 +36,4 @@ jobs:
uv run ty check
- name: Run tests
run: |
uv run pytest tests/
uv run pytest --cov=. --cov-fail-under=95 tests/
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ wheels/

# Ignore test database files
*.db

# Ignore .coverate report
.coverage
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dev = [
"ipdb>=0.13.13",
"ipython>=8.18.1",
"pytest>=8.4.2",
"pytest-cov>=7.0.0",
"ruff>=0.14.3",
"ty>=0.0.1a21",
]
Expand Down Expand Up @@ -45,3 +46,10 @@ target-version = "py39"
quote-style = "double"
indent-style = "space"
docstring-code-format = true

[tool.coverage.report]
omit = ["main.py"]
exclude_also = [
# Don't need to test this everytime
"def __str__",
]
4 changes: 3 additions & 1 deletion src/tasks/presenters/no_task_presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
class NoTaskPresenter:
"""A helper presenter showing that no task matches a given id"""

MSG = "No task exists with id #{}."

def __init__(self, console: Console | None = None) -> None:
self._console = console or Console()

def present(self, task_id: int) -> None:
self._console.print(f"No task exists with id #{task_id}.")
self._console.print(self.MSG.format(task_id))
4 changes: 3 additions & 1 deletion src/tasks/presenters/no_tasks_presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@


class NoTasksPresenter:
MSG = "No tasks available."

def __init__(self, console: Console | None = None) -> None:
self._console = console or Console()

def present(self) -> None:
self._console.print("No tasks available.")
self._console.print(self.MSG)
4 changes: 2 additions & 2 deletions src/tasks/prompts/details_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def prompt(self) -> str:
)

@staticmethod
def _bottom_toolbar() -> str:
def _bottom_toolbar() -> str: # pragma: no cover
return (
"Write details in markdown style. "
"Type Alt+Enter or Esc+Enter when done."
Expand All @@ -42,5 +42,5 @@ def _bottom_toolbar() -> str:
@staticmethod
def _prompt_continuation(
width: int, line_number: int, is_soft_wrap: bool
) -> str:
) -> str: # pragma: no cover
return DETAILS_PROMPT_CHAR
Empty file added tests/app/__init__.py
Empty file.
100 changes: 100 additions & 0 deletions tests/app/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from unittest.mock import patch

import pytest
from typer.testing import CliRunner

from src.app.app import create_app
from src.tasks.models.task import Task
from src.tasks.presenters.no_task_presenter import NoTaskPresenter
from src.tasks.presenters.no_tasks_presenter import NoTasksPresenter

runner = CliRunner()


@pytest.fixture
def tst_app():
return create_app()


def test_view_all(tst_app, tmp_db):
task1 = Task.create(title="buy milk")
task2 = Task.create(title="buy bread")

result = runner.invoke(tst_app, ["view-all"])

assert result.exit_code == 0, "sanity check: no errors"
# Existing tasks are in displayed output
for task in (task1, task2):
assert str(task.id) in result.output
assert task.title in result.output


def test_view_all__no_tasks(tst_app, tmp_db):
result = runner.invoke(tst_app, ["view-all"])

assert result.exit_code == 0, "sanity check: no errors"
assert NoTasksPresenter.MSG in result.output, "shows no tasks presenter"


def test_view_task(tst_app, tmp_db):
task = Task.create(title="buy milk", details="why buy milk")

result = runner.invoke(tst_app, ["view", f"{task.id}"])

assert result.exit_code == 0, "sanity check: no errors"
# Existing task is in displayed output
assert str(task.id) in result.output
assert task.title in result.output
assert task.details in result.output


def test_view_task__non_existing_task(tst_app, tmp_db):
result = runner.invoke(tst_app, ["view", "1"])

assert result.exit_code == 0, "sanity check: no errors"
expected_msg = NoTaskPresenter.MSG.format(1)
assert expected_msg in result.output


def test_add(tst_app, tmp_db):
with patch("src.app.app.add_controller") as m:
runner.invoke(tst_app, ["add"])

# Add controller was called
m.assert_called_once()


def test_edit(tst_app, tmp_db):
task_id = 1
with patch("src.app.app.edit_controller") as m:
runner.invoke(tst_app, ["edit", f"{task_id}"])

# Add controller was called
m.assert_called_once_with(task_id)


def test_delete(tst_app, tmp_db):
task_id = 1
with patch("src.app.app.delete_controller") as m:
runner.invoke(tst_app, ["delete", f"{task_id}"])

# Add controller was called with task_id
m.assert_called_once_with(task_id)


def test_promote(tst_app, tmp_db):
task_ids = [1, 2]
with patch("src.app.app.promote_controller") as m:
runner.invoke(tst_app, ["promote", str(task_ids[0]), str(task_ids[1])])

# Add controller was called
m.assert_called_once_with(task_ids)


def test_regress(tst_app, tmp_db):
task_ids = [1, 2]
with patch("src.app.app.regress_controller") as m:
runner.invoke(tst_app, ["regress", str(task_ids[0]), str(task_ids[1])])

# Add controller was called
m.assert_called_once_with(task_ids)
Loading