Skip to content

Commit

Permalink
fix: restore compatible 404 response and enable api tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dhdaines committed Apr 17, 2024
1 parent 5922f6f commit 98a07f1
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 59 deletions.
26 changes: 23 additions & 3 deletions g2p/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
from typing import List

from fastapi import FastAPI, HTTPException, Path, Query
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse, PlainTextResponse
from networkx.algorithms.dag import ancestors, descendants # type: ignore

from g2p import make_g2p
from g2p.exceptions import InvalidLanguageCode, NoPath
from g2p.log import LOGGER
from g2p.mappings.langs import LANGS_NETWORK

# Create the v1 version of the API
Expand Down Expand Up @@ -39,6 +42,22 @@
Lang = Enum("Lang", [(name, name) for name in LANGS]) # type: ignore


# Be compatible with previous API which returned 404 on an unknown node
@api.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc: RequestValidationError):
error = exc.errors()[0]
LOGGER.error("%s", error.get("msg", "Unknown Error"))
if error.get("type") == "enum":
return PlainTextResponse(
"Unknown input or output language code", status_code=404
)
else:
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)


@api.get(
"/ancestors/{node}",
summary="get all ancestors of node",
Expand Down Expand Up @@ -80,16 +99,17 @@ def g2p(
out_lang: Lang = Query(alias="out-lang", description="output lang of string"),
text: str = Query(description="string to convert"),
index: bool = Query(False, description="return indices"),
debug: bool = Query(False, description="return debugging information"),
debugger: bool = Query(False, description="return debugging information"),
tokenize: bool = Query(False, description="tokenize before transducing"),
) -> dict:
"""Get the converted version of a string, given an input and output lang"""
try:
transducer = make_g2p(in_lang.name, out_lang.name)
transducer = make_g2p(in_lang.name, out_lang.name, tokenize=tokenize)
tg = transducer(text)
return {
"input-text": tg.input_string,
"output-text": tg.output_string,
"debugger": tg.debugger if debug else debug,
"debugger": tg.debugger if debugger else debugger,
"index": tg.edges if index else index,
}
except NoPath:
Expand Down
2 changes: 1 addition & 1 deletion g2p/static/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ $(document).ready(function() {
changeTable()
},
error: function(xhr, ajaxOptions, thrownError) {
if (xhr.status == 404 || xhr.status == 422) {
if (xhr.status == 404) {
$('#input-langselect option[value=custom]').attr('selected', 'selected');
$("#output-langselect").empty();
$("#output-langselect").append("<option value='custom' selected>Custom</option>");
Expand Down
7 changes: 3 additions & 4 deletions g2p/tests/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@

# Unit tests
from g2p.log import LOGGER

# from g2p.tests.test_api_resources import ResourceIntegrationTest
from g2p.tests.test_api_resources import ResourceIntegrationTest
from g2p.tests.test_check_ipa_arpabet import CheckIpaArpabetTest
from g2p.tests.test_cli import CliTest
from g2p.tests.test_create_mapping import MappingCreationTest
Expand Down Expand Up @@ -72,7 +71,7 @@
LOADER.loadTestsFromTestCase(test)
for test in [
CliTest,
# ResourceIntegrationTest,
ResourceIntegrationTest,
DoctorTest,
ExpensiveDoctorTest,
]
Expand Down Expand Up @@ -108,7 +107,7 @@ def describe_suite(suite: TestSuite):
print(
"\nTest suite excludes:",
*sorted(test for test in full_list if test not in requested_set),
sep="\n"
sep="\n",
)


Expand Down
90 changes: 39 additions & 51 deletions g2p/tests/test_api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
import re
from unittest import TestCase, main

from g2p.app import app
from fastapi.testclient import TestClient

from g2p.app import APP
from g2p.log import LOGGER
from g2p.mappings.langs import LANGS_NETWORK
from g2p.tests.public import __file__ as PUB_FILE

PUB_DIR = os.path.dirname(PUB_FILE)
APP = "foo"


class ResourceIntegrationTest(TestCase):
Expand All @@ -24,47 +25,32 @@ class ResourceIntegrationTest(TestCase):
"""

def setUp(self):
return
# Test external hosts
self.client = app.test_client
self.prefix = "/api/v1"
# routes
self.conversion_route = "/api/v1/g2p"
self.static_route = "/static/<path:filename>"
self.routes = [str(route) for route in APP.url_map.iter_rules()]
self.conversion_route = "/g2p"
for route in APP.routes:
if route.path == "/api/v1":
self.api = route.app
self.routes = route.routes
self.routes_no_args = [
route
route.path
for route in self.routes
if "<" not in route and route != self.conversion_route
if "{" not in route.path and route.path != self.conversion_route
]
self.routes_only_args = [
route
for route in self.routes
if "<" in route and route != self.static_route
route.path for route in self.routes if "{" in route.path
]
# endpoints
self.rules_by_endpoint = APP.url_map._rules_by_endpoint
self.endpoints = [rt for rt in self.rules_by_endpoint.keys()]
self.client = TestClient(self.api)
# args
self.arg_match = re.compile(r"\<[a-z:]+\>")
self.arg_match = re.compile(r"\{[a-z:]+\}")
self.args_to_check = "node"

def return_endpoint_arg(self, ep):
split = ep.split(".")
split_length = len(split)
return split[split_length - 1]

def return_route_from_endpoint(self, ep):
return str(self.rules_by_endpoint[ep][0])

def test_response_code(self):
"""
Ensure all routes return 200
"""
return
for rt in self.routes_no_args:
try:
r = self.client().get(rt)
r = self.client.get(rt)
self.assertEqual(r.status_code, 200)
LOGGER.debug("Route " + rt + " returned " + str(r.status_code))
except Exception as exc:
Expand All @@ -74,12 +60,11 @@ def test_response_code_with_args(self):
"""
Ensure all args return 200
"""
return
for ep in self.routes_only_args:
for node in LANGS_NETWORK.nodes:
rt = re.sub(self.arg_match, node, ep)
try:
r = self.client().get(rt)
r = self.client.get(rt)
self.assertEqual(r.status_code, 200)
except Exception as exc:
LOGGER.error("Couldn't connect. Is the API running? %s", exc)
Expand All @@ -95,7 +80,6 @@ def test_g2p_conversion(self):
"""
Ensure conversion returns proper response
"""
return
params = {
"in-lang": "dan",
"out-lang": "eng-arpabet",
Expand All @@ -118,35 +102,41 @@ def test_g2p_conversion(self):
"text": "hej",
}
self.maxDiff = None
response = self.client().get(self.conversion_route, query_string=params)
res_json = response.get_json()
response = self.client.get(self.conversion_route, params=params)
res_json = response.json()
self.assertEqual(response.status_code, 200)
with open(os.path.join(PUB_DIR, "sample_response.json")) as f:
data = json.load(f)
self.assertEqual(res_json, data)
# check minimal response
minimal_response = self.client().get(
self.conversion_route, query_string=minimal_params
)
minimal_response = self.client.get(self.conversion_route, params=minimal_params)
data["debugger"] = False
data["index"] = False
self.assertEqual(minimal_response.status_code, 200)
self.assertEqual(minimal_response.get_json(), data)
self.assertEqual(minimal_response.json(), data)
with self.assertLogs(LOGGER, level="ERROR"):
bad_response = self.client().get(
self.conversion_route, query_string=bad_params
)
bad_response = self.client.get(self.conversion_route, params=bad_params)
with self.assertLogs(LOGGER, level="ERROR"):
same_response = self.client().get(
self.conversion_route, query_string=same_params
)
same_response = self.client.get(self.conversion_route, params=same_params)
self.assertEqual(bad_response.status_code, 400)
self.assertEqual(same_response.status_code, 400)
with self.assertLogs(LOGGER, level="ERROR"):
missing_response = self.client().get(
self.conversion_route, query_string=missing_params
missing_response = self.client.get(
self.conversion_route, params=missing_params
)
self.assertEqual(missing_response.status_code, 404)
invalid_params = {
"in-lang": "dan",
"out-lang": "eng-arpabet",
"text": "hej",
"debugger": "THIS IS NOT A BOOLEAN!!!",
"index": "NEITHER IS THIS!!!",
}
with self.assertLogs(LOGGER, level="ERROR"):
invalid_response = self.client.get(
self.conversion_route, params=invalid_params
)
self.assertEqual(invalid_response.status_code, 422)

def test_g2p_conversion_with_tok(self):
params_with_tok = {
Expand All @@ -157,11 +147,9 @@ def test_g2p_conversion_with_tok(self):
"index": True,
"tokenize": True,
}
response = self.client().get(
self.conversion_route, query_string=params_with_tok
)
response = self.client.get(self.conversion_route, params=params_with_tok)
self.assertEqual(response.status_code, 200)
res_json_tok = response.get_json()
res_json_tok = response.json()
self.assertEqual(res_json_tok["debugger"][0][0][0]["input"], "ceci")

params_no_tok = {
Expand All @@ -172,9 +160,9 @@ def test_g2p_conversion_with_tok(self):
"index": True,
"tokenize": False,
}
response = self.client().get(self.conversion_route, query_string=params_no_tok)
response = self.client.get(self.conversion_route, params=params_no_tok)
self.assertEqual(response.status_code, 200)
res_json_no_tok = response.get_json()
res_json_no_tok = response.json()
self.assertNotEqual(res_json_tok, res_json_no_tok)
self.assertEqual(res_json_no_tok["debugger"][0][0][0]["input"], "ceci, celà")

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ test = [
"playwright>=1.26.1",
"jsonschema>=4.17.3",
"pep440>=0.1.2",
"httpx",
# Kind of bogus that we need both, but socketio wants this
"aiohttp",
]
dev = [
Expand Down

0 comments on commit 98a07f1

Please sign in to comment.