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
=== modified file 'src/webcatalog/forms.py'
--- src/webcatalog/forms.py 2012-05-03 12:39:31 +0000
+++ src/webcatalog/forms.py 2012-05-03 13:10:29 +0000
@@ -144,10 +144,21 @@
144 if 'debtags' in app_data and app_data['debtags']:144 if 'debtags' in app_data and app_data['debtags']:
145 app_data['debtags'] = json.dumps([get_hw_short_description(x)145 app_data['debtags'] = json.dumps([get_hw_short_description(x)
146 for x in app_data['debtags']])146 for x in app_data['debtags']])
147 app_data['application_id'] = app_data.get('id')
148
147 try:149 try:
148 instance = Application.objects.get(150 # XXX 2012-05-03 michaeln bug=993813 We can update to use
149 archive_id=app_data.get('archive_id'),151 # application_id only below once an import runs on production
150 distroseries=distroseries)152 # setting the application_ids.
153 archive_id = app_data.get('archive_id')
154 if archive_id:
155 instance = Application.objects.get(
156 archive_id=archive_id,
157 distroseries=distroseries)
158 else:
159 instance = Application.objects.get(
160 application_id=app_data['application_id'],
161 distroseries=distroseries)
151 except Application.DoesNotExist:162 except Application.DoesNotExist:
152 instance = None163 instance = None
153164
154165
=== added file 'src/webcatalog/migrations/0021_add_archive_id.py'
--- src/webcatalog/migrations/0021_add_archive_id.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/migrations/0021_add_archive_id.py 2012-05-03 13:10:29 +0000
@@ -0,0 +1,166 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding field 'Application.application_id'
12 db.add_column('webcatalog_application', 'application_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
13
14
15 def backwards(self, orm):
16
17 # Deleting field 'Application.application_id'
18 db.delete_column('webcatalog_application', 'application_id')
19
20
21 models = {
22 'auth.group': {
23 'Meta': {'object_name': 'Group'},
24 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
25 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
26 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
27 },
28 'auth.permission': {
29 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
30 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
31 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
32 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
34 },
35 'auth.user': {
36 'Meta': {'object_name': 'User'},
37 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
38 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
39 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
40 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
41 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
43 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
44 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
45 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
46 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
47 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
48 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
49 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
50 },
51 'contenttypes.contenttype': {
52 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
53 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
54 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
56 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
57 },
58 'webcatalog.application': {
59 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'},
60 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
61 'application_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
62 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
63 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
64 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
65 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
66 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
67 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
68 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
69 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
70 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
71 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
72 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
73 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
74 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
76 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
77 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
78 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
79 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
80 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
81 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
82 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
83 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
84 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
85 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
86 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
87 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
88 },
89 'webcatalog.applicationmedia': {
90 'Meta': {'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
91 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
92 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
94 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
95 },
96 'webcatalog.consumer': {
97 'Meta': {'object_name': 'Consumer'},
98 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
99 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
100 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
101 'secret': ('django.db.models.fields.CharField', [], {'default': "'MbyfjpwvpAZhZPLmGbEXCNhSGMCWCv'", 'max_length': '255', 'blank': 'True'}),
102 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
103 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
104 },
105 'webcatalog.department': {
106 'Meta': {'object_name': 'Department'},
107 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
109 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
110 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
111 },
112 'webcatalog.distroseries': {
113 'Meta': {'object_name': 'DistroSeries'},
114 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
115 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
116 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
117 },
118 'webcatalog.exhibit': {
119 'Meta': {'object_name': 'Exhibit'},
120 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
121 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
122 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
123 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
124 'html': ('django.db.models.fields.TextField', [], {}),
125 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
127 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
128 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
129 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
130 },
131 'webcatalog.machine': {
132 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
133 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
134 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
135 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
136 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
137 'package_list': ('django.db.models.fields.TextField', [], {}),
138 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
139 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
140 },
141 'webcatalog.nonce': {
142 'Meta': {'object_name': 'Nonce'},
143 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
144 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
145 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
146 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
147 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
148 },
149 'webcatalog.reviewstatsimport': {
150 'Meta': {'object_name': 'ReviewStatsImport'},
151 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
152 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
153 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
154 },
155 'webcatalog.token': {
156 'Meta': {'object_name': 'Token'},
157 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
158 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
159 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
160 'token': ('django.db.models.fields.CharField', [], {'default': "'afduSDFNmldZMjYZFoBLOhXEULKLrbmDGPKtuoHEUVLCyheFhY'", 'max_length': '50', 'primary_key': 'True'}),
161 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'obbeyYIEgkoxRBtOlkaXbtfgOGnkWUVIppSANzntzFEMuOytlC'", 'max_length': '50'}),
162 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
163 }
164 }
165
166 complete_apps = ['webcatalog']
0167
=== modified file 'src/webcatalog/models/applications.py'
--- src/webcatalog/models/applications.py 2012-04-13 20:45:03 +0000
+++ src/webcatalog/models/applications.py 2012-05-03 13:10:29 +0000
@@ -89,6 +89,7 @@
89 db_index=True, blank=True)89 db_index=True, blank=True)
90 price = models.DecimalField(max_digits=7, decimal_places=2, null=True,90 price = models.DecimalField(max_digits=7, decimal_places=2, null=True,
91 blank=True, help_text="For-purchase applications (in US Dollars).")91 blank=True, help_text="For-purchase applications (in US Dollars).")
92 application_id = models.IntegerField(null=True, blank=True)
9293
93 ratings_total = models.IntegerField(null=True, blank=True)94 ratings_total = models.IntegerField(null=True, blank=True)
94 ratings_average = models.DecimalField(max_digits=3, decimal_places=2,95 ratings_average = models.DecimalField(max_digits=3, decimal_places=2,
9596
=== modified file 'src/webcatalog/tests/factory.py'
--- src/webcatalog/tests/factory.py 2012-04-17 17:09:03 +0000
+++ src/webcatalog/tests/factory.py 2012-05-03 13:10:29 +0000
@@ -101,7 +101,7 @@
101 distroseries=None, arch='i686', ratings_average=None,101 distroseries=None, arch='i686', ratings_average=None,
102 ratings_total=None, ratings_histogram='', screenshot_url='',102 ratings_total=None, ratings_histogram='', screenshot_url='',
103 archive_id=None, version='', is_latest=False, wilson_score=0.0,103 archive_id=None, version='', is_latest=False, wilson_score=0.0,
104 debtags=[]):104 debtags=[], application_id=None):
105 if name is None:105 if name is None:
106 name = self.get_unique_string(prefix='Readable Name')106 name = self.get_unique_string(prefix='Readable Name')
107 if package_name is None:107 if package_name is None:
@@ -120,7 +120,8 @@
120 ratings_average=ratings_average, ratings_total=ratings_total,120 ratings_average=ratings_average, ratings_total=ratings_total,
121 ratings_histogram=ratings_histogram,121 ratings_histogram=ratings_histogram,
122 archive_id=archive_id, version=version, is_latest=is_latest,122 archive_id=archive_id, version=version, is_latest=is_latest,
123 wilson_score=wilson_score, debtags=debtags)123 wilson_score=wilson_score, debtags=debtags,
124 application_id=application_id)
124125
125 if screenshot_url:126 if screenshot_url:
126 ApplicationMedia.objects.create(127 ApplicationMedia.objects.create(
127128
=== modified file 'src/webcatalog/tests/test_commands.py'
--- src/webcatalog/tests/test_commands.py 2012-04-20 16:57:50 +0000
+++ src/webcatalog/tests/test_commands.py 2012-05-03 13:10:29 +0000
@@ -433,26 +433,86 @@
433 command.process_desktop_file(desktop_file, '', distroseries)433 command.process_desktop_file(desktop_file, '', distroseries)
434 # no breakage!434 # no breakage!
435435
436 @patch('urllib.urlopen')
437 def test_fetch_icon_from_extras(self, mock_urlopen):
438 mock_urlopen.return_value.getcode.return_value = 200
439 mock_urlopen.return_value.read.return_value = "I'm a png!"
440 app = self.factory.make_application()
441 candidate = Mock()
442 candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
443 candidate.record = {'Icon': 'foo.png'}
444 command = import_app_install_data.Command()
445
446 command.fetch_icon_from_extras(app, candidate)
447
448 self.assertEqual("I'm a png!", app.icon.read())
449 self.assertEqual(1, mock_urlopen.call_count)
450 mock_urlopen.assert_called_with(
451 'http://extras.ubuntu.com/meta/foo.png')
452
453 @patch('urllib.urlopen')
454 def test_fetches_nothing_if_no_icon(self, mock_urlopen):
455 app = self.factory.make_application()
456 candidate = Mock()
457 candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
458 candidate.record = {}
459 command = import_app_install_data.Command()
460
461 command.fetch_icon_from_extras(app, candidate)
462
463 self.assertEqual(0, mock_urlopen.call_count)
464 self.assertFalse(app.icon)
465
466 @patch('urllib.urlopen')
467 def test_fetches_nothing_if_not_in_extras(self, mock_urlopen):
468 app = self.factory.make_application()
469 candidate = Mock()
470 candidate.uri = 'http://archive.ubuntu.com/pool/f/foobar.deb'
471 candidate.record = {'Icon': 'foobar.png'}
472 command = import_app_install_data.Command()
473
474 command.fetch_icon_from_extras(app, candidate)
475
476 self.assertEqual(0, mock_urlopen.call_count)
477 self.assertFalse(app.icon)
478
436479
437class ImportForPurchaseAppsTestCase(TestCaseWithFactory):480class ImportForPurchaseAppsTestCase(TestCaseWithFactory):
438481
439 def setUp(self):482 def setUp(self):
440 super(ImportForPurchaseAppsTestCase, self).setUp()483 super(ImportForPurchaseAppsTestCase, self).setUp()
441 curdir = os.path.dirname(__file__)
442 sca_apps_file = os.path.join(curdir, 'test_data', 'sca_apps.txt')
443 self.natty = self.factory.make_distroseries(484 self.natty = self.factory.make_distroseries(
444 code_name='natty', version='11.04')485 code_name='natty', version='11.04')
445 self.maverick = self.factory.make_distroseries(486 self.maverick = self.factory.make_distroseries(
446 code_name='maverick', version='10.10')487 code_name='maverick', version='10.10')
447 with open(sca_apps_file) as content:
448 self.response_content = content.read()
449 mock_response = Mock()
450 mock_response.code = 200
451 mock_response.read.return_value = self.response_content
452488
453 self.patch_urlopen = patch('urllib.urlopen')489 self.patch_urlopen = patch('urllib.urlopen')
454 mock_urllib = self.patch_urlopen.start()490 self.mock_urlopen = self.patch_urlopen.start()
455 mock_urllib.return_value = mock_response491
492 def mock_urlopen_fn(url):
493 """Return predetermined test_data content for given urls."""
494 available_apps_url = '%sapplications/any/ubuntu/any/any/' % (
495 settings.SCA_API_URL)
496 icon_arb_url = ('http://sc.staging.ubuntu.com/'
497 'site_media/appmedia/2012/04/ktip.png')
498 icon_hello_url = ('http://localhost:8000/'
499 'site_media/icons/2011/06/eg_64x64.png')
500 filenames = {
501 available_apps_url: 'sca_apps.txt',
502 icon_arb_url: 'ubuntu-cof.png',
503 icon_hello_url: 'ubuntu-cof.png',
504 }
505 curdir = os.path.dirname(__file__)
506 sca_apps_file = os.path.join(curdir, 'test_data',
507 filenames[url])
508 with open(sca_apps_file) as content:
509 self.response_content = content.read()
510 mock_response = Mock()
511 mock_response.code = 200
512 mock_response.read.return_value = self.response_content
513 return mock_response
514
515 self.mock_urlopen.side_effect = mock_urlopen_fn
456 self.addCleanup(self.patch_urlopen.stop)516 self.addCleanup(self.patch_urlopen.stop)
457517
458 def test_app_for_purchase_is_found(self):518 def test_app_for_purchase_is_found(self):
@@ -486,21 +546,30 @@
486 self.factory.make_application(archive_id='launchpad_zematynnad2/myppa',546 self.factory.make_application(archive_id='launchpad_zematynnad2/myppa',
487 package_name='somethingelse',547 package_name='somethingelse',
488 distroseries=self.natty)548 distroseries=self.natty)
489 self.assertEqual(2, Application.objects.count())549 self.assertEqual(2,
550 Application.objects.filter(
551 distroseries__in=(self.natty, self.maverick)).count())
490552
491 call_command('import_for_purchase_apps')553 call_command('import_for_purchase_apps')
492554
493 self.assertEqual(2, Application.objects.count())555 actual_apps = Application.objects.filter(
494 for app in Application.objects.all():556 distroseries__in=(self.natty, self.maverick))
557 self.assertEqual(2, actual_apps.count())
558 for app in actual_apps:
495 self.assertEqual('launchpad_zematynnad2/myppa', app.archive_id)559 self.assertEqual('launchpad_zematynnad2/myppa', app.archive_id)
496 self.assertEqual('hello', app.package_name)560 self.assertEqual('hello', app.package_name)
497561
498 def test_app_gets_icon(self):562 def test_app_gets_icon(self):
499 call_command('import_for_purchase_apps')563 call_command('import_for_purchase_apps')
500564
565 self.mock_urlopen.assert_called_with(
566 'http://sc.staging.ubuntu.com/'
567 'site_media/appmedia/2012/04/ktip.png')
501 app = Application.objects.get(name='MyApp',568 app = Application.objects.get(name='MyApp',
502 distroseries=self.natty)569 distroseries=self.natty)
503 self.assertEqual(6461, app.icon.size)570 # The urlopen double returns the test_data/ubuntu-cof.png which has
571 # a size of 2438.
572 self.assertEqual(2438, app.icon.size)
504573
505 def test_app_gets_version(self):574 def test_app_gets_version(self):
506 call_command('import_for_purchase_apps')575 call_command('import_for_purchase_apps')
@@ -524,48 +593,23 @@
524 qs = app.applicationmedia_set.filter(media_type='screenshot')593 qs = app.applicationmedia_set.filter(media_type='screenshot')
525 self.assertEqual(2, qs.count())594 self.assertEqual(2, qs.count())
526595
527 @patch('urllib.urlopen')596 def test_import_arb_app(self):
528 def test_fetch_icon_from_extras(self, mock_urlopen):597 call_command('import_for_purchase_apps')
529 mock_urlopen.return_value.getcode.return_value = 200598
530 mock_urlopen.return_value.read.return_value = "I'm a png!"599 arb_apps = Application.objects.filter(package_name='eg_arb_app')
531 app = self.factory.make_application()600 self.assertEqual(2, arb_apps.count())
532 candidate = Mock()601 self.assertEqual('', arb_apps[0].archive_id)
533 candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'602 self.assertEqual('', arb_apps[1].archive_id)
534 candidate.record = {'Icon': 'foo.png'}603
535 command = import_app_install_data.Command()604 def test_import_arb_app_handle_existing_none_archive_id(self):
536605 precise = self.factory.make_distroseries(code_name='precise')
537 command.fetch_icon_from_extras(app, candidate)606 self.factory.make_application(archive_id=None, distroseries=precise)
538607 self.factory.make_application(archive_id=None, distroseries=precise)
539 self.assertEqual("I'm a png!", app.icon.read())608
540 self.assertEqual(1, mock_urlopen.call_count)609 call_command('import_for_purchase_apps')
541 mock_urlopen.assert_called_with(610
542 'http://extras.ubuntu.com/meta/foo.png')611 arb_apps = Application.objects.filter(package_name='eg_arb_app')
543612 self.assertEqual(2, arb_apps.count())
544 @patch('urllib.urlopen')
545 def test_fetches_nothing_if_no_icon(self, mock_urlopen):
546 app = self.factory.make_application()
547 candidate = Mock()
548 candidate.uri = 'http://extras.ubuntu.com/pool/f/foobar.deb'
549 candidate.record = {}
550 command = import_app_install_data.Command()
551
552 command.fetch_icon_from_extras(app, candidate)
553
554 self.assertEqual(0, mock_urlopen.call_count)
555 self.assertFalse(app.icon)
556
557 @patch('urllib.urlopen')
558 def test_fetches_nothing_if_not_in_extras(self, mock_urlopen):
559 app = self.factory.make_application()
560 candidate = Mock()
561 candidate.uri = 'http://archive.ubuntu.com/pool/f/foobar.deb'
562 candidate.record = {'Icon': 'foobar.png'}
563 command = import_app_install_data.Command()
564
565 command.fetch_icon_from_extras(app, candidate)
566
567 self.assertEqual(0, mock_urlopen.call_count)
568 self.assertFalse(app.icon)
569613
570614
571class ImportRatingsTestCase(TestCaseWithFactory):615class ImportRatingsTestCase(TestCaseWithFactory):
572616
=== modified file 'src/webcatalog/tests/test_data/sca_apps.txt'
--- src/webcatalog/tests/test_data/sca_apps.txt 2012-03-19 13:39:16 +0000
+++ src/webcatalog/tests/test_data/sca_apps.txt 2012-05-03 13:10:29 +0000
@@ -1,5 +1,6 @@
1[1[
2 {2 {
3 "id": 18,
3 "status": "Published",4 "status": "Published",
4 "signing_key_id": "",5 "signing_key_id": "",
5 "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).",6 "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).",
@@ -8,7 +9,6 @@
8 "series": {"maverick": ["i386", "amd64"], "natty": ["i386", "amd64"]},9 "series": {"maverick": ["i386", "amd64"], "natty": ["i386", "amd64"]},
9 "price": "2.50",10 "price": "2.50",
10 "archive_id": "launchpad_zematynnad2/myppa",11 "archive_id": "launchpad_zematynnad2/myppa",
11 "icon_data": "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",
12 "screenshot_url": "",12 "screenshot_url": "",
13 "screenshot_urls": [13 "screenshot_urls": [
14 "http://localhost:8000/screenshot_1.png",14 "http://localhost:8000/screenshot_1.png",
@@ -19,5 +19,47 @@
19 "icon_url": "http://localhost:8000/site_media/icons/2011/06/eg_64x64.png",19 "icon_url": "http://localhost:8000/site_media/icons/2011/06/eg_64x64.png",
20 "categories": "Audio",20 "categories": "Audio",
21 "name": "MyApp"21 "name": "MyApp"
22 },
23 {
24 "package_name": "eg_arb_app",
25 "video_embedded_html_urls": [],
26 "series": {
27 "precise": [
28 "amd64",
29 "i386"
30 ],
31 "oneiric": [
32 "i386",
33 "amd64"
34 ]
35 },
36 "video_urls": [],
37 "screenshot_url": "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/IMG_20120401_144332.jpg",
38 "id": 595,
39 "archive_id": null,
40 "support_url": "http://example.com/foo",
41 "icon_url": "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/ktip.png",
42 "version": "1.2",
43 "demo": null,
44 "department": [
45 "Accessories"
46 ],
47 "tos_url": "",
48 "channel": "Independent",
49 "status": "Published",
50 "signing_key_id": "",
51 "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.",
52 "price": "0.00",
53 "debtags": [
54 "hardware::input:mouse"
55 ],
56 "date_published": "2012-04-30 19:34:08.011454",
57 "categories": "Utility",
58 "name": "ARB test",
59 "license": "GNU GPL v3",
60 "screenshot_urls": [
61 "http://sc.staging.ubuntu.com/site_media/appmedia/2012/04/IMG_20120401_144332.jpg"
62 ],
63 "archive_root": "http://extras.ubuntu.com/"
22 }64 }
23]65]
2466
=== modified file 'src/webcatalog/tests/test_forms.py'
--- src/webcatalog/tests/test_forms.py 2012-05-02 15:18:48 +0000
+++ src/webcatalog/tests/test_forms.py 2012-05-03 13:10:29 +0000
@@ -203,6 +203,7 @@
203 app.distroseries)203 app.distroseries)
204 self.assertTrue(form.is_valid())204 self.assertTrue(form.is_valid())
205 form.save_media_urls()205 form.save_media_urls()
206
206 self.assertEqual(3, app.applicationmedia_set.count())207 self.assertEqual(3, app.applicationmedia_set.count())
207 actual_urls = [media.url for media in app.applicationmedia_set.all()]208 actual_urls = [media.url for media in app.applicationmedia_set.all()]
208 self.assertEqual([209 self.assertEqual([
@@ -211,6 +212,69 @@
211 'http://example.com/video1.mp4',212 'http://example.com/video1.mp4',
212 ], actual_urls)213 ], actual_urls)
213214
215 def make_valid_data(self, **kwargs):
216 data = {
217 u'status': u'Published',
218 u'signing_key_id': u'',
219 u'description': u'This is a description.',
220 u'package_name': self.factory.get_unique_string(prefix='pkg'),
221 u'video_embedded_html_urls': [],
222 u'series': {
223 u'precise': [u'amd64', u'i386'],
224 u'oneiric': [u'i386', u'amd64']
225 },
226 u'price': u'0.00',
227 u'debtags': '["mouse"]',
228 u'date_published': u'2012-04-30 19:34:08.011454',
229 u'video_urls': [],
230 u'screenshot_url': u'http://sc.staging.ubuntu.com/'
231 'site_media/appmedia/2012/04/'
232 'IMG_20120401_144332.jpg',
233 u'id': 595,
234 u'categories': u'Utility',
235 u'archive_id': self.factory.get_unique_string(prefix='archid/'),
236 u'name': u'Test App',
237 u'license': u'GNU GPL v3',
238 u'support_url': u'http://example.com/foo',
239 u'icon_url': u'http://sc.staging.ubuntu.com/'
240 'site_media/appmedia/2012/04/ktip.png',
241 u'comment': u'This is a test app',
242 u'version': u'1.2',
243 u'screenshot_urls': [
244 u'http://sc.staging.ubuntu.com/'
245 'site_media/appmedia/2012/04/IMG_20120401_144332.jpg',
246 ],
247 u'demo': None,
248 u'department': [u'Accessories'],
249 u'archive_root': u'http://extras.ubuntu.com/',
250 u'tos_url': u'',
251 u'channel': u'Independent',
252 }
253 data.update(**kwargs)
254 return data
255
256 def test_archive_id_none_uses_application_id(self):
257 precise = self.factory.make_distroseries(code_name='precise')
258 existing_app = self.factory.make_application(
259 archive_id=None, application_id=21, distroseries=precise)
260
261 data = self.make_valid_data(archive_id=None, id=21)
262 form = ForPurchaseApplicationForm.from_api_data(data, precise)
263
264 self.assertEqual(existing_app, form.instance)
265
266 def test_updates_application_id(self):
267 precise = self.factory.make_distroseries(code_name='precise')
268 existing_app = self.factory.make_application(
269 archive_id='foo/bar', application_id=None, distroseries=precise)
270
271 data = self.make_valid_data(archive_id='foo/bar', id=21)
272 form = ForPurchaseApplicationForm.from_api_data(data, precise)
273 form.save()
274
275 app_reloaded = Application.objects.get(id=existing_app.id)
276 self.assertEqual(21, app_reloaded.application_id)
277
214278
215class MultiURLFieldTestCase(TestCase):279class MultiURLFieldTestCase(TestCase):
216280

Subscribers

People subscribed via source and target branches