Merge lp:~benji/charmworld/bundle-feature-ui into lp:~juju-jitsu/charmworld/trunk

Proposed by Benji York
Status: Merged
Approved by: Benji York
Approved revision: 379
Merged at revision: 381
Proposed branch: lp:~benji/charmworld/bundle-feature-ui
Merge into: lp:~juju-jitsu/charmworld/trunk
Diff against target: 682 lines (+361/-183)
9 files modified
charmworld/routes.py (+3/-1)
charmworld/search.py (+3/-3)
charmworld/views/bundles.py (+4/-22)
charmworld/views/charms.py (+1/-54)
charmworld/views/featured.py (+81/-0)
charmworld/views/helpers.py (+44/-2)
charmworld/views/tests/test_auth.py (+2/-2)
charmworld/views/tests/test_charms.py (+0/-99)
charmworld/views/tests/test_featured.py (+223/-0)
To merge this branch: bzr merge lp:~benji/charmworld/bundle-feature-ui
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+183709@code.launchpad.net

Commit message

Add a user interface for setting the featured flag for bundles.

Description of the change

Add a user interface for setting the featured flag for bundles.

To post a comment you must log in.
377. By Benji York

merge from trunk, fixing conflicts

378. By Benji York

fix failing test

Revision history for this message
Brad Crittenden (bac) wrote :

Thanks for cleaning up my lint in those tests.

At 206 except ValidationFailure as form:
  This use is too subtle, I think, if the intent is to re-assign the form variable for the remainder of the method. Perhaps catching it as a temporary and reassigning the real 'form' in the handler would be more explicit.

The rest looks good. The tests are nice. Thanks for including the routing test, we need more of them.

QA:

http://127.0.0.1:2464/~bac/bundles/wiki/wiki/featured/edit

This URL should not be supported and gives a 404

http://127.0.0.1:2464/bundles/wiki/wiki/featured/edit

This one, maybe should be supported, but it is not promulgated. It cause an exception to be raised in bundle_featured_edit because find_bundle returns None.

I think you should call found(bundle) to force a 404 in this case.

review: Needs Fixing
379. By Benji York

make non-promulgated charms and bundles generate a 404 when the feature edit form is visited

Revision history for this message
Brad Crittenden (bac) wrote :

