-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests for ApiGateway and ApiProvider (#17)
- Loading branch information
Showing
14 changed files
with
391 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from clean_python import InMemoryGateway | ||
from clean_python.fastapi import Service | ||
|
||
from .presentation import V1Books | ||
|
||
service = Service(V1Books()) | ||
|
||
app = service.create_app( | ||
title="Book service", | ||
description="Service for testing clean-python", | ||
hostname="testserver", | ||
access_logger_gateway=InMemoryGateway([]), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from typing import Optional | ||
|
||
from clean_python import InMemoryGateway | ||
from clean_python import Manage | ||
|
||
from .domain import Book | ||
from .domain import BookRepository | ||
|
||
|
||
class ManageBook(Manage[Book]): | ||
def __init__(self, repo: Optional[BookRepository] = None): | ||
if repo is None: | ||
repo = BookRepository(InMemoryGateway([])) | ||
self.repo = repo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from clean_python import Repository | ||
from clean_python import RootEntity | ||
from clean_python import ValueObject | ||
|
||
|
||
class Author(ValueObject): | ||
name: str | ||
|
||
|
||
class Book(RootEntity): | ||
author: Author | ||
title: str | ||
|
||
|
||
class BookRepository(Repository[Book]): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from http import HTTPStatus | ||
from typing import Optional | ||
|
||
from fastapi import Depends | ||
from fastapi import Form | ||
from fastapi import Response | ||
from fastapi import UploadFile | ||
|
||
from clean_python import DoesNotExist | ||
from clean_python import Page | ||
from clean_python import ValueObject | ||
from clean_python.fastapi import delete | ||
from clean_python.fastapi import get | ||
from clean_python.fastapi import patch | ||
from clean_python.fastapi import post | ||
from clean_python.fastapi import RequestQuery | ||
from clean_python.fastapi import Resource | ||
from clean_python.fastapi import v | ||
|
||
from .application import ManageBook | ||
from .domain import Author | ||
from .domain import Book | ||
|
||
|
||
class BookCreate(ValueObject): | ||
author: Author | ||
title: str | ||
|
||
|
||
class BookUpdate(ValueObject): | ||
author: Optional[Author] = None | ||
title: Optional[str] = None | ||
|
||
|
||
class V1Books(Resource, version=v(1), name="books"): | ||
def __init__(self): | ||
self.manager = ManageBook() | ||
|
||
@get("/books", response_model=Page[Book]) | ||
async def list(self, q: RequestQuery = Depends()): | ||
return await self.manager.filter([], q.as_page_options()) | ||
|
||
@post("/books", status_code=HTTPStatus.CREATED, response_model=Book) | ||
async def create(self, obj: BookCreate): | ||
return await self.manager.create(obj.model_dump()) | ||
|
||
@get("/books/{id}", response_model=Book) | ||
async def retrieve(self, id: int): | ||
return await self.manager.retrieve(id) | ||
|
||
@patch("/books/{id}", response_model=Book) | ||
async def update(self, id: int, obj: BookUpdate): | ||
return await self.manager.update(id, obj.model_dump(exclude_unset=True)) | ||
|
||
@delete("/books/{id}", status_code=HTTPStatus.NO_CONTENT, response_class=Response) | ||
async def destroy(self, id: int): | ||
if not await self.manager.destroy(id): | ||
raise DoesNotExist("object", id) | ||
|
||
@get("/text") | ||
async def text(self): | ||
return Response("foo", media_type="text/plain") | ||
|
||
@post("/form", response_model=Author) | ||
async def form(self, name: str = Form()): | ||
return {"name": name} | ||
|
||
@post("/file") | ||
async def file(self, file: UploadFile): | ||
return {file.filename: (await file.read()).decode()} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import pytest | ||
|
||
from clean_python import ctx | ||
from clean_python import DoesNotExist | ||
from clean_python import Json | ||
from clean_python import Tenant | ||
from clean_python.api_client import SyncApiGateway | ||
from clean_python.api_client import SyncApiProvider | ||
|
||
|
||
class BooksGateway(SyncApiGateway, path="v1/books/{id}"): | ||
pass | ||
|
||
|
||
@pytest.fixture | ||
def provider(fastapi_example_app) -> SyncApiProvider: | ||
ctx.tenant = Tenant(id=2, name="") | ||
yield SyncApiProvider(fastapi_example_app + "/", lambda a, b: "token") | ||
ctx.tenant = None | ||
|
||
|
||
@pytest.fixture | ||
def gateway(provider) -> SyncApiGateway: | ||
return BooksGateway(provider) | ||
|
||
|
||
@pytest.fixture | ||
def book(gateway: SyncApiGateway): | ||
return gateway.add({"title": "fixture", "author": {"name": "foo"}}) | ||
|
||
|
||
def test_add(gateway: SyncApiGateway): | ||
response = gateway.add({"title": "test_add", "author": {"name": "foo"}}) | ||
assert isinstance(response["id"], int) | ||
assert response["title"] == "test_add" | ||
assert response["author"] == {"name": "foo"} | ||
assert response["created_at"] == response["updated_at"] | ||
|
||
|
||
def test_get(gateway: SyncApiGateway, book: Json): | ||
response = gateway.get(book["id"]) | ||
assert response == book | ||
|
||
|
||
def test_remove_and_404(gateway: SyncApiGateway, book: Json): | ||
assert gateway.remove(book["id"]) is True | ||
assert gateway.get(book["id"]) is None | ||
assert gateway.remove(book["id"]) is False | ||
|
||
|
||
def test_update(gateway: SyncApiGateway, book: Json): | ||
response = gateway.update({"id": book["id"], "title": "test_update"}) | ||
|
||
assert response["id"] == book["id"] | ||
assert response["title"] == "test_update" | ||
assert response["author"] == {"name": "foo"} | ||
assert response["created_at"] != response["updated_at"] | ||
|
||
|
||
def test_update_404(gateway: SyncApiGateway): | ||
with pytest.raises(DoesNotExist): | ||
gateway.update({"id": 123456, "title": "test_update_404"}) |
Oops, something went wrong.