Repository: tsifrer/python-twitch-client

Issue:
TypeError upon empty games search results.
## Description
Upon searching for a game using the `games()` function in `twitch.api.search.Search`, if the query yields no results a `TypeError` is thrown.

## Expected Behavior
`games()` should return an empty list like the rest of the Search functions when no results are returned from Twitch.

## Actual Behavior
Since the search function tries to perform a list comprehension on the `None` response of `self._request_get()`, a TypeError is raised and the program is terminated.

## Possible Fix
Do a quick sanity check on the datatype of `response` from `self._request_get()` in games() before trying to parse it. 

## Steps to Reproduce
Create a client instance and search for a game title that doesn't exist.

```python
from twitch import TwitchClient
client = TwitchClient('<my client id>')
client.search.games('This is not a valid game title')
```

This should cause a TypeError to get raised with the following output:

```
Traceback (most recent call last):
  File "crash_test.py", line 3, in <module>
    client.search.games('This is not a valid game title')
  File "C:\Python36\lib\site-packages\twitch\api\search.py", line 27, in games
    return [Game.construct_from(x) for x in response['games']]
TypeError: 'NoneType' object is not iterable
```

## Context
I was doing some exploratory research into making a small script to sync a private streaming community's live stream metadata (Game title, stream title, etc) to their twitch restream.

## My Environment
* Windows 10
* Python 3.6.4
* python-twitch-client 0.5.1 installed via pip3

Comments:

Patch:
diff --git a/twitch/api/search.py b/twitch/api/search.py
index 98ae2e9..415e8e3 100644
--- a/twitch/api/search.py
+++ b/twitch/api/search.py
@@ -16,7 +16,7 @@ class Search(TwitchAPI):
             'offset': offset
         }
         response = self._request_get('search/channels', params=params)
-        return [Channel.construct_from(x) for x in response['channels']]
+        return [Channel.construct_from(x) for x in response['channels'] or []]
 
     def games(self, query, live=False):
         params = {
@@ -24,7 +24,7 @@ class Search(TwitchAPI):
             'live': live,
         }
         response = self._request_get('search/games', params=params)
-        return [Game.construct_from(x) for x in response['games']]
+        return [Game.construct_from(x) for x in response['games'] or []]
 
     def streams(self, query, limit=25, offset=0, hls=None):
         if limit > 100:
@@ -38,4 +38,4 @@ class Search(TwitchAPI):
             'hls': hls
         }
         response = self._request_get('search/streams', params=params)
-        return [Stream.construct_from(x) for x in response['streams']]
+        return [Stream.construct_from(x) for x in response['streams'] or []]


Test patch:
diff --git a/tests/api/test_search.py b/tests/api/test_search.py
index 9869ea3..3789831 100644
--- a/tests/api/test_search.py
+++ b/tests/api/test_search.py
@@ -62,6 +62,23 @@ def test_channels_raises_if_wrong_params_are_passed_in(param, value):
         client.search.channels('mah query', **kwargs)
 
 
+@responses.activate
+def test_channels_does_not_raise_if_no_channels_were_found():
+    response = {'channels': None}
+    responses.add(responses.GET,
+                  '{}search/channels'.format(BASE_URL),
+                  body=json.dumps(response),
+                  status=200,
+                  content_type='application/json')
+
+    client = TwitchClient('client id')
+
+    channels = client.search.channels('mah bad query')
+
+    assert len(responses.calls) == 1
+    assert len(channels) == 0
+
+
 @responses.activate
 def test_games():
     response = {
@@ -86,6 +103,23 @@ def test_games():
     assert game.name == example_game['name']
 
 
+@responses.activate
+def test_games_does_not_raise_if_no_games_were_found():
+    response = {'games': None}
+    responses.add(responses.GET,
+                  '{}search/games'.format(BASE_URL),
+                  body=json.dumps(response),
+                  status=200,
+                  content_type='application/json')
+
+    client = TwitchClient('client id')
+
+    games = client.search.games('mah bad query')
+
+    assert len(responses.calls) == 1
+    assert len(games) == 0
+
+
 @responses.activate
 def test_streams():
     response = {
@@ -123,3 +157,20 @@ def test_streams_raises_if_wrong_params_are_passed_in(param, value):
     kwargs = {param: value}
     with pytest.raises(TwitchAttributeException):
         client.search.streams('mah query', **kwargs)
+
+
+@responses.activate
+def test_streams_does_not_raise_if_no_streams_were_found():
+    response = {'streams': None}
+    responses.add(responses.GET,
+                  '{}search/streams'.format(BASE_URL),
+                  body=json.dumps(response),
+                  status=200,
+                  content_type='application/json')
+
+    client = TwitchClient('client id')
+
+    streams = client.search.streams('mah bad query')
+
+    assert len(responses.calls) == 1
+    assert len(streams) == 0