9fd1d42442d267ebb2429ea05fbd8df3108bc56c
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from django
.test
import TestCase
19 from django
.test
.client
import Client
20 from django
.contrib
.auth
.models
import User
21 from mygpo
.api
.models
import Device
, Podcast
, Subscription
, SubscriptionAction
, UserProfile
, EpisodeAction
22 from django
.http
import HttpRequest
23 from mygpo
.api
.simple
import subscriptions
24 from mygpo
.api
.advanced
import devices
28 #try to import the JSON module (if we are on Python 2.6)
31 # No JSON module available - fallback to simplejson (Python < 2.6)
32 print "No JSON module available - fallback to simplejson (Python < 2.6)"
33 import simplejson
as json
36 class SyncTest(TestCase
):
37 # FIXME: Broken testcase - please fix!
38 def tXest_sync_actions(self
):
39 # this test does not yet complete when run as a unit test
40 # because django does not set up the views
41 u
= User
.objects
.create(username
='u')
42 d1
= Device
.objects
.create(name
='d1', type='other', user
=u
)
43 d2
= Device
.objects
.create(name
='d2', type='other', user
=u
)
44 p1
= Podcast
.objects
.create(title
='p1', url
='http://p1.com/')
45 p2
= Podcast
.objects
.create(title
='p2', url
='http://p2.com/')
46 p3
= Podcast
.objects
.create(title
='p3', url
='http://p3.com/')
47 p4
= Podcast
.objects
.create(title
='p4', url
='http://p4.com/')
49 s1
= SubscriptionAction
.objects
.create(device
=d1
, podcast
=p1
, action
='1')
51 s2
= SubscriptionAction
.objects
.create(device
=d2
, podcast
=p2
, action
='1')
53 u2
= SubscriptionAction
.objects
.create(device
=d2
, podcast
=p2
, action
='-1')
55 s3
= SubscriptionAction
.objects
.create(device
=d1
, podcast
=p3
, action
='1')
57 s3_
= SubscriptionAction
.objects
.create(device
=d2
, podcast
=p3
, action
='1')
59 s4
= SubscriptionAction
.objects
.create(device
=d2
, podcast
=p4
, action
='1')
61 u3
= SubscriptionAction
.objects
.create(device
=d2
, podcast
=p3
, action
='-1')
64 #d2: p2, -p2, p3, p4, -p3
71 sa1
= d1
.get_sync_actions()
72 sa2
= d2
.get_sync_actions()
74 self
.assertEqual( len(sa1
), 2)
75 self
.assertEqual( len(sa2
), 1)
77 self
.assertEqual( sa1
[p4
].device
, d2
)
78 self
.assertEqual( sa1
[p4
].podcast
, p4
)
79 self
.assertEqual( sa1
[p4
].action
, 1)
81 self
.assertEqual( sa1
[p3
].device
, d2
)
82 self
.assertEqual( sa1
[p3
].podcast
, p3
)
83 self
.assertEqual( sa1
[p3
].action
, -1)
85 self
.assertEqual( sa2
[p1
].device
, d1
)
86 self
.assertEqual( sa2
[p1
].podcast
, p1
)
87 self
.assertEqual( sa2
[p1
].action
, 1)
89 class AdvancedAPITest(TestCase
):
91 self
.user
= User
.objects
.create(username
='adv', email
='adv@mygpo')
92 self
.user
.set_password('adv1')
94 self
.device1
= Device
.objects
.create(user
=self
.user
, name
='d1', uid
='uid1', type='desktop')
95 self
.device2
= Device
.objects
.create(user
=self
.user
, name
='d2', uid
='uid2', type='mobile')
96 self
.url1
= 'http://example.com/feed.rss'
97 self
.url2
= 'http://example.org/podcast.php'
98 self
.url3
= 'http://example.net/foo.xml'
100 self
.client
= Client()
101 l
= self
.client
.login(username
='adv', password
='adv1')
102 self
.assertEqual(l
, True)
104 def test_device_list(self
):
105 response
= self
.client
.get('/api/1/devices/%s.json' % self
.user
.username
)
106 json_list
= response
.content
107 list = json
.loads(json_list
)
109 self
.assertEqual(response
.status_code
, 200)
110 self
.assertEqual(len(list), 2)
111 self
.assertEqual(list[0], {u
'id': u
'uid1', u
'caption': u
'd1', u
'type': u
'desktop', u
'subscriptions': 0})
112 self
.assertEqual(list[1], {u
'id': u
'uid2', u
'caption': u
'd2', u
'type': u
'mobile', u
'subscriptions': 0})
114 def test_rename_device(self
):
115 req
= {'caption': 'd2!', 'type': 'server'}
116 reqs
= json
.dumps(req
)
118 self
.client
.post('/api/1/devices/%s/%s.json' % (self
.user
.username
, self
.device2
.uid
), data
={'data': reqs
})
120 response
= self
.client
.get('/api/1/devices/%s.json' % self
.user
.username
)
121 json_list
= response
.content
122 list = json
.loads(json_list
)
123 self
.assertEqual(response
.status_code
, 200)
124 self
.assertEqual(len(list), 2)
125 self
.assertEqual(list[0], {u
'id': u
'uid1', u
'caption': u
'd1', u
'type': u
'desktop', u
'subscriptions': 0})
126 self
.assertEqual(list[1], {u
'id': u
'uid2', u
'caption': u
'd2!', u
'type': u
'server', u
'subscriptions': 0})
129 def test_add_remove_subscriptions(self
):
130 req
= {"add": [self
.url1
, self
.url2
, self
.url3
]}
131 reqs
= json
.dumps(req
)
133 #adding 3 subscriptions
134 response
= self
.client
.post('/api/1/subscriptions/%s/%s.json' % (self
.user
.username
, self
.device1
.uid
), data
={'data': reqs
})
135 self
.assertEqual(response
.status_code
, 200)
137 resp
= json
.loads(response
.content
)
138 add_timestamp
= resp
['timestamp']
140 #qerying subscription changes
141 response
= self
.client
.get('/api/1/subscriptions/%s/%s.json' % (self
.user
.username
, self
.device1
.uid
), {'since': add_timestamp
-1})
142 changes
= json
.loads(response
.content
)
144 self
.assertEqual(changes
['add'], [self
.url1
, self
.url2
, self
.url3
])
145 self
.assertEqual(len(changes
['remove']), 0)
147 #removing the 1st and 3rd subscription
148 req
= {"add": [], 'remove': [self
.url1
, self
.url3
]}
149 reqs
= json
.dumps(req
)
151 response
= self
.client
.post('/api/1/subscriptions/%s/%s.json' % (self
.user
.username
, self
.device1
.uid
), data
={'data': reqs
})
152 self
.assertEqual(response
.status_code
, 200)
154 resp
= json
.loads(response
.content
)
155 timestamp
= resp
['timestamp']
157 #changes since beginning, should return 1 addition, 2 removals
158 response
= self
.client
.get('/api/1/subscriptions/%s/%s.json' % (self
.user
.username
, self
.device1
.uid
), {'since': add_timestamp
-1})
159 changes
= json
.loads(response
.content
)
160 self
.assertEqual(changes
['add'], [self
.url2
])
161 self
.assertEqual(changes
['remove'], [self
.url1
, self
.url3
])
164 def test_episode_update(self
):
165 req
= [{"podcast": self
.url1
,
166 "episode": "http://example.com/files/s01e20.mp3",
167 "device": self
.device1
.uid
,
168 "action": "download",
169 "timestamp": "2009-12-12T09:00:00"},
170 {"podcast": self
.url2
,
171 "episode": "http://ftp.example.org/foo.ogg",
173 "position": "01:00:00"}]
174 reqs
= json
.dumps(req
)
175 response
= self
.client
.post('/api/1/episodes/%s.json' % self
.user
.username
, data
={'data': reqs
})
176 self
.assertEqual(response
.status_code
, 200)
178 resp
= json
.loads(response
.content
)
179 timestamp
= resp
['timestamp']
181 response
= self
.client
.get('/api/1/episodes/%s.json' % self
.user
.username
)
182 changes
= json
.loads(response
.content
)
183 self
.assertEqual(len(changes
['actions']), 2)
184 self
.assertTrue(self
.hash_subset({u
'action': u
'download', u
'podcast': self
.url1
, u
'episode': u
'http://example.com/files/s01e20.mp3'}, changes
['actions'][0]))
185 self
.assertTrue(self
.hash_subset({u
'action': u
'play', u
'podcast': self
.url2
, u
'episode': u
'http://ftp.example.org/foo.ogg'}, changes
['actions'][1]))
187 # #currently fails, seems to be a bug in the test client
188 # response = self.client.get('/api/1/episodes/%s.json' % self.user.username, {'podcast': self.url2})
189 # self.assertEqual(response.status_code, 200)
190 # changes = json.loads(response.content)
191 # self.assertEqual(len(changes['actions']), 1)
192 # self.assertTrue(self.hash_subset({u'action': u'play', u'podcast': self.url2, u'episode': u'http://ftp.example.org/foo.ogg'}, changes['actions'][0]))
195 def hash_subset(self
, hash1
, hash2
):
196 for x
in hash1
.keys():
199 if hash2
[x
] != hash1
[x
]:
204 class SimpleAPITest(TestCase
):
209 self
.user
= User
.objects
.create(username
=un
, email
='u@mygpo')
210 self
.user
.set_password(pw
)
212 UserProfile
.objects
.create(user
=self
.user
)
219 self
.p1
= 'http://www.podcast1.com'
220 self
.p2
= 'http://www.podcast2.com'
221 self
.p3
= 'http://www.podcast3.com'
223 self
.client
= Client()
224 login
= self
.client
.login(username
=un
, password
=pw
)
225 self
.assertEqual(login
, True)
227 def test_put_get_txt(self
):
231 data_txt_1
= '%s\n%s\n' % (self
.p1
, self
.p2
)
232 data_txt_2
= '%s\n%s\n' % (self
.p2
, self
.p3
)
234 #1. put 2 new podcasts
235 self
.req
.raw_post_data
= data_txt_1
236 put
= subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d1
, format
=f1
)
237 # Successful requests should return the empty string + status code 200
238 self
.assertEqual(put
.status_code
, 200)
239 self
.assertEqual(put
.content
, '')
242 device
= Device
.objects
.get(uid
=d1
, user
=self
.user
)
245 s
= [p
.podcast
for p
in device
.get_subscriptions()]
246 urls
= [p
.url
for p
in s
]
247 self
.assertEqual(len(urls
), 2)
248 self
.assertEqual(urls
[0], self
.p1
)
249 self
.assertEqual(urls
[1], self
.p2
)
251 #2. put 1 new podcast and delete 1 old
253 self
.req
.raw_post_data
= data_txt_2
254 subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d1
, format
=f1
)
257 s
= [p
.podcast
for p
in device
.get_subscriptions()]
258 urls
= [p
.url
for p
in s
]
259 self
.assertEqual( len(s
), 2)
260 self
.assertEqual(urls
[0], self
.p2
)
261 self
.assertEqual(urls
[1], self
.p3
)
263 def test_put_get_json(self
):
267 data_json_1
= '[\n"%s",\n"%s"\n]' % (self
.p1
, self
.p2
)
268 data_json_2
= '[\n"%s",\n"%s"\n]' % (self
.p2
, self
.p3
)
270 #1. put 2 new podcasts
271 self
.req
.raw_post_data
= data_json_1
272 subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d2
, format
=f2
)
275 device
= Device
.objects
.get(uid
=d2
, user
=self
.user
)
278 s
= [p
.podcast
for p
in device
.get_subscriptions()]
279 urls
= [p
.url
for p
in s
]
280 self
.assertEqual( len(s
), 2)
281 self
.assertEqual(urls
[0], self
.p1
)
282 self
.assertEqual(urls
[1], self
.p2
)
284 #2. put 1 new podcast and delete 1 old
286 self
.req
.raw_post_data
= data_json_2
287 subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d2
, format
=f2
)
290 s
= [p
.podcast
for p
in device
.get_subscriptions()]
291 urls
= [p
.url
for p
in s
]
292 self
.assertEqual( len(s
), 2)
293 self
.assertEqual(urls
[0], self
.p2
)
294 self
.assertEqual(urls
[1], self
.p3
)
296 def test_put_get_opml(self
):
300 data_opml_1
= '<?xml version="1.0" encoding="UTF-8"?>\n<opml version="2.0">\n<head>\n<title>subscription list</title>\n\
301 <dateCreated>Tue, 01 Dec 2009 10:06:18 +0000</dateCreated>\n</head>\n<body>\n\
302 <outline text="p1" title="p1" type="rss" xmlUrl="%s"/>\n\
303 <outline text="p2" title="p2" type="rss" xmlUrl="%s"/>\n\
304 </body>\n</opml>\n' % (self
.p1
, self
.p2
)
305 data_opml_2
= '<?xml version="1.0" encoding="UTF-8"?><opml version="2.0"><head><title>subscription list</title>\
306 <dateCreated>Tue, 01 Dec 2009 10:06:18 +0000</dateCreated></head><body>\
307 <outline text="p2" title="p2" type="rss" xmlUrl="%s"/>\
308 <outline text="p3" title="p3" type="rss" xmlUrl="%s"/>\
309 </body></opml>' % (self
.p2
, self
.p3
)
311 #1. put 2 new podcasts
312 self
.req
.raw_post_data
= data_opml_1
313 subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d3
, format
=f3
)
316 device
= Device
.objects
.get(uid
=d3
, user
=self
.user
)
319 s
= [p
.podcast
for p
in device
.get_subscriptions()]
320 urls
= [p
.url
for p
in s
]
321 self
.assertEqual( len(s
), 2)
322 self
.assertEqual(urls
[0], self
.p1
)
323 self
.assertEqual(urls
[1], self
.p2
)
325 #2. put 1 new podcast and delete 1 old
327 self
.req
.raw_post_data
= data_opml_2
328 subscriptions(request
=self
.req
, username
=self
.user
.username
, device_uid
=d3
, format
=f3
)
331 s
= [p
.podcast
for p
in device
.get_subscriptions()]
332 urls
= [p
.url
for p
in s
]
333 self
.assertEqual( len(s
), 2)
334 self
.assertEqual(urls
[0], self
.p2
)
335 self
.assertEqual(urls
[1], self
.p3
)
338 def test_put_json_client(self
):
340 urls
= [self
.p1
, self
.p2
, self
.p3
]
341 reqs
= json
.dumps(urls
)
343 #put json - add 3 podcasts
344 response
= self
.client
.put('/subscriptions/%s/%s.json' % (self
.user
.username
, device
), data
={'raw_post_data':reqs
})
345 self
.assertEqual(response
.status_code
, 200)
346 self
.assertEqual(response
.content
, '')
348 #get json - get 3 podcasts
349 response
= self
.client
.get('/subscriptions/%s/%s.json' % (self
.user
.username
, device
), {})
350 get
= json
.loads(response
.content
)
352 self
.assertEqual(get
, [self
.p1
, self
.p2
, self
.p3
])
354 def test_put_txt_get_json_client(self
):
356 reqs
= '%s\n%s\n%s\n' % (self
.p1
, self
.p2
, self
.p3
)
358 #put txt - add 3 podcasts
359 response
= self
.client
.put('/subscriptions/%s/%s.txt' % (self
.user
.username
, device
), data
={'raw_post_data':reqs
})
360 self
.assertEqual(response
.status_code
, 200)
361 self
.assertEqual(response
.content
, '')
363 #qerying subscription changes
364 response
= self
.client
.get('/subscriptions/%s/%s.json' % (self
.user
.username
, device
), {})
365 get
= json
.loads(response
.content
)
367 self
.assertEqual(get
, [self
.p1
, self
.p2
, self
.p3
])
369 def test_put_opml_get_json_client(self
):
371 reqs
= '<?xml version="1.0" encoding="UTF-8"?>\n<opml version="2.0">\n<head>\n<title>subscription list</title>\n\
372 <dateCreated>Tue, 01 Dec 2009 10:06:18 +0000</dateCreated>\n</head>\n<body>\n\
373 <outline text="p1" title="p1" type="rss" xmlUrl="%s"/>\n\
374 <outline text="p2" title="p2" type="rss" xmlUrl="%s"/>\n\
375 <outline text="p3" title="p3" type="rss" xmlUrl="%s"/>\n\
376 </body>\n</opml>\n' % (self
.p1
, self
.p2
, self
.p3
)
378 #put json - add 3 podcasts
379 response
= self
.client
.put('/subscriptions/%s/%s.opml' % (self
.user
.username
, device
), data
={'raw_post_data':reqs
})
380 self
.assertEqual(response
.status_code
, 200)
381 self
.assertEqual(response
.content
, '')
383 #qerying subscription changes
384 response
= self
.client
.get('/subscriptions/%s/%s.json' % (self
.user
.username
, device
), {})
385 get
= json
.loads(response
.content
)
387 self
.assertEqual(get
, [self
.p1
, self
.p2
, self
.p3
])
389 def test_put_invalid_format(self
):
391 urls
= [self
.p1
, self
.p2
, self
.p3
]
392 reqs
= json
.dumps(urls
)
394 #put json - add 3 podcasts
395 response
= self
.client
.put('/subscriptions/%s/%s.iso' % (self
.user
.username
, device
), data
={'raw_post_data':reqs
})
396 self
.assertEqual(response
.status_code
, 400)
397 self
.assertEqual(response
.content
, 'Invalid format')
400 def test_put_valid_json_get_invalid_format(self
):
402 urls
= [self
.p1
, self
.p2
, self
.p3
]
403 reqs
= json
.dumps(urls
)
405 #put json - add 3 podcasts
406 response
= self
.client
.put('/subscriptions/%s/%s.json' % (self
.user
.username
, device
), data
={'raw_post_data':reqs
})
407 self
.assertEqual(response
.status_code
, 200)
408 self
.assertEqual(response
.content
, '')
410 #qerying subscription changes
411 response
= self
.client
.get('/subscriptions/%s/%s.iso' % (self
.user
.username
, device
), {})
412 self
.assertEqual(response
.status_code
, 400)
413 self
.assertEqual(response
.content
, 'Invalid format')
415 def test_get_invalid_device(self
):
418 #qerying subscription changes
419 response
= self
.client
.get('/subscriptions/%s/%s.txt' % (self
.user
.username
, device
), {})
420 self
.assertEqual(response
.status_code
, 404)
421 self
.assertEqual(response
.content
, 'Invalid device ID')
423 def test_get_invalid_user(self
):
427 #qerying subscription changes
428 response
= self
.client
.get('/subscriptions/%s/%s.txt' % (username
, device
), {})
429 self
.assertEqual(response
.status_code
, 401)
430 self
.assertEqual(response
.content
, 'Invalid user')