This is an automated email from the ASF dual-hosted git repository. dill0wn pushed a commit to branch pytest-finalize in repository https://gitbox.apache.org/repos/asf/allura.git
commit c5b17848ba6ba46a300d0c732e2f3066689bab21 Author: Dillon Walls <[email protected]> AuthorDate: Mon Sep 12 20:00:41 2022 +0000 [#8455] allura pytest - fix misc test failures that popped up during pytest conversion --- Allura/allura/lib/macro.py | 15 +-- Allura/allura/model/project.py | 20 +++- Allura/allura/tests/exclude_from_rewrite_hook.py | 31 ++++++ .../allura/tests/functional/test_neighborhood.py | 8 +- .../allura/tests/functional/test_user_profile.py | 70 +++++++------ Allura/allura/tests/model/test_auth.py | 2 +- Allura/allura/tests/test_globals.py | 115 ++++++++++++--------- Allura/allura/tests/test_plugin.py | 34 +++--- Allura/allura/tests/test_tasks.py | 38 +++---- Allura/allura/tests/test_webhooks.py | 101 +++++++++--------- 10 files changed, 249 insertions(+), 185 deletions(-) diff --git a/Allura/allura/lib/macro.py b/Allura/allura/lib/macro.py index b1af50297..0e293037e 100644 --- a/Allura/allura/lib/macro.py +++ b/Allura/allura/lib/macro.py @@ -177,14 +177,15 @@ def project_blog_posts(max_number=5, sort='timestamp', summary=False, mount_poin 'state': 'published', }) posts = posts.sort(sort, pymongo.DESCENDING).limit(int(max_number)).all() - output = ((dict( - href=post.url(), - title=post.title, - author=post.author().display_name, - ago=h.ago(post.timestamp), - description=summary and ' ' or g.markdown.cached_convert(post, 'text'))) + output = [ + dict(href=post.url(), + title=post.title, + author=post.author().display_name, + ago=h.ago(post.timestamp), + description=summary and ' ' or g.markdown.cached_convert(post, 'text')) for post in posts if security.has_access(post, 'read', project=post.app.project)() and - security.has_access(post.app.project, 'read', project=post.app.project)()) + security.has_access(post.app.project, 'read', project=post.app.project)() + ] posts = BlogPosts(posts=output) g.resource_manager.register(posts) response = posts.display(posts=output) diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py index f0d3552b4..fad47aefc 100644 --- a/Allura/allura/model/project.py +++ b/Allura/allura/model/project.py @@ -1102,12 +1102,30 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject): ThreadLocalORMSession.flush_all() def add_user(self, user, role_names): - 'Convenience method to add member with the given role(s).' + '''Convenience method to add member with the given role(s).''' pr = ProjectRole.by_user(user, project=self, upsert=True) for role_name in role_names: r = ProjectRole.by_name(role_name, self) pr.roles.append(r._id) + def remove_user(self, user, role_names=None): + '''Convenience method to add member with the given role(s).''' + pr = ProjectRole.by_user(user, project=self) + if not pr: + return + + if not role_names or not isinstance(role_names, Iterable): + ProjectRole.query.remove({'_id': pr._id}) + return + + for role_name in role_names: + r = ProjectRole.by_name(role_name, self) + if r._id in pr.roles: + pr.roles.remove(r._id) + + if not pr.roles: + pr.remove() + @property def twitter_handle(self): return self.social_account('Twitter').accounturl diff --git a/Allura/allura/tests/exclude_from_rewrite_hook.py b/Allura/allura/tests/exclude_from_rewrite_hook.py new file mode 100644 index 000000000..509841776 --- /dev/null +++ b/Allura/allura/tests/exclude_from_rewrite_hook.py @@ -0,0 +1,31 @@ +import sys + +from allura.app import Application +from allura.lib.decorators import task +from allura.lib.exceptions import CompoundError + + +class ThemeProviderTestApp(Application): + """ + If this test class is added directly to a test module, pkg_resources internals + will throw this error: + NotImplementedError: Can't perform this operation for unregistered loader type + This is because pytest adds a hook to override the default assert behavior and this + conflicts/messes-with pkg_resources. Theoretically on python > py37, importlib.resources + can do the same things as pkg_resources and faster, but those solutions don't currently + work on py37. + """ + icons = { + 24: 'images/testapp_24.png', + } + + +@task +def raise_compound_exception(): + errs = [] + for x in range(10): + try: + assert False, 'assert %d' % x + except Exception: + errs.append(sys.exc_info()) + raise CompoundError(*errs) \ No newline at end of file diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py index a1d1e7d57..ac7ee3c9d 100644 --- a/Allura/allura/tests/functional/test_neighborhood.py +++ b/Allura/allura/tests/functional/test_neighborhood.py @@ -43,12 +43,12 @@ from allura.tests.pytest_helpers import with_nose_compatibility @with_nose_compatibility class TestNeighborhood(TestController): - def setUp(self): - super().setUp() + def setup_method(self, method): + super().setup_method(method) self._cleanup_audit_log() - def tearDown(self): - super().tearDown() + def teardown_method(self, method): + super().teardown_method(method) self._cleanup_audit_log() def _cleanup_audit_log(self): diff --git a/Allura/allura/tests/functional/test_user_profile.py b/Allura/allura/tests/functional/test_user_profile.py index 4a54ffa69..e4df160e1 100644 --- a/Allura/allura/tests/functional/test_user_profile.py +++ b/Allura/allura/tests/functional/test_user_profile.py @@ -26,6 +26,46 @@ from allura.tests import TestController from allura.tests.pytest_helpers import with_nose_compatibility +class TestUserProfileSections(TestController): + + def teardown_method(self, method): + super().teardown_method(method) + project = Project.query.get(shortname='u/test-user') + app = project.app_instance('profile') + if hasattr(type(app), '_sections'): + delattr(type(app), '_sections') + + @td.with_user_project('test-user') + def test_profile_sections(self): + project = Project.query.get(shortname='u/test-user') + app = project.app_instance('profile') + + def ep(n): + m = mock.Mock() + m.name = n + m.load()().display.return_value = 'Section %s' % n + return m + eps = list(map(ep, ['a', 'b', 'c', 'd'])) + order = {'user_profile_sections.order': 'b, d,c , f '} + if hasattr(type(app), '_sections'): + delattr(type(app), '_sections') + with mock.patch('allura.lib.helpers.iter_entry_points') as iep: + with mock.patch.dict(tg.config, order): + iep.return_value = eps + sections = app.profile_sections + assert sections == [ + eps[1].load(), + eps[3].load(), + eps[2].load(), + eps[0].load()] + r = self.app.get('/u/test-user/profile') + assert 'Section a' in r.text + assert 'Section b' in r.text + assert 'Section c' in r.text + assert 'Section d' in r.text + assert 'Section f' not in r.text + + @with_nose_compatibility class TestUserProfile(TestController): @@ -230,36 +270,6 @@ class TestUserProfile(TestController): r = self.app.get('/u/test-user/profile/send_message', status=200) assert 'you currently have user messages disabled' in r - @td.with_user_project('test-user') - def test_profile_sections(self): - project = Project.query.get(shortname='u/test-user') - app = project.app_instance('profile') - - def ep(n): - m = mock.Mock() - m.name = n - m.load()().display.return_value = 'Section %s' % n - return m - eps = list(map(ep, ['a', 'b', 'c', 'd'])) - order = {'user_profile_sections.order': 'b, d,c , f '} - if hasattr(type(app), '_sections'): - delattr(type(app), '_sections') - with mock.patch('allura.lib.helpers.iter_entry_points') as iep: - with mock.patch.dict(tg.config, order): - iep.return_value = eps - sections = app.profile_sections - assert sections == [ - eps[1].load(), - eps[3].load(), - eps[2].load(), - eps[0].load()] - r = self.app.get('/u/test-user/profile') - assert 'Section a' in r.text - assert 'Section b' in r.text - assert 'Section c' in r.text - assert 'Section d' in r.text - assert 'Section f' not in r.text - def test_no_index_tag_in_empty_profile(self): r = self.app.get('/u/test-user/profile/') assert 'content="noindex, follow"' in r.text diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py index 2d6aa75f7..7f48c49bc 100644 --- a/Allura/allura/tests/model/test_auth.py +++ b/Allura/allura/tests/model/test_auth.py @@ -352,8 +352,8 @@ def test_check_sent_user_message_times(): assert not user1.can_send_user_message() [email protected]_user_project('test-admin') @with_setup(setup_method) [email protected]_user_project('test-admin') def test_user_track_active(): # without this session flushing inside track_active raises Exception setup_functional_test() diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py index 4d4b3b592..389c7a228 100644 --- a/Allura/allura/tests/test_globals.py +++ b/Allura/allura/tests/test_globals.py @@ -18,6 +18,7 @@ import re import os +from textwrap import dedent import allura import unittest import hashlib @@ -48,11 +49,6 @@ from forgewiki import model as WM from forgeblog import model as BM -def setup_module(module): - setup_basic_test() - setup_unit_test() - - def squish_spaces(text): # \s is whitespace # \xa0 is in unicode form @@ -84,8 +80,28 @@ def get_projects_property_in_the_same_order(names, prop): @with_nose_compatibility class Test(): + @classmethod + def setup_class(cls): + setup_basic_test() + setup_global_objects() + def setup_method(self, method): setup_global_objects() + p_nbhd = M.Neighborhood.query.get(name='Projects') + p_test = M.Project.query.get(shortname='test', neighborhood_id=p_nbhd._id) + self.acl_bak = p_test.acl.copy() + + def teardown_method(self, method): + user = M.User.by_username('test-admin') + user.display_name = 'Test Admin' + + p_nbhd = M.Neighborhood.query.get(name='Projects') + p_test = M.Project.query.get(shortname='test', neighborhood_id=p_nbhd._id) + p_test.remove_user(M.User.by_username('test-user')) + p_test.remove_user(M.User.by_username('test-user-0')) + p_test.acl = self.acl_bak + + ThreadLocalORMSession.flush_all() @td.with_wiki def test_app_globals(self): @@ -358,17 +374,19 @@ class Test(): def test_markdown_toc(self): with h.push_context('test', neighborhood='Projects'): - r = g.markdown_wiki.convert("""[TOC] + r = g.markdown_wiki.convert(dedent("""\ + [TOC] - # Header 1 + # Header 1 - ## Header 2""") - assert '''<ul> - <li><a href="#header-1">Header 1</a><ul> - <li><a href="#header-2">Header 2</a></li> - </ul> - </li> - </ul>''' in r, r + ## Header 2""")) + assert dedent('''\ + <ul> + <li><a href="#header-1">Header 1</a><ul> + <li><a href="#header-2">Header 2</a></li> + </ul> + </li> + </ul>''') in r @td.with_wiki def test_wiki_artifact_links(self): @@ -442,38 +460,35 @@ class Test(): '<p>Line</p></div>') # should not raise an exception: - assert ( - g.markdown.convert("<class 'foo'>") == - '''<div class="markdown_content"><p><class 'foo'=""></class></p></div>''') - - assert ( - g.markdown.convert('''# Header - - Some text in a regular paragraph - - :::python - for i in range(10): - print i - ''') == - # no <br - '<div class="markdown_content"><h1 id="header">Header</h1>\n' - '<p>Some text in a regular paragraph</p>\n' - '<div class="codehilite"><pre><span></span><code><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>\n' - ' <span class="nb">print</span> <span class="n">i</span>\n' - '</code></pre></div>\n' - '</div>') + assert g.markdown.convert("<class 'foo'>") == \ + '''<div class="markdown_content"><p><class 'foo'=""></class></p></div>''' + + assert g.markdown.convert(dedent('''\ + # Header + + Some text in a regular paragraph + + :::python + for i in range(10): + print i + ''')) == dedent('''\ + <div class="markdown_content"><h1 id="header">Header</h1> + <p>Some text in a regular paragraph</p> + <div class="codehilite"><pre><span></span><code><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span> + <span class="nb">print</span> <span class="n">i</span> + </code></pre></div> + </div>''') assert ( g.forge_markdown(email=True).convert('[Home]') == # uses localhost: '<div class="markdown_content"><p><a class="alink" href="http://localhost/p/test/wiki/Home/">[Home]</a></p></div>') - assert ( - g.markdown.convert(''' - ~~~~ - def foo(): pass - ~~~~''') == - '<div class="markdown_content"><div class="codehilite"><pre><span></span><code>def foo(): pass\n' - '</code></pre></div>\n' - '</div>') + assert g.markdown.convert(dedent('''\ + ~~~~ + def foo(): pass + ~~~~''')) == dedent('''\ + <div class="markdown_content"><div class="codehilite"><pre><span></span><code>def foo(): pass + </code></pre></div> + </div>''') def test_markdown_list_without_break(self): # this is not a valid way to make a list in original Markdown or python-markdown @@ -482,37 +497,37 @@ class Test(): # TODO: try https://github.com/adamb70/mdx-breakless-lists # or https://gitlab.com/ayblaq/prependnewline assert ( - g.markdown.convert('''\ + g.markdown.convert(dedent('''\ Regular text * first item - * second item''') == + * second item''')) == '<div class="markdown_content"><p>Regular text\n' # no <br> '* first item\n' # no <br> '* second item</p></div>') assert ( - g.markdown.convert('''\ + g.markdown.convert(dedent('''\ Regular text - first item - - second item''') == + - second item''')) == '<div class="markdown_content"><p>Regular text<br/>\n' '- first item<br/>\n' '- second item</p></div>') assert ( - g.markdown.convert('''\ + g.markdown.convert(dedent('''\ Regular text + first item - + second item''') == + + second item''')) == '<div class="markdown_content"><p>Regular text<br/>\n' '+ first item<br/>\n' '+ second item</p></div>') assert ( - g.markdown.convert('''\ + g.markdown.convert(dedent('''\ Regular text 1. first item - 2. second item''') == + 2. second item''')) == '<div class="markdown_content"><p>Regular text<br/>\n' '1. first item<br/>\n' '2. second item</p></div>') diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py index 22a7ed8bf..f11eae8d7 100644 --- a/Allura/allura/tests/test_plugin.py +++ b/Allura/allura/tests/test_plugin.py @@ -35,7 +35,6 @@ from alluratest.tools import ( from mock import Mock, MagicMock, patch from allura import model as M -from allura.app import Application from allura.lib import plugin from allura.lib import phone from allura.lib import helpers as h @@ -44,15 +43,19 @@ from allura.lib.plugin import ProjectRegistrationProvider from allura.lib.plugin import ThemeProvider from allura.lib.exceptions import ProjectConflict, ProjectShortnameInvalid from allura.tests.decorators import audits +from allura.tests.exclude_from_rewrite_hook import ThemeProviderTestApp from alluratest.controller import setup_basic_test, setup_global_objects from allura.tests.pytest_helpers import with_nose_compatibility +def setup_module(module): + setup_basic_test() + + @with_nose_compatibility class TestProjectRegistrationProvider: def setup_method(self, method): - setup_basic_test() self.provider = ProjectRegistrationProvider() @patch('allura.lib.security.has_access') @@ -170,7 +173,6 @@ class TestProjectRegistrationProviderPhoneVerification: self.user = UserMock() self.nbhd = MagicMock() - def test_phone_verified_disabled(self): with h.push_config(tg.config, **{'project.verify_phone': 'false'}): assert self.p.phone_verified(self.user, self.nbhd) @@ -280,36 +282,30 @@ class TestProjectRegistrationProviderPhoneVerification: assert result == g.phone_service.verify.return_value assert 5 == g.phone_service.verify.call_count + @with_nose_compatibility class TestThemeProvider: @patch('allura.app.g') @patch('allura.lib.plugin.g') def test_app_icon_str(self, plugin_g, app_g): - class TestApp(Application): - icons = { - 24: 'images/testapp_24.png', - } - plugin_g.entry_points = {'tool': {'testapp': TestApp}} - assert (ThemeProvider().app_icon_url('testapp', 24) == - app_g.theme_href.return_value) + plugin_g.entry_points = {'tool': {'testapp': ThemeProviderTestApp}} + app_icon = ThemeProvider().app_icon_url('testapp', 24) + other = app_g.theme_href.return_value + assert app_icon == other + app_g.theme_href.assert_called_with('images/testapp_24.png') @patch('allura.lib.plugin.g') def test_app_icon_str_invalid(self, g): g.entry_points = {'tool': {'testapp': Mock()}} - assert (ThemeProvider().app_icon_url('invalid', 24) == - None) + assert ThemeProvider().app_icon_url('invalid', 24) == None @patch('allura.app.g') def test_app_icon_app(self, g): - class TestApp(Application): - icons = { - 24: 'images/testapp_24.png', - } - app = TestApp(None, None) - assert (ThemeProvider().app_icon_url(app, 24) == - g.theme_href.return_value) + app = ThemeProviderTestApp(None, None) + assert ThemeProvider().app_icon_url(app, 24) == \ + g.theme_href.return_value g.theme_href.assert_called_with('images/testapp_24.png') diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py index 735b3294b..429effd80 100644 --- a/Allura/allura/tests/test_tasks.py +++ b/Allura/allura/tests/test_tasks.py @@ -18,6 +18,7 @@ import operator import shutil import sys +from textwrap import dedent import unittest import six @@ -52,6 +53,7 @@ from allura.tasks import export_tasks from allura.tasks import admin_tasks from allura.tests import decorators as td from allura.tests.pytest_helpers import with_nose_compatibility +from allura.tests.exclude_from_rewrite_hook import raise_compound_exception from allura.lib.decorators import event_handler, task @@ -148,7 +150,7 @@ class TestEventTasks(unittest.TestCase): assert M.MonQTask.query.get(task_name='allura.tasks.event_tasks.event', args=['my_event4']) def test_compound_error(self): - t = raise_exc.post() + t = raise_compound_exception.post() with LogCapture(level=logging.ERROR) as l, \ mock.patch.dict(tg.config, {'monq.raise_errors': False}): # match normal non-test behavior t() @@ -158,7 +160,7 @@ class TestEventTasks(unittest.TestCase): assert "AssertionError('assert 0'" in msg assert "AssertionError('assert 5'" in msg assert ' on job <MonQTask ' in msg - assert ' (error) P:10 allura.tests.test_tasks.raise_exc ' in msg + assert ' (error) P:10 allura.tests.exclude_from_rewrite_hook.raise_compound_exception ' in msg for x in range(10): assert ('assert %d' % x) in t.result @@ -510,16 +512,17 @@ class TestMailTasks(unittest.TestCase): @td.with_tool('test', 'Tickets', 'bugs') def test_receive_autoresponse(self): - message = '''Date: Wed, 30 Oct 2013 01:38:40 -0700 -From: <[email protected]> -To: <[email protected]> -Message-ID: <super-unique-id> -Subject: Not here Re: Message notification -Precedence: bulk -X-Autoreply: yes -Auto-Submitted: auto-replied - -I'm not here''' + message = dedent('''\ + Date: Wed, 30 Oct 2013 01:38:40 -0700 + From: <[email protected]> + To: <[email protected]> + Message-ID: <super-unique-id> + Subject: Not here Re: Message notification + Precedence: bulk + X-Autoreply: yes + Auto-Submitted: auto-replied + + I'm not here''') import forgetracker c.user = M.User.by_username('test-admin') with mock.patch.object(forgetracker.tracker_main.ForgeTrackerApp, 'handle_message') as hm: @@ -597,17 +600,6 @@ def _my_event(event_type, testcase, *args, **kwargs): testcase.called_with.append((args, kwargs)) -@task -def raise_exc(): - errs = [] - for x in range(10): - try: - assert False, 'assert %d' % x - except Exception: - errs.append(sys.exc_info()) - raise CompoundError(*errs) - - class _TestArtifact(M.Artifact): _shorthand_id = FieldProperty(str) text = FieldProperty(str) diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py index 5b9d5b1f2..8237847d6 100644 --- a/Allura/allura/tests/test_webhooks.py +++ b/Allura/allura/tests/test_webhooks.py @@ -132,6 +132,7 @@ class TestValidators(TestWebhookBase): @with_nose_compatibility class TestWebhookController(TestController): + def setup_method(self, method): super().setup_method(method) self.patches = self.monkey_patch() @@ -182,6 +183,56 @@ class TestWebhookController(TestController): else: assert False, 'Validation error not found' + def test_AAAA_WORKAROUND__edit(self): + """ + This must run first in this test class for unknown reasons ever since + https://github.com/TurboGears/tg2/commit/02fb49b14e70fdd8ac16973488fb3637e5e59114 + + If any test runs the self.app.post from create_webhook before this one, then this test will fail on: + with td.audits(msg): + r = form.submit() + because WebhookValidator's `value` will be "create" instead of an objectid str + + Maybe something to do with WebhookControllerMeta setup of `validate` decorators? + """ + data1 = {'url': 'http://httpbin.org/post', + 'secret': 'secret'} + data2 = {'url': 'http://example.com/hook', + 'secret': 'secret2'} + self.create_webhook(data1).follow() + self.create_webhook(data2).follow() + assert M.Webhook.query.find().count() == 2 + wh1 = M.Webhook.query.get(hook_url=data1['url']) + r = self.app.get(self.url + '/repo-push/%s' % wh1._id) + form = r.forms[0] + assert form['url'].value == data1['url'] + assert form['secret'].value == data1['secret'] + assert form['webhook'].value == str(wh1._id) + form['url'] = 'http://host.org/hook' + form['secret'] = 'new secret' + msg = 'edit webhook repo-push\n{} => {}\n{}'.format( + data1['url'], form['url'].value, 'secret changed') + with td.audits(msg): + r = form.submit() + wf = json.loads(self.webflash(r)) + assert wf['status'] == 'ok' + assert wf['message'] == 'Edited successfully' + assert M.Webhook.query.find().count() == 2 + wh1 = M.Webhook.query.get(_id=wh1._id) + assert wh1.hook_url == 'http://host.org/hook' + assert wh1.app_config_id == self.git.config._id + assert wh1.secret == 'new secret' + assert wh1.type == 'repo-push' + + # Duplicates + r = self.app.get(self.url + '/repo-push/%s' % wh1._id) + form = r.forms[0] + form['url'] = data2['url'] + r = form.submit() + self.find_error(r, '_the_form', + '"repo-push" webhook already exists for Git http://example.com/hook', + form_type='edit') + def test_access(self): self.app.get(self.url + '/repo-push/') self.app.get(self.url + '/repo-push/', @@ -256,56 +307,6 @@ class TestWebhookController(TestController): self.find_error(r, 'url', 'You must provide a full domain name (like qwer.com)') - def test_AAAA_WORKAROUND__edit(self): - """ - This must run first in this test class for unknown reasons ever since - https://github.com/TurboGears/tg2/commit/02fb49b14e70fdd8ac16973488fb3637e5e59114 - - If any test runs the self.app.post from create_webhook before this one, then this test will fail on: - with td.audits(msg): - r = form.submit() - because WebhookValidator's `value` will be "create" instead of an objectid str - - Maybe something to do with WebhookControllerMeta setup of `validate` decorators? - """ - data1 = {'url': 'http://httpbin.org/post', - 'secret': 'secret'} - data2 = {'url': 'http://example.com/hook', - 'secret': 'secret2'} - self.create_webhook(data1).follow() - self.create_webhook(data2).follow() - assert M.Webhook.query.find().count() == 2 - wh1 = M.Webhook.query.get(hook_url=data1['url']) - r = self.app.get(self.url + '/repo-push/%s' % wh1._id) - form = r.forms[0] - assert form['url'].value == data1['url'] - assert form['secret'].value == data1['secret'] - assert form['webhook'].value == str(wh1._id) - form['url'] = 'http://host.org/hook' - form['secret'] = 'new secret' - msg = 'edit webhook repo-push\n{} => {}\n{}'.format( - data1['url'], form['url'].value, 'secret changed') - with td.audits(msg): - r = form.submit() - wf = json.loads(self.webflash(r)) - assert wf['status'] == 'ok' - assert wf['message'] == 'Edited successfully' - assert M.Webhook.query.find().count() == 2 - wh1 = M.Webhook.query.get(_id=wh1._id) - assert wh1.hook_url == 'http://host.org/hook' - assert wh1.app_config_id == self.git.config._id - assert wh1.secret == 'new secret' - assert wh1.type == 'repo-push' - - # Duplicates - r = self.app.get(self.url + '/repo-push/%s' % wh1._id) - form = r.forms[0] - form['url'] = data2['url'] - r = form.submit() - self.find_error(r, '_the_form', - '"repo-push" webhook already exists for Git http://example.com/hook', - form_type='edit') - def test_edit_validation(self): invalid = M.Webhook( type='invalid type',
