Complete architectural refactoring from simple MVC to Clean Architecture/DDD pattern: Domain Layer: - Add entities (Post, BaseEntity) with business logic - Add value objects (Title, Content, Slug) with validation - Add repository interfaces (PostRepository) - Add domain exceptions Application Layer: - Add use cases (CreatePost, GetPost, UpdatePost, DeletePost, ListPosts, PublishPost) - Add DTOs for data transfer - Add TransactionManager interface Infrastructure Layer: - Add SQLAlchemy models and async database connection - Add SQLAlchemyPostRepository implementation - Add Dishka DI container with providers - Add error handlers and middleware Presentation Layer: - Add FastAPI routes with Dishka integration - Add Pydantic schemas - Add dependency injection using FromDishka[T] Other Changes: - Remove old flat structure (api/, common/, core/, modules/) - Add hatchling build system for package scripts - Add blog CLI command - Update AGENTS.md with new architecture docs - All 48 tests passing, mypy clean, ruff clean
50 lines
1.4 KiB
Python
50 lines
1.4 KiB
Python
"""Tests for main application."""
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
|
|
from app.main import app_factory, lifespan, main
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_lifespan() -> None:
|
|
"""Test lifespan context manager."""
|
|
app = FastAPI()
|
|
|
|
with (
|
|
patch("app.main.init_db") as mock_init,
|
|
patch("app.main.close_db") as mock_close,
|
|
):
|
|
async with lifespan(app):
|
|
mock_init.assert_called_once()
|
|
mock_close.assert_not_called()
|
|
mock_close.assert_called_once()
|
|
|
|
|
|
def test_app_factory() -> None:
|
|
"""Test app factory creates FastAPI app."""
|
|
app = app_factory()
|
|
assert isinstance(app, FastAPI)
|
|
|
|
|
|
def test_app_factory_has_routes() -> None:
|
|
"""Test app has registered routes."""
|
|
app = app_factory()
|
|
routes = [str(route.path) for route in app.routes if hasattr(route, "path")]
|
|
assert "/health" in routes
|
|
# Check that API routes are included
|
|
assert any("api" in path for path in routes)
|
|
|
|
|
|
@patch("app.main.uvicorn.run")
|
|
def test_main(mock_uvicorn_run: Mock) -> None:
|
|
"""Test main function starts uvicorn."""
|
|
main()
|
|
mock_uvicorn_run.assert_called_once()
|
|
call_kwargs = mock_uvicorn_run.call_args.kwargs
|
|
assert call_kwargs.get("factory") is True
|
|
assert call_kwargs.get("host") == "0.0.0.0"
|
|
assert call_kwargs.get("port") == 8000
|