alessandro trinca tornidor commited on
Commit
1c72750
·
1 Parent(s): 46e78fd

test: update test cases

Browse files
.gitignore CHANGED
@@ -105,6 +105,7 @@ coverage.xml
105
  .hypothesis/
106
  */playwright-report/*
107
  */test-results/*
 
108
 
109
  # Translations
110
  *.mo
 
105
  .hypothesis/
106
  */playwright-report/*
107
  */test-results/*
108
+ coverage.json
109
 
110
  # Translations
111
  *.mo
my_ghost_writer/app.py CHANGED
@@ -14,7 +14,7 @@ from fastapi.staticfiles import StaticFiles
14
  from pymongo import __version__ as pymongo_version
15
  from pymongo.errors import PyMongoError
16
 
17
- from my_ghost_writer import pymongo_operations_rw
18
  from my_ghost_writer.constants import (app_logger, ALLOWED_ORIGIN_LIST, API_MODE, DOMAIN, IS_TESTING, LOG_LEVEL, PORT,
19
  STATIC_FOLDER, WORDSAPI_KEY, WORDSAPI_URL, RAPIDAPI_HOST, MONGO_USE_OK, MONGO_HEALTHCHECK_SLEEP)
20
  from my_ghost_writer.pymongo_utils import mongodb_health_check
@@ -159,15 +159,11 @@ def get_thesaurus_wordsapi(body: RequestQueryThesaurusWordsapiBody | str) -> JSO
159
 
160
  @app.exception_handler(RequestValidationError)
161
  def request_validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
162
- from my_ghost_writer import exception_handlers
163
-
164
  return exception_handlers.request_validation_exception_handler(request, exc)
165
 
166
 
167
  @app.exception_handler(HTTPException)
168
  def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
169
- from my_ghost_writer import exception_handlers
170
-
171
  return exception_handlers.http_exception_handler(request, exc)
172
 
173
 
 
14
  from pymongo import __version__ as pymongo_version
15
  from pymongo.errors import PyMongoError
16
 
17
+ from my_ghost_writer import pymongo_operations_rw, exception_handlers
18
  from my_ghost_writer.constants import (app_logger, ALLOWED_ORIGIN_LIST, API_MODE, DOMAIN, IS_TESTING, LOG_LEVEL, PORT,
19
  STATIC_FOLDER, WORDSAPI_KEY, WORDSAPI_URL, RAPIDAPI_HOST, MONGO_USE_OK, MONGO_HEALTHCHECK_SLEEP)
20
  from my_ghost_writer.pymongo_utils import mongodb_health_check
 
159
 
160
  @app.exception_handler(RequestValidationError)
161
  def request_validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
 
 
162
  return exception_handlers.request_validation_exception_handler(request, exc)
163
 
164
 
165
  @app.exception_handler(HTTPException)
166
  def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
 
 
167
  return exception_handlers.http_exception_handler(request, exc)
168
 
169
 
tests/test_app.py CHANGED
@@ -1,8 +1,11 @@
 
1
  import unittest
2
  from unittest.mock import patch, MagicMock
 
 
3
  from fastapi.testclient import TestClient
4
 
5
- from my_ghost_writer.app import app
6
 
7
 
8
  class TestAppEndpoints(unittest.TestCase):
@@ -76,6 +79,19 @@ class TestAppEndpoints(unittest.TestCase):
76
  self.assertEqual(response.status_code, 200)
77
  self.assertEqual(response.json()["source"], "wordsapi")
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  @patch("my_ghost_writer.app.WORDSAPI_URL", "http://mocked-url.com")
80
  @patch("my_ghost_writer.app.RAPIDAPI_HOST", "mocked-rapidapi-host.com")
81
  @patch("my_ghost_writer.app.WORDSAPI_KEY", "WORDSAPI_KEY")
@@ -107,5 +123,68 @@ class TestAppEndpoints(unittest.TestCase):
107
  self.assertEqual(response.text, "")
108
 
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  if __name__ == "__main__":
111
  unittest.main()
 
1
+ import asyncio
2
  import unittest
3
  from unittest.mock import patch, MagicMock
4
+
5
+ from fastapi import Request
6
  from fastapi.testclient import TestClient
7
 
8
+ from my_ghost_writer.app import app, mongo_health_check_background_task, lifespan
9
 
10
 
11
  class TestAppEndpoints(unittest.TestCase):
 
79
  self.assertEqual(response.status_code, 200)
80
  self.assertEqual(response.json()["source"], "wordsapi")
81
 
82
+ @patch("my_ghost_writer.app.pymongo_operations_rw.get_document_by_word", side_effect=AssertionError("fail"))
83
+ @patch("my_ghost_writer.app.requests.get")
84
+ def test_thesaurus_wordsapi_remote_non_200(self, mock_requests_get, mock_get_doc):
85
+ mock_response = MagicMock()
86
+ mock_response.status_code = 400
87
+ mock_response.json.return_value = {"error": "not found"}
88
+ mock_requests_get.return_value = mock_response
89
+ with patch("my_ghost_writer.app.db_ok", {"mongo_ok": True}):
90
+ body = '{"query": "test"}'
91
+ response = self.client.post("/thesaurus-wordsapi", json=body)
92
+ self.assertEqual(response.status_code, 500)
93
+ self.assertIn("Error - Internal Server Error", response.text)
94
+
95
  @patch("my_ghost_writer.app.WORDSAPI_URL", "http://mocked-url.com")
96
  @patch("my_ghost_writer.app.RAPIDAPI_HOST", "mocked-rapidapi-host.com")
97
  @patch("my_ghost_writer.app.WORDSAPI_KEY", "WORDSAPI_KEY")
 
123
  self.assertEqual(response.text, "")
124
 
125
 
126
+ @patch("my_ghost_writer.app.pymongo_operations_rw.get_document_by_word", side_effect=AssertionError("fail"))
127
+ @patch("my_ghost_writer.app.requests.get")
128
+ def test_thesaurus_wordsapi_remote_non_200(self, mock_requests_get, mock_get_doc):
129
+ mock_response = MagicMock()
130
+ mock_response.status_code = 400
131
+ mock_response.json.return_value = {"error": "not found"}
132
+ mock_requests_get.return_value = mock_response
133
+ with patch("my_ghost_writer.app.db_ok", {"mongo_ok": True}):
134
+ body = '{"query": "test"}'
135
+ response = self.client.post("/thesaurus-wordsapi", json=body)
136
+ self.assertEqual(response.status_code, 500)
137
+ self.assertIn("Error - Internal Server Error", response.text)
138
+
139
+ def test_lifespan(self):
140
+ # Test that lifespan yields and cancels the task
141
+ async def run_lifespan():
142
+ gen = lifespan(app)
143
+ await gen.asend(None)
144
+ await gen.aclose()
145
+ asyncio.run(run_lifespan())
146
+
147
+ def test_mongo_health_check_background_task(self):
148
+ # Patch sleep and health_mongo to exit after one loop
149
+ with patch("my_ghost_writer.app.MONGO_USE_OK", True), \
150
+ patch("my_ghost_writer.app.health_mongo", return_value="Mongodb: still alive..."), \
151
+ patch("my_ghost_writer.app.asyncio.sleep", side_effect=Exception("stop")):
152
+ with self.assertRaises(Exception):
153
+ asyncio.run(mongo_health_check_background_task())
154
+
155
+ def test_index_route(self):
156
+ from pathlib import Path
157
+ import tempfile
158
+ with tempfile.TemporaryDirectory() as tmpdir:
159
+ index_path = Path(tmpdir) / "index.html"
160
+ index_path.write_text("<html>Test</html>")
161
+ with patch("my_ghost_writer.app.STATIC_FOLDER", Path(tmpdir)):
162
+ response = self.client.get("/")
163
+ self.assertEqual(response.status_code, 200)
164
+ self.assertIn("Test", response.text)
165
+
166
+ def test_static_route(self):
167
+ with patch("my_ghost_writer.app.STATIC_FOLDER") as mock_static:
168
+ mock_static.__truediv__.return_value = "index.html"
169
+ response = self.client.get("/static/")
170
+ self.assertEqual(response.status_code, 200)
171
+
172
+ @patch("my_ghost_writer.app.exception_handlers.request_validation_exception_handler")
173
+ def test_request_validation_exception_handler(self, mock_handler):
174
+ req = MagicMock(spec=Request)
175
+ exc = MagicMock()
176
+ from my_ghost_writer.app import request_validation_exception_handler
177
+ request_validation_exception_handler(req, exc)
178
+ mock_handler.assert_called_once_with(req, exc)
179
+
180
+ @patch("my_ghost_writer.app.exception_handlers.http_exception_handler")
181
+ def test_http_exception_handler(self, mock_handler):
182
+ req = MagicMock(spec=Request)
183
+ exc = MagicMock()
184
+ from my_ghost_writer.app import http_exception_handler
185
+ http_exception_handler(req, exc)
186
+ mock_handler.assert_called_once_with(req, exc)
187
+
188
+
189
  if __name__ == "__main__":
190
  unittest.main()
tests/test_exception_handlers.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ from fastapi import HTTPException, status
3
+ from fastapi.exceptions import RequestValidationError
4
+ from fastapi.responses import JSONResponse
5
+
6
+ from my_ghost_writer.exception_handlers import request_validation_exception_handler, http_exception_handler
7
+
8
+
9
+ class DummyRequest:
10
+ def __init__(self):
11
+ self.headers = {"x-test": "header"}
12
+ self.query_params = {"q": "param"}
13
+ @property
14
+ def headers(self):
15
+ return self._headers
16
+ @headers.setter
17
+ def headers(self, value):
18
+ self._headers = value
19
+ @property
20
+ def query_params(self):
21
+ return self._query_params
22
+ @query_params.setter
23
+ def query_params(self, value):
24
+ self._query_params = value
25
+
26
+
27
+ def test_request_validation_exception_handler():
28
+ request = DummyRequest()
29
+ exc = RequestValidationError([{"loc": ["body", "field"], "msg": "field required", "type": "value_error.missing"}])
30
+ exc.body = {"field": None}
31
+ response = request_validation_exception_handler(request, exc)
32
+ assert isinstance(response, JSONResponse)
33
+ assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
34
+ assert response.body
35
+ assert b"Unprocessable Entity" in response.body
36
+
37
+ def test_http_exception_handler():
38
+ request = DummyRequest()
39
+ exc = HTTPException(status_code=400, detail="Bad request")
40
+ response = http_exception_handler(request, exc)
41
+ assert isinstance(response, JSONResponse)
42
+ assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
43
+ assert response.body
44
+ assert b"Internal Server Error" in response.body
45
+
46
+
47
+ if __name__ == "__main__":
48
+ pytest.main([__file__])