Merge lp:~michael.nelson/ubuntu-webcatalog/993813-cannot-import-for-purchase-with-archive-id-none into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Łukasz Czyżykowski
Approved revision: 120
Merged at revision: 116
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/993813-cannot-import-for-purchase-with-archive-id-none
Merge into: lp:ubuntu-webcatalog
Diff against target: 583 lines (+390/-61)
7 files modified
src/webcatalog/forms.py (+14/-3)
src/webcatalog/migrations/0021_add_archive_id.py (+166/-0)
src/webcatalog/models/applications.py (+1/-0)
src/webcatalog/tests/factory.py (+3/-2)
src/webcatalog/tests/test_commands.py (+99/-55)
src/webcatalog/tests/test_data/sca_apps.txt (+43/-1)
src/webcatalog/tests/test_forms.py (+64/-0)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/993813-cannot-import-for-purchase-with-archive-id-none
Reviewer Review Type Date Requested Status
Łukasz Czyżykowski (community) Approve
Review via email: mp+104546@code.launchpad.net

Commit message

Add Application.application_id so we have a unique ref for apps, now that archive_id is nullable (for arb apps) in the sca api.

Description of the change

Overview
========

Updates the ForPurchaseApplicationForm so that, when interpreting API available apps from sca, it does not barf on archive_id=null, but instead tries to grab an application using the application_id instead.

I've also moved a bunch of tests for extras that were in the ImportForPurchaseAppsTestCase, but are really for ImportAppInstallTestCase.

Initially, I thought arb apps should be saved with archive_id=None, but as Application.archive_id is a simple CharField in this app, the default empty value is ''. We could also pull in our custom ArchiveId field and update the default empty value to None - see what you think.

`fab test`

To post a comment you must log in.
120. By Michael Nelson

