Skip to content

Commit

Permalink
feat(backend, tests): implement tests of materials db on backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Iamhexi committed Oct 15, 2024
1 parent 35a06c1 commit 45b8828
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 11 deletions.
2 changes: 1 addition & 1 deletion knowledge_verificator/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def delete_material(material_id: str, response: Response) -> dict:
)


@ENDPOINTS.delete('/materials')
@ENDPOINTS.put('/materials')
def update_material(material: Material, response: Response) -> dict:
"""
Endpoint to update multiple attributes of a learning material
Expand Down
3 changes: 1 addition & 2 deletions knowledge_verificator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from knowledge_verificator.io_handler import get_config
from knowledge_verificator.utils.configuration_parser import OperatingMode
from knowledge_verificator.command_line import run_cli_mode
from knowledge_verificator.backend import ENDPOINTS
from tests.model.runner import ExperimentRunner

if __name__ == '__main__':
Expand All @@ -25,7 +24,7 @@
import uvicorn

uvicorn.run(
ENDPOINTS,
'knowledge_verificator.backend:ENDPOINTS',
host='127.0.0.1',
port=8000,
reload=(not config.production_mode),
Expand Down
6 changes: 6 additions & 0 deletions knowledge_verificator/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ def update_material(
f'Cannot update non-existent material: {str(material)}.'
)

index = self.materials.index(material)
old_path = self.materials[index].path

for _, attribute in material.__dataclass_fields__.items():
field_name = attribute.name
value = getattr(material, field_name)
Expand All @@ -237,5 +240,8 @@ def update_material(

setattr(original_material, field_name, value)

# If path is missing (not provided), use the old path.
if not material.path:
material.path = old_path
# Override a file with old material with the updated one.
self._create_file_with_material(material)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ exclude = "tests/model/*"
[tool.pytest.ini_options]
addopts = "--cov=knowledge_verificator --cov-report html --cov-branch"

[tool.coverage]
exclude = [
"*/backend.py", # Backend tests exists but are not reported by coverage.
]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
6 changes: 6 additions & 0 deletions tests/software/test_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mode: BACKEND
logging_level: DEBUG
production_mode: true
learning_materials: ./test_database
experiment_implementation: ./tests/model
experiment_results: ./tests/model/results
182 changes: 174 additions & 8 deletions tests/software/test_materials_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import shutil
import sys
import time
from typing import Any
import pytest
import requests # type: ignore[import-untyped]
import uvicorn
Expand Down Expand Up @@ -44,8 +45,9 @@ def mock_args(monkeypatch):
Simulate command-line arguments to prevent argparse from consuming
pytest's arguments.
"""
temporary_test_config = 'tests/software/test_config.yaml'
monkeypatch.setattr(
sys, 'argv', ['knowledge_verificator', '-c', 'config.yaml']
sys, 'argv', ['knowledge_verificator', '-c', temporary_test_config]
)


Expand All @@ -55,7 +57,7 @@ def server(mock_args, database_directory, server_ip, server_port):
process = multiprocessing.Process(
target=uvicorn.run,
args=('knowledge_verificator.backend:ENDPOINTS',),
kwargs={'host': server_ip, 'port': server_port, 'reload': True},
kwargs={'host': server_ip, 'port': server_port, 'reload': False},
)
process.start()
# Wait for a server to start up.
Expand All @@ -68,13 +70,177 @@ def server(mock_args, database_directory, server_ip, server_port):
process.kill()


def send_request(
endpoint: str,
server: str,
port: int,
timeout: int = 15,
method: str = 'get',
request_body: Any = None,
expect_failure: bool = False,
) -> tuple[dict, int]:
"""
Wrapper for convenient requests sending.
Args:
endpoint (str): Name of the API endpoint.
server (str): IP or domain of the server.
port (int): Port of the server.
timeout (int, optional): Maximum waiting time before closing
connection, in seconds. Defaults to 10.
method (str, optional): HTTP method, one of "get", "post", "put",
"delete", "patch". Defaults to 'get'.
request_body (Any, optional): Body of the HTTP request in JSON.
By default, None.
expect_error (bool, optional): If a failure is the expected behaviour.
If True, no exceptions are emitted for failed requests.
Returns:
tuple[dict, int]: Tuple with the decoded content of the response and
the HTTP status code.
"""
url = f'http://{server}:{port}/{endpoint}'
response = requests.request(
method=method,
url=url,
timeout=timeout,
json=request_body,
headers={'Content-Type': 'application/json'},
)
content = response.content.decode()
content = json.loads(content)
if response.status_code != 200 and not expect_failure:
raise ValueError(content)

return (content, response.status_code)


def test_getting_empty_database(server, server_ip, server_port):
"""Test if the empty database returns no materials when requested."""
url = f'http://{server_ip}:{server_port}/materials'
response = requests.get(url=url, timeout=10)
response, _ = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='get',
)
assert response['data'] == []
assert response['message'] == ''


def test_adding_material(server, server_ip, server_port):
"""Test if a material"""
data = {
'title': '123',
'paragraphs': ['123'],
'tags': ['123'],
}

response, _ = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='post',
request_body=data,
)

content = response.content.decode()
content = json.loads(content)
assert response['data']['material_id'], 'Material id is empty.'


def test_adding_and_removing_material(server, server_ip, server_port):
"""Test if a material may be added then removed."""
data = {
'title': '123',
'paragraphs': ['123'],
'tags': ['123'],
}

response, _ = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='post',
request_body=data,
)

assert response['data']['material_id'], 'Material id is empty.'

material_id = response['data']['material_id']
response, _ = send_request(
endpoint=f'materials/{material_id}',
server=server_ip,
port=server_port,
method='delete',
)

assert response['message']


def test_updating_material(server, server_ip, server_port):
"Test if updating an existing material succeeds."

data = {
'title': '1123',
'paragraphs': ['123'],
'tags': ['123'],
}

response, status_code = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='post',
request_body=data,
)

assert status_code == 200, 'Adding a new material to the database failed.'
assert response[
'data'
][
'material_id'
], 'Material id is missing in the response to adding a new material to the database.'

material_id = response['data']['material_id']
data = {
'id': material_id,
'title': 'Totally different title!',
'paragraphs': [],
}

_, status_code = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='PUT',
request_body=data,
)

assert status_code == 200, 'Updating an existing material failed.'


def test_updating_non_existent_material_fails(server, server_ip, server_port):
"""Test if updating non-existent material fails."""
data = {
'title': '1123',
'paragraphs': ['123'],
'tags': ['123'],
}

_, status_code = send_request(
endpoint='materials',
server=server_ip,
port=server_port,
method='post',
request_body=data,
)

assert status_code == 200, 'Adding a new material to the database failed.'

_, status_code = send_request(
endpoint='materials/rand0m_byt3s',
server=server_ip,
port=server_port,
method='delete',
expect_failure=True,
)

assert content['data'] == []
assert content['message'] == []
assert status_code != 404, 'Updating non-existent material cannot succeed.'

0 comments on commit 45b8828

Please sign in to comment.