mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Upgrade Dialogflow to work with V2 API (#25975)
* Handle v1 and v2 dialog api requests * Stylistic changes
This commit is contained in:
parent
65aa4148a4
commit
6907e8e9dc
@ -17,6 +17,9 @@ SOURCE = "Home Assistant Dialogflow"
|
|||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA)
|
CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
V1 = 1
|
||||||
|
V2 = 2
|
||||||
|
|
||||||
|
|
||||||
class DialogFlowError(HomeAssistantError):
|
class DialogFlowError(HomeAssistantError):
|
||||||
"""Raised when a DialogFlow error happens."""
|
"""Raised when a DialogFlow error happens."""
|
||||||
@ -84,23 +87,45 @@ async_remove_entry = config_entry_flow.webhook_async_remove_entry
|
|||||||
|
|
||||||
def dialogflow_error_response(message, error):
|
def dialogflow_error_response(message, error):
|
||||||
"""Return a response saying the error message."""
|
"""Return a response saying the error message."""
|
||||||
dialogflow_response = DialogflowResponse(message["result"]["parameters"])
|
api_version = get_api_version(message)
|
||||||
|
if api_version is V1:
|
||||||
|
parameters = message["result"]["parameters"]
|
||||||
|
elif api_version is V2:
|
||||||
|
parameters = message["queryResult"]["parameters"]
|
||||||
|
dialogflow_response = DialogflowResponse(parameters, api_version)
|
||||||
dialogflow_response.add_speech(error)
|
dialogflow_response.add_speech(error)
|
||||||
return dialogflow_response.as_dict()
|
return dialogflow_response.as_dict()
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_version(message):
|
||||||
|
"""Get API version of Dialogflow message."""
|
||||||
|
if message.get("id") is not None:
|
||||||
|
return V1
|
||||||
|
if message.get("responseId") is not None:
|
||||||
|
return V2
|
||||||
|
|
||||||
|
|
||||||
async def async_handle_message(hass, message):
|
async def async_handle_message(hass, message):
|
||||||
"""Handle a DialogFlow message."""
|
"""Handle a DialogFlow message."""
|
||||||
|
_api_version = get_api_version(message)
|
||||||
|
if _api_version is V1:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Dialogflow V1 API will be removed on October 23, 2019. Please change your DialogFlow settings to use the V2 api"
|
||||||
|
)
|
||||||
req = message.get("result")
|
req = message.get("result")
|
||||||
action_incomplete = req["actionIncomplete"]
|
action_incomplete = req.get("actionIncomplete", True)
|
||||||
|
|
||||||
if action_incomplete:
|
if action_incomplete:
|
||||||
return None
|
return
|
||||||
|
|
||||||
|
elif _api_version is V2:
|
||||||
|
req = message.get("queryResult")
|
||||||
|
if req.get("allRequiredParamsPresent", False) is False:
|
||||||
|
return
|
||||||
|
|
||||||
action = req.get("action", "")
|
action = req.get("action", "")
|
||||||
parameters = req.get("parameters").copy()
|
parameters = req.get("parameters").copy()
|
||||||
parameters["dialogflow_query"] = message
|
parameters["dialogflow_query"] = message
|
||||||
dialogflow_response = DialogflowResponse(parameters)
|
dialogflow_response = DialogflowResponse(parameters, _api_version)
|
||||||
|
|
||||||
if action == "":
|
if action == "":
|
||||||
raise DialogFlowError(
|
raise DialogFlowError(
|
||||||
@ -123,10 +148,11 @@ async def async_handle_message(hass, message):
|
|||||||
class DialogflowResponse:
|
class DialogflowResponse:
|
||||||
"""Help generating the response for Dialogflow."""
|
"""Help generating the response for Dialogflow."""
|
||||||
|
|
||||||
def __init__(self, parameters):
|
def __init__(self, parameters, api_version):
|
||||||
"""Initialize the Dialogflow response."""
|
"""Initialize the Dialogflow response."""
|
||||||
self.speech = None
|
self.speech = None
|
||||||
self.parameters = {}
|
self.parameters = {}
|
||||||
|
self.api_version = api_version
|
||||||
# Parameter names replace '.' and '-' for '_'
|
# Parameter names replace '.' and '-' for '_'
|
||||||
for key, value in parameters.items():
|
for key, value in parameters.items():
|
||||||
underscored_key = key.replace(".", "_").replace("-", "_")
|
underscored_key = key.replace(".", "_").replace("-", "_")
|
||||||
@ -143,4 +169,8 @@ class DialogflowResponse:
|
|||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
"""Return response in a Dialogflow valid dictionary."""
|
"""Return response in a Dialogflow valid dictionary."""
|
||||||
|
if self.api_version is V1:
|
||||||
return {"speech": self.speech, "displayText": self.speech, "source": SOURCE}
|
return {"speech": self.speech, "displayText": self.speech, "source": SOURCE}
|
||||||
|
|
||||||
|
if self.api_version is V2:
|
||||||
|
return {"fulfillmentText": self.speech, "source": SOURCE}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""The tests for the Dialogflow component."""
|
"""The tests for the Dialogflow component."""
|
||||||
import json
|
import json
|
||||||
|
import copy
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -90,18 +91,15 @@ async def fixture(hass, aiohttp_client):
|
|||||||
return await aiohttp_client(hass.http.app), webhook_id
|
return await aiohttp_client(hass.http.app), webhook_id
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_action_incomplete(fixture):
|
class _Data:
|
||||||
"""Test when action is not completed."""
|
_v1 = {
|
||||||
mock_client, webhook_id = fixture
|
|
||||||
data = {
|
|
||||||
"id": REQUEST_ID,
|
"id": REQUEST_ID,
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
"timestamp": REQUEST_TIMESTAMP,
|
||||||
"result": {
|
"result": {
|
||||||
"source": "agent",
|
"source": "agent",
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
"resolvedQuery": "my zodiac sign is virgo",
|
||||||
"speech": "",
|
|
||||||
"action": "GetZodiacHoroscopeIntent",
|
"action": "GetZodiacHoroscopeIntent",
|
||||||
"actionIncomplete": True,
|
"actionIncomplete": False,
|
||||||
"parameters": {"ZodiacSign": "virgo"},
|
"parameters": {"ZodiacSign": "virgo"},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"intentId": INTENT_ID,
|
"intentId": INTENT_ID,
|
||||||
@ -117,6 +115,55 @@ async def test_intent_action_incomplete(fixture):
|
|||||||
"originalRequest": None,
|
"originalRequest": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_v2 = {
|
||||||
|
"responseId": REQUEST_ID,
|
||||||
|
"timestamp": REQUEST_TIMESTAMP,
|
||||||
|
"queryResult": {
|
||||||
|
"queryText": "my zodiac sign is virgo",
|
||||||
|
"action": "GetZodiacHoroscopeIntent",
|
||||||
|
"allRequiredParamsPresent": True,
|
||||||
|
"parameters": {"ZodiacSign": "virgo"},
|
||||||
|
"intent": {
|
||||||
|
"name": INTENT_ID,
|
||||||
|
"webhookState": "true",
|
||||||
|
"displayName": INTENT_NAME,
|
||||||
|
},
|
||||||
|
"fulfillment": {"text": "", "messages": [{"type": 0, "speech": ""}]},
|
||||||
|
"intentDetectionConfidence": 1,
|
||||||
|
},
|
||||||
|
"status": {"code": 200, "errorType": "success"},
|
||||||
|
"session": SESSION_ID,
|
||||||
|
"originalDetectIntentRequest": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def v1(self):
|
||||||
|
return copy.deepcopy(self._v1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def v2(self):
|
||||||
|
return copy.deepcopy(self._v2)
|
||||||
|
|
||||||
|
|
||||||
|
Data = _Data()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_v1_data():
|
||||||
|
"""Test for version 1 api based on message."""
|
||||||
|
assert dialogflow.get_api_version(Data.v1) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_v2_data():
|
||||||
|
"""Test for version 2 api based on message."""
|
||||||
|
assert dialogflow.get_api_version(Data.v2) == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_action_incomplete_v1(fixture):
|
||||||
|
"""Test when action is not completed."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v1
|
||||||
|
data["result"]["actionIncomplete"] = True
|
||||||
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
@ -124,20 +171,30 @@ async def test_intent_action_incomplete(fixture):
|
|||||||
assert "" == await response.text()
|
assert "" == await response.text()
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_slot_filling(fixture):
|
async def test_intent_action_incomplete_v2(fixture):
|
||||||
|
"""Test when action is not completed."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
data["queryResult"]["allRequiredParamsPresent"] = False
|
||||||
|
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
assert "" == await response.text()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_slot_filling_v1(fixture):
|
||||||
"""Test when Dialogflow asks for slot-filling return none."""
|
"""Test when Dialogflow asks for slot-filling return none."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
|
||||||
"id": REQUEST_ID,
|
data = Data.v1
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
data["result"].update(
|
||||||
"result": {
|
resolvedQuery="my zodiac sign is",
|
||||||
"source": "agent",
|
speech="",
|
||||||
"resolvedQuery": "my zodiac sign is",
|
actionIncomplete=True,
|
||||||
"speech": "",
|
parameters={"ZodiacSign": ""},
|
||||||
"action": "GetZodiacHoroscopeIntent",
|
contexts=[
|
||||||
"actionIncomplete": True,
|
|
||||||
"parameters": {"ZodiacSign": ""},
|
|
||||||
"contexts": [
|
|
||||||
{
|
{
|
||||||
"name": CONTEXT_NAME,
|
"name": CONTEXT_NAME,
|
||||||
"parameters": {"ZodiacSign.original": "", "ZodiacSign": ""},
|
"parameters": {"ZodiacSign.original": "", "ZodiacSign": ""},
|
||||||
@ -154,22 +211,13 @@ async def test_intent_slot_filling(fixture):
|
|||||||
"lifespan": 1,
|
"lifespan": 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"metadata": {
|
fulfillment={
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "true",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {
|
|
||||||
"speech": "What is the ZodiacSign?",
|
"speech": "What is the ZodiacSign?",
|
||||||
"messages": [{"type": 0, "speech": "What is the ZodiacSign?"}],
|
"messages": [{"type": 0, "speech": "What is the ZodiacSign?"}],
|
||||||
},
|
},
|
||||||
"score": 0.77,
|
score=0.77,
|
||||||
},
|
)
|
||||||
"status": {"code": 200, "errorType": "success"},
|
data["result"]["metadata"].update(webhookForSlotFillingUsed="true")
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
@ -178,33 +226,10 @@ async def test_intent_slot_filling(fixture):
|
|||||||
assert "" == await response.text()
|
assert "" == await response.text()
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_request_with_parameters(fixture):
|
async def test_intent_request_with_parameters_v1(fixture):
|
||||||
"""Test a request with parameters."""
|
"""Test a request with parameters."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
|
||||||
"result": {
|
|
||||||
"source": "agent",
|
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
|
||||||
"speech": "",
|
|
||||||
"action": "GetZodiacHoroscopeIntent",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {"ZodiacSign": "virgo"},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
@ -213,33 +238,23 @@ async def test_intent_request_with_parameters(fixture):
|
|||||||
assert "You told us your sign is virgo." == text
|
assert "You told us your sign is virgo." == text
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_request_with_parameters_but_empty(fixture):
|
async def test_intent_request_with_parameters_v2(fixture):
|
||||||
|
"""Test a request with parameters."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
assert "You told us your sign is virgo." == text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_request_with_parameters_but_empty_v1(fixture):
|
||||||
"""Test a request with parameters but empty value."""
|
"""Test a request with parameters but empty value."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
data["result"].update(parameters={"ZodiacSign": ""})
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
|
||||||
"result": {
|
|
||||||
"source": "agent",
|
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
|
||||||
"speech": "",
|
|
||||||
"action": "GetZodiacHoroscopeIntent",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {"ZodiacSign": ""},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
@ -248,33 +263,30 @@ async def test_intent_request_with_parameters_but_empty(fixture):
|
|||||||
assert "You told us your sign is ." == text
|
assert "You told us your sign is ." == text
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_request_without_slots(hass, fixture):
|
async def test_intent_request_with_parameters_but_empty_v2(fixture):
|
||||||
|
"""Test a request with parameters but empty value."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
data["queryResult"].update(parameters={"ZodiacSign": ""})
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
assert "You told us your sign is ." == text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_request_without_slots_v1(hass, fixture):
|
||||||
"""Test a request without slots."""
|
"""Test a request without slots."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
data["result"].update(
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
resolvedQuery="where are we",
|
||||||
"result": {
|
action="WhereAreWeIntent",
|
||||||
"source": "agent",
|
parameters={},
|
||||||
"resolvedQuery": "where are we",
|
contexts=[],
|
||||||
"speech": "",
|
)
|
||||||
"action": "WhereAreWeIntent",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
@ -294,37 +306,45 @@ async def test_intent_request_without_slots(hass, fixture):
|
|||||||
assert "You are both home, you silly" == text
|
assert "You are both home, you silly" == text
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_request_calling_service(fixture, calls):
|
async def test_intent_request_without_slots_v2(hass, fixture):
|
||||||
|
"""Test a request without slots."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
data["queryResult"].update(
|
||||||
|
queryText="where are we",
|
||||||
|
action="WhereAreWeIntent",
|
||||||
|
parameters={},
|
||||||
|
outputContexts=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
|
||||||
|
assert "Anne Therese is at unknown and Paulus is at unknown" == text
|
||||||
|
|
||||||
|
hass.states.async_set("device_tracker.paulus", "home")
|
||||||
|
hass.states.async_set("device_tracker.anne_therese", "home")
|
||||||
|
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
assert "You are both home, you silly" == text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_request_calling_service_v1(fixture, calls):
|
||||||
"""Test a request for calling a service.
|
"""Test a request for calling a service.
|
||||||
|
|
||||||
If this request is done async the test could finish before the action
|
If this request is done async the test could finish before the action
|
||||||
has been executed. Hard to test because it will be a race condition.
|
has been executed. Hard to test because it will be a race condition.
|
||||||
"""
|
"""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
data["result"]["action"] = "CallServiceIntent"
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
|
||||||
"result": {
|
|
||||||
"source": "agent",
|
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
|
||||||
"speech": "",
|
|
||||||
"action": "CallServiceIntent",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {"ZodiacSign": "virgo"},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
call_count = len(calls)
|
call_count = len(calls)
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
@ -338,33 +358,34 @@ async def test_intent_request_calling_service(fixture, calls):
|
|||||||
assert "virgo" == call.data.get("hello")
|
assert "virgo" == call.data.get("hello")
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_with_no_action(fixture):
|
async def test_intent_request_calling_service_v2(fixture, calls):
|
||||||
|
"""Test a request for calling a service.
|
||||||
|
|
||||||
|
If this request is done async the test could finish before the action
|
||||||
|
has been executed. Hard to test because it will be a race condition.
|
||||||
|
"""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
data["queryResult"]["action"] = "CallServiceIntent"
|
||||||
|
call_count = len(calls)
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
assert call_count + 1 == len(calls)
|
||||||
|
call = calls[-1]
|
||||||
|
assert "test" == call.domain
|
||||||
|
assert "dialogflow" == call.service
|
||||||
|
assert ["switch.test"] == call.data.get("entity_id")
|
||||||
|
assert "virgo" == call.data.get("hello")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_with_no_action_v1(fixture):
|
||||||
"""Test an intent with no defined action."""
|
"""Test an intent with no defined action."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
del data["result"]["action"]
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
assert "action" not in data["result"]
|
||||||
"result": {
|
|
||||||
"source": "agent",
|
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
|
||||||
"speech": "",
|
|
||||||
"action": "",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {"ZodiacSign": ""},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
@ -373,36 +394,41 @@ async def test_intent_with_no_action(fixture):
|
|||||||
assert "You have not defined an action in your Dialogflow intent." == text
|
assert "You have not defined an action in your Dialogflow intent." == text
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_with_unknown_action(fixture):
|
async def test_intent_with_no_action_v2(fixture):
|
||||||
|
"""Test an intent with no defined action."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
del data["queryResult"]["action"]
|
||||||
|
assert "action" not in data["queryResult"]
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
assert "You have not defined an action in your Dialogflow intent." == text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_with_unknown_action_v1(fixture):
|
||||||
"""Test an intent with an action not defined in the conf."""
|
"""Test an intent with an action not defined in the conf."""
|
||||||
mock_client, webhook_id = fixture
|
mock_client, webhook_id = fixture
|
||||||
data = {
|
data = Data.v1
|
||||||
"id": REQUEST_ID,
|
data["result"]["action"] = "unknown"
|
||||||
"timestamp": REQUEST_TIMESTAMP,
|
|
||||||
"result": {
|
|
||||||
"source": "agent",
|
|
||||||
"resolvedQuery": "my zodiac sign is virgo",
|
|
||||||
"speech": "",
|
|
||||||
"action": "unknown",
|
|
||||||
"actionIncomplete": False,
|
|
||||||
"parameters": {"ZodiacSign": ""},
|
|
||||||
"contexts": [],
|
|
||||||
"metadata": {
|
|
||||||
"intentId": INTENT_ID,
|
|
||||||
"webhookUsed": "true",
|
|
||||||
"webhookForSlotFillingUsed": "false",
|
|
||||||
"intentName": INTENT_NAME,
|
|
||||||
},
|
|
||||||
"fulfillment": {"speech": "", "messages": [{"type": 0, "speech": ""}]},
|
|
||||||
"score": 1,
|
|
||||||
},
|
|
||||||
"status": {"code": 200, "errorType": "success"},
|
|
||||||
"sessionId": SESSION_ID,
|
|
||||||
"originalRequest": None,
|
|
||||||
}
|
|
||||||
response = await mock_client.post(
|
response = await mock_client.post(
|
||||||
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
)
|
)
|
||||||
assert 200 == response.status
|
assert 200 == response.status
|
||||||
text = (await response.json()).get("speech")
|
text = (await response.json()).get("speech")
|
||||||
assert "This intent is not yet configured within Home Assistant." == text
|
assert "This intent is not yet configured within Home Assistant." == text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_with_unknown_action_v2(fixture):
|
||||||
|
"""Test an intent with an action not defined in the conf."""
|
||||||
|
mock_client, webhook_id = fixture
|
||||||
|
data = Data.v2
|
||||||
|
data["queryResult"]["action"] = "unknown"
|
||||||
|
response = await mock_client.post(
|
||||||
|
"/api/webhook/{}".format(webhook_id), data=json.dumps(data)
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
text = (await response.json()).get("fulfillmentText")
|
||||||
|
assert "This intent is not yet configured within Home Assistant." == text
|
||||||
|
Loading…
x
Reference in New Issue
Block a user