Merged trunk and resolved conflicts.

Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/forms.py'
2--- src/webcatalog/forms.py 2012-05-03 12:39:31 +0000
3+++ src/webcatalog/forms.py 2012-05-03 13:10:29 +0000
4@@ -144,10 +144,21 @@
5 if 'debtags' in app_data and app_data['debtags']:
6 app_data['debtags'] = json.dumps([get_hw_short_description(x)
7 for x in app_data['debtags']])
8+ app_data['application_id'] = app_data.get('id')
9+
10 try:
11- instance = Application.objects.get(
12- archive_id=app_data.get('archive_id'),
13- distroseries=distroseries)
14+ # XXX 2012-05-03 michaeln bug=993813 We can update to use
15+ # application_id only below once an import runs on production
16+ # setting the application_ids.
17+ archive_id = app_data.get('archive_id')
18+ if archive_id:
19+ instance = Application.objects.get(
20+ archive_id=archive_id,
21+ distroseries=distroseries)
22+ else:
23+ instance = Application.objects.get(
24+ application_id=app_data['application_id'],
25+ distroseries=distroseries)
26 except Application.DoesNotExist:
27 instance = None
28
29
30=== added file 'src/webcatalog/migrations/0021_add_archive_id.py'
31--- src/webcatalog/migrations/0021_add_archive_id.py 1970-01-01 00:00:00 +0000
32+++ src/webcatalog/migrations/0021_add_archive_id.py 2012-05-03 13:10:29 +0000
33@@ -0,0 +1,166 @@
34+# encoding: utf-8
35+import datetime
36+from south.db import db
37+from south.v2 import SchemaMigration
38+from django.db import models
39+
40+class Migration(SchemaMigration):
41+
42+ def forwards(self, orm):
43+
44+ # Adding field 'Application.application_id'
45+ db.add_column('webcatalog_application', 'application_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
46+
47+
48+ def backwards(self, orm):
49+
50+ # Deleting field 'Application.application_id'
51+ db.delete_column('webcatalog_application', 'application_id')
52+
53+
54+ models = {
55+ 'auth.group': {
56+ 'Meta': {'object_name': 'Group'},
57+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
58+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
59+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
60+ },
61+ 'auth.permission': {
62+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
63+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
64+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
65+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
66+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
67+ },
68+ 'auth.user': {
69+ 'Meta': {'object_name': 'User'},
70+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
71+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
72+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
73+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
74+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
76+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
77+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
78+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
79+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
80+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
81+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
82+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
83+ },
84+ 'contenttypes.contenttype': {
85+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
86+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
87+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
88+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
89+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
90+ },
91+ 'webcatalog.application': {
92+ 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'},
93+ 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
94+ 'application_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
95+ 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
96+ 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
97+ 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
98+ 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
99+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
100+ 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
101+ 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
102+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
103+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
104+ 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
105+ 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
106+ 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
107+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108+ 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
109+ 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
110+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
111+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
112+ 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
113+ 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
114+ 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
115+ 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
116+ 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
117+ 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
118+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
119+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
120+ 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
121+ },
122+ 'webcatalog.applicationmedia': {
123+ 'Meta': {'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
124+ 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
125+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
127+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
128+ },
129+ 'webcatalog.consumer': {
130+ 'Meta': {'object_name': 'Consumer'},
131+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
132+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
133+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
134+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'MbyfjpwvpAZhZPLmGbEXCNhSGMCWCv'", 'max_length': '255', 'blank': 'True'}),
135+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
136+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
137+ },
138+ 'webcatalog.department': {
139+ 'Meta': {'object_name': 'Department'},
140+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
141+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
142+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
143+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
144+ },
145+ 'webcatalog.distroseries': {
146+ 'Meta': {'object_name': 'DistroSeries'},
147+ 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
148+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
149+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
150+ },
151+ 'webcatalog.exhibit': {
152+ 'Meta': {'object_name': 'Exhibit'},
153+ 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
154+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
155+ 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
156+ 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
157+ 'html': ('django.db.models.fields.TextField', [], {}),
158+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159+ 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
160+ 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
161+ 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
162+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
163+ },
164+ 'webcatalog.machine': {
165+ 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
166+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
167+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
168+ 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
169+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
170+ 'package_list': ('django.db.models.fields.TextField', [], {}),
171+ 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
172+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
173+ },
174+ 'webcatalog.nonce': {
175+ 'Meta': {'object_name': 'Nonce'},
176+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
177+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
178+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
179+ 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
180+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
181+ },
182+ 'webcatalog.reviewstatsimport': {
183+ 'Meta': {'object_name': 'ReviewStatsImport'},
184+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
185+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
186+ 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
187+ },
188+ 'webcatalog.token': {
189+ 'Meta': {'object_name': 'Token'},
190+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
191+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
192+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
193+ 'token': ('django.db.models.fields.CharField', [], {'default': "'afduSDFNmldZMjYZFoBLOhXEULKLrbmDGPKtuoHEUVLCyheFhY'", 'max_length': '50', 'primary_key': 'True'}),
194+ 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'obbeyYIEgkoxRBtOlkaXbtfgOGnkWUVIppSANzntzFEMuOytlC'", 'max_length': '50'}),
195+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
196+ }
197+ }
198+
199+ complete_apps = ['webcatalog']
200
201=== modified file 'src/webcatalog/models/applications.py'
202--- src/webcatalog/models/applications.py 2012-04-13 20:45:03 +0000
203+++ src/webcatalog/models/applications.py 2012-05-03 13:10:29 +0000
204@@ -89,6 +89,7 @@
205 db_index=True, blank=True)
206 price = models.DecimalField(max_digits=7, decimal_places=2, null=True,
207 blank=True, help_text="For-purchase applications (in US Dollars).")
208+ application_id = models.IntegerField(null=True, blank=True)
209
210 ratings_total = models.IntegerField(null=True, blank=True)
211 ratings_average = models.DecimalField(max_digits=3, decimal_places=2,
212
213=== modified file 'src/webcatalog/tests/factory.py'
214--- src/webcatalog/tests/factory.py 2012-04-17 17:09:03 +0000
215+++ src/webcatalog/tests/factory.py 2012-05-03 13:10:29 +0000
216@@ -101,7 +101,7 @@
217 distroseries=None, arch='i686', ratings_average=None,
218 ratings_total=None, ratings_histogram='', screenshot_url='',
219 archive_id=None, version='', is_latest=False, wilson_score=0.0,
220- debtags=[]):
221+ debtags=[], application_id=None):
222 if name is None:
223 name = self.get_unique_string(prefix='Readable Name')
224 if package_name is None:
225@@ -120,7 +120,8 @@
226 ratings_average=ratings_average, ratings_total=ratings_total,
227 ratings_histogram=ratings_histogram,
228 archive_id=archive_id, version=version, is_latest=is_latest,
229- wilson_score=wilson_score, debtags=debtags)
230+ wilson_score=wilson_score, debtags=debtags,
231+ application_id=application_id)
232
233 if screenshot_url:
234 ApplicationMedia.objects.create(
235
236=== modified file 'src/webcatalog/tests/test_commands.py'
237--- src/webcatalog/tests/test_commands.py 2012-04-20 16:57:50 +0000
238+++ src/webcatalog/tests/test_commands.py 2012-05-03 13:10:29 +0000
239@@ -433,26 +433,86 @@
240 command.process_desktop_file(desktop_file, '', distroseries)
241 # no breakage!
242
243+ @patch('urllib.urlopen')
244+ def test_fetch_icon_from_extras(self, mock_urlopen):
245+ mock_urlopen.return_value.getcode.return_value = 200
246+ mock_urlopen.return_value.read.return_value = "I'm a png!"
247+ app = self.factory.make_application()
248+ candidate = Mock()
249+ candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
250+ candidate.record = {'Icon': 'foo.png'}
251+ command = import_app_install_data.Command()
252+
253+ command.fetch_icon_from_extras(app, candidate)
254+
255+ self.assertEqual("I'm a png!", app.icon.read())
256+ self.assertEqual(1, mock_urlopen.call_count)
257+ mock_urlopen.assert_called_with(
258+ 'http://extras.ubuntu.com/meta/foo.png')
259+
260+ @patch('urllib.urlopen')
261+ def test_fetches_nothing_if_no_icon(self, mock_urlopen):
262+ app = self.factory.make_application()
263+ candidate = Mock()
264+ candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
265+ candidate.record = {}
266+ command = import_app_install_data.Command()
267+
268+ command.fetch_icon_from_extras(app, candidate)
269+
270+ self.assertEqual(0, mock_urlopen.call_count)
271+ self.assertFalse(app.icon)
272+
273+ @patch('urllib.urlopen')
274+ def test_fetches_nothing_if_not_in_extras(self, mock_urlopen):
275+ app = self.factory.make_application()
276+ candidate = Mock()
277+ candidate.uri = 'http://archive.ubuntu.com/pool/f/foobar.deb'
278+ candidate.record = {'Icon': 'foobar.png'}
279+ command = import_app_install_data.Command()
280+
281+ command.fetch_icon_from_extras(app, candidate)
282+
283+ self.assertEqual(0, mock_urlopen.call_count)
284+ self.assertFalse(app.icon)
285+
286
287 class ImportForPurchaseAppsTestCase(TestCaseWithFactory):
288
289 def setUp(self):
290 super(ImportForPurchaseAppsTestCase, self).setUp()
291- curdir = os.path.dirname(__file__)
292- sca_apps_file = os.path.join(curdir, 'test_data', 'sca_apps.txt')
293 self.natty = self.factory.make_distroseries(
294 code_name='natty', version='11.04')
295 self.maverick = self.factory.make_distroseries(
296 code_name='maverick', version='10.10')
297- with open(sca_apps_file) as content:
298- self.response_content = content.read()
299- mock_response = Mock()
300- mock_response.code = 200
301- mock_response.read.return_value = self.response_content
302
303 self.patch_urlopen = patch('urllib.urlopen')
304- mock_urllib = self.patch_urlopen.start()
305- mock_urllib.return_value = mock_response
306+ self.mock_urlopen = self.patch_urlopen.start()
307+
308+ def mock_urlopen_fn(url):
309+ """Return predetermined test_data content for given urls."""
310+ available_apps_url = '%sapplications/any/ubuntu/any/any/' % (
311+ settings.SCA_API_URL)
312+ icon_arb_url = ('http://sc.staging.ubuntu.com/'
313+ 'site_media/appmedia/2012/04/ktip.png')
314+ icon_hello_url = ('http://localhost:8000/'
315+ 'site_media/icons/2011/06/eg_64x64.png')
316+ filenames = {
317+ available_apps_url: 'sca_apps.txt',
318+ icon_arb_url: 'ubuntu-cof.png',
319+ icon_hello_url: 'ubuntu-cof.png',
320+ }
321+ curdir = os.path.dirname(__file__)
322+ sca_apps_file = os.path.join(curdir, 'test_data',
323+ filenames[url])
324+ with open(sca_apps_file) as content:
325+ self.response_content = content.read()
326+ mock_response = Mock()
327+ mock_response.code = 200
328+ mock_response.read.return_value = self.response_content
329+ return mock_response
330+
331+ self.mock_urlopen.side_effect = mock_urlopen_fn
332 self.addCleanup(self.patch_urlopen.stop)
333
334 def test_app_for_purchase_is_found(self):
335@@ -486,21 +546,30 @@
336 self.factory.make_application(archive_id='launchpad_zematynnad2/myppa',
337 package_name='somethingelse',
338 distroseries=self.natty)
339- self.assertEqual(2, Application.objects.count())
340+ self.assertEqual(2,
341+ Application.objects.filter(
342+ distroseries__in=(self.natty, self.maverick)).count())
343
344 call_command('import_for_purchase_apps')
345
346- self.assertEqual(2, Application.objects.count())
347- for app in Application.objects.all():
348+ actual_apps = Application.objects.filter(
349+ distroseries__in=(self.natty, self.maverick))
350+ self.assertEqual(2, actual_apps.count())
351+ for app in actual_apps:
352 self.assertEqual('launchpad_zematynnad2/myppa', app.archive_id)
353 self.assertEqual('hello', app.package_name)
354
355 def test_app_gets_icon(self):
356 call_command('import_for_purchase_apps')
357
358+ self.mock_urlopen.assert_called_with(
359+ 'http://sc.staging.ubuntu.com/'
360+ 'site_media/appmedia/2012/04/ktip.png')
361 app = Application.objects.get(name='MyApp',
362 distroseries=self.natty)
363- self.assertEqual(6461, app.icon.size)
364+ # The urlopen double returns the test_data/ubuntu-cof.png which has
365+ # a size of 2438.
366+ self.assertEqual(2438, app.icon.size)
367
368 def test_app_gets_version(self):
369 call_command('import_for_purchase_apps')
370@@ -524,48 +593,23 @@
371 qs = app.applicationmedia_set.filter(media_type='screenshot')
372 self.assertEqual(2, qs.count())
373
374- @patch('urllib.urlopen')
375- def test_fetch_icon_from_extras(self, mock_urlopen):
376- mock_urlopen.return_value.getcode.return_value = 200
377- mock_urlopen.return_value.read.return_value = "I'm a png!"
378- app = self.factory.make_application()
379- candidate = Mock()
380- candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
381- candidate.record = {'Icon': 'foo.png'}
382- command = import_app_install_data.Command()
383-
384- command.fetch_icon_from_extras(app, candidate)
385-
386- self.assertEqual("I'm a png!", app.icon.read())
387- self.assertEqual(1, mock_urlopen.call_count)
388- mock_urlopen.assert_called_with(
389- 'http://extras.ubuntu.com/meta/foo.png')
390-
391- @patch('urllib.urlopen')
392- def test_fetches_nothing_if_no_icon(self, mock_urlopen):
393- app = self.factory.make_application()
394- candidate = Mock()
395- candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
396- candidate.record = {}
397- command = import_app_install_data.Command()
398-
399- command.fetch_icon_from_extras(app, candidate)
400-
401- self.assertEqual(0, mock_urlopen.call_count)
402- self.assertFalse(app.icon)
403-
404- @patch('urllib.urlopen')
405- def test_fetches_nothing_if_not_in_extras(self, mock_urlopen):
406- app = self.factory.make_application()
407- candidate = Mock()
408- candidate.uri = 'http://archive.ubuntu.com/pool/f/foobar.deb'
409- candidate.record = {'Icon': 'foobar.png'}
410- command = import_app_install_data.Command()
411-
412- command.fetch_icon_from_extras(app, candidate)
413-
414- self.assertEqual(0, mock_urlopen.call_count)
415- self.assertFalse(app.icon)
416+ def test_import_arb_app(self):
417+ call_command('import_for_purchase_apps')
418+
419+ arb_apps = Application.objects.filter(package_name='eg_arb_app')
420+ self.assertEqual(2, arb_apps.count())
421+ self.assertEqual('', arb_apps[0].archive_id)
422+ self.assertEqual('', arb_apps[1].archive_id)
423+
424+ def test_import_arb_app_handle_existing_none_archive_id(self):
425+ precise = self.factory.make_distroseries(code_name='precise')
426+ self.factory.make_application(archive_id=None, distroseries=precise)
427+ self.factory.make_application(archive_id=None, distroseries=precise)
428+
429+ call_command('import_for_purchase_apps')
430+
431+ arb_apps = Application.objects.filter(package_name='eg_arb_app')
432+ self.assertEqual(2, arb_apps.count())
433
434
435 class ImportRatingsTestCase(TestCaseWithFactory):
436
437=== modified file 'src/webcatalog/tests/test_data/sca_apps.txt'
438--- src/webcatalog/tests/test_data/sca_apps.txt 2012-03-19 13:39:16 +0000
439+++ src/webcatalog/tests/test_data/sca_apps.txt 2012-05-03 13:10:29 +0000
440@@ -1,5 +1,6 @@
441 [
442 {
443+ "id": 18,
444 "status": "Published",
445 "signing_key_id": "",
446 "description": "MyAppTagline\nThe classic greeting, and a good example\r\n The GNU hello program produces a familiar, friendly greeting. It\r\n allows non-programmers to use a classic computer science tool which\r\n would otherwise be unavailable to them.\r\n .\r\n Seriously, though: this is an example of how to do a Debian package.\r\n It is the Debian version of the GNU Project's `hello world' program\r\n (which is itself an example for the GNU Project).",
447@@ -8,7 +9,6 @@
448 "series": {"maverick": ["i386", "amd64"], "natty": ["i386", "amd64"]},
449 "price": "2.50",
450 "archive_id": "launchpad_zematynnad2/myppa",
451- "icon_data": "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",
452 "screenshot_url": "",
453 "screenshot_urls": [
454 "http://localhost:8000/screenshot_1.png",
455@@ -19,5 +19,47 @@
456 "icon_url": "http://localhost:8000/site_media/icons/2011/06/eg_64x64.png",
457 "categories": "Audio",
458 "name": "MyApp"
459+ },
460+ {
461+ "package_name": "eg_arb_app",
462+ "video_embedded_html_urls": [],
463+ "series": {
464+ "precise": [
465+ "amd64",
466+ "i386"
467+ ],
468+ "oneiric": [
469+ "i386",
470+ "amd64"
471+ ]
472+ },
473+ "video_urls": [],
474+ "screenshot_url": "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/IMG_20120401_144332.jpg",
475+ "id": 595,
476+ "archive_id": null,
477+ "support_url": "http://example.com/foo",
478+ "icon_url": "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/ktip.png",
479+ "version": "1.2",
480+ "demo": null,
481+ "department": [
482+ "Accessories"
483+ ],
484+ "tos_url": "",
485+ "channel": "Independent",
486+ "status": "Published",
487+ "signing_key_id": "",
488+ "description": "This is a test about ARB apps\nThis is a description. My test application will provide magic and wonder to kids of all ages.",
489+ "price": "0.00",
490+ "debtags": [
491+ "hardware::input:mouse"
492+ ],
493+ "date_published": "2012-04-30 19:34:08.011454",
494+ "categories": "Utility",
495+ "name": "ARB test",
496+ "license": "GNU GPL v3",
497+ "screenshot_urls": [
498+ "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/IMG_20120401_144332.jpg"
499+ ],
500+ "archive_root": "http://extras.ubuntu.com/"
501 }
502 ]
503
504=== modified file 'src/webcatalog/tests/test_forms.py'
505--- src/webcatalog/tests/test_forms.py 2012-05-02 15:18:48 +0000
506+++ src/webcatalog/tests/test_forms.py 2012-05-03 13:10:29 +0000
507@@ -203,6 +203,7 @@
508 app.distroseries)
509 self.assertTrue(form.is_valid())
510 form.save_media_urls()
511+
512 self.assertEqual(3, app.applicationmedia_set.count())
513 actual_urls = [media.url for media in app.applicationmedia_set.all()]
514 self.assertEqual([
515@@ -211,6 +212,69 @@
516 'http://example.com/video1.mp4',
517 ], actual_urls)
518
519+ def make_valid_data(self, **kwargs):
520+ data = {
521+ u'status': u'Published',
522+ u'signing_key_id': u'',
523+ u'description': u'This is a description.',
524+ u'package_name': self.factory.get_unique_string(prefix='pkg'),
525+ u'video_embedded_html_urls': [],
526+ u'series': {
527+ u'precise': [u'amd64', u'i386'],
528+ u'oneiric': [u'i386', u'amd64']
529+ },
530+ u'price': u'0.00',
531+ u'debtags': '["mouse"]',
532+ u'date_published': u'2012-04-30 19:34:08.011454',
533+ u'video_urls': [],
534+ u'screenshot_url': u'http://sc.staging.ubuntu.com/'
535+ 'site_media/appmedia/2012/04/'
536+ 'IMG_20120401_144332.jpg',
537+ u'id': 595,
538+ u'categories': u'Utility',
539+ u'archive_id': self.factory.get_unique_string(prefix='archid/'),
540+ u'name': u'Test App',
541+ u'license': u'GNU GPL v3',
542+ u'support_url': u'http://example.com/foo',
543+ u'icon_url': u'http://sc.staging.ubuntu.com/'
544+ 'site_media/appmedia/2012/04/ktip.png',
545+ u'comment': u'This is a test app',
546+ u'version': u'1.2',
547+ u'screenshot_urls': [
548+ u'http://sc.staging.ubuntu.com/'
549+ 'site_media/appmedia/2012/04/IMG_20120401_144332.jpg',
550+ ],
551+ u'demo': None,
552+ u'department': [u'Accessories'],
553+ u'archive_root': u'http://extras.ubuntu.com/',
554+ u'tos_url': u'',
555+ u'channel': u'Independent',
556+ }
557+ data.update(**kwargs)
558+ return data
559+
560+ def test_archive_id_none_uses_application_id(self):
561+ precise = self.factory.make_distroseries(code_name='precise')
562+ existing_app = self.factory.make_application(
563+ archive_id=None, application_id=21, distroseries=precise)
564+
565+ data = self.make_valid_data(archive_id=None, id=21)
566+ form = ForPurchaseApplicationForm.from_api_data(data, precise)
567+
568+ self.assertEqual(existing_app, form.instance)
569+
570+ def test_updates_application_id(self):
571+ precise = self.factory.make_distroseries(code_name='precise')
572+ existing_app = self.factory.make_application(
573+ archive_id='foo/bar', application_id=None, distroseries=precise)
574+
575+ data = self.make_valid_data(archive_id='foo/bar', id=21)
576+ form = ForPurchaseApplicationForm.from_api_data(data, precise)
577+ form.save()
578+
579+ app_reloaded = Application.objects.get(id=existing_app.id)
580+ self.assertEqual(21, app_reloaded.application_id)
581+
582
583 class MultiURLFieldTestCase(TestCase):
584

Subscribers

People subscribed via source and target branches