5 from datetime
import datetime
, timedelta
6 from urllib
.parse
import urlencode
8 from django
.test
.client
import Client
9 from django
.test
import TestCase
10 from django
.urls
import reverse
11 from django
.contrib
.auth
import get_user_model
12 from django
.test
.utils
import override_settings
14 from mygpo
.podcasts
.models
import Podcast
, Episode
15 from mygpo
.api
.advanced
import episodes
16 from mygpo
.history
.models
import EpisodeHistoryEntry
17 from mygpo
.test
import create_auth_string
, anon_request
18 from mygpo
.utils
import get_timestamp
21 class AdvancedAPITests(unittest
.TestCase
):
24 User
= get_user_model()
25 self
.password
= 'asdf'
26 self
.username
= 'adv-api-user'
27 self
.user
= User(username
=self
.username
, email
='user@example.com')
28 self
.user
.set_password(self
.password
)
30 self
.user
.is_active
= True
31 self
.client
= Client()
34 'HTTP_AUTHORIZATION': create_auth_string(self
.username
,
40 "podcast": "http://example.com/feed.rss",
41 "episode": "http://example.com/files/s01e20.mp3",
42 "device": "gpodder_abcdef123",
44 "timestamp": "2009-12-12T09:00:00"
47 "podcast": "http://example.org/podcast.php",
48 "episode": "http://ftp.example.org/foo.ogg",
59 def test_episode_actions(self
):
60 response
= self
._upload
_episode
_actions
(self
.user
, self
.action_data
,
62 self
.assertEqual(response
.status_code
, 200, response
.content
)
64 url
= reverse(episodes
, kwargs
={
66 'username': self
.user
.username
,
68 response
= self
.client
.get(url
, {'since': '0'}, **self
.extra
)
69 self
.assertEqual(response
.status_code
, 200, response
.content
)
70 response_obj
= json
.loads(response
.content
.decode('utf-8'))
71 actions
= response_obj
['actions']
72 self
.assertTrue(self
.compare_action_list(self
.action_data
, actions
))
74 def test_invalid_client_id(self
):
75 """ Invalid Client ID should return 400 """
76 action_data
= copy
.deepcopy(self
.action_data
)
77 action_data
[0]['device'] = "gpodder@abcdef123"
79 response
= self
._upload
_episode
_actions
(self
.user
, action_data
,
82 self
.assertEqual(response
.status_code
, 400, response
.content
)
84 def _upload_episode_actions(self
, user
, action_data
, extra
):
85 url
= reverse(episodes
, kwargs
={
87 'username': self
.user
.username
,
89 return self
.client
.post(url
, json
.dumps(action_data
),
90 content_type
="application/json",
93 def compare_action_list(self
, as1
, as2
):
97 if self
.compare_actions(a1
, a2
):
101 raise ValueError('%s not found in %s' % (a1
, as2
))
106 def compare_actions(self
, a1
, a2
):
107 for key
, val
in a1
.items():
108 if a2
.get(key
, None) != val
:
113 class SubscriptionAPITests(unittest
.TestCase
):
114 """ Tests the Subscription API """
117 User
= get_user_model()
118 self
.password
= 'asdf'
119 self
.username
= 'subscription-api-user'
120 self
.device_uid
= 'test-device'
121 self
.user
= User(username
=self
.username
, email
='user@example.com')
122 self
.user
.set_password(self
.password
)
124 self
.user
.is_active
= True
125 self
.client
= Client()
128 'HTTP_AUTHORIZATION': create_auth_string(self
.username
,
133 'add': ['http://example.com/podcast.rss'],
136 self
.url
= reverse('subscriptions-api', kwargs
={
138 'username': self
.user
.username
,
139 'device_uid': self
.device_uid
,
145 def test_set_get_subscriptions(self
):
146 """ Tests that an upload subscription is returned back correctly """
148 # upload a subscription
149 response
= self
.client
.post(self
.url
, json
.dumps(self
.action_data
),
150 content_type
="application/json",
152 self
.assertEqual(response
.status_code
, 200, response
.content
)
154 # verify that the subscription is returned correctly
155 response
= self
.client
.get(self
.url
, {'since': '0'}, **self
.extra
)
156 self
.assertEqual(response
.status_code
, 200, response
.content
)
157 response_obj
= json
.loads(response
.content
.decode('utf-8'))
158 self
.assertEqual(self
.action_data
['add'], response_obj
['add'])
159 self
.assertEqual([], response_obj
.get('remove', []))
161 def test_unauth_request(self
):
162 """ Tests that an unauthenticated request gives a 401 response """
163 response
= self
.client
.get(self
.url
, {'since': '0'})
164 self
.assertEqual(response
.status_code
, 401, response
.content
)
167 class DirectoryTest(TestCase
):
168 """ Test Directory API """
171 self
.podcast
= Podcast
.objects
.get_or_create_for_url(
172 'http://example.com/directory-podcast.xml',
174 'title': 'My Podcast',
177 self
.episode
= Episode
.objects
.get_or_create_for_url(
179 'http://example.com/directory-podcast/1.mp3',
181 'title': 'My Episode',
184 self
.client
= Client()
186 def test_episode_info(self
):
187 """ Test that the expected number of queries is executed """
188 url
= reverse('api-episode-info') + '?' + urlencode(
189 (('podcast', self
.podcast
.url
), ('url', self
.episode
.url
)))
191 resp
= self
.client
.get(url
)
193 self
.assertEqual(resp
.status_code
, 200)
196 class EpisodeActionTests(TestCase
):
199 self
.podcast
= Podcast
.objects
.get_or_create_for_url(
200 'http://example.com/directory-podcast.xml',
202 'title': 'My Podcast',
205 self
.episode
= Episode
.objects
.get_or_create_for_url(
207 'http://example.com/directory-podcast/1.mp3',
209 'title': 'My Episode',
212 User
= get_user_model()
213 self
.password
= 'asdf'
214 self
.username
= 'adv-api-user'
215 self
.user
= User(username
=self
.username
, email
='user@example.com')
216 self
.user
.set_password(self
.password
)
218 self
.user
.is_active
= True
219 self
.client
= Client()
221 'HTTP_AUTHORIZATION': create_auth_string(self
.username
,
226 self
.episode
.delete()
227 self
.podcast
.delete()
230 @override_settings(MAX_EPISODE_ACTIONS
=10)
231 def test_limit_actions(self
):
232 """ Test that max MAX_EPISODE_ACTIONS episodes are returned """
235 t
= datetime
.utcnow()
237 timestamp
= t
- timedelta(seconds
=n
)
238 EpisodeHistoryEntry
.objects
.create(
239 timestamp
= timestamp
,
240 episode
= self
.episode
,
242 action
= EpisodeHistoryEntry
.DOWNLOAD
,
244 timestamps
.append(timestamp
)
246 url
= reverse(episodes
, kwargs
={
248 'username': self
.user
.username
,
250 response
= self
.client
.get(url
, {'since': '0'}, **self
.extra
)
251 self
.assertEqual(response
.status_code
, 200, response
.content
)
252 response_obj
= json
.loads(response
.content
.decode('utf-8'))
253 actions
= response_obj
['actions']
255 # 10 actions should be returned
256 self
.assertEqual(len(actions
), 10)
258 timestamps
= sorted(timestamps
)
260 # the first 10 actions, according to their timestamp should be returned
261 for action
, timestamp
in zip(actions
, timestamps
):
262 self
.assertEqual(timestamp
.isoformat(), action
['timestamp'])
264 # the `timestamp` field in the response should be the timestamp of the
265 # last returned action
267 get_timestamp(timestamps
[9]),
268 response_obj
['timestamp']
272 def test_no_actions(self
):
273 """ Test when there are no actions to return """
275 t1
= get_timestamp(datetime
.utcnow())
277 url
= reverse(episodes
, kwargs
={
279 'username': self
.user
.username
,
281 response
= self
.client
.get(url
, {'since': '0'}, **self
.extra
)
282 self
.assertEqual(response
.status_code
, 200, response
.content
)
283 response_obj
= json
.loads(response
.content
.decode('utf-8'))
284 actions
= response_obj
['actions']
286 # 10 actions should be returned
287 self
.assertEqual(len(actions
), 0)
289 returned
= response_obj
['timestamp']
290 t2
= get_timestamp(datetime
.utcnow())
291 # the `timestamp` field in the response should be the timestamp of the
292 # last returned action
293 self
.assertGreaterEqual(returned
, t1
)
294 self
.assertGreaterEqual(t2
, returned
)