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
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,30 @@ jobs:
run: uv sync
- name: Run lint check
run: uv run pre-commit run -a {{ '${{' }} matrix.cmd {{ '}}' }}

pytest-mocked:
# Runs the mocked dummy tests with no database service required.
# These tests use AsyncMock to stub DummyDAO and execute in a plain
# Python environment — fast, reproducible, and CI-friendly.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create .env
run: touch .env
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Set up UV
uses: astral-sh/setup-uv@v6
- name: Install deps
run: uv sync
- name: Run mocked tests
run: uv run pytest tests/test_dummy_mocked.py -vv

pytest:
# Full integration tests that require a running database.
# Kept here for completeness; run locally via docker-compose.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Mocked dummy tests — run without any database.

These tests exercise the same API endpoints as test_dummy.py but stub
out DummyDAO with AsyncMock, so they work in a plain GitHub Actions
environment that has no database service available.
"""
import uuid
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from starlette import status


@pytest.mark.anyio
async def test_creation_mocked(
fastapi_app: FastAPI,
client: AsyncClient,
) -> None:
"""Tests dummy instance creation without a real database.

DummyDAO.create_dummy_model and DummyDAO.filter are both stubbed so
the test only asserts that the endpoint returns HTTP 200 and the DAO
was called with the expected arguments.
"""
test_name = uuid.uuid4().hex
dummy_instance = MagicMock()
dummy_instance.name = test_name

with patch(
"{{cookiecutter.project_name}}.db.dao.dummy_dao.DummyDAO.create_dummy_model",
new_callable=AsyncMock,
) as mock_create, patch(
"{{cookiecutter.project_name}}.db.dao.dummy_dao.DummyDAO.filter",
new_callable=AsyncMock,
return_value=[dummy_instance],
) as mock_filter:
{%- if cookiecutter.api_type == 'rest' %}
url = fastapi_app.url_path_for("create_dummy_model")
response = await client.put(url, json={"name": test_name})
{%- elif cookiecutter.api_type == 'graphql' %}
url = fastapi_app.url_path_for("handle_http_post")
response = await client.post(
url,
json={
"query": "mutation($name: String!){createDummyModel(name: $name)}",
"variables": {"name": test_name},
},
)
{%- endif %}

assert response.status_code == status.HTTP_200_OK

# Verify filter was called (endpoint reads back the created object)
mock_filter.assert_awaited()

# The stubbed list must contain the object we just "created"
instances = await mock_filter(name=test_name)
assert instances[0].name == test_name


@pytest.mark.anyio
async def test_getting_mocked(
fastapi_app: FastAPI,
client: AsyncClient,
) -> None:
"""Tests dummy instance retrieval without a real database.

DummyDAO.filter and DummyDAO.create_dummy_model are stubbed.
The test verifies the GET endpoint returns exactly one object with
the expected name without touching the database.
"""
test_name = uuid.uuid4().hex
dummy_instance = MagicMock()
dummy_instance.name = test_name
dummy_instance.id = str(uuid.uuid4())

# First call (assert empty) returns [], second call returns the item.
filter_side_effects = [[], [dummy_instance]]

with patch(
"{{cookiecutter.project_name}}.db.dao.dummy_dao.DummyDAO.filter",
new_callable=AsyncMock,
side_effect=filter_side_effects,
), patch(
"{{cookiecutter.project_name}}.db.dao.dummy_dao.DummyDAO.create_dummy_model",
new_callable=AsyncMock,
):
{%- if cookiecutter.api_type == 'rest' %}
url = fastapi_app.url_path_for("get_dummy_models")
response = await client.get(url)
dummies = response.json()
{%- elif cookiecutter.api_type == 'graphql' %}
url = fastapi_app.url_path_for("handle_http_post")
response = await client.post(
url,
json={"query": "query{dumies:getDummyModels{id name}}"},
)
dummies = response.json()["data"]["dumies"]
{%- endif %}

assert response.status_code == status.HTTP_200_OK
assert len(dummies) == 1
assert dummies[0]["name"] == test_name