def test_duckling_api_success() -> None: """ Initialize DucklingParser and `.get_entities`. This test-case bypasses duckling API calls via httpretty. The expected response was originally generated using: ```shell export month_q="I need four of those on 27th next month." export time_q="Can I have it at 5 am""&dims="[date"]" export text="$month_q $time_q" curl -XPOST http://0.0.0.0:8000/parse --data 'locale=en_US&text=$text' ``` """ body = "I need four of those on 27th next month. Can I have it at 5 am" expected_response = [ config.mock_number_entity, config.mock_date_entity, config.mock_time_entity, ] request_callback = request_builder(expected_response) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) parser = DucklingParser(locale="en_IN") response = parser.get_entities(body) assert response == expected_response
def test_entity_json_to_object_people_entity() -> None: """ Reshape converts json response from Duckling APIs to dialogy's BaseEntity. We are checking for PeopleEntity here. """ entities_json = [config.mock_people_entity] parser = DucklingParser(locale="en_IN") entities = parser.reshape(entities_json) assert isinstance(entities[0], PeopleEntity)
def test_entity_json_to_object_time_interval_entity(): """ Reshape converts json response from Duckling APIs to dialogy's BaseEntity. We are checking for TimeIntervalEntity here. """ parser = DucklingParser(locale="en_IN") entities_json = [config.mock_interval_entity] entities = parser.reshape(entities_json) assert isinstance(entities[0], TimeIntervalEntity)
def test_entity_object_not_implemented() -> None: """ Reshape converts json response from Duckling APIs to dialogy's BaseEntity. We are checking for an unknown entity type here. This would lead to a `NotImplementedError`. """ entities_json = [config.mock_unknown_entity] parser = DucklingParser(locale="en_IN") with pytest.raises(NotImplementedError): parser.reshape(entities_json)
def test_entity_mutation_dict() -> None: """ This piece of code is run by DucklingParser when the Duckling API returns a set of entities. There are a few keys added/removed. We are making sure of those for numerical entities here. """ entities_json = config.mock_number_entity parser = DucklingParser(locale="en_IN") entity = parser.mutate_entity(entities_json) assert "range" in entity assert "start" in entity["range"] assert "end" in entity["range"] assert "values" in entity assert entity["values"][0]["value"] == 4
def test_duckling_wrong_tz() -> None: """ In case the timezone is incorrect or exceptions need to be handled. """ body = "i need it at 5 am" request_callback = request_builder({}) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) parser = DucklingParser(locale="en_IN", timezone="Earth/Someplace") with pytest.raises(pytz.UnknownTimeZoneError): _ = parser.get_entities(body)
def test_entity_mutation_list() -> None: """ This piece of code is run by DucklingParser when the Duckling API returns a set of entities. There are a few keys added/removed. We are making sure of those for time entities here. """ entity_json = { "body": "at 5 am", "start": 55, "value": { "values": [ { "value": "2021-01-21T05:00:00.000+05:30", "grain": "hour", "type": "value", }, { "value": "2021-01-22T05:00:00.000+05:30", "grain": "hour", "type": "value", }, { "value": "2021-01-23T05:00:00.000+05:30", "grain": "hour", "type": "value", }, ], "value": "2021-01-21T05:00:00.000+05:30", "grain": "hour", "type": "value", }, "end": 62, "dim": "time", "latent": False, } parser = DucklingParser(locale="en_IN") entity = parser.mutate_entity(entity_json) assert "range" in entity assert "start" in entity["range"] assert "end" in entity["range"] assert "values" in entity assert "value" not in entity assert entity["values"][0]["value"] == "2021-01-21T05:00:00.000+05:30"
def test_duckling_with_tz() -> None: """ Using DucklingParser with timezone. """ body = "i need it at 5 am" expected_response = [config.mock_time_entity] request_callback = request_builder(expected_response) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) parser = DucklingParser(locale="en_IN", timezone="Asia/Kolkata") response = parser.get_entities(body) assert response == expected_response
def test_plugin_no_entities() -> None: """ An end-to-end example showing how `DucklingParser` works in case the input has no entities. """ body = "i need it" expected = [] def access(workflow): return workflow.input, None def mutate(workflow, entities): workflow.output = {"entities": entities} parser = DucklingParser(access=access, mutate=mutate, dimensions=["people"], locale="en_IN") request_callback = request_builder(expected) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) workflow = Workflow(preprocessors=[parser()], postprocessors=[]) workflow.run(body) assert workflow.output["entities"] == []
def test_entity_object_error_on_missing_value() -> None: """ This json has missing `value` key. We can see that we will get a `KeyError` raised, this happens during the validation phase. """ entities_json = [{ "body": "3 people", "start": 67, "end": 75, "dim": "people", "latent": False, }] parser = DucklingParser(locale="en_IN") with pytest.raises(KeyError): parser.reshape(entities_json)
def test_duckling_api_failure() -> None: """ Simulate Duckling returning 500. """ body = "27th next month" expected_response = [config.mock_date_entity] request_callback = request_builder(expected_response, response_code=500) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) parser = DucklingParser(dimensions=["time"], locale="en_IN") with pytest.raises(ValueError): parser.get_entities(body)
def test_plugin_io_type_mismatch(access, mutate) -> None: """ Here we are chcking if the plugin has access to workflow. Since we have provided `access`, `mutate` of incorrect types to `DucklingParser` we will receive a `TypeError`. """ parser = DucklingParser(access=access, mutate=mutate, locale="en_IN") plugin = parser() workflow = Workflow(preprocessors=[plugin], postprocessors=[]) with pytest.raises(TypeError): workflow.run("")
def test_entity_json_to_object_numerical_entity() -> None: """ Reshape converts json response from Duckling APIs to dialogy's BaseEntity. We are checking for NumericalEntity here. """ entities_json = [{ "body": "four", "start": 7, "value": { "value": 4, "type": "value" }, "end": 11, "dim": "number", "latent": False, }] parser = DucklingParser(locale="en_IN") entities = parser.reshape(entities_json) assert isinstance(entities[0], NumericalEntity)
def test_plugin(body, expected) -> None: """ An end-to-end example showing how to use `DucklingParser` with a `Worflow`. """ def access(workflow): return workflow.input, None def mutate(workflow, entities): workflow.output = {"entities": entities} parser = DucklingParser(access=access, mutate=mutate, dimensions=["people"], locale="en_IN") request_callback = request_builder(expected) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) workflow = Workflow(preprocessors=[parser()], postprocessors=[]) workflow.run(body) assert isinstance(workflow.output["entities"][0], PeopleEntity)
def test_plugin_value_errors() -> None: """ An end-to-end example showing how `DucklingParser` works in case the input is invalid. """ def access(workflow): return workflow.input, None def mutate(workflow, entities): workflow.output = {"entities": entities} parser = DucklingParser(access=access, mutate=mutate, dimensions=["people"], locale="en_IN") request_callback = request_builder([], response_code=500) httpretty.register_uri(httpretty.POST, "http://0.0.0.0:8000/parse", body=request_callback) with pytest.raises(ValueError): workflow = Workflow(preprocessors=[parser()], postprocessors=[]) workflow.run("")