2016-03-09 09:25:50 +00:00
|
|
|
"""The tests for the Alexa component."""
|
2015-12-13 06:29:02 +00:00
|
|
|
# pylint: disable=protected-access,too-many-public-methods
|
|
|
|
import unittest
|
|
|
|
import json
|
|
|
|
|
|
|
|
import requests
|
|
|
|
|
|
|
|
from homeassistant import bootstrap, const
|
|
|
|
from homeassistant.components import alexa, http
|
|
|
|
|
2016-02-14 23:08:23 +00:00
|
|
|
from tests.common import get_test_instance_port, get_test_home_assistant
|
2015-12-13 06:29:02 +00:00
|
|
|
|
2016-02-14 20:54:16 +00:00
|
|
|
API_PASSWORD = "test1234"
|
|
|
|
SERVER_PORT = get_test_instance_port()
|
2015-12-13 06:29:02 +00:00
|
|
|
API_URL = "http://127.0.0.1:{}{}".format(SERVER_PORT, alexa.API_ENDPOINT)
|
|
|
|
HA_HEADERS = {const.HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
|
|
|
|
2016-02-14 20:54:16 +00:00
|
|
|
SESSION_ID = 'amzn1.echo-api.session.0000000-0000-0000-0000-00000000000'
|
|
|
|
APPLICATION_ID = 'amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe'
|
|
|
|
REQUEST_ID = 'amzn1.echo-api.request.0000000-0000-0000-0000-00000000000'
|
|
|
|
|
2015-12-13 06:29:02 +00:00
|
|
|
hass = None
|
2016-01-09 00:58:44 +00:00
|
|
|
calls = []
|
2015-12-13 06:29:02 +00:00
|
|
|
|
|
|
|
|
2016-02-15 06:01:30 +00:00
|
|
|
def setUpModule(): # pylint: disable=invalid-name
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Initialize a Home Assistant server for testing this module."""
|
2015-12-13 06:29:02 +00:00
|
|
|
global hass
|
|
|
|
|
2016-02-14 23:08:23 +00:00
|
|
|
hass = get_test_home_assistant()
|
2015-12-13 06:29:02 +00:00
|
|
|
|
|
|
|
bootstrap.setup_component(
|
|
|
|
hass, http.DOMAIN,
|
|
|
|
{http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD,
|
|
|
|
http.CONF_SERVER_PORT: SERVER_PORT}})
|
|
|
|
|
2016-01-09 00:58:44 +00:00
|
|
|
hass.services.register('test', 'alexa', lambda call: calls.append(call))
|
|
|
|
|
2015-12-13 06:29:02 +00:00
|
|
|
bootstrap.setup_component(hass, alexa.DOMAIN, {
|
|
|
|
'alexa': {
|
|
|
|
'intents': {
|
|
|
|
'WhereAreWeIntent': {
|
|
|
|
'speech': {
|
|
|
|
'type': 'plaintext',
|
|
|
|
'text':
|
|
|
|
"""
|
2016-02-14 20:54:16 +00:00
|
|
|
{%- if is_state('device_tracker.paulus', 'home')
|
|
|
|
and is_state('device_tracker.anne_therese',
|
|
|
|
'home') -%}
|
2015-12-13 06:29:02 +00:00
|
|
|
You are both home, you silly
|
|
|
|
{%- else -%}
|
2016-02-14 20:54:16 +00:00
|
|
|
Anne Therese is at {{
|
|
|
|
states("device_tracker.anne_therese")
|
|
|
|
}} and Paulus is at {{
|
|
|
|
states("device_tracker.paulus")
|
|
|
|
}}
|
2015-12-13 06:29:02 +00:00
|
|
|
{% endif %}
|
|
|
|
""",
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'GetZodiacHoroscopeIntent': {
|
|
|
|
'speech': {
|
|
|
|
'type': 'plaintext',
|
2016-01-09 00:58:44 +00:00
|
|
|
'text': 'You told us your sign is {{ ZodiacSign }}.',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'CallServiceIntent': {
|
|
|
|
'speech': {
|
|
|
|
'type': 'plaintext',
|
|
|
|
'text': 'Service called',
|
|
|
|
},
|
|
|
|
'action': {
|
|
|
|
'service': 'test.alexa',
|
|
|
|
'data': {
|
|
|
|
'hello': 1
|
|
|
|
},
|
|
|
|
'entity_id': 'switch.test',
|
2015-12-13 06:29:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
hass.start()
|
|
|
|
|
|
|
|
|
|
|
|
def tearDownModule(): # pylint: disable=invalid-name
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop the Home Assistant server."""
|
2015-12-13 06:29:02 +00:00
|
|
|
hass.stop()
|
|
|
|
|
|
|
|
|
|
|
|
def _req(data={}):
|
|
|
|
return requests.post(API_URL, data=json.dumps(data), timeout=5,
|
|
|
|
headers=HA_HEADERS)
|
|
|
|
|
|
|
|
|
|
|
|
class TestAlexa(unittest.TestCase):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test Alexa."""
|
2015-12-13 06:29:02 +00:00
|
|
|
|
2016-02-15 07:01:49 +00:00
|
|
|
def tearDown(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop everything that was started."""
|
2016-02-15 07:01:49 +00:00
|
|
|
hass.pool.block_till_done()
|
|
|
|
|
2015-12-13 06:29:02 +00:00
|
|
|
def test_launch_request(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test the launch of a request."""
|
2015-12-13 06:29:02 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': True,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2015-12-13 06:29:02 +00:00
|
|
|
},
|
|
|
|
'attributes': {},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'LaunchRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
|
|
|
resp = req.json()
|
|
|
|
self.assertIn('outputSpeech', resp['response'])
|
|
|
|
|
|
|
|
def test_intent_request_with_slots(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test a request with slots."""
|
2015-12-13 06:29:02 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': False,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2015-12-13 06:29:02 +00:00
|
|
|
},
|
|
|
|
'attributes': {
|
|
|
|
'supportedHoroscopePeriods': {
|
|
|
|
'daily': True,
|
|
|
|
'weekly': False,
|
|
|
|
'monthly': False
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'IntentRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z',
|
|
|
|
'intent': {
|
|
|
|
'name': 'GetZodiacHoroscopeIntent',
|
|
|
|
'slots': {
|
|
|
|
'ZodiacSign': {
|
|
|
|
'name': 'ZodiacSign',
|
|
|
|
'value': 'virgo'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
2016-02-14 20:54:16 +00:00
|
|
|
text = req.json().get('response', {}).get('outputSpeech',
|
|
|
|
{}).get('text')
|
2015-12-13 06:29:02 +00:00
|
|
|
self.assertEqual('You told us your sign is virgo.', text)
|
|
|
|
|
2015-12-22 10:08:46 +00:00
|
|
|
def test_intent_request_with_slots_but_no_value(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test a request with slots but no value."""
|
2015-12-22 10:08:46 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': False,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2015-12-22 10:08:46 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2015-12-22 10:08:46 +00:00
|
|
|
},
|
|
|
|
'attributes': {
|
|
|
|
'supportedHoroscopePeriods': {
|
|
|
|
'daily': True,
|
|
|
|
'weekly': False,
|
|
|
|
'monthly': False
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'IntentRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2015-12-22 10:08:46 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z',
|
|
|
|
'intent': {
|
|
|
|
'name': 'GetZodiacHoroscopeIntent',
|
|
|
|
'slots': {
|
|
|
|
'ZodiacSign': {
|
|
|
|
'name': 'ZodiacSign',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
2016-02-14 20:54:16 +00:00
|
|
|
text = req.json().get('response', {}).get('outputSpeech',
|
|
|
|
{}).get('text')
|
2015-12-22 10:08:46 +00:00
|
|
|
self.assertEqual('You told us your sign is .', text)
|
|
|
|
|
2015-12-13 06:29:02 +00:00
|
|
|
def test_intent_request_without_slots(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test a request without slots."""
|
2015-12-13 06:29:02 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': False,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2015-12-13 06:29:02 +00:00
|
|
|
},
|
|
|
|
'attributes': {
|
|
|
|
'supportedHoroscopePeriods': {
|
|
|
|
'daily': True,
|
|
|
|
'weekly': False,
|
|
|
|
'monthly': False
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'IntentRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z',
|
|
|
|
'intent': {
|
|
|
|
'name': 'WhereAreWeIntent',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
2016-02-14 20:54:16 +00:00
|
|
|
text = req.json().get('response', {}).get('outputSpeech',
|
|
|
|
{}).get('text')
|
2015-12-13 06:29:02 +00:00
|
|
|
|
2016-02-14 20:54:16 +00:00
|
|
|
self.assertEqual('Anne Therese is at unknown and Paulus is at unknown',
|
|
|
|
text)
|
2015-12-13 06:29:02 +00:00
|
|
|
|
|
|
|
hass.states.set('device_tracker.paulus', 'home')
|
|
|
|
hass.states.set('device_tracker.anne_therese', 'home')
|
|
|
|
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
2016-02-14 20:54:16 +00:00
|
|
|
text = req.json().get('response', {}).get('outputSpeech',
|
|
|
|
{}).get('text')
|
2015-12-13 06:29:02 +00:00
|
|
|
self.assertEqual('You are both home, you silly', text)
|
|
|
|
|
2016-01-09 00:58:44 +00:00
|
|
|
def test_intent_request_calling_service(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test a request for calling a service."""
|
2016-01-09 00:58:44 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': False,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2016-01-09 00:58:44 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2016-01-09 00:58:44 +00:00
|
|
|
},
|
|
|
|
'attributes': {},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'IntentRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2016-01-09 00:58:44 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z',
|
|
|
|
'intent': {
|
|
|
|
'name': 'CallServiceIntent',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
call_count = len(calls)
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
|
|
|
self.assertEqual(call_count + 1, len(calls))
|
|
|
|
call = calls[-1]
|
|
|
|
self.assertEqual('test', call.domain)
|
|
|
|
self.assertEqual('alexa', call.service)
|
|
|
|
self.assertEqual(['switch.test'], call.data.get('entity_id'))
|
|
|
|
self.assertEqual(1, call.data.get('hello'))
|
|
|
|
|
2015-12-13 06:29:02 +00:00
|
|
|
def test_session_ended_request(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test the request for ending the session."""
|
2015-12-13 06:29:02 +00:00
|
|
|
data = {
|
|
|
|
'version': '1.0',
|
|
|
|
'session': {
|
|
|
|
'new': False,
|
2016-02-14 20:54:16 +00:00
|
|
|
'sessionId': SESSION_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'application': {
|
2016-02-14 20:54:16 +00:00
|
|
|
'applicationId': APPLICATION_ID
|
2015-12-13 06:29:02 +00:00
|
|
|
},
|
|
|
|
'attributes': {
|
|
|
|
'supportedHoroscopePeriods': {
|
|
|
|
'daily': True,
|
|
|
|
'weekly': False,
|
|
|
|
'monthly': False
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'user': {
|
|
|
|
'userId': 'amzn1.account.AM3B00000000000000000000000'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'request': {
|
|
|
|
'type': 'SessionEndedRequest',
|
2016-02-14 20:54:16 +00:00
|
|
|
'requestId': REQUEST_ID,
|
2015-12-13 06:29:02 +00:00
|
|
|
'timestamp': '2015-05-13T12:34:56Z',
|
|
|
|
'reason': 'USER_INITIATED'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
req = _req(data)
|
|
|
|
self.assertEqual(200, req.status_code)
|
|
|
|
self.assertEqual('', req.text)
|