Return-Path: X-Original-To: apmail-allura-commits-archive@www.apache.org Delivered-To: apmail-allura-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CE4A617B5D for ; Tue, 24 Feb 2015 11:48:54 +0000 (UTC) Received: (qmail 26417 invoked by uid 500); 24 Feb 2015 11:47:56 -0000 Delivered-To: apmail-allura-commits-archive@allura.apache.org Received: (qmail 26307 invoked by uid 500); 24 Feb 2015 11:47:56 -0000 Mailing-List: contact commits-help@allura.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@allura.apache.org Delivered-To: mailing list commits@allura.apache.org Received: (qmail 25705 invoked by uid 99); 24 Feb 2015 11:47:55 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 24 Feb 2015 11:47:55 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id A4977E084F; Tue, 24 Feb 2015 11:47:55 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jetmind@apache.org To: commits@allura.apache.org Date: Tue, 24 Feb 2015 11:48:09 -0000 Message-Id: In-Reply-To: <47f4fbba54ef4bf38237664832d3145a@git.apache.org> References: <47f4fbba54ef4bf38237664832d3145a@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [15/50] [abbrv] allura git commit: [#4542] ticket:714 Add tests for new webhook functionality [#4542] ticket:714 Add tests for new webhook functionality Project: http://git-wip-us.apache.org/repos/asf/allura/repo Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/e7ace573 Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/e7ace573 Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/e7ace573 Branch: refs/heads/ib/7827 Commit: e7ace573ae8995955fb37de7ec62cd87e9d7bc2e Parents: ba555ec Author: Igor Bondarenko Authored: Fri Jan 30 15:01:49 2015 +0000 Committer: Igor Bondarenko Committed: Mon Feb 16 10:17:38 2015 +0000 ---------------------------------------------------------------------- Allura/allura/tests/test_utils.py | 9 + Allura/allura/tests/test_webhooks.py | 469 +++++++++++++++++++ Allura/allura/webhooks.py | 2 +- .../forgegit/tests/model/test_repository.py | 25 + .../forgesvn/tests/model/test_repository.py | 25 + 5 files changed, 529 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/tests/test_utils.py ---------------------------------------------------------------------- diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py index e5f9c43..06579c5 100644 --- a/Allura/allura/tests/test_utils.py +++ b/Allura/allura/tests/test_utils.py @@ -17,8 +17,10 @@ # specific language governing permissions and limitations # under the License. +import json import time import unittest +import datetime as dt from os import path from webob import Request @@ -295,3 +297,10 @@ def test_empty_cursor(): assert_raises(ValueError, cursor.one) assert_raises(StopIteration, cursor.next) assert_raises(StopIteration, cursor._next_impl) + + +def test_DateJSONEncoder(): + data = {'message': u'Hi!', + 'date': dt.datetime(2015, 01, 30, 13, 13, 13)} + result = json.dumps(data, cls=utils.DateJSONEncoder) + assert_equal(result, '{"date": "2015-01-30T13:13:13Z", "message": "Hi!"}') http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/tests/test_webhooks.py ---------------------------------------------------------------------- diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py new file mode 100644 index 0000000..fa0305f --- /dev/null +++ b/Allura/allura/tests/test_webhooks.py @@ -0,0 +1,469 @@ +import json +import hmac +import hashlib + +from mock import Mock, patch +from nose.tools import ( + assert_raises, + assert_equal, + assert_not_in, + assert_in, +) +from formencode import Invalid +from ming.odm import session +from pylons import tmpl_context as c + +from allura import model as M +from allura.lib import helpers as h +from allura.lib.utils import DateJSONEncoder +from allura.webhooks import ( + MingOneOf, + WebhookValidator, + WebhookController, + send_webhook, + RepoPushWebhookSender, +) +from allura.tests import decorators as td +from alluratest.controller import setup_basic_test, TestController + + +# important to be distinct from 'test' and 'test2' which ForgeGit and +# ForgeImporter use, so that the tests can run in parallel and not clobber each +# other +test_project_with_repo = 'adobe-1' +with_git = td.with_tool(test_project_with_repo, 'git', 'src', 'Git') +with_git2 = td.with_tool(test_project_with_repo, 'git', 'src2', 'Git2') + + +class TestWebhookBase(object): + + def setUp(self): + setup_basic_test() + self.setup_with_tools() + self.project = M.Project.query.get(shortname=test_project_with_repo) + self.git = self.project.app_instance('src') + self.wh = M.Webhook( + type='repo-push', + app_config_id=self.git.config._id, + hook_url='http://httpbin.org/post', + secret='secret') + session(self.wh).flush(self.wh) + + @with_git + def setup_with_tools(self): + pass + + +class TestValidators(TestWebhookBase): + + def test_ming_one_of(self): + ids = [ac._id for ac in M.AppConfig.query.find().all()[:2]] + v = MingOneOf(cls=M.AppConfig, ids=ids, not_empty=True) + with assert_raises(Invalid) as cm: + v.to_python(None) + assert_equal(cm.exception.msg, u'Please enter a value') + with assert_raises(Invalid) as cm: + v.to_python('invalid id') + assert_equal(cm.exception.msg, + u'Object must be one of: %s, not invalid id' % ids) + assert_equal(v.to_python(ids[0]), M.AppConfig.query.get(_id=ids[0])) + assert_equal(v.to_python(ids[1]), M.AppConfig.query.get(_id=ids[1])) + assert_equal(v.to_python(unicode(ids[0])), + M.AppConfig.query.get(_id=ids[0])) + assert_equal(v.to_python(unicode(ids[1])), + M.AppConfig.query.get(_id=ids[1])) + + def test_webhook_validator(self): + sender = Mock(type='repo-push') + ids = [ac._id for ac in M.AppConfig.query.find().all()[:3]] + ids, invalid_id = ids[:2], ids[2] + v = WebhookValidator(sender=sender, ac_ids=ids, not_empty=True) + with assert_raises(Invalid) as cm: + v.to_python(None) + assert_equal(cm.exception.msg, u'Please enter a value') + with assert_raises(Invalid) as cm: + v.to_python('invalid id') + assert_equal(cm.exception.msg, u'Invalid webhook') + + wh = M.Webhook(type='invalid type', + app_config_id=invalid_id, + hook_url='http://httpbin.org/post', + secret='secret') + session(wh).flush(wh) + with assert_raises(Invalid) as cm: + v.to_python(wh._id) + assert_equal(cm.exception.msg, u'Invalid webhook') + + wh.type = 'repo-push' + session(wh).flush(wh) + with assert_raises(Invalid) as cm: + v.to_python(wh._id) + assert_equal(cm.exception.msg, u'Invalid webhook') + + wh.app_config_id = ids[0] + session(wh).flush(wh) + assert_equal(v.to_python(wh._id), wh) + assert_equal(v.to_python(unicode(wh._id)), wh) + + +class TestWebhookController(TestController): + + def setUp(self): + super(TestWebhookController, self).setUp() + self.setup_with_tools() + self.patches = self.monkey_patch() + for p in self.patches: + p.start() + self.project = M.Project.query.get(shortname=test_project_with_repo) + self.git = self.project.app_instance('src') + self.git2 = self.project.app_instance('src2') + self.url = str(self.project.url() + 'admin/webhooks') + + def tearDown(self): + super(TestWebhookController, self).tearDown() + for p in self.patches: + p.stop() + + @with_git + @with_git2 + def setup_with_tools(self): + pass + + def monkey_patch(self): + gen_secret = patch.object( + WebhookController, + 'gen_secret', + return_value='super-secret', + autospec=True) + return [gen_secret] + + def create_webhook(self, data): + r = self.app.post(self.url + '/repo-push/create', data) + wf = json.loads(self.webflash(r)) + assert_equal(wf['status'], 'ok') + assert_equal(wf['message'], 'Created successfully') + return r + + def find_error(self, r, field, msg, form_type='create'): + form = r.html.find('form', attrs={'action': form_type}) + if field == '_the_form': + error = form.findPrevious('div', attrs={'class': 'error'}) + else: + widget = 'select' if field == 'app' else 'input' + error = form.find(widget, attrs={'name': field}) + error = error.findNext('div', attrs={'class': 'error'}) + if error: + assert_in(h.escape(msg), error.getText()) + else: + assert False, 'Validation error not found' + + def test_access(self): + self.app.get(self.url + '/repo-push/') + self.app.get(self.url + '/repo-push/', + extra_environ={'username': 'test-user'}, + status=403) + r = self.app.get(self.url + '/repo-push/', + extra_environ={'username': '*anonymous'}, + status=302) + assert_equal(r.location, + 'http://localhost/auth/' + '?return_to=%2Fadobe%2Fadobe-1%2Fadmin%2Fwebhooks%2Frepo-push%2F') + + def test_invalid_hook_type(self): + self.app.get(self.url + '/invalid-hook-type/', status=404) + + def test_create(self): + assert_equal(M.Webhook.query.find().count(), 0) + r = self.app.get(self.url) + assert_in('

repo-push

', r) + assert_not_in('http://httpbin.org/post', r) + data = {'url': u'http://httpbin.org/post', + 'app': unicode(self.git.config._id), + 'secret': ''} + msg = 'add webhook repo-push {} {}'.format( + data['url'], self.git.config.url()) + with td.audits(msg): + r = self.create_webhook(data).follow().follow(status=200) + assert_in('http://httpbin.org/post', r) + + hooks = M.Webhook.query.find().all() + assert_equal(len(hooks), 1) + assert_equal(hooks[0].type, 'repo-push') + assert_equal(hooks[0].hook_url, 'http://httpbin.org/post') + assert_equal(hooks[0].app_config_id, self.git.config._id) + assert_equal(hooks[0].secret, 'super-secret') + + # Try to create duplicate + with td.out_audits(msg): + r = self.app.post(self.url + '/repo-push/create', data) + self.find_error(r, '_the_form', + '"repo-push" webhook already exists for Git http://httpbin.org/post') + assert_equal(M.Webhook.query.find().count(), 1) + + def test_create_validation(self): + assert_equal(M.Webhook.query.find().count(), 0) + r = self.app.post( + self.url + '/repo-push/create', {}, status=404) + + data = {'url': '', 'app': '', 'secret': ''} + r = self.app.post(self.url + '/repo-push/create', data) + self.find_error(r, 'url', 'Please enter a value') + self.find_error(r, 'app', 'Please enter a value') + + data = {'url': 'qwer', 'app': '123', 'secret': 'qwe'} + r = self.app.post(self.url + '/repo-push/create', data) + self.find_error(r, 'url', + 'You must provide a full domain name (like qwer.com)') + self.find_error(r, 'app', 'Object must be one of: ') + self.find_error(r, 'app', '%s' % self.git.config._id) + self.find_error(r, 'app', '%s' % self.git2.config._id) + + def test_edit(self): + data1 = {'url': u'http://httpbin.org/post', + 'app': unicode(self.git.config._id), + 'secret': u'secret'} + data2 = {'url': u'http://example.com/hook', + 'app': unicode(self.git2.config._id), + 'secret': u'secret2'} + self.create_webhook(data1).follow().follow(status=200) + self.create_webhook(data2).follow().follow(status=200) + assert_equal(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_equal(form['url'].value, data1['url']) + assert_equal(form['app'].value, data1['app']) + assert_equal(form['secret'].value, data1['secret']) + assert_equal(form['webhook'].value, unicode(wh1._id)) + form['url'] = 'http://host.org/hook' + form['app'] = unicode(self.git2.config._id) + form['secret'] = 'new secret' + msg = 'edit webhook repo-push\n{} => {}\n{} => {}\n{}'.format( + data1['url'], form['url'].value, + self.git.config.url(), self.git2.config.url(), + 'secret changed') + with td.audits(msg): + r = form.submit() + wf = json.loads(self.webflash(r)) + assert_equal(wf['status'], 'ok') + assert_equal(wf['message'], 'Edited successfully') + assert_equal(M.Webhook.query.find().count(), 2) + wh1 = M.Webhook.query.get(_id=wh1._id) + assert_equal(wh1.hook_url, 'http://host.org/hook') + assert_equal(wh1.app_config_id, self.git2.config._id) + assert_equal(wh1.secret, 'new secret') + assert_equal(wh1.type, 'repo-push') + + # Duplicates + r = self.app.get(self.url + '/repo-push/%s' % wh1._id) + form = r.forms[0] + form['url'] = data2['url'] + form['app'] = data2['app'] + r = form.submit() + self.find_error(r, '_the_form', + u'"repo-push" webhook already exists for Git2 http://example.com/hook', + form_type='edit') + + def test_edit_validation(self): + invalid = M.Webhook( + type='invalid type', + app_config_id=None, + hook_url='http://httpbin.org/post', + secret='secret') + session(invalid).flush(invalid) + self.app.get(self.url + '/repo-push/%s' % invalid._id, status=404) + + data = {'url': u'http://httpbin.org/post', + 'app': unicode(self.git.config._id), + 'secret': u'secret'} + self.create_webhook(data).follow().follow(status=200) + wh = M.Webhook.query.get(hook_url=data['url'], type='repo-push') + + # invalid id in hidden field, just in case + r = self.app.get(self.url + '/repo-push/%s' % wh._id) + data = {k: v[0].value for (k, v) in r.forms[0].fields.items()} + data['webhook'] = unicode(invalid._id) + self.app.post(self.url + '/repo-push/edit', data, status=404) + + # empty values + data = {'url': '', 'app': '', 'secret': '', 'webhook': str(wh._id)} + r = self.app.post(self.url + '/repo-push/edit', data) + self.find_error(r, 'url', 'Please enter a value', 'edit') + self.find_error(r, 'app', 'Please enter a value', 'edit') + + data = {'url': 'qwe', 'app': '123', 'secret': 'qwe', + 'webhook': str(wh._id)} + r = self.app.post(self.url + '/repo-push/edit', data) + self.find_error(r, 'url', + 'You must provide a full domain name (like qwe.com)', 'edit') + self.find_error(r, 'app', 'Object must be one of:', 'edit') + self.find_error(r, 'app', '%s' % self.git.config._id, 'edit') + self.find_error(r, 'app', '%s' % self.git2.config._id, 'edit') + + def test_delete(self): + data = {'url': u'http://httpbin.org/post', + 'app': unicode(self.git.config._id), + 'secret': u'secret'} + self.create_webhook(data).follow().follow(status=200) + assert_equal(M.Webhook.query.find().count(), 1) + wh = M.Webhook.query.get(hook_url=data['url']) + data = {'webhook': unicode(wh._id)} + msg = 'delete webhook repo-push {} {}'.format( + wh.hook_url, self.git.config.url()) + with td.audits(msg): + r = self.app.post(self.url + '/repo-push/delete', data) + assert_equal(r.json, {'status': 'ok'}) + assert_equal(M.Webhook.query.find().count(), 0) + + def test_delete_validation(self): + invalid = M.Webhook( + type='invalid type', + app_config_id=None, + hook_url='http://httpbin.org/post', + secret='secret') + session(invalid).flush(invalid) + assert_equal(M.Webhook.query.find().count(), 1) + + data = {'webhook': ''} + self.app.post(self.url + '/repo-push/delete', data, status=404) + + data = {'webhook': unicode(invalid._id)} + self.app.post(self.url + '/repo-push/delete', data, status=404) + assert_equal(M.Webhook.query.find().count(), 1) + + def test_list_webhooks(self): + data1 = {'url': u'http://httpbin.org/post', + 'app': unicode(self.git.config._id), + 'secret': 'secret'} + data2 = {'url': u'http://another-host.org/', + 'app': unicode(self.git2.config._id), + 'secret': 'secret2'} + self.create_webhook(data1).follow().follow(status=200) + self.create_webhook(data2).follow().follow(status=200) + wh1 = M.Webhook.query.get(hook_url=data1['url']) + wh2 = M.Webhook.query.get(hook_url=data2['url']) + + r = self.app.get(self.url) + assert_in('

repo-push

', r) + rows = r.html.find('table').findAll('tr') + assert_equal(len(rows), 2) + rows = sorted([self._format_row(row) for row in rows]) + expected_rows = sorted([ + [{'href': self.url + '/repo-push/' + str(wh1._id), + 'text': wh1.hook_url}, + {'href': self.git.url, + 'text': self.git.config.options.mount_label}, + {'text': wh1.secret}, + {'href': self.url + '/repo-push/delete', + 'data-id': str(wh1._id)}], + [{'href': self.url + '/repo-push/' + str(wh2._id), + 'text': wh2.hook_url}, + {'href': self.git2.url, + 'text': self.git2.config.options.mount_label}, + {'text': wh2.secret}, + {'href': self.url + '/repo-push/delete', + 'data-id': str(wh2._id)}], + ]) + assert_equal(rows, expected_rows) + + def _format_row(self, row): + def link(td): + a = td.find('a') + return {'href': a.get('href'), 'text': a.getText()} + def text(td): + return {'text': td.getText()} + def delete_btn(td): + a = td.find('a') + return {'href': a.get('href'), 'data-id': a.get('data-id')} + tds = row.findAll('td') + return [link(tds[0]), link(tds[1]), text(tds[2]), delete_btn(tds[3])] + + +class TestTasks(TestWebhookBase): + + @patch('allura.webhooks.requests', autospec=True) + @patch('allura.webhooks.log', autospec=True) + def test_send_webhook(self, log, requests): + requests.post.return_value = Mock(status_code=200) + payload = {'some': ['data']} + json_payload = json.dumps(payload, cls=DateJSONEncoder) + send_webhook(self.wh._id, payload) + signature = hmac.new( + self.wh.secret.encode('utf-8'), + json_payload.encode('utf-8'), + hashlib.sha1) + signature = 'sha1=' + signature.hexdigest() + headers = {'content-type': 'application/json', + 'User-Agent': 'Allura Webhook (https://allura.apache.org/)', + 'X-Allura-Signature': signature} + requests.post.assert_called_once_with( + self.wh.hook_url, + data=json_payload, + headers=headers, + timeout=30) + log.info.assert_called_once_with( + 'Webhook successfully sent: %s %s %s', + self.wh.type, self.wh.hook_url, self.wh.app_config.url()) + + @patch('allura.webhooks.requests', autospec=True) + @patch('allura.webhooks.log', autospec=True) + def test_send_webhook_error(self, log, requests): + requests.post.return_value = Mock(status_code=500) + send_webhook(self.wh._id, {}) + assert_equal(requests.post.call_count, 1) + assert_equal(log.info.call_count, 0) + log.error.assert_called_once_with( + 'Webhook send error: %s %s %s %s %s', + self.wh.type, self.wh.hook_url, + self.wh.app_config.url(), + requests.post.return_value.status_code, + requests.post.return_value.reason) + +class TestRepoPushWebhookSender(TestWebhookBase): + + @patch('allura.webhooks.send_webhook', autospec=True) + def test_send(self, send_webhook): + sender = RepoPushWebhookSender() + sender.get_payload = Mock() + with h.push_config(c, app=self.git): + sender.send(arg1=1, arg2=2) + send_webhook.post.assert_called_once_with( + self.wh._id, + sender.get_payload.return_value) + + @patch('allura.webhooks.send_webhook', autospec=True) + def test_send_no_configured_webhooks(self, send_webhook): + self.wh.delete() + session(self.wh).flush(self.wh) + sender = RepoPushWebhookSender() + with h.push_config(c, app=self.git): + sender.send(arg1=1, arg2=2) + assert_equal(send_webhook.post.call_count, 0) + + def test_get_payload(self): + sender = RepoPushWebhookSender() + _ci = list(range(1, 4)) + _se = [Mock(info=str(x)) for x in _ci] + with patch.object(self.git.repo, 'commit', autospec=True, side_effect=_se): + with h.push_config(c, app=self.git): + result = sender.get_payload(commit_ids=_ci) + expected_result = { + 'url': 'http://localhost/adobe/adobe-1/src/', + 'count': 3, + 'revisions': ['1', '2', '3'], + } + assert_equal(result, expected_result) + + +class TestModels(TestWebhookBase): + + def test_webhook_find(self): + p = M.Project.query.get(shortname='test') + assert_equal(M.Webhook.find('smth', p), []) + assert_equal(M.Webhook.find('repo-push', p), []) + assert_equal(M.Webhook.find('smth', self.project), []) + assert_equal(M.Webhook.find('repo-push', self.project), [self.wh]) + + def test_webhook_url(self): + assert_equal(self.wh.url(), + '/adobe/adobe-1/admin/webhooks/repo-push/{}'.format(self.wh._id)) http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/webhooks.py ---------------------------------------------------------------------- diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py index 8495786..2393acd 100644 --- a/Allura/allura/webhooks.py +++ b/Allura/allura/webhooks.py @@ -236,7 +236,7 @@ def send_webhook(webhook_id, payload): # TODO: catch # TODO: configurable timeout r = requests.post(url, data=json_payload, headers=headers, timeout=30) - if r.status_code >= 200 and r.status_code <= 300: + if r.status_code >= 200 and r.status_code < 300: log.info('Webhook successfully sent: %s %s %s', webhook.type, webhook.hook_url, webhook.app_config.url()) else: http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/ForgeGit/forgegit/tests/model/test_repository.py ---------------------------------------------------------------------- diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py index 4d7a0c0..5e86ab0 100644 --- a/ForgeGit/forgegit/tests/model/test_repository.py +++ b/ForgeGit/forgegit/tests/model/test_repository.py @@ -39,6 +39,7 @@ from allura.tests import decorators as td from allura.tests.model.test_repo import RepoImplTestBase from allura import model as M from allura.model.repo_refresh import send_notifications +from allura.webhooks import RepoPushWebhookSender from forgegit import model as GM from forgegit.tests import with_git from forgewiki import model as WM @@ -519,6 +520,30 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase): self.repo.clone_url('https', 'user'), 'https://user@foo.com/') + def test_webhook_payload(self): + sender = RepoPushWebhookSender() + cids = list(self.repo.all_commit_ids())[:2] + payload = sender.get_payload(commit_ids=cids) + expected_payload = { + 'url': 'http://localhost/p/test/src-git/', + 'count': 2, + 'revisions': [ + {'author': u'Cory Johns', + 'author_email': u'cjohns@slashdotmedia.com', + 'author_url': None, + 'date': datetime.datetime(2013, 3, 28, 18, 54, 16), + 'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c', + 'shortlink': u'[5c4724]', + 'summary': u'Not repo root'}, + {'author': u'Rick Copeland', + 'author_email': u'rcopeland@geek.net', + 'author_url': None, + 'date': datetime.datetime(2010, 10, 7, 18, 44, 11), + 'id': u'1e146e67985dcd71c74de79613719bef7bddca4a', + 'shortlink': u'[1e146e]', + 'summary': u'Change README'}]} + assert_equal(payload, expected_payload) + class TestGitImplementation(unittest.TestCase): http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/ForgeSVN/forgesvn/tests/model/test_repository.py ---------------------------------------------------------------------- diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py index 70c34ac..5267614 100644 --- a/ForgeSVN/forgesvn/tests/model/test_repository.py +++ b/ForgeSVN/forgesvn/tests/model/test_repository.py @@ -38,6 +38,7 @@ from alluratest.controller import setup_basic_test, setup_global_objects from allura import model as M from allura.model.repo_refresh import send_notifications from allura.lib import helpers as h +from allura.webhooks import RepoPushWebhookSender from allura.tests.model.test_repo import RepoImplTestBase from forgesvn import model as SM @@ -569,6 +570,30 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase): ThreadLocalORMSession.flush_all() assert repo2.is_empty() + def test_webhook_payload(self): + sender = RepoPushWebhookSender() + cids = list(self.repo.all_commit_ids())[:2] + payload = sender.get_payload(commit_ids=cids) + expected_payload = { + 'url': 'http://localhost/p/test/src/', + 'count': 2, + 'revisions': [ + {'author': u'coldmind', + 'author_email': u'', + 'author_url': None, + 'date': datetime(2013, 11, 8, 13, 38, 11, 152000), + 'id': u'{}:6'.format(self.repo._id), + 'shortlink': '[r6]', + 'summary': ''}, + {'author': u'rick446', + 'author_email': u'', + 'author_url': None, + 'date': datetime(2010, 11, 18, 20, 14, 21, 515000), + 'id': u'{}:5'.format(self.repo._id), + 'shortlink': '[r5]', + 'summary': u'Copied a => b'}]} + assert_equal(payload, expected_payload) + class TestSVNRev(unittest.TestCase):