diff --git a/.github/workflows/test.yaml b/.github/workflows/ci.yaml similarity index 98% rename from .github/workflows/test.yaml rename to .github/workflows/ci.yaml index 89b8984..51d282e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/ci.yaml @@ -1,4 +1,4 @@ -name: Run tests +name: CI on: pull_request: @@ -9,6 +9,7 @@ on: - review_requested branches: - main + - develop jobs: test: diff --git a/backend/requirements-test.txt b/backend/requirements-test.txt index 14808f7..8a80d46 100644 --- a/backend/requirements-test.txt +++ b/backend/requirements-test.txt @@ -1,4 +1,4 @@ -pytest==7.3.1 -pytest-django==4.5.2 -factory-boy==3.2.1 -pytest-bdd==7.0.1 +pytest==8.3.4 +pytest-django==4.10.0 +factory-boy==3.3.3 +pytest-bdd==8.1.0 diff --git a/backend/requirements.txt b/backend/requirements.txt index e455e0a..a451376 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,20 +1,20 @@ # Django -Django==4.2.5 -djangorestframework==3.14.0 -drf-nested-routers==0.93.4 -drf-spectacular==0.26.2 -psycopg2-binary==2.9.9 -django-unfold==0.16.0 # Django Admin Theme +Django==4.2.19 +djangorestframework==3.15.2 +drf-nested-routers==0.94.1 +drf-spectacular==0.28.0 +psycopg2-binary==2.9.10 +django-unfold==0.49.1 # Django Admin Theme # celery -celery==5.2.7 -redis==4.5.4 +celery==5.4.0 +redis==5.2.1 # excel -pandas==2.0.1 -openpyxl==3.1.2 +pandas==2.2.3 +openpyxl==3.1.5 -python-dotenv==1.0.0 +python-dotenv==1.0.1 # Formatter -black==23.3.0 +black==25.1.0 diff --git a/backend/tests/apis/v1/forms/conftest.py b/backend/tests/apis/v1/forms/conftest.py index b7cdac1..cec867f 100644 --- a/backend/tests/apis/v1/forms/conftest.py +++ b/backend/tests/apis/v1/forms/conftest.py @@ -5,8 +5,8 @@ from django.utils import timezone from apps.forms.models import Component -from tests.apis.factories import ComponentFactory -from tests.apis.factories import FormFactory +from tests.factories import ComponentFactory +from tests.factories import FormFactory @pytest.fixture diff --git a/backend/tests/apis/v1/forms/form_create.feature b/backend/tests/apis/v1/forms/form_create.feature index 5ba1341..65ff5c9 100644 --- a/backend/tests/apis/v1/forms/form_create.feature +++ b/backend/tests/apis/v1/forms/form_create.feature @@ -1,7 +1,9 @@ @django_db Feature: Form Create Test - Background: - Given The data to be sent is as follows. + Scenario Outline: Form Create Permission Test + Given I am a user. + And I am logged in. + And The following data will be sent: """ { "slug": "test", @@ -10,11 +12,7 @@ Feature: Form Create Test "end_date": "2023-12-31" } """ - - Scenario Outline: Form Create Permission Test - Given I am a/an user. - And I am logging in. - When I am making a request to the server with data using the POST and /v1/forms/. + When I am sending a POST request to /v1/forms/ with data. Then The response status code is . Examples: | user_type | status_code | @@ -23,9 +21,18 @@ Feature: Form Create Test | staff | 201 | Scenario: Form Create Test - Given I am a/an staff user. - And I am logging in. - When I am making a request to the server with data using the POST and /v1/forms/. + Given I am a staff user. + And I am logged in. + And The following data will be sent: + """ + { + "slug": "test", + "title": "test", + "start_date": "2023-12-01", + "end_date": "2023-12-31" + } + """ + When I am sending a POST request to /v1/forms/ with data. Then The response status code is 201. And The slug data in the response JSON is the same as test. And The title data in the response JSON is of type str and the same as test. diff --git a/backend/tests/apis/v1/forms/form_delete.feature b/backend/tests/apis/v1/forms/form_delete.feature index a49c586..a363095 100644 --- a/backend/tests/apis/v1/forms/form_delete.feature +++ b/backend/tests/apis/v1/forms/form_delete.feature @@ -1,10 +1,10 @@ @django_db Feature: Form Delete Test Background: - Given I will save the following data using backend.tests.apis.factories's FormFactory. + Given I will save the following data using Form model: """ { - "id": 1, + "id": 101, "slug": "test", "title": "test", "start_date": "2023-12-01", @@ -13,9 +13,9 @@ Feature: Form Delete Test """ Scenario Outline: Form Delete Permission Test - Given I am a/an user. - And I am logging in. - When I am making a request to the server using the DELETE and /v1/forms/test/. + Given I am a user. + And I am logged in. + When I am making a DELETE request to /v1/forms/test/. Then The response status code is . Examples: | user_type | status_code | @@ -24,8 +24,8 @@ Feature: Form Delete Test | staff | 204 | Scenario: Form Delete Test - Given I am a/an staff user. - And I am logging in. - When I am making a request to the server using the DELETE and /v1/forms/test/. + Given I am a staff user. + And I am logged in. + When I am making a DELETE request to /v1/forms/test/. Then The response status code is 204. - And The existence of data with an ID of 1 in the Form model from apps.forms.models is False. \ No newline at end of file + And It is False that a record with an ID of 101 exists in the Form model from apps.forms.models. \ No newline at end of file diff --git a/backend/tests/apis/v1/forms/form_list.feature b/backend/tests/apis/v1/forms/form_list.feature index 8269d3a..894415d 100644 --- a/backend/tests/apis/v1/forms/form_list.feature +++ b/backend/tests/apis/v1/forms/form_list.feature @@ -1,10 +1,10 @@ @django_db Feature: Form List Test Background: - Given I will save the following data using backend.tests.apis.factories's FormFactory. + Given I will save the following data using Form model: """ { - "id": 1, + "id": 101, "slug": "test", "title": "test", "start_date": "2023-12-01", @@ -13,9 +13,9 @@ Feature: Form List Test """ Scenario Outline: Form List Permission Test - Given I am a/an user. - And I am logging in. - When I am making a request to the server using the GET and /v1/forms/. + Given I am a user. + And I am logged in. + When I am making a GET request to /v1/forms/. Then The response status code is . Examples: | user_type | status_code | @@ -24,10 +24,10 @@ Feature: Form List Test | staff | 200 | Scenario: Form List Test - Given I am a/an general user. - And I am logging in. - When I am making a request to the server using the GET and /v1/forms/. + Given I am a general user. + And I am logged in. + When I am making a GET request to /v1/forms/. Then The response status code is 200. - And The number of result in the response JSON is 1. - And The slug data in the 1st/nd/rd/th entry of the response JSON list is the same as test. - And The title data in the 1st/nd/rd/th entry of the response JSON list is of type str and the same as test. + And The number of results in the response JSON is 1. + And The slug data in the 1th entry of the response JSON list is the same as test. + And The title data in the 1th entry of the response JSON list is of type str and the same as test. diff --git a/backend/tests/apis/v1/forms/form_partial_update.feature b/backend/tests/apis/v1/forms/form_partial_update.feature new file mode 100644 index 0000000..3ef1196 --- /dev/null +++ b/backend/tests/apis/v1/forms/form_partial_update.feature @@ -0,0 +1,47 @@ +@django_db +Feature: Form Partial Update Test + Background: + Given I will save the following data using Form model: + """ + { + "id": 101, + "slug": "test", + "title": "test", + "start_date": "2023-12-01", + "end_date": "2023-12-31" + } + """ + + Scenario Outline: Form Partial Update Permission Test + Given I am a user. + And I am logged in. + And The following data will be sent: + """ + { + "title": "test1" + } + """ + When I am sending a PATCH request to /v1/forms/test/ with data. + Then The response status code is . + Examples: + | user_type | status_code | + | anonymous | 403 | + | general | 403 | + | staff | 200 | + + + Scenario: Form Partial Update Test + Given I am a staff user. + And I am logged in. + And The following data will be sent: + """ + { + "title": "test2" + } + """ + When I am sending a PATCH request to /v1/forms/test/ with data. + Then The response status code is 200. + And The id data in the response JSON is the same as 101. + And The title data in the response JSON is the same as test2. + And It is True that a record with an ID of 101 exists in the Form model from apps.forms.models. + And The title field of the Form model from apps.forms.models with an ID of 101 is of type str and equals test2. diff --git a/backend/tests/apis/v1/forms/form_test.py b/backend/tests/apis/v1/forms/form_test.py index 4691baa..2a2218f 100644 --- a/backend/tests/apis/v1/forms/form_test.py +++ b/backend/tests/apis/v1/forms/form_test.py @@ -21,12 +21,12 @@ def test_form_create_test(): pass -@scenario("form_update.feature", "Form Partial Update Permission Test") +@scenario("form_partial_update.feature", "Form Partial Update Permission Test") def test_form_partial_update_permission_test(): pass -@scenario("form_update.feature", "Form Partial Update Test") +@scenario("form_partial_update.feature", "Form Partial Update Test") def test_form_partial_update_test(): pass diff --git a/backend/tests/apis/v1/forms/form_update.feature b/backend/tests/apis/v1/forms/form_update.feature deleted file mode 100644 index 279a467..0000000 --- a/backend/tests/apis/v1/forms/form_update.feature +++ /dev/null @@ -1,41 +0,0 @@ -@django_db -Feature: Form Update Test - Background: - Given I will save the following data using backend.tests.apis.factories's FormFactory. - """ - { - "id": 1, - "slug": "test", - "title": "test", - "start_date": "2023-12-01", - "end_date": "2023-12-31" - } - """ - And The data to be sent is as follows. - """ - { - "title": "test1" - } - """ - - Scenario Outline: Form Partial Update Permission Test - Given I am a/an user. - And I am logging in. - When I am making a request to the server with data using the PATCH and /v1/forms/test/. - Then The response status code is . - Examples: - | user_type | status_code | - | anonymous | 403 | - | general | 403 | - | staff | 200 | - - - Scenario: Form Partial Update Test - Given I am a/an staff user. - And I am logging in. - When I am making a request to the server with data using the PATCH and /v1/forms/test/. - Then The response status code is 200. - And The id data in the response JSON is the same as 1. - And The title data in the response JSON is the same as test1. - And The existence of data with an ID of 1 in the Form model from apps.forms.models is True. - And The title data of the Form model from apps.forms.models with an ID of 1 is of type str and the same as test1. diff --git a/backend/tests/apis/v1/forms/test_serializers.py b/backend/tests/apis/v1/forms/test_serializers.py index 5de12a7..2c8572d 100644 --- a/backend/tests/apis/v1/forms/test_serializers.py +++ b/backend/tests/apis/v1/forms/test_serializers.py @@ -3,8 +3,8 @@ from apis.v1.forms.serializers import SubmitSerializer, FormSerializer from apps.forms.models import Choice from apps.forms.models import Component -from tests.apis.factories import ChoiceFactory -from tests.apis.factories import ComponentFactory +from tests.factories import ChoiceFactory +from tests.factories import ComponentFactory class TestFormSerializer: diff --git a/backend/tests/apis/v1/forms/test_views.py b/backend/tests/apis/v1/forms/test_views.py index 72a66dd..9c95265 100644 --- a/backend/tests/apis/v1/forms/test_views.py +++ b/backend/tests/apis/v1/forms/test_views.py @@ -3,7 +3,7 @@ from rest_framework.reverse import reverse from apps.forms.models import Component, Choice -from tests.apis.factories import ComponentFactory, ChoiceFactory +from tests.factories import ComponentFactory, ChoiceFactory @pytest.mark.urls(urls="apis.v1.urls") diff --git a/backend/tests/apps/forms/test_tasks.py b/backend/tests/apps/forms/test_tasks.py index 249c4ea..e0697b3 100644 --- a/backend/tests/apps/forms/test_tasks.py +++ b/backend/tests/apps/forms/test_tasks.py @@ -8,9 +8,9 @@ from apps.forms.models import Component from apps.forms.tasks import get_dataframe -from tests.apis.factories import ComponentFactory -from tests.apis.factories import FormFactory -from tests.apis.factories import SubmitFactory, AnswerFactory, ChoiceFactory, UserFactory +from tests.factories import ComponentFactory +from tests.factories import FormFactory +from tests.factories import SubmitFactory, AnswerFactory, ChoiceFactory, UserFactory @pytest.mark.django_db diff --git a/backend/tests/apis/conftest.py b/backend/tests/conftest.py similarity index 62% rename from backend/tests/apis/conftest.py rename to backend/tests/conftest.py index 1146619..d83777d 100644 --- a/backend/tests/apis/conftest.py +++ b/backend/tests/conftest.py @@ -7,7 +7,7 @@ from pytest_bdd import given, parsers, when, then from rest_framework.test import APIClient -from tests.apis.factories import UserFactory +from tests.factories import UserFactory @pytest.fixture @@ -36,7 +36,7 @@ def client_staff(): } -@given(parsers.parse("I am a/an {user_type} user."), target_fixture="user") +@given(parsers.parse("I am a {user_type} user."), target_fixture="user") def i_am_a_user_type_user(user_type): if user_type == "anonymous": return None @@ -47,16 +47,16 @@ def i_am_a_user_type_user(user_type): return UserFactory(is_staff=False, is_superuser=False) -@given(parsers.parse("I am logging in."), target_fixture="client") -def i_am_logging_in(user): +@given(parsers.parse("I am logged in."), target_fixture="client") +def i_am_logged_in(user): client: APIClient = APIClient() if user: client.force_authenticate(user=user) return client -@when(parsers.parse("I am making a request to the server using the {method} and {path}."), target_fixture="response") -def i_am_making_a_request_to_the_server_using_the_method_and_path(client, method, path): +@when(parsers.parse("I am making a {method} request to {path}."), target_fixture="response") +def i_am_making_a_method_request_to_path(client, method, path): response = None if method in ["GET", "get"]: response = client.get(path=path) @@ -71,19 +71,16 @@ def i_am_making_a_request_to_the_server_using_the_method_and_path(client, method return response -@given( - parsers.parse("The data to be sent is as follows.\n{payload}"), - target_fixture="data", -) -def the_data_to_be_sent_is_as_follows(payload): - return json.loads(payload) +@given(parsers.parse("The following data will be sent:"), target_fixture="data") +def the_following_data_will_be_sent(docstring): + return json.loads(docstring) @when( - parsers.parse("I am making a request to the server with data using the {method} and {path}."), + parsers.parse("I am sending a {method} request to {path} with data."), target_fixture="response", ) -def i_am_making_a_request_to_the_server_with_data_using_the_method_and_path(client, method, path, data): +def i_am_sending_a_method_request_to_path_with_data(client, method, path, data): response = None if method in ["GET", "get"]: response = client.get(path=path, data=data) @@ -99,12 +96,13 @@ def i_am_making_a_request_to_the_server_with_data_using_the_method_and_path(clie @given( - parsers.parse("I will save the following data using {module}'s {factory_class_name}.\n{data}"), + parsers.cfparse("I will save the following data using {model_class_name} model:"), ) -def i_will_save_the_following_data_using_modules_factory_class_name(module: str, factory_class_name: str, data): - module = importlib.import_module(module) +def i_will_save_the_following_data_using_factory_class_name_from_module(model_class_name: str, docstring): + module = importlib.import_module("backend.tests.factories") + factory_class_name = f"{model_class_name}Factory" factory_class = getattr(module, factory_class_name) - factory_class(**json.loads(data)) + factory_class(**json.loads(docstring)) return None @@ -113,8 +111,8 @@ def the_response_status_code_is_status_code(response, status_code): assert response.status_code == status_code -@then(parsers.parse("The number of result in the response JSON is {number:d}.")) -def the_number_of_result_in_the_response_json_is_number(response, number): +@then(parsers.parse("The number of results in the response JSON is {number:d}.")) +def the_number_of_results_in_the_response_json_is_number(response, number): assert len(response.json()) == number @@ -132,30 +130,24 @@ def the_field_data_in_the_response_json_is_of_type_data_type_and_is_the_same_as_ assert str(data) == value -@then( - parsers.parse( - "The {field} data in the {index:d}st/nd/rd/th entry of the response JSON list is the same as {value}." - ) -) -def the_field_data_in_the_indexstndrdth_entry_of_the_response_json_list_is_the_same_as_value( - response, field, index, value -): +@then(parsers.parse("The {field} data in the {index:d}th entry of the response JSON list is the same as {value}.")) +def the_field_data_in_the_index_th_entry_of_the_response_json_list_is_the_same_as_value(response, field, index, value): assert str(response.json()[int(index) - 1][field]) == value @then( parsers.parse( - "The {field} data in the {index:d}st/nd/rd/th entry of the response JSON list is of type {data_type} and the same as {value}." + "The {field} data in the {index:d}th entry of the response JSON list is of type {data_type} and the same as {value}." ) ) -def the_field_data_in_the_indexstndrdth_entry_of_the_response_json_results_list_is_of_type_data_type_and_the_same_as_value( +def the_field_data_in_the_index_th_entry_of_the_response_json_results_list_is_of_type_data_type_and_the_same_as_value( response, field, index, value ): assert str(response.json()[int(index) - 1][field]) == value -@then(parsers.parse("The existence of data with an ID of {pk:d} in the {model} model from {module} is {existance}.")) -def the_existence_of_data_with_an_id_of_pk_in_the_model_model_from_module_is_existance(pk, model, module, existance): +@then(parsers.parse("It is {existance} that a record with an ID of {pk:d} exists in the {model} model from {module}.")) +def it_is_existance_that_a_record_with_an_id_of_pk_exists_in_the_model_model_from_module(pk, model, module, existance): module = importlib.import_module(module) model_class = getattr(module, model) obj = model_class.objects.filter(pk=pk).last() @@ -164,10 +156,10 @@ def the_existence_of_data_with_an_id_of_pk_in_the_model_model_from_module_is_exi @then( parsers.parse( - "The {field} data of the {model} model from {module} with an ID of {pk:d} is of type {data_type} and the same as {value}." + "The {field} field of the {model} model from {module} with an ID of {pk:d} is of type {data_type} and equals {value}." ) ) -def the_field_data_of_the_model_model_from_module_with_an_id_of_pk_is_of_type_data_type_and_the_same_as_value( +def the_field_field_of_the_model_model_from_module_with_an_id_of_pk_is_of_type_data_type_and_equals_value( field, model, module, pk, data_type, value ): module = importlib.import_module(module) diff --git a/backend/tests/apis/factories.py b/backend/tests/factories.py similarity index 100% rename from backend/tests/apis/factories.py rename to backend/tests/factories.py