Add gogs integration inside taiga distribution
parent
5856ad6410
commit
c4ff960435
|
@ -313,6 +313,7 @@ INSTALLED_APPS = [
|
||||||
"taiga.hooks.github",
|
"taiga.hooks.github",
|
||||||
"taiga.hooks.gitlab",
|
"taiga.hooks.gitlab",
|
||||||
"taiga.hooks.bitbucket",
|
"taiga.hooks.bitbucket",
|
||||||
|
"taiga.hooks.gogs",
|
||||||
"taiga.webhooks",
|
"taiga.webhooks",
|
||||||
|
|
||||||
"djmail",
|
"djmail",
|
||||||
|
@ -506,6 +507,7 @@ PROJECT_MODULES_CONFIGURATORS = {
|
||||||
"github": "taiga.hooks.github.services.get_or_generate_config",
|
"github": "taiga.hooks.github.services.get_or_generate_config",
|
||||||
"gitlab": "taiga.hooks.gitlab.services.get_or_generate_config",
|
"gitlab": "taiga.hooks.gitlab.services.get_or_generate_config",
|
||||||
"bitbucket": "taiga.hooks.bitbucket.services.get_or_generate_config",
|
"bitbucket": "taiga.hooks.bitbucket.services.get_or_generate_config",
|
||||||
|
"gogs": "taiga.hooks.gogs.services.get_or_generate_config",
|
||||||
}
|
}
|
||||||
|
|
||||||
BITBUCKET_VALID_ORIGIN_IPS = ["131.103.20.165", "131.103.20.166", "104.192.143.192/28", "104.192.143.208/28"]
|
BITBUCKET_VALID_ORIGIN_IPS = ["131.103.20.165", "131.103.20.166", "104.192.143.192/28", "104.192.143.208/28"]
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from taiga.hooks.api import BaseWebhookApiViewSet
|
||||||
|
|
||||||
|
from . import event_hooks
|
||||||
|
|
||||||
|
|
||||||
|
class GogsViewSet(BaseWebhookApiViewSet):
|
||||||
|
event_hook_classes = {
|
||||||
|
"push": event_hooks.PushEventHook
|
||||||
|
}
|
||||||
|
|
||||||
|
def _validate_signature(self, project, request):
|
||||||
|
payload = self._get_payload(request)
|
||||||
|
|
||||||
|
if not hasattr(project, "modules_config"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if project.modules_config.config is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
secret = project.modules_config.config.get("gogs", {}).get("secret", None)
|
||||||
|
if secret is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return payload.get('secret', None) == secret
|
||||||
|
|
||||||
|
def _get_event_name(self, request):
|
||||||
|
return "push"
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from taiga.hooks.event_hooks import BasePushEventHook
|
||||||
|
|
||||||
|
|
||||||
|
class BaseGogsEventHook():
|
||||||
|
platform = "Gogs"
|
||||||
|
platform_slug = "gogs"
|
||||||
|
|
||||||
|
def replace_gogs_references(self, project_url, wiki_text):
|
||||||
|
if wiki_text is None:
|
||||||
|
wiki_text = ""
|
||||||
|
|
||||||
|
template = "\g<1>[Gogs#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
|
||||||
|
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
|
||||||
|
|
||||||
|
|
||||||
|
class PushEventHook(BaseGogsEventHook, BasePushEventHook):
|
||||||
|
def get_data(self):
|
||||||
|
result = []
|
||||||
|
commits = self.payload.get("commits", [])
|
||||||
|
project_url = self.payload.get("repository", {}).get("url", None)
|
||||||
|
|
||||||
|
for commit in filter(None, commits):
|
||||||
|
user_name = commit.get('author', {}).get('username', None)
|
||||||
|
result.append({
|
||||||
|
"user_id": user_name,
|
||||||
|
"user_name": user_name,
|
||||||
|
"user_url": os.path.join(os.path.dirname(os.path.dirname(project_url)), user_name),
|
||||||
|
"commit_id": commit.get("id", None),
|
||||||
|
"commit_url": commit.get("url", None),
|
||||||
|
"commit_message": commit.get("message", None),
|
||||||
|
})
|
||||||
|
return result
|
|
@ -0,0 +1,39 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
from django.core.files import File
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
|
||||||
|
CUR_DIR = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_gogs_system_user(apps, schema_editor):
|
||||||
|
# We get the model from the versioned app registry;
|
||||||
|
# if we directly import it, it'll be the wrong version
|
||||||
|
User = apps.get_model("users", "User")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
random_hash = uuid.uuid4().hex
|
||||||
|
user = User.objects.using(db_alias).create(
|
||||||
|
username="gogs-{}".format(random_hash),
|
||||||
|
email="gogs-{}@taiga.io".format(random_hash),
|
||||||
|
full_name="Gogs",
|
||||||
|
is_active=False,
|
||||||
|
is_system=True,
|
||||||
|
bio="",
|
||||||
|
)
|
||||||
|
f = open("{}/logo.png".format(CUR_DIR), "rb")
|
||||||
|
user.photo.save("logo.png", File(f))
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('users', '0010_auto_20150414_0936')
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(create_gogs_system_user),
|
||||||
|
]
|
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
|
@ -0,0 +1 @@
|
||||||
|
# This file is needed to load migrations
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from taiga.base.utils.urls import get_absolute_url
|
||||||
|
|
||||||
|
|
||||||
|
# Set this in settings.PROJECT_MODULES_CONFIGURATORS["gogs"]
|
||||||
|
def get_or_generate_config(project):
|
||||||
|
config = project.modules_config.config
|
||||||
|
if config and "gogs" in config:
|
||||||
|
g_config = project.modules_config.config["gogs"]
|
||||||
|
else:
|
||||||
|
g_config = {"secret": uuid.uuid4().hex}
|
||||||
|
|
||||||
|
url = reverse("gogs-hook-list")
|
||||||
|
url = get_absolute_url(url)
|
||||||
|
url = "%s?project=%s" % (url, project.id)
|
||||||
|
g_config["webhooks_url"] = url
|
||||||
|
return g_config
|
|
@ -208,6 +208,12 @@ from taiga.hooks.bitbucket.api import BitBucketViewSet
|
||||||
router.register(r"bitbucket-hook", BitBucketViewSet, base_name="bitbucket-hook")
|
router.register(r"bitbucket-hook", BitBucketViewSet, base_name="bitbucket-hook")
|
||||||
|
|
||||||
|
|
||||||
|
# Gogs webhooks
|
||||||
|
from taiga.hooks.gogs.api import GogsViewSet
|
||||||
|
|
||||||
|
router.register(r"gogs-hook", GogsViewSet, base_name="gogs-hook")
|
||||||
|
|
||||||
|
|
||||||
# Importer
|
# Importer
|
||||||
from taiga.export_import.api import ProjectImporterViewSet, ProjectExporterViewSet
|
from taiga.export_import.api import ProjectImporterViewSet, ProjectExporterViewSet
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,502 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# Copyright (C) 2014-2016 Anler Hernández <hello@anler.me>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.core import mail
|
||||||
|
|
||||||
|
from taiga.base.utils import json
|
||||||
|
from taiga.hooks.gogs import event_hooks
|
||||||
|
from taiga.hooks.gogs.api import GogsViewSet
|
||||||
|
from taiga.hooks.exceptions import ActionSyntaxException
|
||||||
|
from taiga.projects import choices as project_choices
|
||||||
|
from taiga.projects.issues.models import Issue
|
||||||
|
from taiga.projects.tasks.models import Task
|
||||||
|
from taiga.projects.userstories.models import UserStory
|
||||||
|
from taiga.projects.models import Membership
|
||||||
|
from taiga.projects.history.services import get_history_queryset_by_model_instance, take_snapshot
|
||||||
|
from taiga.projects.notifications.choices import NotifyLevel
|
||||||
|
from taiga.projects.notifications.models import NotifyPolicy
|
||||||
|
from taiga.projects import services
|
||||||
|
from .. import factories as f
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
def test_bad_signature(client):
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
url = reverse("gogs-hook-list")
|
||||||
|
url = "%s?project=%s" % (url, project.id)
|
||||||
|
data = {
|
||||||
|
"secret": "badbadbad"
|
||||||
|
}
|
||||||
|
response = client.post(url, json.dumps(data),
|
||||||
|
content_type="application/json")
|
||||||
|
response_content = response.data
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert "Bad signature" in response_content["_error_message"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_ok_signature(client):
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.ProjectModulesConfigFactory(project=project, config={
|
||||||
|
"gogs": {
|
||||||
|
"secret": "tpnIwJDz4e"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
url = reverse("gogs-hook-list")
|
||||||
|
url = "%s?project=%s" % (url, project.id)
|
||||||
|
data = {"test:": "data", "secret": "tpnIwJDz4e"}
|
||||||
|
response = client.post(url, json.dumps(data),
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_blocked_project(client):
|
||||||
|
project = f.ProjectFactory(blocked_code=project_choices.BLOCKED_BY_STAFF)
|
||||||
|
f.ProjectModulesConfigFactory(project=project, config={
|
||||||
|
"gogs": {
|
||||||
|
"secret": "tpnIwJDz4e"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
url = reverse("gogs-hook-list")
|
||||||
|
url = "%s?project=%s" % (url, project.id)
|
||||||
|
data = {"test:": "data", "secret": "tpnIwJDz4e"}
|
||||||
|
response = client.post(url, json.dumps(data),
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
assert response.status_code == 451
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_detected(client):
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
url = reverse("gogs-hook-list")
|
||||||
|
url = "%s?project=%s" % (url, project.id)
|
||||||
|
data = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": "test message",
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GogsViewSet._validate_signature = mock.Mock(return_value=True)
|
||||||
|
|
||||||
|
with mock.patch.object(event_hooks.PushEventHook, "process_event") as process_event_mock:
|
||||||
|
response = client.post(url, json.dumps(data),
|
||||||
|
HTTP_X_GITHUB_EVENT="push",
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
assert process_event_mock.call_count == 1
|
||||||
|
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_issue_processing(client):
|
||||||
|
creation_status = f.IssueStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
new_status = f.IssueStatusFactory(project=creation_status.project)
|
||||||
|
issue = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (issue.ref, new_status.slug),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(issue.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
issue = Issue.objects.get(id=issue.id)
|
||||||
|
assert issue.status.id == new_status.id
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_task_processing(client):
|
||||||
|
creation_status = f.TaskStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
new_status = f.TaskStatusFactory(project=creation_status.project)
|
||||||
|
task = f.TaskFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (task.ref, new_status.slug),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(task.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
task = Task.objects.get(id=task.id)
|
||||||
|
assert task.status.id == new_status.id
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_user_story_processing(client):
|
||||||
|
creation_status = f.UserStoryStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_us"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
new_status = f.UserStoryStatusFactory(project=creation_status.project)
|
||||||
|
user_story = f.UserStoryFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (user_story.ref, new_status.slug),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(user_story.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
user_story = UserStory.objects.get(id=user_story.id)
|
||||||
|
assert user_story.status.id == new_status.id
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_issue_mention(client):
|
||||||
|
creation_status = f.IssueStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
issue = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
take_snapshot(issue, user=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s ok
|
||||||
|
bye!
|
||||||
|
""" % (issue.ref),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(issue.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
issue_history = get_history_queryset_by_model_instance(issue)
|
||||||
|
assert issue_history.count() == 1
|
||||||
|
assert issue_history[0].comment.startswith("This issue has been mentioned by")
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_task_mention(client):
|
||||||
|
creation_status = f.TaskStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
task = f.TaskFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
take_snapshot(task, user=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s ok
|
||||||
|
bye!
|
||||||
|
""" % (task.ref),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(task.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
task_history = get_history_queryset_by_model_instance(task)
|
||||||
|
assert task_history.count() == 1
|
||||||
|
assert task_history[0].comment.startswith("This task has been mentioned by")
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_user_story_mention(client):
|
||||||
|
creation_status = f.UserStoryStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_us"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
user_story = f.UserStoryFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
take_snapshot(user_story, user=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s ok
|
||||||
|
bye!
|
||||||
|
""" % (user_story.ref),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(user_story.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
us_history = get_history_queryset_by_model_instance(user_story)
|
||||||
|
assert us_history.count() == 1
|
||||||
|
assert us_history[0].comment.startswith("This user story has been mentioned by")
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_multiple_actions(client):
|
||||||
|
creation_status = f.IssueStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
new_status = f.IssueStatusFactory(project=creation_status.project)
|
||||||
|
issue1 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
issue2 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #%s ok
|
||||||
|
test TG-%s #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (issue1.ref, new_status.slug, issue2.ref, new_status.slug),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook1 = event_hooks.PushEventHook(issue1.project, payload)
|
||||||
|
ev_hook1.process_event()
|
||||||
|
issue1 = Issue.objects.get(id=issue1.id)
|
||||||
|
issue2 = Issue.objects.get(id=issue2.id)
|
||||||
|
assert issue1.status.id == new_status.id
|
||||||
|
assert issue2.status.id == new_status.id
|
||||||
|
assert len(mail.outbox) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_processing_case_insensitive(client):
|
||||||
|
creation_status = f.TaskStatusFactory()
|
||||||
|
role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"])
|
||||||
|
f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner)
|
||||||
|
new_status = f.TaskStatusFactory(project=creation_status.project)
|
||||||
|
task = f.TaskFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner)
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test tg-%s #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (task.ref, new_status.slug.upper()),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
ev_hook = event_hooks.PushEventHook(task.project, payload)
|
||||||
|
ev_hook.process_event()
|
||||||
|
task = Task.objects.get(id=task.id)
|
||||||
|
assert task.status.id == new_status.id
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_task_bad_processing_non_existing_ref(client):
|
||||||
|
issue_status = f.IssueStatusFactory()
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-6666666 #%s ok
|
||||||
|
bye!
|
||||||
|
""" % (issue_status.slug),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
ev_hook = event_hooks.PushEventHook(issue_status.project, payload)
|
||||||
|
with pytest.raises(ActionSyntaxException) as excinfo:
|
||||||
|
ev_hook.process_event()
|
||||||
|
|
||||||
|
assert str(excinfo.value) == "The referenced element doesn't exist"
|
||||||
|
assert len(mail.outbox) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_us_bad_processing_non_existing_status(client):
|
||||||
|
user_story = f.UserStoryFactory.create()
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #non-existing-slug ok
|
||||||
|
bye!
|
||||||
|
""" % (user_story.ref),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
ev_hook = event_hooks.PushEventHook(user_story.project, payload)
|
||||||
|
with pytest.raises(ActionSyntaxException) as excinfo:
|
||||||
|
ev_hook.process_event()
|
||||||
|
|
||||||
|
assert str(excinfo.value) == "The status doesn't exist"
|
||||||
|
assert len(mail.outbox) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_event_bad_processing_non_existing_status(client):
|
||||||
|
issue = f.IssueFactory.create()
|
||||||
|
payload = {
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"message": """test message
|
||||||
|
test TG-%s #non-existing-slug ok
|
||||||
|
bye!
|
||||||
|
""" % (issue.ref),
|
||||||
|
"author": {
|
||||||
|
"username": "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "http://test-url/test/project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
ev_hook = event_hooks.PushEventHook(issue.project, payload)
|
||||||
|
with pytest.raises(ActionSyntaxException) as excinfo:
|
||||||
|
ev_hook.process_event()
|
||||||
|
|
||||||
|
assert str(excinfo.value) == "The status doesn't exist"
|
||||||
|
assert len(mail.outbox) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_get_project_modules(client):
|
||||||
|
project = f.create_project()
|
||||||
|
f.MembershipFactory(project=project, user=project.owner, is_admin=True)
|
||||||
|
|
||||||
|
url = reverse("projects-modules", args=(project.id,))
|
||||||
|
|
||||||
|
client.login(project.owner)
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
content = response.data
|
||||||
|
assert "gogs" in content
|
||||||
|
assert content["gogs"]["secret"] != ""
|
||||||
|
assert content["gogs"]["webhooks_url"] != ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_patch_project_modules(client):
|
||||||
|
project = f.create_project()
|
||||||
|
f.MembershipFactory(project=project, user=project.owner, is_admin=True)
|
||||||
|
|
||||||
|
url = reverse("projects-modules", args=(project.id,))
|
||||||
|
|
||||||
|
client.login(project.owner)
|
||||||
|
data = {
|
||||||
|
"gogs": {
|
||||||
|
"secret": "test_secret",
|
||||||
|
"url": "test_url",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = client.patch(url, json.dumps(data), content_type="application/json")
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
config = services.get_modules_config(project).config
|
||||||
|
assert "gogs" in config
|
||||||
|
assert config["gogs"]["secret"] == "test_secret"
|
||||||
|
assert config["gogs"]["webhooks_url"] != "test_url"
|
||||||
|
|
||||||
|
|
||||||
|
def test_replace_gogs_references():
|
||||||
|
ev_hook = event_hooks.BaseGogsEventHook
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", "#2") == "[Gogs#2](project-url/issues/2)"
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", "#2 ") == "[Gogs#2](project-url/issues/2) "
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", " #2 ") == " [Gogs#2](project-url/issues/2) "
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", " #2") == " [Gogs#2](project-url/issues/2)"
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", "#test") == "#test"
|
||||||
|
assert ev_hook.replace_gogs_references(None, "project-url", None) == ""
|
Loading…
Reference in New Issue