Thanks

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmworld/routes.py'
--- charmworld/routes.py 2013-09-03 19:57:54 +0000
+++ charmworld/routes.py 2013-09-04 16:51:29 +0000
@@ -83,9 +83,11 @@
83 # Quality Assessment83 # Quality Assessment
84 config.add_route('charm-qa-edit', 'charms/{charm}/{series}/qa/edit')84 config.add_route('charm-qa-edit', 'charms/{charm}/{series}/qa/edit')
8585
86 # Featured charms86 # Featured charms and bundles.
87 config.add_route('charm-featured-edit',87 config.add_route('charm-featured-edit',
88 'charms/{series}/{charm}/featured/edit')88 'charms/{series}/{charm}/featured/edit')
89 config.add_route('bundle-featured-edit',
90 'bundles/{basket}/{bundle}/featured/edit')
8991
90 # Search92 # Search
91 config.add_route("search", "/search")93 config.add_route("search", "/search")
9294
=== modified file 'charmworld/search.py'
--- charmworld/search.py 2013-09-04 13:38:14 +0000
+++ charmworld/search.py 2013-09-04 16:51:29 +0000
@@ -135,9 +135,9 @@
135 settings = get_ini()135 settings = get_ini()
136 self._client.create_index(136 self._client.create_index(
137 self.index_name, settings={137 self.index_name, settings={
138 "index" : {138 "index": {
139 "number_of_shards" : settings['es_shards'],139 "number_of_shards": settings['es_shards'],
140 "number_of_replicas" : settings['es_replicas'],140 "number_of_replicas": settings['es_replicas'],
141 }141 }
142 })142 })
143 # The ES server may need some time to actually create the index143 # The ES server may need some time to actually create the index
144144
=== modified file 'charmworld/views/bundles.py'
--- charmworld/views/bundles.py 2013-08-29 13:55:25 +0000
+++ charmworld/views/bundles.py 2013-09-04 16:51:29 +0000
@@ -4,7 +4,10 @@
4from charmworld import cached_view_config4from charmworld import cached_view_config
5from charmworld.models import Bundle5from charmworld.models import Bundle
6from charmworld.views.api import json_response6from charmworld.views.api import json_response
7from charmworld.views.helpers import found7from charmworld.views.helpers import (
8 find_bundle,
9 found,
10)
811
912
10class BundleDetail(Bundle):13class BundleDetail(Bundle):
@@ -63,27 +66,6 @@
63 raise66 raise
6467
6568
66def find_bundle(request, promulgated=False):
67 """Find the bundle.
68
69 May return None, which must be handled by the caller.
70 """
71 spec = dict(
72 name=request.matchdict['bundle'],
73 )
74 basket = request.matchdict.get('basket')
75 rev = request.matchdict.get('rev')
76 if basket is not None:
77 spec['basket_name'] = basket
78 if rev is not None:
79 spec['basket_revision'] = int(rev)
80 if promulgated:
81 spec["promulgated"] = True
82 else:
83 spec["owner"] = request.matchdict['owner']
84 return Bundle.from_query(spec, request.db)
85
86
87def _bundle_view(request, bundle):69def _bundle_view(request, bundle):
88 bundle = BundleDetail(dict(bundle))70 bundle = BundleDetail(dict(bundle))
89 try:71 try:
9072
=== modified file 'charmworld/views/charms.py'
--- charmworld/views/charms.py 2013-08-30 21:03:41 +0000
+++ charmworld/views/charms.py 2013-09-04 16:51:29 +0000
@@ -8,13 +8,11 @@
8import json8import json
9import os9import os
10import pymongo10import pymongo
11from pyramid.httpexceptions import HTTPFound
12from pyramid.view import view_config11from pyramid.view import view_config
13from webob import Response12from webob import Response
1413
15from charmworld import cached_view_config14from charmworld import cached_view_config
16from charmworld.forms.qa_assessment import get_qa_form_schema15from charmworld.forms.qa_assessment import get_qa_form_schema
17from charmworld.forms.featured import CharmFeaturedness
18from charmworld.models import (16from charmworld.models import (
19 Charm,17 Charm,
20 CharmFileSet,18 CharmFileSet,
@@ -30,6 +28,7 @@
30)28)
31from charmworld.views.api import API329from charmworld.views.api import API3
32from charmworld.views.helpers import (30from charmworld.views.helpers import (
31 find_charm,
33 format_change,32 format_change,
34 format_proof,33 format_proof,
35 found,34 found,
@@ -278,23 +277,6 @@
278 return found_charm_collection(request.db, query, sub_only)277 return found_charm_collection(request.db, query, sub_only)
279278
280279
281def find_charm(request, promulgated=False):
282 spec = {
283 "name": request.matchdict['charm'],
284 "series": request.matchdict['series'],
285 }
286 if promulgated:
287 spec["promulgated"] = True
288 else:
289 spec["owner"] = request.matchdict['owner']
290 charm_data = found(request.db.charms.find_one(
291 spec, sort=[('store_data.revision', pymongo.DESCENDING)]))
292 # We are phasing out is_featured from the charm data, so remove it if this
293 # is an old charm record that still has it.
294 charm_data.pop('is_featured', None)
295 return charm_data
296
297
298@cached_view_config(280@cached_view_config(
299 route_name="personal-charm",281 route_name="personal-charm",
300 renderer="charmworld:templates/charm.pt")282 renderer="charmworld:templates/charm.pt")
@@ -437,38 +419,3 @@
437 'form': form,419 'form': form,
438 "values": valid_data,420 "values": valid_data,
439 }421 }
440
441
442@view_config(
443 route_name="charm-featured-edit",
444 renderer='charmworld:templates/qa_edit.pt',
445 permission='edit',
446)
447def featured_edit(request):
448 charm = find_charm(request, promulgated=True)
449 form = Form(CharmFeaturedness(), buttons=('submit',))
450 form.css_class = 'form-horizontal well'
451 featured_source = FeaturedSource.from_db(request.db)
452
453 if 'submit' in request.POST:
454 try:
455 data = request.POST.items()
456 valid_data = form.validate(data)
457 if valid_data['is_featured']:
458 featured_source.set_featured(charm, 'charm')
459 else:
460 featured_source.clear_featured(charm, 'charm')
461 series = request.matchdict['series']
462 name = request.matchdict['charm']
463 url = request.route_url('charm', series=series, charm=name)
464 return HTTPFound(url)
465 except ValidationFailure as form:
466 valid_data = {}
467 else:
468 valid_data = {
469 'is_featured': featured_source.is_featured(charm, 'charm')}
470
471 return {
472 'form': form.render(valid_data),
473 'values': valid_data,
474 }
475422
=== added file 'charmworld/views/featured.py'
--- charmworld/views/featured.py 1970-01-01 00:00:00 +0000
+++ charmworld/views/featured.py 2013-09-04 16:51:29 +0000
@@ -0,0 +1,81 @@
1# Copyright 2012, 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from deform import (
5 Form,
6 ValidationFailure,
7)
8from pyramid.httpexceptions import HTTPFound
9from pyramid.view import view_config
10
11from charmworld.forms.featured import CharmFeaturedness
12from charmworld.models import FeaturedSource
13from charmworld.views.helpers import (
14 find_charm,
15 find_bundle,
16 found,
17)
18
19
20def featured_edit(request, item, kind, url):
21 """Back end for featuring an "item" which can be a charm or bundle."""
22 form = Form(CharmFeaturedness(), buttons=('submit',))
23 form.css_class = 'form-horizontal well'
24 featured_source = FeaturedSource.from_db(request.db)
25
26 if 'submit' in request.POST:
27 try:
28 data = request.POST.items()
29 valid_data = form.validate(data)
30 if valid_data['is_featured']:
31 featured_source.set_featured(item, kind)
32 else:
33 featured_source.clear_featured(item, kind)
34 # Send the user back to the item's main page after saving.
35 return HTTPFound(url)
36 except ValidationFailure as form:
37 valid_data = {}
38 else:
39 valid_data = {
40 'is_featured': featured_source.is_featured(item, kind)}
41
42 return {
43 'form': form.render(valid_data),
44 'values': valid_data,
45 }
46
47
48@view_config(
49 route_name="charm-featured-edit",
50 renderer='charmworld:templates/qa_edit.pt',
51 permission='edit',
52)
53def charm_featured_edit(request):
54 """Back end for featuring a charm."""
55 charm = find_charm(request, promulgated=True)
56 found(charm)
57 series = request.matchdict['series']
58 charm_name = request.matchdict['charm']
59 url = request.route_url('charm', series=series, charm=charm_name)
60 return featured_edit(request, charm, 'charm', url)
61
62
63@view_config(
64 route_name="bundle-featured-edit",
65 renderer='charmworld:templates/qa_edit.pt',
66 permission='edit',
67)
68def bundle_featured_edit(request):
69 """Back end for featuring a bundle."""
70 bundle = find_bundle(request, promulgated=True)
71 # Assert that the bundle was found, generating a 404 if not.
72 found(bundle)
73 basket = request.matchdict.get('basket')
74 basket_name = request.matchdict.get('bundle')
75 # Since there is not yet a web page for promulgated bundles, we redirect
76 # to the owner's page for the bundle.
77 # TODO Change this to the promulgated bundle page when they exist.
78 url = request.route_url(
79 'personal-bundle', owner=bundle.owner, basket=basket,
80 bundle=basket_name)
81 return featured_edit(request, dict(bundle), 'bundle', url)
082
=== modified file 'charmworld/views/helpers.py'
--- charmworld/views/helpers.py 2013-07-31 14:45:14 +0000
+++ charmworld/views/helpers.py 2013-09-04 16:51:29 +0000
@@ -3,13 +3,17 @@
33
4from datetime import datetime4from datetime import datetime
5from pyramid.exceptions import NotFound5from pyramid.exceptions import NotFound
6import pymongo
67
7from charmworld.models import Charm8from charmworld.models import (
9 Bundle,
10 Charm,
11)
8from charmworld.models import find_charms12from charmworld.models import find_charms
913
10
11# View helpers used throughout various views14# View helpers used throughout various views
1215
16
13def format_change(change, charm=None):17def format_change(change, charm=None):
14 """Format a bzr commit."""18 """Format a bzr commit."""
15 parts = []19 parts = []
@@ -131,3 +135,41 @@
131 names.append(c.name)135 names.append(c.name)
132 results.sort(lambda x, y: cmp(x.name, y.name))136 results.sort(lambda x, y: cmp(x.name, y.name))
133 return results137 return results
138
139
140def find_charm(request, promulgated=False):
141 spec = {
142 "name": request.matchdict['charm'],
143 "series": request.matchdict['series'],
144 }
145 if promulgated:
146 spec["promulgated"] = True
147 else:
148 spec["owner"] = request.matchdict['owner']
149 charm_data = found(request.db.charms.find_one(
150 spec, sort=[('store_data.revision', pymongo.DESCENDING)]))
151 # We are phasing out is_featured from the charm data, so remove it if this
152 # is an old charm record that still has it.
153 charm_data.pop('is_featured', None)
154 return charm_data
155
156
157def find_bundle(request, promulgated=False):
158 """Find the bundle.
159
160 May return None, which must be handled by the caller.
161 """
162 spec = dict(
163 name=request.matchdict['bundle'],
164 )
165 basket = request.matchdict.get('basket')
166 rev = request.matchdict.get('rev')
167 if basket is not None:
168 spec['basket_name'] = basket
169 if rev is not None:
170 spec['basket_revision'] = int(rev)
171 if promulgated:
172 spec["promulgated"] = True
173 else:
174 spec["owner"] = request.matchdict['owner']
175 return Bundle.from_query(spec, request.db)
134176
=== modified file 'charmworld/views/tests/test_auth.py'
--- charmworld/views/tests/test_auth.py 2013-09-04 12:40:00 +0000
+++ charmworld/views/tests/test_auth.py 2013-09-04 16:51:29 +0000
@@ -56,8 +56,8 @@
5656
57 def test_login_route(self):57 def test_login_route(self):
58 path = '/login/openid'58 path = '/login/openid'
59 response = self.app.get(path, status=200)59 self.app.get(path, status=200)
6060
61 def test_logout_route(self):61 def test_logout_route(self):
62 path = '/logout'62 path = '/logout'
63 response = self.app.get(path, status=302)63 self.app.get(path, status=302)
6464
=== modified file 'charmworld/views/tests/test_charms.py'
--- charmworld/views/tests/test_charms.py 2013-08-30 21:03:41 +0000
+++ charmworld/views/tests/test_charms.py 2013-09-04 16:51:29 +0000
@@ -10,7 +10,6 @@
10from charmworld.models import (10from charmworld.models import (
11 Charm,11 Charm,
12 CharmSource,12 CharmSource,
13 FeaturedSource,
14 QADataSource,13 QADataSource,
15 getfs,14 getfs,
16)15)
@@ -37,7 +36,6 @@
37 found_charm_collection,36 found_charm_collection,
38 hook,37 hook,
39 interface,38 interface,
40 featured_edit,
41 personal_collection,39 personal_collection,
42 personal_config,40 personal_config,
43 personal_hook,41 personal_hook,
@@ -746,100 +744,3 @@
746 charm['series']),744 charm['series']),
747 status=200)745 status=200)
748 self.assertEqual(resp.status_code, 200)746 self.assertEqual(resp.status_code, 200)
749
750
751class TestFeaturedForm(ViewTestBase):
752
753 def test_form_renders(self):
754 """Sanity check that the form renders without errors."""
755 cid, charm = factory.makeCharm(self.db, promulgated=True, owner='foo')
756 request = self.getRequest()
757 request.matchdict = {
758 'charm': charm['name'],
759 'series': charm['series'],
760 }
761 response = featured_edit(request)
762
763 self.assertIn('form', response)
764 self.assertIn('<label', response['form'])
765
766 def test_checking_checkbox_sets_featured_status(self):
767 """Verify checking the checkbox causes the charm to be featured."""
768 charm = factory.get_charm_json(name='name1', promulgated=True)
769 self.use_index_client()
770 self.enable_routes()
771 charm_source = CharmSource.from_request(self)
772 charm_source.save(charm)
773 # Add the submit button to trigger the right path through the view.
774 post_data = {
775 '_charset_': 'UTF-8',
776 '__formid__': 'deform',
777 'submit': 'submit',
778 'is_featured': 'true',
779 }
780 request = self.getRequest(post=post_data)
781 request.matchdict = {
782 'charm': charm['name'],
783 'series': charm['series'],
784 }
785 # Before processing the request, the charm is not featured.
786 featured_source = FeaturedSource.from_db(self.db)
787 self.assertFalse(featured_source.is_featured(charm, 'charm'))
788 response = featured_edit(request)
789 self.assertEqual(302, response.status_code)
790 self.assertRegexpMatches(response.headers['Location'],
791 'http://[^/]*/charms/precise/name1')
792 for found in charm_source._get_all(charm['_id']):
793 self.assertTrue(featured_source.is_featured(found, 'charm'))
794
795 def test_unchecking_checkbox_clears_featured_status(self):
796 """Verify unchecking the checkbox causes the charm to be unfeatured."""
797 charm = factory.get_charm_json(name='name1', promulgated=True)
798 self.use_index_client()
799 self.enable_routes()
800 charm_source = CharmSource.from_request(self)
801 charm_source.save(charm)
802 # Add the submit button to trigger the right path through the view.
803 post_data = {
804 '_charset_': 'UTF-8',
805 '__formid__': 'deform',
806 'submit': 'submit',
807 }
808 request = self.getRequest(post=post_data)
809 request.matchdict = {
810 'charm': charm['name'],
811 'series': charm['series'],
812 }
813 # Before processing the request, the charm is featured.
814 featured_source = FeaturedSource.from_db(self.db)
815 featured_source.set_featured(charm, 'charm')
816 self.assertTrue(featured_source.is_featured(charm, 'charm'))
817 response = featured_edit(request)
818 self.assertEqual(302, response.status_code)
819 self.assertRegexpMatches(response.headers['Location'],
820 'http://[^/]*/charms/precise/name1')
821 for found in charm_source._get_all(charm['_id']):
822 self.assertFalse(featured_source.is_featured(found, 'charm'))
823
824
825class TestFeaturedFormFunc(WebTestBase):
826
827 def test_form_403s_for_non_charmers(self):
828 """The form is not permitted if you're not a charmer."""
829 with login_request(self.app, groups=[]) as resp:
830 cid, charm = factory.makeCharm(self.db)
831 # The status force the check against the status code.
832 resp = resp.goto(
833 'charms/%s/%s/featured/edit' %
834 (charm['series'], charm['name']), status=403)
835 self.assertEqual(403, resp.status_code)
836
837 def test_form_secured_from_users(self):
838 """The form is only allowed to be edited/submitted by charmers."""
839 with login_request(self.app, groups=['charmers']) as resp:
840 cid, charm = factory.makeCharm(self.db, promulgated=True)
841 resp = resp.goto(
842 'charms/%s/%s/featured/edit' %
843 (charm['series'], charm['name']),
844 status=200)
845 self.assertEqual(resp.status_code, 200)
846747
=== added file 'charmworld/views/tests/test_featured.py'
--- charmworld/views/tests/test_featured.py 1970-01-01 00:00:00 +0000
+++ charmworld/views/tests/test_featured.py 2013-09-04 16:51:29 +0000
@@ -0,0 +1,223 @@
1# Copyright 2012, 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from charmworld.models import (
5 CharmSource,
6 FeaturedSource,
7)
8from charmworld.testing import (
9 factory,
10 login_request,
11 ViewTestBase,
12 WebTestBase,
13)
14from charmworld.views.featured import (
15 bundle_featured_edit,
16 charm_featured_edit,
17 featured_edit,
18)
19
20
21class TestFeaturedForm(ViewTestBase):
22
23 def test_form_renders(self):
24 """Sanity check that the form renders without errors."""
25 cid, charm = factory.makeCharm(self.db, promulgated=True, owner='foo')
26 request = self.getRequest()
27 request.matchdict = {
28 'charm': charm['name'],
29 'series': charm['series'],
30 }
31 response = featured_edit(request, charm, 'charm', '')
32
33 self.assertIn('form', response)
34 self.assertIn('<label', response['form'])
35
36 def test_form_includes_current_featured_value(self):
37 """GET requests result in the current featured value being returned."""
38 cid, charm = factory.makeCharm(self.db, promulgated=True, owner='foo')
39 request = self.getRequest()
40 request.matchdict = {
41 'charm': charm['name'],
42 'series': charm['series'],
43 }
44 response = featured_edit(request, charm, 'charm', '')
45 self.assertFalse(response['values']['is_featured'])
46
47 def test_checking_checkbox_sets_featured_status(self):
48 """Verify checking the checkbox causes the charm to be featured."""
49 charm = factory.get_charm_json(name='name1', promulgated=True)
50 self.use_index_client()
51 self.enable_routes()
52 charm_source = CharmSource.from_request(self)
53 charm_source.save(charm)
54 # Add the submit button to trigger the right path through the view.
55 post_data = {
56 '_charset_': 'UTF-8',
57 '__formid__': 'deform',
58 'submit': 'submit',
59 'is_featured': 'true',
60 }
61 request = self.getRequest(post=post_data)
62 request.matchdict = {
63 'charm': charm['name'],
64 'series': charm['series'],
65 }
66 # Before processing the request, the charm is not featured.
67 featured_source = FeaturedSource.from_db(self.db)
68 self.assertFalse(featured_source.is_featured(charm, 'charm'))
69 featured_edit(request, charm, 'charm', '')
70 for found in charm_source._get_all(charm['_id']):
71 self.assertTrue(featured_source.is_featured(found, 'charm'))
72
73 def test_unchecking_checkbox_clears_featured_status(self):
74 """Verify unchecking the checkbox causes the charm to be unfeatured."""
75 charm = factory.get_charm_json(name='name1', promulgated=True)
76 self.use_index_client()
77 self.enable_routes()
78 charm_source = CharmSource.from_request(self)
79 charm_source.save(charm)
80 # Add the submit button to trigger the right path through the view.
81 post_data = {
82 '_charset_': 'UTF-8',
83 '__formid__': 'deform',
84 'submit': 'submit',
85 }
86 request = self.getRequest(post=post_data)
87 request.matchdict = {
88 'charm': charm['name'],
89 'series': charm['series'],
90 }
91 # Before processing the request, the charm is featured.
92 featured_source = FeaturedSource.from_db(self.db)
93 featured_source.set_featured(charm, 'charm')
94 self.assertTrue(featured_source.is_featured(charm, 'charm'))
95 featured_edit(request, charm, 'charm', '')
96 for found in charm_source._get_all(charm['_id']):
97 self.assertFalse(featured_source.is_featured(found, 'charm'))
98
99 def test_redirect_on_submit(self):
100 """After a submit, an HTTP redirect is raised with the given URL."""
101 charm = factory.get_charm_json(name='name1', promulgated=True)
102 self.use_index_client()
103 self.enable_routes()
104 charm_source = CharmSource.from_request(self)
105 charm_source.save(charm)
106 # Add the submit button to trigger the right path through the view.
107 post_data = {
108 '_charset_': 'UTF-8',
109 '__formid__': 'deform',
110 'submit': 'submit',
111 'is_featured': 'true',
112 }
113 request = self.getRequest(post=post_data)
114 request.matchdict = {
115 'charm': charm['name'],
116 'series': charm['series'],
117 }
118 # Before processing the request, the charm is not featured.
119 featured_source = FeaturedSource.from_db(self.db)
120 self.assertFalse(featured_source.is_featured(charm, 'charm'))
121 response = featured_edit(request, charm, 'charm', 'URL')
122 self.assertEqual(302, response.status_code)
123 self.assertEqual(response.headers['Location'], 'URL')
124
125
126class TestFeaturedCharmForm(ViewTestBase, WebTestBase):
127
128 def setUp(self):
129 super(TestFeaturedCharmForm, self).setUp()
130 # Register the routes so URL construction will work in the tests.
131 self.enable_routes()
132
133 def test_form_renders(self):
134 """Sanity check that the form renders without errors."""
135 cid, charm = factory.makeCharm(self.db, promulgated=True, owner='foo')
136 request = self.getRequest()
137 request.matchdict = {
138 'charm': charm['name'],
139 'series': charm['series'],
140 }
141 response = charm_featured_edit(request)
142
143 self.assertIn('form', response)
144 self.assertIn('<label', response['form'])
145
146 def test_charm_feature_form_route(self):
147 """The charm feature form is wired up to a route."""
148 charm = factory.makeCharm(self.db, promulgated=True, owner='foo')[1]
149 url = '/charms/{series}/{name}/featured/edit'.format(**charm)
150 with login_request(self.app, groups=['charmers']):
151 # Get the URL and assert the given status code.
152 self.app.get(url, status=200)
153
154 def test_404_if_charm_is_not_promulgated(self):
155 # If the charm indicated by the URL does not exist, a 404 is generated.
156 charm = factory.makeCharm(self.db, promulgated=False, owner='foo')[1]
157 url = '/charms/{series}/{name}/featured/edit'.format(**charm)
158 with login_request(self.app, groups=['charmers']):
159 # Get the URL and assert the given status code.
160 self.app.get(url, status=404)
161
162
163class TestFeaturedBundleForm(ViewTestBase, WebTestBase):
164
165 def setUp(self):
166 super(TestFeaturedBundleForm, self).setUp()
167 # Register the routes so URL construction will work in the tests.
168 self.enable_routes()
169
170 def test_form_renders(self):
171 """Sanity check that the form renders without errors."""
172 bundle, bundle_data = factory.makeBundle(self.db, promulgated=True)
173 request = self.getRequest()
174 request.matchdict = {
175 'bundle': bundle_data['name'],
176 'basket': bundle_data['basket_name'],
177 }
178 response = bundle_featured_edit(request)
179
180 self.assertIn('form', response)
181 self.assertIn('<label', response['form'])
182
183 def test_bundle_feature_form_route(self):
184 """The bundle feature form is wired up to a route."""
185 bundle, bundle_data = factory.makeBundle(self.db, promulgated=True)
186 url = '/bundles/{basket_name}/{name}/featured/edit'.format(
187 **bundle_data)
188 with login_request(self.app, groups=['charmers']):
189 # Get the URL and assert the given status code.
190 self.app.get(url, status=200)
191
192 def test_404_generated_when_bundle_is_not_promulgated(self):
193 # If the bundle indicated by the URL does not exist, a 404 is
194 # generated.
195 bundle, bundle_data = factory.makeBundle(self.db, promulgated=False)
196 url = '/bundles/{basket_name}/{name}/featured/edit'.format(
197 **bundle_data)
198 with login_request(self.app, groups=['charmers']):
199 # Get the URL and assert the given status code.
200 self.app.get(url, status=404)
201
202
203class TestFeaturedFormAuthorization(WebTestBase):
204
205 def test_form_403s_for_non_charmers(self):
206 """The form is not permitted if you're not a charmer."""
207 with login_request(self.app, groups=[]) as resp:
208 cid, charm = factory.makeCharm(self.db)
209 # The status force the check against the status code.
210 resp = resp.goto(
211 'charms/%s/%s/featured/edit' %
212 (charm['series'], charm['name']), status=403)
213 self.assertEqual(403, resp.status_code)
214
215 def test_form_secured_from_users(self):
216 """The form is only allowed to be edited/submitted by charmers."""
217 with login_request(self.app, groups=['charmers']) as resp:
218 cid, charm = factory.makeCharm(self.db, promulgated=True)
219 resp = resp.goto(
220 'charms/%s/%s/featured/edit' %
221 (charm['series'], charm['name']),
222 status=200)
223 self.assertEqual(resp.status_code, 200)

Subscribers

People subscribed via source and target branches