-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor REST Api using routers and increase test coverage
- Loading branch information
Showing
22 changed files
with
279 additions
and
144 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 was deleted.
Oops, something went wrong.
Empty file.
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,18 @@ | ||
async def get_resource_state(sql_registry, drone_uuid: str): | ||
sql_query = """ | ||
SELECT R.drone_uuid, RS.state | ||
FROM Resources R | ||
JOIN ResourceStates RS ON R.state_id = RS.state_id | ||
WHERE R.drone_uuid = :drone_uuid""" | ||
return await sql_registry.async_execute(sql_query, dict(drone_uuid=drone_uuid)) | ||
|
||
|
||
async def get_resources(sql_registry): | ||
sql_query = """ | ||
SELECT R.remote_resource_uuid , RS.state, R.drone_uuid, S.site_name, | ||
MT.machine_type, R.created, R.updated | ||
FROM Resources R | ||
JOIN ResourceStates RS ON R.state_id = RS.state_id | ||
JOIN Sites S ON R.site_id = S.site_id | ||
JOIN MachineTypes MT ON R.machine_type_id = MT.machine_type_id""" | ||
return await sql_registry.async_execute(sql_query, {}) |
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,6 @@ | ||
from .routers import resources | ||
from fastapi import FastAPI | ||
|
||
app = FastAPI() | ||
|
||
app.include_router(resources.router) |
Empty file.
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,29 @@ | ||
from .. import security, crud, database | ||
from ....plugins.sqliteregistry import SqliteRegistry | ||
from fastapi import APIRouter, Depends, HTTPException, Path, Security | ||
|
||
|
||
router = APIRouter(prefix="/resources") | ||
|
||
|
||
@router.get("/state/{drone_uuid}") | ||
async def get_resource_state( | ||
drone_uuid: str = Path(..., regex=r"^\S+-[A-Fa-f0-9]{10}$"), | ||
sql_registry: SqliteRegistry = Depends(database.get_sql_registry()), | ||
_: str = Security(security.check_authorization, scopes=["user:read"]), | ||
): | ||
query_result = await crud.get_resource_state(sql_registry, drone_uuid) | ||
try: | ||
query_result = query_result[0] | ||
except IndexError: | ||
raise HTTPException(status_code=404, detail="Drone not found") from None | ||
return query_result | ||
|
||
|
||
@router.get("/") | ||
async def get_resources( | ||
sql_registry: SqliteRegistry = Depends(database.get_sql_registry()), | ||
_: str = Security(security.check_authorization, scopes=["user:read"]), | ||
): | ||
query_result = await crud.get_resources(sql_registry) | ||
return query_result |
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 was deleted.
Oops, something went wrong.
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
Empty file.
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
4 changes: 2 additions & 2 deletions
4
tests/rest_t/test_database.py → tests/rest_t/app_t/test_database.py
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 |
---|---|---|
@@ -1,10 +1,10 @@ | ||
from tardis.rest.database import get_sql_registry | ||
from tardis.rest.app.database import get_sql_registry | ||
|
||
from unittest import TestCase | ||
from unittest.mock import patch | ||
|
||
|
||
class TestDatabase(TestCase): | ||
@patch("tardis.rest.database.SqliteRegistry") | ||
@patch("tardis.rest.app.database.SqliteRegistry") | ||
def test_get_sql_registry(self, mocked_sqlite_registry): | ||
self.assertEqual(get_sql_registry()(), mocked_sqlite_registry()) |
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
Empty file.
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,59 @@ | ||
from tardis.rest.app.security import get_algorithm, get_secret_key | ||
from tests.utilities.utilities import run_async | ||
|
||
from httpx import AsyncClient | ||
|
||
from unittest import TestCase | ||
from unittest.mock import patch | ||
|
||
|
||
class TestCaseRouters(TestCase): | ||
mock_sqlite_registry_patcher = None | ||
mock_crud_patcher = None | ||
mock_config_patcher = None | ||
|
||
@classmethod | ||
def setUpClass(cls) -> None: | ||
cls.mock_sqlite_registry_patcher = patch( | ||
"tardis.rest.app.database.SqliteRegistry" | ||
) | ||
cls.mock_crud_patcher = patch("tardis.rest.app.routers.resources.crud") | ||
cls.mock_config_patcher = patch("tardis.rest.app.security.Configuration") | ||
cls.mock_sqlite_registry = cls.mock_sqlite_registry_patcher.start() | ||
cls.mock_crud = cls.mock_crud_patcher.start() | ||
cls.mock_config = cls.mock_config_patcher.start() | ||
|
||
@classmethod | ||
def tearDownClass(cls) -> None: | ||
cls.mock_sqlite_registry_patcher.stop() | ||
cls.mock_crud_patcher.stop() | ||
cls.mock_config_patcher.stop() | ||
|
||
def setUp(self) -> None: | ||
secret_key = "63328dc6b8524bf08b0ba151e287edb498852b77b97f837088de4d17247d032c" | ||
algorithm = "HS256" | ||
|
||
config = self.mock_config.return_value | ||
config.Services.restapi.secret_key = secret_key | ||
config.Services.restapi.algorithm = algorithm | ||
|
||
from tardis.rest.app.main import ( | ||
app, | ||
) # has to be imported after SqliteRegistry patch | ||
|
||
self.client = AsyncClient(app=app, base_url="http://test") | ||
|
||
def tearDown(self) -> None: | ||
run_async(self.client.aclose) | ||
|
||
@staticmethod | ||
def clear_lru_cache(): | ||
get_algorithm.cache_clear() | ||
get_secret_key.cache_clear() | ||
|
||
@property | ||
def headers( | ||
self, | ||
token="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0YXJkaXMiLCJzY29wZXMiOlsidXNlcjpyZWFkIl19.l2xDqxEQOLYQq6cDX7RGDcT1XvyupRcBUpvvW1l4yeM", # noqa B950 | ||
): | ||
return {"accept": "application/json", "Authorization": token} |
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,83 @@ | ||
from tests.rest_t.routers_t.base_test_case_routers import TestCaseRouters | ||
from tests.utilities.utilities import async_return, run_async | ||
|
||
|
||
class TestResources(TestCaseRouters): | ||
# Reminder: When defining `setUp`, `setUpClass`, `tearDown` and `tearDownClass` | ||
# in router tests the corresponding super().function() needs to be called as well. | ||
def test_get_resource_state(self): | ||
self.clear_lru_cache() | ||
self.mock_crud.get_resource_state.return_value = async_return( | ||
return_value=[{"drone_uuid": "test-0123456789", "state": "AvailableState"}] | ||
) | ||
|
||
response = run_async( | ||
self.client.get, "/resources/state/test-0123456789", headers=self.headers | ||
) | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual( | ||
response.json(), | ||
{"drone_uuid": "test-0123456789", "state": "AvailableState"}, | ||
) | ||
|
||
self.mock_crud.get_resource_state.return_value = async_return(return_value=[]) | ||
response = run_async( | ||
self.client.get, "/resources/state/test-1234567890", headers=self.headers | ||
) | ||
self.assertEqual(response.status_code, 404) | ||
self.assertEqual(response.json(), {"detail": "Drone not found"}) | ||
|
||
response = run_async( | ||
self.client.get, "/resources/state/test-invalid", headers=self.headers | ||
) | ||
self.assertEqual(response.status_code, 422) | ||
self.assertEqual( | ||
response.json(), | ||
{ | ||
"detail": [ | ||
{ | ||
"ctx": {"pattern": "^\\S+-[A-Fa-f0-9]{10}$"}, | ||
"loc": ["path", "drone_uuid"], | ||
"msg": 'string does not match regex "^\\S+-[A-Fa-f0-9]{10}$"', | ||
"type": "value_error.str.regex", | ||
} | ||
] | ||
}, | ||
) | ||
|
||
response = run_async(self.client.get, "/resources/state", headers=self.headers) | ||
self.assertEqual(response.status_code, 404) | ||
self.assertEqual(response.json(), {"detail": "Not Found"}) | ||
|
||
def test_get_resources(self): | ||
self.clear_lru_cache() | ||
full_expected_resources = [ | ||
{ | ||
"remote_resource_uuid": "14fa5640a7c146e482e8be41ec5dffea", | ||
"state": "AvailableState", | ||
"drone_uuid": "test-0125bc9fd8", | ||
"site_name": "Test", | ||
"machine_type": "m1.test", | ||
"created": "2021-10-08T12:42:16.354400", | ||
"updated": "2021-10-08T12:42:28.382025", | ||
}, | ||
{ | ||
"remote_resource_uuid": "b3efcc5bc8b741af9222987e0434ca61", | ||
"state": "AvailableState", | ||
"drone_uuid": "test-6af3cfef14", | ||
"site_name": "Test", | ||
"machine_type": "m1.test", | ||
"created": "2021-10-08T12:42:16.373454", | ||
"updated": "2021-10-08T12:42:30.648325", | ||
}, | ||
] | ||
self.mock_crud.get_resources.return_value = async_return( | ||
return_value=full_expected_resources | ||
) | ||
|
||
response = run_async(self.client.get, "/resources", headers=self.headers) | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual( | ||
response.json(), | ||
full_expected_resources, | ||
) |
Oops, something went wrong.