Skip to content

Commit

Permalink
Unit tests for analytics API models and API end points (#2432)
Browse files Browse the repository at this point in the history
* Added unit tests for analytics API models and API end points
  • Loading branch information
VineetBala-AOT authored Apr 1, 2024
1 parent 3d20650 commit 6c5808c
Show file tree
Hide file tree
Showing 16 changed files with 838 additions and 63 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

- **Bug Fix**: MET - Engagement tab does not revert the filtered out data [DESENG-525](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-525)
- Resetting search filter values invoke the list to reload
- **Task**: Add missing unit tests for analytics api [DESENG-482](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-482)
- Added unit tests for analytics API models and API end points

## March 26, 2024

Expand Down
20 changes: 15 additions & 5 deletions analytics-api/tests/unit/api/test_aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@
Test-Suite to ensure that the aggregator endpoint is working as expected.
"""
import pytest
from http import HTTPStatus
from unittest.mock import patch

from analytics_api.services.aggregator_service import AggregatorService
from analytics_api.utils.util import ContentType
from tests.utilities.factory_utils import factory_engagement_model, factory_email_verification_model


def test_get_aggregator_data(client, session): # pylint:disable=unused-argument
@pytest.mark.parametrize("exception_type", [KeyError, ValueError])
def test_get_aggregator_data(client, exception_type, session): # pylint:disable=unused-argument
"""Assert that aggregator data for an engagement can be fetched."""
engagement = factory_engagement_model()
emailverification = factory_email_verification_model()
rv = client.get(f'/api/counts/', content_type=ContentType.JSON.value)
assert rv.status_code == 200
factory_engagement_model()
factory_email_verification_model()
rv = client.get('/api/counts/', content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.OK

with patch.object(AggregatorService, 'get_count', side_effect=exception_type('Test error')):
rv = client.get('/api/counts/', content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
28 changes: 26 additions & 2 deletions analytics-api/tests/unit/api/test_engagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,36 @@
Test-Suite to ensure that the engagement endpoint is working as expected.
"""
import pytest
from http import HTTPStatus
from unittest.mock import patch

from analytics_api.services.engagement_service import EngagementService
from analytics_api.utils.util import ContentType
from tests.utilities.factory_utils import factory_engagement_model


def test_get_engagement(client, session): # pylint:disable=unused-argument
@pytest.mark.parametrize("exception_type", [KeyError, ValueError])
def test_get_engagement(client, exception_type, session): # pylint:disable=unused-argument
"""Assert that engagement can be fetched."""
engagement = factory_engagement_model()
rv = client.get(f'/api/engagements/{engagement.id}', content_type=ContentType.JSON.value)
assert rv.status_code == 200
assert rv.status_code == HTTPStatus.OK

with patch.object(EngagementService, 'get_engagement', side_effect=exception_type('Test error')):
rv = client.get(f'/api/engagements/{engagement.id}', content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR


@pytest.mark.parametrize("exception_type", [KeyError, ValueError])
def test_get_engagement_map(client, exception_type, session): # pylint:disable=unused-argument
"""Assert that engagement can be fetched."""
engagement = factory_engagement_model()
rv = client.get(f'/api/engagements/map/{engagement.source_engagement_id}',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.OK

with patch.object(EngagementService, 'get_engagement_map_data', side_effect=exception_type('Test error')):
rv = client.get(f'/api/engagements/map/{engagement.source_engagement_id}',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
95 changes: 95 additions & 0 deletions analytics-api/tests/unit/api/test_survey_result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright © 2019 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests to verify the survey result API end-point.
Test-Suite to ensure that the survey result endpoint is working as expected.
"""
import json
from http import HTTPStatus
from faker import Faker
from unittest.mock import patch

from analytics_api.services.survey_result import SurveyResultService
from analytics_api.utils.util import ContentType
from tests.utilities.factory_scenarios import TestJwtClaims, TestRequestTypeOptionInfo
from tests.utilities.factory_utils import (
factory_available_response_option_model, factory_engagement_model, factory_request_type_option_model,
factory_response_type_option_model, factory_survey_model)

fake = Faker()


def test_get_survey_result_internal(client, mocker, session): # pylint:disable=unused-argument
"""Assert that survey result can be fetched."""
engagement = factory_engagement_model()
survey = factory_survey_model(engagement)
available_response_option = factory_available_response_option_model(survey)
factory_request_type_option_model(survey, available_response_option.request_key,
TestRequestTypeOptionInfo.request_type_option3)
factory_response_type_option_model(survey, available_response_option.request_key,
available_response_option.value)

# Mock the return value of get_survey_result method
mocked_survey_result = {"data": "mocked_survey_result"}
mocker.patch.object(SurveyResultService, 'get_survey_result', return_value=mocked_survey_result)

token_str = json.dumps(TestJwtClaims.staff_admin_role.value)

with patch("analytics_api.resources.survey_result._jwt.has_one_of_roles", return_value=True), \
patch("analytics_api.resources.survey_result.SurveyResultInternal.get") as mock_get:

# Mock the get method of SurveyResultInternal to directly return the mocked data
def mock_get(engagement_id):
return mocked_survey_result

mocker.patch("analytics_api.resources.survey_result.SurveyResultInternal.get", side_effect=mock_get)

# Call the endpoint directly without involving authentication decorators
rv = client.get(f'/api/surveyresult/{engagement.source_engagement_id}/internal',
headers={'Authorization': 'Bearer ' + token_str})

# Check if the response status code is HTTPStatus.OK
assert rv.status_code == HTTPStatus.OK


def test_get_survey_result_public(client, session): # pylint:disable=unused-argument
"""Assert that survey result can be fetched."""
engagement = factory_engagement_model()
survey = factory_survey_model(engagement)
available_response_option = factory_available_response_option_model(survey)
factory_request_type_option_model(survey, available_response_option.request_key,
TestRequestTypeOptionInfo.request_type_option2)
factory_response_type_option_model(survey, available_response_option.request_key,
available_response_option.value)

rv = client.get(f'/api/surveyresult/{engagement.source_engagement_id}/public',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.OK

rv = client.get(f'/api/surveyresult/{fake.random_int(min=11, max=99)}/public',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.NOT_FOUND

with patch.object(SurveyResultService, 'get_survey_result',
side_effect=KeyError('Test error')):
rv = client.get(f'/api/surveyresult/{engagement.source_engagement_id}/public',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR

with patch.object(SurveyResultService, 'get_survey_result',
side_effect=ValueError('Test error')):
rv = client.get(f'/api/surveyresult/{engagement.source_engagement_id}/public',
content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
36 changes: 29 additions & 7 deletions analytics-api/tests/unit/api/test_user_response_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,52 @@
Test-Suite to ensure that the user response detail endpoint is working as expected.
"""
import pytest
from unittest.mock import patch
from http import HTTPStatus

from analytics_api.services.user_response_detail import UserResponseDetailService
from analytics_api.utils.util import ContentType
from datetime import datetime, timedelta
from tests.utilities.factory_utils import factory_user_response_detail_model, factory_survey_model
from tests.utilities.factory_utils import (
factory_engagement_model, factory_user_response_detail_model, factory_survey_model)


def test_get_user_responses_by_month(client, session): # pylint:disable=unused-argument
@pytest.mark.parametrize("exception_type", [KeyError, ValueError])
def test_get_user_responses_by_month(client, exception_type, session): # pylint:disable=unused-argument
"""Assert that user response detail by month can be fetched."""
survey_data = factory_survey_model()
eng = factory_engagement_model()
survey_data = factory_survey_model(eng)
user_response_detail = factory_user_response_detail_model(survey_data.id)
from_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
to_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
rv = client.get(f'/api/responses/month/{user_response_detail.engagement_id}\
?&from_date={from_date}&to_date={to_date}', content_type=ContentType.JSON.value)
assert rv.json[0].get('responses') == 1
assert rv.status_code == 200
assert rv.status_code == HTTPStatus.OK

with patch.object(UserResponseDetailService, 'get_response_count_by_created_month',
side_effect=exception_type('Test error')):
rv = client.get(f'/api/responses/month/{user_response_detail.engagement_id}\
?&from_date={from_date}&to_date={to_date}', content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR


def test_get_user_responses_by_week(client, session): # pylint:disable=unused-argument
@pytest.mark.parametrize("exception_type", [KeyError, ValueError])
def test_get_user_responses_by_week(client, exception_type, session): # pylint:disable=unused-argument
"""Assert that user response detail by week can be fetched."""
survey_data = factory_survey_model()
eng = factory_engagement_model()
survey_data = factory_survey_model(eng)
user_response_detail = factory_user_response_detail_model(survey_data.id)
from_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
to_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
rv = client.get(f'/api/responses/week/{user_response_detail.engagement_id}\
?&from_date={from_date}&to_date={to_date}', content_type=ContentType.JSON.value)
assert rv.json[0].get('responses') == 1
assert rv.status_code == 200
assert rv.status_code == HTTPStatus.OK

with patch.object(UserResponseDetailService, 'get_response_count_by_created_week',
side_effect=exception_type('Test error')):
rv = client.get(f'/api/responses/week/{user_response_detail.engagement_id}\
?&from_date={from_date}&to_date={to_date}', content_type=ContentType.JSON.value)
assert rv.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
36 changes: 36 additions & 0 deletions analytics-api/tests/unit/models/test_available_response_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright © 2019 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests to verify the User response detail API end-point.
Test-Suite to ensure that the user response detail endpoint is working as expected.
"""
from http import HTTPStatus
from analytics_api.utils.util import ContentType
from datetime import datetime, timedelta
from tests.utilities.factory_utils import (
factory_engagement_model, factory_user_response_detail_model, factory_survey_model)


def test_get_user_responses_by_month(client, session): # pylint:disable=unused-argument
"""Assert that user response detail by month can be fetched."""
eng = factory_engagement_model()
survey_data = factory_survey_model(eng)
user_response_detail = factory_user_response_detail_model(survey_data.id)
from_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
to_date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
rv = client.get(f'/api/responses/month/{user_response_detail.engagement_id}\
?&from_date={from_date}&to_date={to_date}', content_type=ContentType.JSON.value)
assert rv.json[0].get('responses') == 1
assert rv.status_code == HTTPStatus.OK
36 changes: 36 additions & 0 deletions analytics-api/tests/unit/models/test_email_verification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright © 2019 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the Engagement model.
Test suite to ensure that the Engagement model routines are working as expected.
"""

from analytics_api.models import EmailVerification as EmailVerificationModel
from tests.utilities.factory_utils import factory_email_verification_model


def test_get_email_verification_data_by_id(session):
"""Assert that an email verification data can be created and fetched."""
email_verification = factory_email_verification_model()
assert email_verification.id is not None
retrieved_email_verification = EmailVerificationModel.find_by_id(email_verification.id)
assert email_verification.participant_id == retrieved_email_verification.participant_id


def test_get_email_verification_count(session):
"""Test get count for email verification."""
email_verification = factory_email_verification_model()
assert email_verification.id is not None
email_verification_count = EmailVerificationModel.get_email_verification_count(email_verification.engagement_id)
assert email_verification_count == 1
28 changes: 27 additions & 1 deletion analytics-api/tests/unit/models/test_engagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,35 @@
from tests.utilities.factory_utils import factory_engagement_model


def test_engagement_map_data(session):
def test_get_engagement_map_data(session):
"""Assert that an map data related to an engagement can be created and fetched."""
eng = factory_engagement_model()
assert eng.id is not None
eng_existing = EngagementModel.find_by_id(eng.id)
assert eng.latitude == eng_existing.latitude


def test_create_engagement_by_source_id(session):
"""Test creating an engagement by source identifier."""
eng = factory_engagement_model()
assert eng.id is not None

# Check if the engagement can be retrieved by source identifier
eng_by_source_id = EngagementModel.find_by_source_id(eng.source_engagement_id)
assert len(eng_by_source_id) == 1
assert eng_by_source_id[0].source_engagement_id == eng.source_engagement_id


def test_deactivate_engagement_by_source_id(session):
"""Test deactivating an engagement by source identifier."""
eng = factory_engagement_model()
assert eng.id is not None

# Deactivate the engagement by source identifier
num_deactivated = EngagementModel.deactivate_by_source_id(eng.source_engagement_id)
assert num_deactivated == 1

# Check if the deactivated engagement is not active anymore
eng_by_source_id = EngagementModel.find_by_source_id(eng.source_engagement_id)
assert len(eng_by_source_id) == 1
assert not eng_by_source_id[0].is_active
Loading

0 comments on commit 6c5808c

Please sign in to comment.