Skip to content

Commit

Permalink
Release/2.4.2 Retornar erro HTTP no preditor (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
yxuo authored Jan 16, 2024
1 parent e5d8136 commit c496985
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ def test_get_predictor_error(self):
data = dict(response.json())

# assert
self.assertEqual(response.status_code, 200)
self.assertEqual(response.status_code, 500)
self.assertNotEqual(data, {'detail': "Not found."})
self.assertIsNotNone(data['error'])
self.assertEqual(data['count'], 0)
137 changes: 137 additions & 0 deletions mobilidade_rio/mobilidade_rio/predictor/tests/test_views_e2e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""test_views"""

import json
from unittest import mock

from django.test import Client, TransactionTestCase
from django.utils import timezone

from mobilidade_rio.predictor.models import PredictorResult
from mobilidade_rio.settings.base import BASE_DIR


class TestViewsE2E(TransactionTestCase):
"""
Unit test of predictor.views
Test different scenatrios of views
Requirements - #172: \
https://github.com/RJ-SMTR/mobilidade-rio-api/issues/172#issuecomment-1892673761
"""

reset_sequences = True
mocks = {
'api_realtime': {}
}
is_first_run = True

def setUp(self):
if self.is_first_run:
self.before_all()
self.is_first_run = False

def before_all(self):
"""Run it once, before all tests"""
api_realtime_path = f"{BASE_DIR}/predictor/tests/data/api_realtime.json".replace(
"\\", "/")
with open(api_realtime_path, encoding="utf8") as f:
self.mocks['api_realtime'] = json.load(f)

def test_predictor_success(self):
"""Predictor should return 200 if valid result"""
# arrange
PredictorResult.objects.create( # pylint: disable=E1101
pk=1,
result_json={
'result': [{'a': 1}],
'error': None,
}
)

# act
url = "/predictor/"
client = Client()
response = client.get(url)

# assert
self.assertEqual(response.status_code, 200)

def test_predictor_no_result_found(self):
"""Predictor should return 404 if no PredictorResult found in db is under 5 minutes"""
# arrange

# act
url = "/predictor/"
client = Client()
response = client.get(url)

# assert
self.assertEqual(response.status_code, 404)
self.assertEqual(response.json()['code'], "no_result")

def test_predictor_no_result_timeout(self):
"""Predictor should return 500 if no PredictorResult found in db after 5 minutes"""
# arrange
now = timezone.now()
last_minutes = now - timezone.timedelta(minutes=5, seconds=1)

with mock.patch.object(timezone, 'now', return_value=last_minutes):
PredictorResult.objects.create( # pylint: disable=E1101
pk=2,
result_json={'dev': 'last check'},
)

# act
url = "/predictor/"
client = Client()
response = client.get(url)

# assert
self.assertEqual(response.status_code, 500)
self.assertEqual(response.json()['code'],
"no_result-unchanged-timeout")

def test_predictor_result_timeout(self):
"""Predictor should return if valid PredictorResult is not updated after 5 minutes"""
# arrange
now = timezone.now()
last_minutes = now - timezone.timedelta(minutes=5)

with mock.patch.object(timezone, 'now', return_value=last_minutes):
PredictorResult.objects.create( # pylint: disable=E1101
pk=1,
result_json={
'result': [],
'error': {'code': "mock-error"},
}
)

# act
url = "/predictor/"
client = Client()
response = client.get(url)

# assert
self.assertEqual(response.status_code, 500)
self.assertEqual(response.json()['code'], "result-unchanged-timeout")

def test_predictor_error(self):
"""Predictor should return 500 if no PredictorResult found in db after 5 minutes"""
# arrange
PredictorResult.objects.create( # pylint: disable=E1101
pk=1,
result_json={
'result': [],
'error': {'code': "mock-error"},
}
)

# act
url = "/predictor/"
client = Client()
response = client.get(url)

# assert
self.assertEqual(response.status_code, 500)
self.assertEqual(response.json()['code'], "predictor-error")
94 changes: 82 additions & 12 deletions mobilidade_rio/mobilidade_rio/predictor/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Views for predictor app"""
import logging
import math
from datetime import timedelta

import pytz
from django.conf import settings
from django.utils import timezone
from rest_framework import viewsets
from rest_framework.response import Response

Expand All @@ -22,29 +25,96 @@ def list(self, _):
Return a JSON representation of the data.
"""

# default execution
unchanged_timeout_secs = 60 * 5

results = PredictorResult.objects.filter(pk=1) # pylint: disable=E1101
last_check = PredictorResult.objects.filter( # pylint: disable=E1101
pk=2)
# error: no_result
if not results.exists():
results = {"detail": "Not found."}
return Response(results)
# no result more than 5 minutes
last_check_count_seconds = 0 if not last_check.exists() else (
timezone.now() - last_check[0].last_modified).total_seconds()
if (last_check and last_check_count_seconds >= unchanged_timeout_secs):
error_message = {
'code': "no_result-unchanged-timeout",
'message': "Sem alterações no banco do preditor há mais de " +
f"{math.floor(unchanged_timeout_secs/60)} minutos.",
"lastCheck": last_check[0].last_modified,
"lastCheckCount": str(timedelta(seconds=int(last_check_count_seconds))),
"lastCheckCountSeeconds": int(last_check_count_seconds),
}
return Response(error_message, status=500)

PredictorResult.objects.update_or_create( # pylint: disable=E1101
pk=2,
defaults={
'result_json': {'about': "Save last result check"},
}
)

# no result
error_message = {
'code': "no_result",
'message': "Resultado do preditor não encontrado no banco.",
"lastCheck": last_check[0].last_modified if last_check.exists() else None,
"lastCheckCount": str(timedelta(seconds=int(last_check_count_seconds))
) if last_check.exists() else None,
"lastCheckCountSeconds": int(last_check_count_seconds
) if last_check.exists() else None,
}
return Response(error_message, status=404)

if last_check.exists():
last_check.delete()

last_update = results[0].last_modified.astimezone(
pytz.timezone(settings.TIME_ZONE))

result_data = results[0].result_json['result']
result_error = results[0].result_json['error']

# error: result-unchanged more than 5 minutes
last_update_count_seconds = (
timezone.now() - results[0].last_modified).total_seconds()
if last_update_count_seconds >= unchanged_timeout_secs:
error_message = {
'code': "result-unchanged-timeout",
'message': "Sem alterações no banco do preditor há mais de " +
f"{math.floor(unchanged_timeout_secs/60)} minutos.",
'data': result_data,
'error': result_error,
'lastUpdate': last_update,
'lastUpdateCount': str(timedelta(seconds=int(last_update_count_seconds))),
'lastUpdateCountSeconds': int(last_update_count_seconds),
}
return Response(error_message, status=500)

result_list = results[0].result_json['result']
# error: predictor-error
if result_error:
error_message = {
'code': "predictor-error",
'message': "O preditor retornou erro.",
"error": results[0].result_json["error"],
}
return Response(error_message, status=500)

# stop_id
# query filter
stop_id = self.request.query_params.get("stop_id")
if stop_id:
result_list = [i for i in result_list if i['stop_id'] == stop_id]
result_data = [i for i in result_data if i['stop_id'] == stop_id]

# return prediction
last_modified = results[0].last_modified.astimezone(pytz.timezone(settings.TIME_ZONE))

results = {
"count": len(result_list),
response_data = {
"count": len(result_data),
"next": None,
"previous": None,
"lastUpdate": last_modified,
"lastUpdate": last_update,
"lastUpdateCount": str(timedelta(seconds=int(last_update_count_seconds))),
"lastUpdateCountSeconds": int(last_update_count_seconds),
"error": results[0].result_json["error"],
"results" : result_list,
"results": result_data,
}

return Response(results)
return Response(response_data)

0 comments on commit c496985

Please sign in to comment.