Skip to content

Commit

Permalink
Merge pull request #35 from reagento/feature/tests
Browse files Browse the repository at this point in the history
Feature/tests
  • Loading branch information
Tishka17 authored May 17, 2024
2 parents c313806 + 39b4851 commit ca0d325
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 24 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: CI

on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]

jobs:
cpython:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
python-version:
- "3.10"
- "3.11"
- "3.12"

steps:
- uses: actions/checkout@v4
- name: Set up ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install '.' -r requirements_dev.txt
- name: Run tests
run: |
pytest
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

6 changes: 4 additions & 2 deletions requirements.txt → requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ adaptix
typing_extensions
aiohttp
requests
nose2
mypy
requests-mock
mypy
pytest
pytest-asyncio
Empty file added tests/__init__.py
Empty file.
Empty file added tests/requests/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions tests/requests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
import requests_mock

from dataclass_rest.http import requests

@pytest.fixture
def session():
return requests.Session()


@pytest.fixture
def mocker(session):
with requests_mock.Mocker(session=session, case_sensitive=True) as session_mock:
yield session_mock
59 changes: 59 additions & 0 deletions tests/requests/test_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from dataclasses import dataclass
from enum import Enum

from adaptix import Retort, NameStyle, name_mapping

from dataclass_rest import patch
from dataclass_rest.http.requests import RequestsClient


class Selection(Enum):
ONE = "ONE"
TWO = "TWO"


@dataclass
class RequestBody:
int_param: int
selection: Selection


@dataclass
class ResponseBody:
int_param: int
selection: Selection


def test_body(session, mocker):
class Api(RequestsClient):
def _init_request_body_factory(self) -> Retort:
return Retort(recipe=[
name_mapping(name_style=NameStyle.CAMEL),
])

def _init_request_args_factory(self) -> Retort:
return Retort(recipe=[
name_mapping(name_style=NameStyle.UPPER_DOT),
])

def _init_response_body_factory(self) -> Retort:
return Retort(recipe=[
name_mapping(name_style=NameStyle.LOWER_KEBAB),
])

@patch("/post/")
def post_x(self, long_param: str, body: RequestBody) -> ResponseBody:
raise NotImplementedError()

mocker.patch(
url="http://example.com/post/?LONG.PARAM=hello",
text="""{"int-param": 1, "selection": "TWO"}""",
complete_qs=True,
)
client = Api(base_url="http://example.com", session=session)
result = client.post_x(
long_param="hello", body=RequestBody(int_param=42, selection=Selection.ONE),
)
assert result == ResponseBody(int_param=1, selection=Selection.TWO)
assert mocker.called_once
assert mocker.request_history[0].json() == {"intParam": 42, "selection": "ONE"}
69 changes: 69 additions & 0 deletions tests/requests/test_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from dataclasses import dataclass
from typing import Optional

from dataclass_rest import get, post
from dataclass_rest.http.requests import RequestsClient


def test_methods(session, mocker):
class Api(RequestsClient):
@get("/get")
def get_x(self) -> list[int]:
raise NotImplementedError()

@post("/post")
def post_x(self) -> list[int]:
raise NotImplementedError()

mocker.get("http://example.com/get", text="[1,2]", complete_qs=True)
mocker.post("http://example.com/post", text="[1,2,3]", complete_qs=True)
client = Api(base_url="http://example.com", session=session)
assert client.get_x() == [1, 2]
assert client.post_x() == [1, 2, 3]


def test_path_params(session, mocker):
class Api(RequestsClient):
@post("/post/{id}")
def post_x(self, id) -> list[int]:
raise NotImplementedError()

mocker.post("http://example.com/post/1", text="[1]", complete_qs=True)
mocker.post("http://example.com/post/2", text="[1,2]", complete_qs=True)
client = Api(base_url="http://example.com", session=session)
assert client.post_x(1) == [1]
assert client.post_x(2) == [1, 2]


def test_query_params(session, mocker):
class Api(RequestsClient):
@post("/post/{id}")
def post_x(self, id: str, param: Optional[int]) -> list[int]:
raise NotImplementedError()

mocker.post("http://example.com/post/x?", text="[0]", complete_qs=True)
mocker.post("http://example.com/post/x?param=1", text="[1]", complete_qs=True)
mocker.post("http://example.com/post/x?param=2", text="[1,2]", complete_qs=True)
client = Api(base_url="http://example.com", session=session)
assert client.post_x("x", None) == [0]
assert client.post_x("x", 1) == [1]
assert client.post_x("x", 2) == [1, 2]


@dataclass
class RequestBody:
x: int
y: str


def test_body(session, mocker):
class Api(RequestsClient):
@post("/post/")
def post_x(self, body: RequestBody) -> None:
raise NotImplementedError()

mocker.post("http://example.com/post/", text="null", complete_qs=True)
client = Api(base_url="http://example.com", session=session)
assert client.post_x(RequestBody(x=1, y="test")) is None
assert mocker.called_once
assert mocker.request_history[0].json() == {"x": 1, "y": "test"}
32 changes: 20 additions & 12 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
from dataclasses import dataclass

from dataclass_factory import Factory, NameStyle, Schema
import pytest
from adaptix import Retort, NameStyle, name_mapping
from requests import Session

from dataclass_rest import get
from dataclass_rest.async_base import AsyncClient
from dataclass_rest.sync_base import Client
from dataclass_rest.http.aiohttp import AiohttpClient
from dataclass_rest.http.requests import RequestsClient


@dataclass
class Todo:
id: int


def test_sync():
class RealClient(Client):
class RealClient(RequestsClient):
def __init__(self):
super().__init__("https://jsonplaceholder.typicode.com/", Session())

def _init_factory(self):
return Factory(default_schema=Schema(name_style=NameStyle.camel_lower))
def _init_request_body_factory(self) -> Retort:
return Retort(recipe=[
name_mapping(name_style=NameStyle.CAMEL),
])

@get("todos/{id}")
def get_todo(self, id: str) -> Todo:
Expand All @@ -27,16 +31,20 @@ def get_todo(self, id: str) -> Todo:
assert RealClient()


def test_async():
class RealClient(AsyncClient):
@pytest.mark.asyncio
async def test_async():
class RealClient(AiohttpClient):
def __init__(self):
super().__init__("https://jsonplaceholder.typicode.com/", Session())
super().__init__("https://jsonplaceholder.typicode.com/")

def _init_factory(self):
return Factory(default_schema=Schema(name_style=NameStyle.camel_lower))
def _init_request_body_factory(self) -> Retort:
return Retort(recipe=[
name_mapping(name_style=NameStyle.CAMEL),
])

@get("todos/{id}")
async def get_todo(self, id: str) -> Todo:
pass

assert RealClient()
client = RealClient()
await client.session.close()

0 comments on commit ca0d325

Please sign in to comment.