- 268 unit tests covering all sync and async client methods - 196 integration tests against a real sandbox API - Mock-based tests using respx for sync and async HTTP mocking - Paging, error handling, and edge case coverage (400/401/403/404/500/418) - Integration test utilities and test data files - .env.example with sandbox environment variables
1345 lines
53 KiB
Python
1345 lines
53 KiB
Python
import pytest
|
|
import respx
|
|
from httpx import Response
|
|
|
|
from onspring_api_sdk import AsyncOnspringClient
|
|
from onspring_api_sdk.errors import (
|
|
OnspringAuthenticationError,
|
|
OnspringError,
|
|
OnspringNotFoundError,
|
|
OnspringRateLimitError,
|
|
)
|
|
from onspring_api_sdk.models import (
|
|
AddOrUpdateListItemResponse,
|
|
AddOrUpdateRecordResponse,
|
|
ApiResponse,
|
|
GetAppByIdResponse,
|
|
GetAppsByIdsResponse,
|
|
GetAppsResponse,
|
|
GetBatchRecordsResponse,
|
|
GetFieldByIdResponse,
|
|
GetFieldsByAppIdResponse,
|
|
GetFieldsByIdsResponse,
|
|
GetFileByIdResponse,
|
|
GetFileInfoByIdResponse,
|
|
GetRecordByIdRequest,
|
|
GetRecordsByAppRequest,
|
|
GetRecordsResponse,
|
|
GetReportByIdRequest,
|
|
GetReportByIdResponse,
|
|
GetReportsByAppIdResponse,
|
|
Record,
|
|
SaveFileRequest,
|
|
SaveFileResponse,
|
|
)
|
|
|
|
from .conftest import (
|
|
MOCK_APP,
|
|
MOCK_APPS_BATCH_RESPONSE,
|
|
MOCK_APPS_RESPONSE,
|
|
MOCK_FIELD,
|
|
MOCK_FIELDS_BATCH_RESPONSE,
|
|
MOCK_FIELDS_RESPONSE,
|
|
MOCK_FILE_INFO,
|
|
MOCK_LIST_ITEM_RESPONSE,
|
|
MOCK_MESSAGE_RESPONSE,
|
|
MOCK_RECORD,
|
|
MOCK_RECORDS_BATCH_RESPONSE,
|
|
MOCK_RECORDS_RESPONSE,
|
|
MOCK_REPORT_RESPONSE,
|
|
MOCK_REPORTS_BY_APP_RESPONSE,
|
|
MOCK_SAVE_FILE_RESPONSE,
|
|
MOCK_SAVE_RECORD_RESPONSE,
|
|
TEST_URL,
|
|
create_temp_file,
|
|
)
|
|
|
|
|
|
def _mock_json(status: int, json: dict | list | None = None) -> Response:
|
|
return Response(status, json=json)
|
|
|
|
|
|
def _assert_error(response: ApiResponse, status: int, message: str | None) -> None:
|
|
assert response.status_code == status
|
|
assert response.is_successful is False
|
|
assert response.data is None
|
|
assert response.message == message
|
|
|
|
|
|
class TestCanConnect:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Ping").mock(return_value=Response(200))
|
|
|
|
assert await async_client.can_connect() is True
|
|
|
|
async def test_failure(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Ping").mock(return_value=Response(401))
|
|
|
|
assert await async_client.can_connect() is False
|
|
|
|
async def test_async_context_manager(self, async_client: AsyncOnspringClient):
|
|
async with async_client as cm:
|
|
assert cm is async_client
|
|
|
|
assert async_client.client.is_closed
|
|
|
|
async def test_aclose(self, async_client: AsyncOnspringClient):
|
|
await async_client.aclose()
|
|
assert async_client.client.is_closed
|
|
|
|
|
|
class TestGetApps:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps").mock(return_value=Response(200, json=MOCK_APPS_RESPONSE))
|
|
|
|
response = await async_client.get_apps()
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetAppsResponse)
|
|
assert len(response.data.apps) == 2
|
|
assert response.data.apps[0].name == "Test App"
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps").mock(return_value=Response(400))
|
|
|
|
_assert_error(await async_client.get_apps(), 400, "Invalid paging information")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_apps(), 401, "Unauthorized request")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_apps(), 418, None)
|
|
|
|
async def test_with_explicit_paging(self, async_client: AsyncOnspringClient):
|
|
from onspring_api_sdk.models import PagingRequest
|
|
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps").mock(return_value=Response(200, json=MOCK_APPS_RESPONSE))
|
|
|
|
paging = PagingRequest(page_number=2, page_size=10)
|
|
response = await async_client.get_apps(paging_request=paging)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetAppsResponse)
|
|
|
|
|
|
class TestGetAppById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps/id/1").mock(return_value=Response(200, json=MOCK_APP))
|
|
|
|
response = await async_client.get_app_by_id(1)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetAppByIdResponse)
|
|
assert response.data.app.id == 1
|
|
assert response.data.app.name == "Test App"
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps/id/1").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_app_by_id(1), 401, "Unauthorized request")
|
|
|
|
async def test_403(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps/id/1").mock(return_value=Response(403))
|
|
|
|
_assert_error(await async_client.get_app_by_id(1), 403, "Client does not have read access to the app")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps/id/999").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.get_app_by_id(999), 404, "App could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Apps/id/1").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_app_by_id(1), 418, None)
|
|
|
|
|
|
class TestGetAppsByIds:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Apps/batch-get").mock(return_value=Response(200, json=MOCK_APPS_BATCH_RESPONSE))
|
|
|
|
response = await async_client.get_apps_by_ids([1, 2])
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetAppsByIdsResponse)
|
|
assert response.data.count == 2
|
|
|
|
async def test_type_check(self, async_client: AsyncOnspringClient):
|
|
response = await async_client.get_apps_by_ids("not a list")
|
|
|
|
_assert_error(response, 400, "App ids should be of type list or tuple")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Apps/batch-get").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_apps_by_ids([1]), 401, "Unauthorized request")
|
|
|
|
async def test_403(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Apps/batch-get").mock(return_value=Response(403))
|
|
|
|
_assert_error(await async_client.get_apps_by_ids([1]), 403, "Client does not have read access to the app")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Apps/batch-get").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_apps_by_ids([1]), 418, None)
|
|
|
|
|
|
class TestGetFieldById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/id/1").mock(return_value=Response(200, json=MOCK_FIELD))
|
|
|
|
response = await async_client.get_field_by_id(1)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFieldByIdResponse)
|
|
assert response.data.field.id == 1
|
|
assert response.data.field.name == "Test Field"
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/id/1").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_field_by_id(1), 401, "Unauthorized request")
|
|
|
|
async def test_403(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/id/1").mock(return_value=Response(403))
|
|
|
|
_assert_error(await async_client.get_field_by_id(1), 403, "Client does not have read access to the field")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/id/999").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.get_field_by_id(999), 404, "Field could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/id/1").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_field_by_id(1), 418, None)
|
|
|
|
|
|
class TestGetFieldsByIds:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Fields/batch-get").mock(return_value=Response(200, json=MOCK_FIELDS_BATCH_RESPONSE))
|
|
|
|
response = await async_client.get_fields_by_ids([1, 2])
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFieldsByIdsResponse)
|
|
assert response.data.count == 2
|
|
|
|
async def test_type_check(self, async_client: AsyncOnspringClient):
|
|
response = await async_client.get_fields_by_ids("not a list")
|
|
|
|
_assert_error(response, 400, "Field ids should be of type list or tuple")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Fields/batch-get").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_fields_by_ids([1]), 401, "Unauthorized request")
|
|
|
|
async def test_403(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Fields/batch-get").mock(return_value=Response(403))
|
|
|
|
_assert_error(
|
|
await async_client.get_fields_by_ids([1]), 403, "Client does not have read access to the field(s)"
|
|
)
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Fields/batch-get").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.get_fields_by_ids([1]), 404, "Field(s) could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Fields/batch-get").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_fields_by_ids([1]), 418, None)
|
|
|
|
|
|
class TestGetFieldsByAppId:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/appId/1").mock(return_value=Response(200, json=MOCK_FIELDS_RESPONSE))
|
|
|
|
response = await async_client.get_fields_by_app_id(1)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFieldsByAppIdResponse)
|
|
assert len(response.data.fields) == 2
|
|
assert response.data.fields[0].id == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/appId/1").mock(return_value=Response(400))
|
|
|
|
_assert_error(await async_client.get_fields_by_app_id(1), 400, "Invalid paging information")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/appId/1").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_fields_by_app_id(1), 401, "Unauthorized request")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/appId/1").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_fields_by_app_id(1), 418, None)
|
|
|
|
async def test_with_explicit_paging(self, async_client: AsyncOnspringClient):
|
|
from onspring_api_sdk.models import PagingRequest
|
|
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Fields/appId/1").mock(return_value=Response(200, json=MOCK_FIELDS_RESPONSE))
|
|
|
|
paging = PagingRequest(page_number=2, page_size=10)
|
|
response = await async_client.get_fields_by_app_id(1, paging_request=paging)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFieldsByAppIdResponse)
|
|
|
|
|
|
class TestGetFileInfoById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(
|
|
return_value=Response(200, json=MOCK_FILE_INFO)
|
|
)
|
|
|
|
response = await async_client.get_file_info_by_id(1, 2, 3)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFileInfoByIdResponse)
|
|
assert response.data.file_info.name == "test.txt"
|
|
assert response.data.file_info.content_type == "text/plain"
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(400))
|
|
|
|
_assert_error(
|
|
await async_client.get_file_info_by_id(1, 2, 3), 400, "Request is invalid based on underlying data"
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_file_info_by_id(1, 2, 3), 401, "Unauthorized request")
|
|
|
|
async def test_403(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(403))
|
|
|
|
_assert_error(
|
|
await async_client.get_file_info_by_id(1, 2, 3), 403, "Client does not have read access to the file"
|
|
)
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.get_file_info_by_id(1, 2, 3), 404, "File could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_file_info_by_id(1, 2, 3), 418, None)
|
|
|
|
|
|
class TestDeleteFileById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(204))
|
|
|
|
response = await async_client.delete_file_by_id(1, 2, 3)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 204
|
|
assert response.message == "File deleted successfully"
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(400))
|
|
|
|
_assert_error(
|
|
await async_client.delete_file_by_id(1, 2, 3), 400, "Request is invalid based on underlying data"
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.delete_file_by_id(1, 2, 3), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(
|
|
return_value=Response(403, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.delete_file_by_id(1, 2, 3)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(
|
|
return_value=Response(404, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.delete_file_by_id(1, 2, 3)
|
|
|
|
_assert_error(response, 404, "An error occurred")
|
|
|
|
async def test_500(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(500))
|
|
|
|
_assert_error(
|
|
await async_client.delete_file_by_id(1, 2, 3), 500, "File could not be deleted due to internal error"
|
|
)
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.delete_file_by_id(1, 2, 3), 418, None)
|
|
|
|
|
|
class TestGetFileById:
|
|
def _mock_file_response(self, headers: dict | None = None) -> Response:
|
|
h = {
|
|
"content-disposition": "filename=test.txt",
|
|
"content-type": "text/plain",
|
|
"content-length": "12",
|
|
}
|
|
|
|
if headers:
|
|
h.update(headers)
|
|
|
|
return Response(200, headers=h, content=b"Hello World!")
|
|
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(
|
|
return_value=self._mock_file_response()
|
|
)
|
|
|
|
response = await async_client.get_file_by_id(1, 2, 3)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetFileByIdResponse)
|
|
assert response.data.file.name == "test.txt"
|
|
assert response.data.file.content_type == "text/plain"
|
|
assert response.data.file.content_length == 12
|
|
assert response.data.file.content == b"Hello World!"
|
|
|
|
async def test_quoted_filename(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(
|
|
return_value=self._mock_file_response({"content-disposition": 'attachment; filename="quoted.pdf"'})
|
|
)
|
|
|
|
response = await async_client.get_file_by_id(1, 2, 3)
|
|
|
|
assert response.data.file.name == "quoted.pdf"
|
|
|
|
async def test_success_onspring_fallback(self, async_client: AsyncOnspringClient):
|
|
"""When no content-disposition header, fall back to 'OnspringFile'."""
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(
|
|
return_value=self._mock_file_response({"content-disposition": ""})
|
|
)
|
|
|
|
response = await async_client.get_file_by_id(1, 2, 3)
|
|
|
|
assert response.data.file.name == "OnspringFile"
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(return_value=Response(400))
|
|
|
|
_assert_error(
|
|
await async_client.get_file_by_id(1, 2, 3), 400, "Request is invalid based on underlying data"
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_file_by_id(1, 2, 3), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(
|
|
return_value=Response(403, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.get_file_by_id(1, 2, 3)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(
|
|
return_value=Response(404, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.get_file_by_id(1, 2, 3)
|
|
|
|
_assert_error(response, 404, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Files/recordId/1/fieldId/2/fileId/3/file").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_file_by_id(1, 2, 3), 418, None)
|
|
|
|
|
|
class TestSaveFile:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
file_path = create_temp_file()
|
|
request = SaveFileRequest(
|
|
recordId=1,
|
|
fieldId=2,
|
|
fileName="test.txt",
|
|
filePath=str(file_path),
|
|
contentType="text/plain",
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Files").mock(return_value=Response(201, json=MOCK_SAVE_FILE_RESPONSE))
|
|
|
|
response = await async_client.save_file(request)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 201
|
|
assert isinstance(response.data, SaveFileResponse)
|
|
assert response.data.id == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
file_path = create_temp_file()
|
|
request = SaveFileRequest(
|
|
recordId=1,
|
|
fieldId=2,
|
|
fileName="test.txt",
|
|
filePath=str(file_path),
|
|
contentType="text/plain",
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Files").mock(return_value=Response(400))
|
|
|
|
_assert_error(await async_client.save_file(request), 400, "Request is invalid based on underlying data")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
file_path = create_temp_file()
|
|
request = SaveFileRequest(
|
|
recordId=1,
|
|
fieldId=2,
|
|
fileName="test.txt",
|
|
filePath=str(file_path),
|
|
contentType="text/plain",
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Files").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.save_file(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
file_path = create_temp_file()
|
|
request = SaveFileRequest(
|
|
recordId=1,
|
|
fieldId=2,
|
|
fileName="test.txt",
|
|
filePath=str(file_path),
|
|
contentType="text/plain",
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Files").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
response = await async_client.save_file(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
file_path = create_temp_file()
|
|
request = SaveFileRequest(
|
|
recordId=1,
|
|
fieldId=2,
|
|
fileName="test.txt",
|
|
filePath=str(file_path),
|
|
contentType="text/plain",
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Files").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.save_file(request), 418, None)
|
|
|
|
|
|
class TestAddOrUpdateListItem:
|
|
async def test_200_update(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Lists/id/100/items").mock(return_value=Response(200, json=MOCK_LIST_ITEM_RESPONSE))
|
|
from onspring_api_sdk.models import ListItemRequest
|
|
|
|
request = ListItemRequest(listId=100, name="Updated Item")
|
|
response = await async_client.add_or_update_list_item(request)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 200
|
|
assert response.message == "Existing list value successfully updated"
|
|
assert isinstance(response.data, AddOrUpdateListItemResponse)
|
|
|
|
async def test_201_create(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Lists/id/100/items").mock(return_value=Response(201, json=MOCK_LIST_ITEM_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import ListItemRequest
|
|
|
|
request = ListItemRequest(listId=100, name="New Item")
|
|
response = await async_client.add_or_update_list_item(request)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 201
|
|
assert response.message == "New list value successfully added"
|
|
assert isinstance(response.data, AddOrUpdateListItemResponse)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Lists/id/100/items").mock(return_value=Response(401))
|
|
|
|
from onspring_api_sdk.models import ListItemRequest
|
|
|
|
request = ListItemRequest(listId=100, name="Item")
|
|
|
|
_assert_error(await async_client.add_or_update_list_item(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Lists/id/100/items").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import ListItemRequest
|
|
|
|
request = ListItemRequest(listId=100, name="Item")
|
|
response = await async_client.add_or_update_list_item(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Lists/id/100/items").mock(return_value=Response(418))
|
|
|
|
from onspring_api_sdk.models import ListItemRequest
|
|
|
|
request = ListItemRequest(listId=100, name="Item")
|
|
|
|
_assert_error(await async_client.add_or_update_list_item(request), 418, None)
|
|
|
|
|
|
class TestDeleteListItem:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
item_id = "2c1af5b1-0f90-4378-b9a5-8b7e22f2bc84"
|
|
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Lists/id/100/itemId/{item_id}").mock(return_value=Response(204))
|
|
|
|
response = await async_client.delete_list_item(100, item_id)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 204
|
|
assert response.message == "Item deleted successfully"
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Lists/id/100/itemId/test").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.delete_list_item(100, "test"), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Lists/id/100/itemId/test").mock(
|
|
return_value=Response(403, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.delete_list_item(100, "test")
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Lists/id/100/itemId/test").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.delete_list_item(100, "test"), 404, "List/item could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Lists/id/100/itemId/test").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.delete_list_item(100, "test"), 418, None)
|
|
|
|
|
|
class TestGetRecordsByAppRequestDefaults:
|
|
async def test_default_page_values(self):
|
|
from onspring_api_sdk.models import GetRecordsByAppRequest
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
assert request.page_number == 1
|
|
assert request.page_size == 50
|
|
|
|
|
|
class TestGetRecordsByAppId:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100").mock(return_value=Response(200, json=MOCK_RECORDS_RESPONSE))
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
response = await async_client.get_records_by_app_id(request)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetRecordsResponse)
|
|
assert len(response.data.records) == 1
|
|
assert response.data.records[0].record_id == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100").mock(return_value=Response(400))
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
_assert_error(
|
|
await async_client.get_records_by_app_id(request),
|
|
400,
|
|
"Invalid paging information/size of the data requested was too large.",
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100").mock(return_value=Response(401))
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
_assert_error(await async_client.get_records_by_app_id(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
response = await async_client.get_records_by_app_id(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100").mock(return_value=Response(418))
|
|
|
|
request = GetRecordsByAppRequest(app_id=100)
|
|
|
|
_assert_error(await async_client.get_records_by_app_id(request), 418, None)
|
|
|
|
|
|
class TestGetRecordById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(200, json=MOCK_RECORD))
|
|
|
|
request = GetRecordByIdRequest(app_id=100, record_id=1)
|
|
|
|
response = await async_client.get_record_by_id(request)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, Record)
|
|
assert response.data.record_id == 1
|
|
assert len(response.data.fields) == 2
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(401))
|
|
|
|
request = GetRecordByIdRequest(app_id=100, record_id=1)
|
|
|
|
_assert_error(await async_client.get_record_by_id(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100/recordId/1").mock(
|
|
return_value=Response(403, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
request = GetRecordByIdRequest(app_id=100, record_id=1)
|
|
|
|
response = await async_client.get_record_by_id(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100/recordId/999").mock(return_value=Response(404))
|
|
|
|
request = GetRecordByIdRequest(app_id=100, record_id=999)
|
|
|
|
_assert_error(await async_client.get_record_by_id(request), 404, "Record could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(418))
|
|
|
|
request = GetRecordByIdRequest(app_id=100, record_id=1)
|
|
|
|
_assert_error(await async_client.get_record_by_id(request), 418, None)
|
|
|
|
|
|
class TestDeleteRecordById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(204))
|
|
|
|
response = await async_client.delete_record_by_id(100, 1)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 204
|
|
assert response.message == "Record deleted successfully"
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.delete_record_by_id(100, 1), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Records/appId/100/recordId/1").mock(
|
|
return_value=Response(403, json=MOCK_MESSAGE_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.delete_record_by_id(100, 1)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Records/appId/100/recordId/999").mock(return_value=Response(404))
|
|
|
|
_assert_error(await async_client.delete_record_by_id(100, 999), 404, "Record could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.delete(f"{TEST_URL}/Records/appId/100/recordId/1").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.delete_record_by_id(100, 1), 418, None)
|
|
|
|
|
|
class TestGetRecordsByIds:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-get").mock(
|
|
return_value=Response(200, json=MOCK_RECORDS_BATCH_RESPONSE)
|
|
)
|
|
|
|
from onspring_api_sdk.models import GetBatchRecordsRequest
|
|
|
|
request = GetBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
response = await async_client.get_records_by_ids(request)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetBatchRecordsResponse)
|
|
assert response.data.count == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-get").mock(return_value=Response(400))
|
|
|
|
from onspring_api_sdk.models import GetBatchRecordsRequest
|
|
|
|
request = GetBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(
|
|
await async_client.get_records_by_ids(request),
|
|
400,
|
|
"Batch request is invalid/size of the data requested was too large.",
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-get").mock(return_value=Response(401))
|
|
|
|
from onspring_api_sdk.models import GetBatchRecordsRequest
|
|
|
|
request = GetBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.get_records_by_ids(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-get").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import GetBatchRecordsRequest
|
|
|
|
request = GetBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
response = await async_client.get_records_by_ids(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-get").mock(return_value=Response(418))
|
|
|
|
from onspring_api_sdk.models import GetBatchRecordsRequest
|
|
|
|
request = GetBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.get_records_by_ids(request), 418, None)
|
|
|
|
|
|
class TestQueryRecordsRequestDefaults:
|
|
async def test_default_page_values(self):
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="test")
|
|
|
|
assert request.page_number == 1
|
|
assert request.page_size == 50
|
|
|
|
|
|
class TestQueryRecords:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(200, json=MOCK_RECORDS_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test")
|
|
|
|
response = await async_client.query_records(request)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetRecordsResponse)
|
|
assert len(response.data.records) == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(400))
|
|
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test")
|
|
|
|
_assert_error(
|
|
await async_client.query_records(request),
|
|
400,
|
|
"Query request is invalid/size of the data requested was too large.",
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(401))
|
|
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test")
|
|
|
|
_assert_error(await async_client.query_records(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test")
|
|
|
|
response = await async_client.query_records(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(418))
|
|
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test")
|
|
|
|
_assert_error(await async_client.query_records(request), 418, None)
|
|
|
|
async def test_request_body_excludes_page_params(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
route = respx.post(f"{TEST_URL}/Records/Query").mock(return_value=Response(200, json=MOCK_RECORDS_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import QueryRecordsRequest
|
|
|
|
request = QueryRecordsRequest(app_id=100, filter="Test", page_number=2, page_size=10)
|
|
await async_client.query_records(request)
|
|
|
|
body = route.calls[0].request.content
|
|
assert b"pageNumber" not in body
|
|
assert b"pageSize" not in body
|
|
|
|
|
|
class TestAddOrUpdateRecord:
|
|
def _make_record(self) -> Record:
|
|
from onspring_api_sdk.models import StringFieldValue
|
|
|
|
return Record(
|
|
appId=100,
|
|
fieldData=[StringFieldValue(fieldId=1, value="test")],
|
|
)
|
|
|
|
async def test_200_update(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(200, json=MOCK_SAVE_RECORD_RESPONSE))
|
|
|
|
response = await async_client.add_or_update_record(self._make_record())
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 200
|
|
assert response.message == "Record updated successfully"
|
|
assert isinstance(response.data, AddOrUpdateRecordResponse)
|
|
assert response.data.id == 1
|
|
|
|
async def test_201_create(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(201, json=MOCK_SAVE_RECORD_RESPONSE))
|
|
|
|
response = await async_client.add_or_update_record(self._make_record())
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 201
|
|
assert response.message == "Record created successfully"
|
|
assert isinstance(response.data, AddOrUpdateRecordResponse)
|
|
assert response.data.id == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(400))
|
|
|
|
_assert_error(await async_client.add_or_update_record(self._make_record()), 400, "Request data is invalid")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.add_or_update_record(self._make_record()), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
response = await async_client.add_or_update_record(self._make_record())
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.add_or_update_record(self._make_record()), 418, None)
|
|
|
|
async def test_with_guid_field(self, async_client: AsyncOnspringClient):
|
|
import uuid
|
|
|
|
from onspring_api_sdk.models import GuidFieldValue, Record
|
|
|
|
record = Record(
|
|
appId=100,
|
|
fieldData=[GuidFieldValue(fieldId=1, value=uuid.UUID("12345678-1234-5678-1234-567812345678"))],
|
|
)
|
|
|
|
with respx.mock:
|
|
respx.put(f"{TEST_URL}/Records").mock(return_value=Response(200, json=MOCK_SAVE_RECORD_RESPONSE))
|
|
response = await async_client.add_or_update_record(record)
|
|
|
|
assert response.is_successful
|
|
|
|
async def test_payload_excludes_field_data(self, async_client: AsyncOnspringClient):
|
|
from onspring_api_sdk.models import Record, StringFieldValue
|
|
|
|
record = Record(
|
|
appId=100,
|
|
fieldData=[StringFieldValue(fieldId=1, value="test")],
|
|
)
|
|
|
|
with respx.mock:
|
|
route = respx.put(f"{TEST_URL}/Records").mock(return_value=Response(200, json=MOCK_SAVE_RECORD_RESPONSE))
|
|
await async_client.add_or_update_record(record)
|
|
|
|
body = route.calls[0].request.content
|
|
assert b"fieldData" not in body
|
|
|
|
|
|
class TestDeleteRecordsByIds:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(204))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1, 2])
|
|
|
|
response = await async_client.delete_records_by_ids(request)
|
|
|
|
assert response.is_successful
|
|
assert response.status_code == 204
|
|
assert response.message == "Record(s) deleted successfully"
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(400))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.delete_records_by_ids(request), 400, "Invalid request provided")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(401))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.delete_records_by_ids(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
response = await async_client.delete_records_by_ids(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(404))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.delete_records_by_ids(request), 404, "Records could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.post(f"{TEST_URL}/Records/batch-delete").mock(return_value=Response(418))
|
|
|
|
from onspring_api_sdk.models import DeleteBatchRecordsRequest
|
|
|
|
request = DeleteBatchRecordsRequest(app_id=100, recordIds=[1])
|
|
|
|
_assert_error(await async_client.delete_records_by_ids(request), 418, None)
|
|
|
|
|
|
class TestGetReportById:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(200, json=MOCK_REPORT_RESPONSE))
|
|
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
response = await async_client.get_report_by_id(request)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetReportByIdResponse)
|
|
assert len(response.data.columns) == 2
|
|
assert len(response.data.rows) == 1
|
|
assert response.data.rows[0].record_id == 1
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(400))
|
|
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
_assert_error(await async_client.get_report_by_id(request), 400, "Invalid request based on underlying data")
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(401))
|
|
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
_assert_error(await async_client.get_report_by_id(request), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
response = await async_client.get_report_by_id(request)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_404(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/999").mock(return_value=Response(404))
|
|
|
|
request = GetReportByIdRequest(report_id=999)
|
|
|
|
_assert_error(await async_client.get_report_by_id(request), 404, "Report could not be found")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(418))
|
|
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
_assert_error(await async_client.get_report_by_id(request), 418, None)
|
|
|
|
async def test_params_excludes_report_id(self, async_client: AsyncOnspringClient):
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
with respx.mock:
|
|
route = respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(200, json=MOCK_REPORT_RESPONSE))
|
|
await async_client.get_report_by_id(request)
|
|
|
|
assert "reportId" not in route.calls[0].request.url.params
|
|
|
|
async def test_403_empty_body(self, async_client: AsyncOnspringClient):
|
|
request = GetReportByIdRequest(report_id=53)
|
|
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/id/53").mock(return_value=Response(403, content=b""))
|
|
response = await async_client.get_report_by_id(request)
|
|
|
|
assert response.status_code == 403
|
|
assert response.is_successful is False
|
|
|
|
|
|
class TestGetReportsByAppId:
|
|
async def test_success(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(
|
|
return_value=Response(200, json=MOCK_REPORTS_BY_APP_RESPONSE)
|
|
)
|
|
|
|
response = await async_client.get_reports_by_app_id(10)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetReportsByAppIdResponse)
|
|
assert len(response.data.reports) == 1
|
|
assert response.data.reports[0].id == 53
|
|
|
|
async def test_400(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(return_value=Response(400))
|
|
|
|
_assert_error(
|
|
await async_client.get_reports_by_app_id(10), 400, "Client does not have read access to the app."
|
|
)
|
|
|
|
async def test_401(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(return_value=Response(401))
|
|
|
|
_assert_error(await async_client.get_reports_by_app_id(10), 401, "Unauthorized request")
|
|
|
|
async def test_403_with_message(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(return_value=Response(403, json=MOCK_MESSAGE_RESPONSE))
|
|
|
|
response = await async_client.get_reports_by_app_id(10)
|
|
|
|
_assert_error(response, 403, "An error occurred")
|
|
|
|
async def test_fallthrough(self, async_client: AsyncOnspringClient):
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(return_value=Response(418))
|
|
|
|
_assert_error(await async_client.get_reports_by_app_id(10), 418, None)
|
|
|
|
async def test_with_explicit_paging(self, async_client: AsyncOnspringClient):
|
|
from onspring_api_sdk.models import PagingRequest
|
|
|
|
with respx.mock:
|
|
respx.get(f"{TEST_URL}/Reports/appId/10").mock(
|
|
return_value=Response(200, json=MOCK_REPORTS_BY_APP_RESPONSE)
|
|
)
|
|
|
|
paging = PagingRequest(page_number=2, page_size=10)
|
|
response = await async_client.get_reports_by_app_id(10, paging_request=paging)
|
|
|
|
assert response.is_successful
|
|
assert isinstance(response.data, GetReportsByAppIdResponse)
|
|
|
|
|
|
class TestRaiseForStatus:
|
|
async def test_401_raises_authentication_error(self):
|
|
response = ApiResponse(status_code=401, message="Unauthorized")
|
|
|
|
with pytest.raises(OnspringAuthenticationError, match="Unauthorized"):
|
|
response.raise_for_status()
|
|
|
|
async def test_403_raises_authentication_error(self):
|
|
response = ApiResponse(status_code=403, message="Forbidden")
|
|
|
|
with pytest.raises(OnspringAuthenticationError, match="Forbidden"):
|
|
response.raise_for_status()
|
|
|
|
async def test_404_raises_not_found_error(self):
|
|
response = ApiResponse(status_code=404, message="Not Found")
|
|
|
|
with pytest.raises(OnspringNotFoundError, match="Not Found"):
|
|
response.raise_for_status()
|
|
|
|
async def test_429_raises_rate_limit_error(self):
|
|
response = ApiResponse(status_code=429, message="Rate limited")
|
|
|
|
with pytest.raises(OnspringRateLimitError, match="Rate limited"):
|
|
response.raise_for_status()
|
|
|
|
async def test_418_raises_generic_error(self):
|
|
response = ApiResponse(status_code=418, message="Teapot")
|
|
|
|
with pytest.raises(OnspringError, match="Teapot"):
|
|
response.raise_for_status()
|
|
|
|
async def test_success_does_not_raise(self):
|
|
response = ApiResponse(status_code=200, data="ok")
|
|
response.raise_for_status()
|