From d794f0a6615e160bb7fa116cec5c4a30a0bff2bd Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 03 2017 07:17:39 +0000 Subject: [PATCH 1/250] Allow project-less API token to create new tickets This allows users to create projet-less API token to create new tickets on any project, just like what they can do in the UI. Fixes https://pagure.io/pagure/issue/2250 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/api/issue.py b/pagure/api/issue.py index a561779..d7bd0b3 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -195,10 +195,7 @@ def api_new_issue(repo, username=None, namespace=None): output = {} repo = _get_repo(repo, username, namespace) _check_issue_tracker(repo) - - if flask.g.token.project and repo != flask.g.token.project: - raise pagure.exceptions.APIError( - 401, error_code=APIERROR.EINVALIDTOK) + _check_token(repo, project_token=False) user_obj = pagure.lib.get_user( SESSION, flask.g.fas_user.username) diff --git a/tests/test_pagure_flask_api_issue_create.py b/tests/test_pagure_flask_api_issue_create.py new file mode 100644 index 0000000..e4fba79 --- /dev/null +++ b/tests/test_pagure_flask_api_issue_create.py @@ -0,0 +1,320 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2017 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + + +import datetime +import unittest +import sys +import os +import json + +from mock import patch, MagicMock + +sys.path.insert(0, os.path.join(os.path.dirname( + os.path.abspath(__file__)), '..')) + +import pagure # noqa: E402 +import pagure.lib # noqa: E402 +import tests # noqa: E402 + + +class PagureFlaskApiIssueCreatetests(tests.Modeltests): + """ Tests for the flask API of pagure for creating an issue + """ + + @patch('pagure.lib.notify.send_email', MagicMock(return_value=True)) + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureFlaskApiIssueCreatetests, self).setUp() + + pagure.APP.config['TESTING'] = True + pagure.SESSION = self.session + pagure.api.SESSION = self.session + pagure.api.issue.SESSION = self.session + pagure.lib.SESSION = self.session + + pagure.APP.config['TICKETS_FOLDER'] = None + + tests.create_projects(self.session) + tests.create_projects_git(os.path.join(self.path, 'tickets')) + tests.create_tokens(self.session) + tests.create_tokens_acl(self.session) + + # Create project-less token for user foo + item = pagure.lib.model.Token( + id='project-less-foo', + user_id=2, + project_id=None, + expiration=datetime.datetime.utcnow() + + datetime.timedelta(days=30) + ) + self.session.add(item) + self.session.commit() + tests.create_tokens_acl(self.session, token_id='project-less-foo') + + # Create project-specific token for user foo + item = pagure.lib.model.Token( + id='project-specific-foo', + user_id=2, + project_id=1, + expiration=datetime.datetime.utcnow() + + datetime.timedelta(days=30) + ) + self.session.add(item) + self.session.commit() + tests.create_tokens_acl( + self.session, token_id='project-specific-foo') + + def test_create_issue_own_project_no_data(self): + """ Test creating a new ticket on a project for which you're the + main maintainer. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token aaabbbcccddd'} + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post('/api/0/test/new_issue', headers=headers) + self.assertEqual(output.status_code, 400) + data = json.loads(output.data) + self.assertEqual( + pagure.api.APIERROR.EINVALIDREQ.name, data['error_code']) + self.assertEqual( + pagure.api.APIERROR.EINVALIDREQ.value, data['error']) + self.assertEqual( + data['errors'], + { + u'issue_content': [u'This field is required.'], + u'title': [u'This field is required.'] + } + ) + + def test_create_issue_own_project_incomplete_data(self): + """ Test creating a new ticket on a project for which you're the + main maintainer. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token aaabbbcccddd'} + + # complete data set + data = { + 'title': 'test issue', + } + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post( + '/api/0/test/new_issue', + headers=headers, + data=data) + self.assertEqual(output.status_code, 400) + data = json.loads(output.data) + self.assertEqual( + pagure.api.APIERROR.EINVALIDREQ.name, data['error_code']) + self.assertEqual( + pagure.api.APIERROR.EINVALIDREQ.value, data['error']) + self.assertEqual( + data['errors'], + { + u'issue_content': [u'This field is required.'] + } + ) + + def test_create_issue_own_project(self): + """ Test creating a new ticket on a project for which you're the + main maintainer. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token aaabbbcccddd'} + + # complete data set + data = { + 'title': 'test issue', + 'issue_content': 'This issue needs attention', + } + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post( + '/api/0/test/new_issue', + headers=headers, + data=data) + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + data['issue']['date_created'] = '1431414800' + data['issue']['last_updated'] = '1431414800' + self.assertEqual( + data, + { + "issue": { + "assignee": None, + "blocks": [], + "close_status": None, + "closed_at": None, + "comments": [], + "content": "This issue needs attention", + "custom_fields": [], + "date_created": "1431414800", + "depends": [], + "id": 1, + "last_updated": "1431414800", + "milestone": "", + "priority": None, + "private": False, + "status": "Open", + "tags": [], + "title": "test issue", + "user": { + "fullname": "PY C", + "name": "pingou" + } + }, + "message": "Issue created" + } + ) + + @patch('pagure.lib.notify.send_email', MagicMock(return_value=True)) + def test_create_issue_someone_else_project_project_less_token(self): + """ Test creating a new ticket on a project with which you have + nothing to do. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token project-less-foo'} + + # complete data set + data = { + 'title': 'test issue', + 'issue_content': 'This issue needs attention', + } + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post( + '/api/0/test/new_issue', + headers=headers, + data=data) + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + data['issue']['date_created'] = '1431414800' + data['issue']['last_updated'] = '1431414800' + self.assertEqual( + data, + { + "issue": { + "assignee": None, + "blocks": [], + "close_status": None, + "closed_at": None, + "comments": [], + "content": "This issue needs attention", + "custom_fields": [], + "date_created": "1431414800", + "depends": [], + "id": 1, + "last_updated": "1431414800", + "milestone": "", + "priority": None, + "private": False, + "status": "Open", + "tags": [], + "title": "test issue", + "user": { + "fullname": "foo bar", + "name": "foo" + } + }, + "message": "Issue created" + } + ) + + @patch('pagure.lib.notify.send_email', MagicMock(return_value=True)) + def test_create_issue_project_specific_token(self): + """ Test creating a new ticket on a project with a regular + project-specific token. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token project-specific-foo'} + + # complete data set + data = { + 'title': 'test issue', + 'issue_content': 'This issue needs attention', + } + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post( + '/api/0/test/new_issue', + headers=headers, + data=data) + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + data['issue']['date_created'] = '1431414800' + data['issue']['last_updated'] = '1431414800' + self.assertEqual( + data, + { + "issue": { + "assignee": None, + "blocks": [], + "close_status": None, + "closed_at": None, + "comments": [], + "content": "This issue needs attention", + "custom_fields": [], + "date_created": "1431414800", + "depends": [], + "id": 1, + "last_updated": "1431414800", + "milestone": "", + "priority": None, + "private": False, + "status": "Open", + "tags": [], + "title": "test issue", + "user": { + "fullname": "foo bar", + "name": "foo" + } + }, + "message": "Issue created" + } + ) + + @patch('pagure.lib.notify.send_email', MagicMock(return_value=True)) + def test_create_issue_invalid_project_specific_token(self): + """ Test creating a new ticket on a project with a regular + project-specific token but for another project. + """ + + # pingou's token with all the ACLs + headers = {'Authorization': 'token project-specific-foo'} + + # complete data set + data = { + 'title': 'test issue', + 'issue_content': 'This issue needs attention', + } + + # Create an issue on /test/ where pingou is the main admin + output = self.app.post( + '/api/0/test2/new_issue', + headers=headers, + data=data) + self.assertEqual(output.status_code, 401) + data = json.loads(output.data) + self.assertEqual( + pagure.api.APIERROR.EINVALIDTOK.name, data['error_code']) + self.assertEqual( + pagure.api.APIERROR.EINVALIDTOK.value, data['error']) + + +if __name__ == '__main__': + unittest.main(verbosity=2) From c9b5bc78b2884a2e420b8ae6f449d639d1665797 Mon Sep 17 00:00:00 2001 From: Vivek Anand Date: Jul 03 2017 08:13:48 +0000 Subject: [PATCH 2/250] Tips/tricks: add for case when EMAIL_SEND is False Signed-off-by: Vivek Anand --- diff --git a/doc/usage/tips_tricks.rst b/doc/usage/tips_tricks.rst index fe4e1bd..4319085 100644 --- a/doc/usage/tips_tricks.rst +++ b/doc/usage/tips_tricks.rst @@ -29,3 +29,13 @@ To do this, simply prepend a ``!`` in front of the tag. Example: ~~~~~~~~ https://pagure.io/pagure/issues?tags=!easyfix + + +Local user creation without email verification +---------------------------------------------- + +If you set ``EMAIL_SEND`` to ```False``` from the configuration file, you +will get the emails printed to the console instead of being sent. The admin +of the instance can then access the URL to manually validate the account from +there. This is generally used for development where we don't need to send +any emails. From 274e607ab4fa5afb110ec339a6144f570dba52fd Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2017 08:44:54 +0000 Subject: [PATCH 3/250] Optimize the generation of the gitolite configuration With this change we support only updating part of the configuration instead of re-generating it entirely all the time. Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/__init__.py b/pagure/__init__.py index 5d9d7f5..f07f7bf 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -401,9 +401,10 @@ def generate_user_key_files(): if gitolite_home: users = pagure.lib.search_user(SESSION) for user in users: - pagure.lib.update_user_ssh(SESSION, user, user.public_ssh_key, - APP.config.get('GITOLITE_KEYDIR', None)) - pagure.lib.git.generate_gitolite_acls() + pagure.lib.update_user_ssh( + SESSION, user, user.public_ssh_key, + APP.config.get('GITOLITE_KEYDIR', None)) + pagure.lib.git.generate_gitolite_acls(project=None) def login_required(function): diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 307119a..3821f39 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2733,7 +2733,7 @@ def update_user_ssh(session, user, ssh_key, keydir): user.public_ssh_key = ssh_key if keydir and user.public_ssh_key: create_user_ssh_keys_on_disk(user, keydir) - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=None) session.add(user) session.flush() diff --git a/pagure/lib/git.py b/pagure/lib/git.py index 8ab5afc..8b2f4e9 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -88,8 +88,28 @@ Subject: {subject} return patch -def generate_gitolite_acls(): - tasks.generate_gitolite_acls.delay() +def generate_gitolite_acls(project=None): + """ Generate the gitolite configuration file. + + :arg project: the project of which to update the ACLs. This argument + can take three values: ``-1``, ``None`` and a project. + If project is ``-1``, the configuration should be refreshed for + *all* projects. + If project is ``None``, there no specific project to refresh + but the ssh key of an user was added and updated. + If project is a pagure.lib.model.Project, the configuration of + this project should be updated. + :type project: None, int or pagure.lib.model.Project + + """ + if project != -1: + tasks.generate_gitolite_acls.delay( + namespace=project.namespace if project else None, + name=project.name if project else None, + user=project.user.user if project and project.is_fork else None + ) + else: + tasks.generate_gitolite_acls.delay(name=-1) def update_git(obj, repo, repofolder): diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index 1924962..c2c46b1 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -53,9 +53,24 @@ class GitAuthHelper(object): @classmethod @abc.abstractmethod - def generate_acls(self): + def generate_acls(self, project): """ This is the method that is called by pagure to generate the configuration file. + + :arg project: the project of which to update the ACLs. This argument + can take three values: ``-1``, ``None`` and a project. + If project is ``-1``, the configuration should be refreshed for + *all* projects. + If project is ``None``, there no specific project to refresh + but the ssh key of an user was added and updated. + If project is a pagure.lib.model.Project, the configuration of + this project should be updated. + :type project: None, int or pagure.lib.model.Project + + (This behaviour is based on the workflow of gitolite, if you are + implementing a different auth backend and need more granularity, + feel free to let us know.) + """ pass @@ -75,9 +90,155 @@ class Gitolite2Auth(GitAuthHelper): """ A gitolite 2 authentication module. """ @classmethod + def _process_project(cls, project, config, groups, global_pr_only): + """ Generate the gitolite configuration for the specified project. + + :arg project: the project to generate the configuration for + :type project: pagure.lib.model.Project + :arg config: a list containing the different lines of the + configuration file + :type config: list + :arg groups: a dictionary containing the group name as key and the + users member of the group as values + :type groups: dict(str: list) + :arg global_pr_only: boolean on whether the pagure instance enforces + the PR workflow only or not + :type global_pr_only: bool + :return: a tuple containing the updated config and groups variables + :return type: tuple(list, dict(str: list)) + + """ + _log.debug(' Processing project: %s', project.fullname) + for group in project.committer_groups: + if group.group_name not in groups: + groups[group.group_name] = [ + user.username for user in group.users] + + # Check if the project or the pagure instance enforce the PR only + # development model. + pr_only = project.settings.get('pull_request_access_only', False) + + for repos in ['repos', 'docs/', 'tickets/', 'requests/']: + if repos == 'repos': + # Do not grant access to project enforcing the PR model + if pr_only or (global_pr_only and not project.is_fork): + continue + repos = '' + + config.append('repo %s%s' % (repos, project.fullname)) + if repos not in ['tickets/', 'requests/']: + config.append(' R = @all') + if project.committer_groups: + config.append(' RW+ = @%s' % ' @'.join( + [ + group.group_name + for group in project.committer_groups + ] + )) + config.append(' RW+ = %s' % project.user.user) + for user in project.committers: + # This should never be the case (that the project.user + # is in the committers) but better safe than sorry + if user.user != project.user.user: + config.append(' RW+ = %s' % user.user) + for deploykey in project.deploykeys: + access = 'R' + if deploykey.pushaccess: + access = 'RW+' + # Note: the replace of / with _ is because gitolite + # users can't contain a /. At first, this might look + # like deploy keys in a project called + # $namespace_$project would give access to the repos of + # a project $namespace/$project or vica versa, however + # this is NOT the case because we add the deploykey.id + # to the end of the deploykey name, which means it is + # unique. The project name is solely there to make it + # easier to determine what project created the deploykey + # for admins. + config.append(' %s = deploykey_%s_%s' % + (access, + werkzeug.secure_filename(project.fullname), + deploykey.id)) + config.append('') + + return (groups, config) + + @classmethod + def _clean_current_config( + cls, current_config, project, preconfig=None, postconfig=None): + """ Remove the specified project from the current configuration file + + :arg current_config: the content of the current/actual gitolite + configuration file read from the disk + :type current_config: list + :arg project: the project to update in the configuration file + :type project: pagure.lib.model.Project + :kwarg preconfig: the content of the file to place at the top of + gitolite configuration file read from the disk + :type preconfig: list + :kwarg postconfig: the content of the file to place at the bottom + of gitolite configuration file read from the disk + :type postconfig: list + + """ + keys = [ + 'repo %s%s' % (repos, project.fullname) + for repos in ['', 'docs/', 'tickets/', 'requests/'] + ] + prestart = preend = None + if preconfig: + preconfig = preconfig.strip().split('\n') + prestart = preconfig[0].strip() + preend = preconfig[-1].strip() + poststart = postend = None + if postconfig: + postconfig = postconfig.strip().split('\n') + poststart = postconfig[0].strip() + postend = postconfig[-1].strip() + + keep = True + header = footer = False + config = [] + for line in current_config: + line = line.rstrip() + if line == prestart: + header = True + continue + + if line == poststart: + footer = True + continue + + if line in keys: + keep = False + continue + + if line == preend: + header = False + continue + + if line == postend: + footer = False + continue + + if keep is False and line == '': + keep = True + + # Avoid too many empty lines + if line == '' and ( + config and config[-1] == '' + or not config): + continue + + if keep and not header and not footer: + config.append(line) + + return config + + @classmethod def write_gitolite_acls( - cls, session, configfile, preconf=None, postconf=None): - ''' Generate the configuration file for gitolite for all projects + cls, session, configfile, project, preconf=None, postconf=None): + """ Generate the configuration file for gitolite for all projects on the forge. :arg cls: the current class @@ -85,6 +246,15 @@ class Gitolite2Auth(GitAuthHelper): :arg session: a session to connect to the database with :arg configfile: the name of the configuration file to generate/write :type configfile: str + :arg project: the project to update in the gitolite configuration + file. It can be of three types/values. + If it is ``-1`` or if the file does not exist on disk, the + entire gitolite configuration will be re-generated. + If it is ``None``, the gitolite configuration will not be + changed but will be re-compiled. + If it is a ``pagure.lib.model.Project``, the gitolite + configuration will be updated for just this project. + :type project: None, int or spagure.lib.model.Project :kwarg preconf: a file to include at the top of the configuration file :type preconf: None or str @@ -92,7 +262,7 @@ class Gitolite2Auth(GitAuthHelper): configuration file :type postconf: None or str - ''' + """ _log.info('Write down the gitolite configuration file') preconfig = None @@ -110,64 +280,30 @@ class Gitolite2Auth(GitAuthHelper): global_pr_only = pagure.APP.config.get('PR_ONLY', False) config = [] groups = {} - query = session.query( - model.Project - ).order_by( - model.Project.id - ) - for project in query.all(): - _log.debug(' Processing project: %s', project.fullname) - for group in project.committer_groups: - if group.group_name not in groups: - groups[group.group_name] = [ - user.username for user in group.users] - - # Check if the project or the pagure instance enforce the PR only - # development model. - pr_only = project.settings.get('pull_request_access_only', False) - - for repos in ['repos', 'docs/', 'tickets/', 'requests/']: - if repos == 'repos': - # Do not grant access to project enforcing the PR model - if pr_only or (global_pr_only and not project.is_fork): - continue - repos = '' - - config.append('repo %s%s' % (repos, project.fullname)) - if repos not in ['tickets/', 'requests/']: - config.append(' R = @all') - if project.committer_groups: - config.append(' RW+ = @%s' % ' @'.join( - [ - group.group_name - for group in project.committer_groups - ] - )) - config.append(' RW+ = %s' % project.user.user) - for user in project.committers: - # This should never be the case (that the project.user - # is in the committers) but better safe than sorry - if user.user != project.user.user: - config.append(' RW+ = %s' % user.user) - for deploykey in project.deploykeys: - access = 'R' - if deploykey.pushaccess: - access = 'RW+' - # Note: the replace of / with _ is because gitolite - # users can't contain a /. At first, this might look - # like deploy keys in a project called - # $namespace_$project would give access to the repos of - # a project $namespace/$project or vica versa, however - # this is NOT the case because we add the deploykey.id - # to the end of the deploykey name, which means it is - # unique. The project name is solely there to make it - # easier to determine what project created the deploykey - # for admins. - config.append(' %s = deploykey_%s_%s' % - (access, - werkzeug.secure_filename(project.fullname), - deploykey.id)) - config.append('') + if project == -1 or not os.path.exists(configfile): + _log.info('Refreshing the configuration for all projects') + query = session.query(model.Project).order_by(model.Project.id) + for project in query.all(): + groups, config = cls._process_project( + project, config, groups, global_pr_only) + elif project: + _log.info('Refreshing the configuration for one project') + groups, config = cls._process_project( + project, config, groups, global_pr_only) + + _log.info('Reading in the current configuration') + with open(configfile) as stream: + current_config = stream.readlines() + current_config = cls._clean_current_config( + current_config, + project, + preconfig, + postconfig) + + config = current_config + config + + if not config: + return with open(configfile, 'w') as stream: if preconfig: @@ -199,16 +335,30 @@ class Gitolite2Auth(GitAuthHelper): return cmd @classmethod - def generate_acls(cls): + def generate_acls(cls, project): """ Generate the gitolite configuration file for all repos + + :arg project: the project to update in the gitolite configuration + file. It can be of three types/values. + If it is ``-1`` or if the file does not exist on disk, the + entire gitolite configuration will be re-generated. + If it is ``None``, the gitolite configuration will not be + changed but will be re-compiled. + If it is a ``pagure.lib.model.Project``, the gitolite + configuration will be updated for just this project. + :type project: None, int or spagure.lib.model.Project + """ _log.info('Refresh gitolite configuration') - cls.write_gitolite_acls( - pagure.SESSION, - pagure.APP.config['GITOLITE_CONFIG'], - preconf=pagure.APP.config.get('GITOLITE_PRE_CONFIG') or None, - postconf=pagure.APP.config.get('GITOLITE_POST_CONFIG') or None - ) + + if project is not None: + cls.write_gitolite_acls( + pagure.SESSION, + project=project, + configfile=pagure.APP.config['GITOLITE_CONFIG'], + preconf=pagure.APP.config.get('GITOLITE_PRE_CONFIG') or None, + postconf=pagure.APP.config.get('GITOLITE_POST_CONFIG') or None + ) cmd = cls._get_gitolite_command() if cmd: @@ -249,8 +399,15 @@ class GitAuthTestHelper(GitAuthHelper): """ Simple test auth module to check the auth customization system. """ @classmethod - def generate_acls(cls): - """ Print a statement when called, useful for debugging, only. """ - out = 'Called GitAuthTestHelper.generate_acls()' + def generate_acls(cls, project): + """ Print a statement when called, useful for debugging, only. + + :arg project: this variable is just printed out but not used + in any real place. + :type project: None, int or spagure.lib.model.Project + + """ + out = 'Called GitAuthTestHelper.generate_acls() ' \ + 'with arg %s' % project print(out) return out diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 2f7d314..a63b3e0 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -45,6 +45,13 @@ conn.conf.update(APP.config['CELERY_CONFIG']) def get_result(uuid): + """ Returns the AsyncResult object for a given task. + + :arg uuid: the unique identifier of the task to retrieve. + :type uuid: str + :return: celery.result.AsyncResult + + """ return AsyncResult(uuid, conn.backend) @@ -55,21 +62,57 @@ def ret(endpoint, **kwargs): def gc_clean(): + """ Force a run of the garbage collector. """ # https://pagure.io/pagure/issue/2302 gc.collect() @conn.task -def generate_gitolite_acls(): +def generate_gitolite_acls(namespace=None, name=None, user=None): + """ Generate the gitolite configuration file either entirely or for a + specific project. + + :kwarg namespace: the namespace of the project + :type namespace: str + :kwarg name: the name of the project + :type name: str + :kwarg user: the user of the project, only set if the project is a fork + :type user: str + + """ + session = pagure.lib.create_session() + project = None + if name and name != -1: + project = pagure.lib._get_project( + session, namespace=namespace, name=name, user=user) + elif name == -1: + project = name helper = pagure.lib.git_auth.get_git_auth_helper( APP.config['GITOLITE_BACKEND']) - helper.generate_acls() + helper.generate_acls(project=project) + session.remove() gc_clean() @conn.task def create_project(username, namespace, name, add_readme, ignore_existing_repo): + """ Create a project. + + :kwarg username: the user creating the project + :type user: str + :kwarg namespace: the namespace of the project + :type namespace: str + :kwarg name: the name of the project + :type name: str + :kwarg add_readme: a boolean specifying if the project should be + created with a README file or not + :type add_readme: bool + :kwarg ignore_existing_repo: a boolean specifying whether the creation + of the project should fail if the repo exists on disk or not + :type ignore_existing_repo: bool + + """ session = pagure.lib.create_session() project = pagure.lib._get_project(session, namespace=namespace, @@ -157,10 +200,14 @@ def create_project(username, namespace, name, add_readme, plugin.install(project, dbobj) session.commit() + generate_gitolite_acls.delay( + namespace=project.namespace, + name=project.name, + user=project.user.user if project.is_fork else None) + session.remove() gc_clean() - generate_gitolite_acls.delay() return ret('view_repo', repo=name, namespace=namespace) @@ -248,12 +295,30 @@ def delete_branch(name, namespace, user, branchname): @conn.task def fork(name, namespace, user_owner, user_forker, editbranch, editfile): + """ Forks the specified project for the specified user. + + :arg namespace: the namespace of the project + :type namespace: str + :arg name: the name of the project + :type name: str + :arg user_owner: the user of which the project is forked, only set + if the project is already a fork + :type user_owner: str + :arg user_forker: the user forking the project + :type user_forker: str + :kwarg editbranch: the name of the branch in which the user asked to + edit a file + :type editbranch: str + :kwarg editfile: the file the user asked to edit + :type editfile: str + + """ session = pagure.lib.create_session() - repo_from = pagure.lib._get_project(session, namespace=namespace, - name=name, user=user_owner) - repo_to = pagure.lib._get_project(session, namespace=namespace, name=name, - user=user_forker) + repo_from = pagure.lib._get_project( + session, namespace=namespace, name=name, user=user_owner) + repo_to = pagure.lib._get_project( + session, namespace=namespace, name=name, user=user_forker) with repo_to.lock('WORKER'): reponame = os.path.join(APP.config['GIT_FOLDER'], repo_from.path) @@ -315,9 +380,13 @@ def fork(name, namespace, user_owner, user_forker, editbranch, editfile): ), ) - session.remove() del frepo - generate_gitolite_acls() + session.remove() + + generate_gitolite_acls( + namespace=repo_to.namespace, + name=repo_to.name, + user=repo_to.user.user if repo_to.is_fork else None) gc_clean() if editfile is None: diff --git a/pagure/ui/groups.py b/pagure/ui/groups.py index df4b3f5..40e6515 100644 --- a/pagure/ui/groups.py +++ b/pagure/ui/groups.py @@ -84,7 +84,7 @@ def view_group(group): is_admin=pagure.is_admin(), ) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=-1) flask.flash(msg) except pagure.exceptions.PagureException as err: pagure.SESSION.rollback() @@ -195,7 +195,7 @@ def group_user_delete(user, group): is_admin=pagure.is_admin() ) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=-1) flask.flash( 'User `%s` removed from the group `%s`' % (user, group)) except pagure.exceptions.PagureException as err: @@ -249,7 +249,7 @@ def group_delete(group): pagure.SESSION.delete(group_obj) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=-1) flask.flash( 'Group `%s` has been deleted' % (group)) diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index e5415d4..6252161 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -1526,7 +1526,7 @@ def remove_deploykey(repo, keyid, username=None, namespace=None): break try: SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=None) pagure.lib.create_deploykeys_ssh_keys_on_disk( repo, APP.config.get('GITOLITE_KEYDIR', None) @@ -1588,7 +1588,7 @@ def remove_user(repo, userid, username=None, namespace=None): break try: SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=repo) flask.flash('User removed') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() @@ -1641,7 +1641,7 @@ def add_deploykey(repo, username=None, namespace=None): user=flask.g.fas_user.username, ) SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=None) pagure.lib.create_deploykeys_ssh_keys_on_disk( repo, APP.config.get('GITOLITE_KEYDIR', None) @@ -1724,7 +1724,7 @@ def add_user(repo, username=None, namespace=None): required_groups=APP.config.get('REQUIRED_GROUPS') ) SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=repo) flask.flash(msg) return flask.redirect(flask.url_for( '.view_settings', repo=repo.name, username=username, @@ -1797,7 +1797,7 @@ def remove_group_project(repo, groupid, username=None, namespace=None): break try: SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=repo) flask.flash('Group removed') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() @@ -1868,7 +1868,7 @@ def add_group_project(repo, username=None, namespace=None): is_admin=pagure.is_admin(), ) SESSION.commit() - pagure.lib.git.generate_gitolite_acls() + pagure.lib.git.generate_gitolite_acls(project=repo) flask.flash(msg) return flask.redirect(flask.url_for( '.view_settings', repo=repo.name, username=username, diff --git a/tests/test_pagure_lib_git.py b/tests/test_pagure_lib_git.py index 84a6f53..bf63316 100644 --- a/tests/test_pagure_lib_git.py +++ b/tests/test_pagure_lib_git.py @@ -73,7 +73,8 @@ class PagureLibGittests(tests.Modeltests): outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls( + self.session, configfile=outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -162,6 +163,7 @@ repo requests/forks/pingou/test3 helper.write_gitolite_acls( self.session, outputconf, + project=-1, preconf=preconf ) self.assertTrue(os.path.exists(outputconf)) @@ -239,6 +241,7 @@ repo requests/somenamespace/test3 helper.write_gitolite_acls( self.session, outputconf, + project=-1, preconf=preconf, postconf=postconf ) @@ -313,6 +316,7 @@ repo requests/somenamespace/test3 helper.write_gitolite_acls( self.session, outputconf, + project=-1, postconf=postconf ) self.assertTrue(os.path.exists(outputconf)) @@ -409,7 +413,7 @@ repo requests/somenamespace/test3 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -520,7 +524,7 @@ repo requests/forks/pingou/test3 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -623,7 +627,7 @@ repo requests/forks/pingou/test3 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -775,7 +779,7 @@ repo requests/forks/pingou/test3 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -935,7 +939,7 @@ repo requests/forks/pingou/test2 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -1089,7 +1093,7 @@ repo requests/forks/pingou/test2 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -1209,7 +1213,7 @@ repo requests/forks/pingou/test2 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -1314,7 +1318,7 @@ repo requests/forks/pingou/test3 outputconf = os.path.join(self.path, 'test_gitolite.conf') helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.write_gitolite_acls(self.session, outputconf) + helper.write_gitolite_acls(self.session, outputconf, project=-1) self.assertTrue(os.path.exists(outputconf)) @@ -2991,7 +2995,7 @@ index 0000000..60f7480 proc.returncode = 0 popen.return_value = proc helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') - helper.generate_acls() + helper.generate_acls(project=None) popen.assert_called_with( 'HOME=/tmp gitolite compile && ' 'HOME=/tmp gitolite trigger POST_COMPILE', diff --git a/tests/test_pagure_lib_gitolite_config.py b/tests/test_pagure_lib_gitolite_config.py new file mode 100644 index 0000000..6105790 --- /dev/null +++ b/tests/test_pagure_lib_gitolite_config.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2017 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +__requires__ = ['SQLAlchemy >= 0.8'] + +import pkg_resources + +import datetime +import os +import shutil +import sys +import tempfile +import time +import unittest + +import pygit2 +from mock import patch, MagicMock + +sys.path.insert(0, os.path.join(os.path.dirname( + os.path.abspath(__file__)), '..')) + +import pagure +import pagure.lib.git +import tests +from pagure.lib.repo import PagureRepo + + +CORE_CONFIG = """repo test + R = @all + RW+ = pingou + +repo docs/test + R = @all + RW+ = pingou + +repo tickets/test + RW+ = pingou + +repo requests/test + RW+ = pingou + +repo test2 + R = @all + RW+ = pingou + +repo docs/test2 + R = @all + RW+ = pingou + +repo tickets/test2 + RW+ = pingou + +repo requests/test2 + RW+ = pingou + +repo somenamespace/test3 + R = @all + RW+ = pingou + +repo docs/somenamespace/test3 + R = @all + RW+ = pingou + +repo tickets/somenamespace/test3 + RW+ = pingou + +repo requests/somenamespace/test3 + RW+ = pingou""" + + +class PagureLibGitoliteConfigtests(tests.Modeltests): + """ Tests for pagure.lib.git """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureLibGitoliteConfigtests, self).setUp() + + pagure.lib.git.SESSION = self.session + tests.create_projects(self.session) + + self.outputconf = os.path.join(self.path, 'test_gitolite.conf') + + self.preconf = os.path.join(self.path, 'header_gitolite') + with open(self.preconf, 'w') as stream: + stream.write('# this is a header that is manually added\n') + stream.write('\n') + stream.write('@group1 = foo bar baz\n') + stream.write('@group2 = threebean puiterwijk kevin pingou\n') + + self.postconf = os.path.join(self.path, 'footer_gitolite') + with open(self.postconf, 'w') as stream: + stream.write('# end of generated configuration\n') + stream.write('# \ó/\n') + stream.write('# end of footer\n') + + def tearDown(self): + """ Tearn down the environnment, ran before every tests. """ + super(PagureLibGitoliteConfigtests, self).tearDown() + + if os.path.exists(self.outputconf): + os.unlink(self.outputconf) + self.assertFalse(os.path.exists(self.outputconf)) + + def test_write_gitolite_pre_post_projectNone(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=None, + preconf=self.preconf, + postconf=self.postconf + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read() + + exp = """# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + + +%s + +# end of generated configuration +# \ó/ +# end of footer + +""" % CORE_CONFIG + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_pre_post_projectNone(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + with open(self.outputconf, 'w') as stream: + pass + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=None, + preconf=self.preconf, + postconf=self.postconf + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read() + self.assertEqual(data, '') + + def test_write_gitolite_pre_post_project_1(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + with open(self.outputconf, 'w') as stream: + pass + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=-1, + preconf=self.preconf, + postconf=self.postconf + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read() + + exp = """# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + + +%s + +# end of generated configuration +# \ó/ +# end of footer + +""" % CORE_CONFIG + + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_pre_post_project_test(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + with open(self.outputconf, 'w') as stream: + pass + + project = pagure.lib._get_project(self.session, 'test') + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=project, + preconf=self.preconf, + postconf=self.postconf + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read() + + exp = """# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + + +repo test + R = @all + RW+ = pingou + +repo docs/test + R = @all + RW+ = pingou + +repo tickets/test + RW+ = pingou + +repo requests/test + RW+ = pingou + +# end of generated configuration +# \ó/ +# end of footer + +""" + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_pre_post_project_test_full_file(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + # Re-generate the gitolite config for all the projects + self.test_write_gitolite_pre_post_project_1() + self.assertTrue(os.path.exists(self.outputconf)) + + project = pagure.lib._get_project(self.session, 'test') + project.user_id = 2 + self.session.add(project) + self.session.commit() + + project = pagure.lib._get_project(self.session, 'test') + msg = pagure.lib.add_user_to_project( + self.session, + project=project, + new_user='pingou', + user='foo', + access='commit' + ) + self.assertEqual(msg, 'User added') + self.session.commit() + + project = pagure.lib._get_project(self.session, 'test') + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=project, + preconf=self.preconf, + postconf=self.postconf + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read() + + exp = """# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + + +repo test2 + R = @all + RW+ = pingou + +repo docs/test2 + R = @all + RW+ = pingou + +repo tickets/test2 + RW+ = pingou + +repo requests/test2 + RW+ = pingou + +repo somenamespace/test3 + R = @all + RW+ = pingou + +repo docs/somenamespace/test3 + R = @all + RW+ = pingou + +repo tickets/somenamespace/test3 + RW+ = pingou + +repo requests/somenamespace/test3 + RW+ = pingou + +repo test + R = @all + RW+ = foo + RW+ = pingou + +repo docs/test + R = @all + RW+ = foo + RW+ = pingou + +repo tickets/test + RW+ = foo + RW+ = pingou + +repo requests/test + RW+ = foo + RW+ = pingou + +# end of generated configuration +# \ó/ +# end of footer + +""" + #print data + self.assertEqual(data, exp) + + +if __name__ == '__main__': + unittest.main(verbosity=2) From 9a0f1f805ec926cb945d595784f264533357a895 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2017 08:50:22 +0000 Subject: [PATCH 4/250] Improve logging and load only the plugin of interest instead of all of them Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index c2c46b1..e5c7168 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -36,10 +36,11 @@ def get_git_auth_helper(backend, *args, **kwargs): :type backend: str """ + _log.info('Looking for backend: %s', backend) points = pkg_resources.iter_entry_points('pagure.git_auth.helpers') - classes = dict([(point.name, point.load()) for point in points]) + classes = dict([(point.name, point) for point in points]) _log.debug("Found the following installed helpers %r" % classes) - cls = classes[backend] + cls = classes[backend].load() _log.debug("Instantiating helper %r from backend key %r" % (cls, backend)) return cls(*args, **kwargs) diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index a63b3e0..fe9e1c3 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -89,6 +89,7 @@ def generate_gitolite_acls(namespace=None, name=None, user=None): project = name helper = pagure.lib.git_auth.get_git_auth_helper( APP.config['GITOLITE_BACKEND']) + _log.debug('Got helper: %s', helper) helper.generate_acls(project=project) session.remove() gc_clean() @@ -383,6 +384,7 @@ def fork(name, namespace, user_owner, user_forker, editbranch, editfile): del frepo session.remove() + _log.info('Project created, refreshing auth') generate_gitolite_acls( namespace=repo_to.namespace, name=repo_to.name, From 98408b68a15dda7a04e26a583b59446ea017ede5 Mon Sep 17 00:00:00 2001 From: Vivek Anand Date: Jul 04 2017 13:22:42 +0000 Subject: [PATCH 5/250] Remove celery as dependecy for pagure-ev Signed-off-by: Vivek Anand --- diff --git a/dev/docker/ev b/dev/docker/ev index 9b1753f..3e1bcea 100644 --- a/dev/docker/ev +++ b/dev/docker/ev @@ -14,8 +14,6 @@ RUN dnf install -y python2-devel python-setuptools python-nose py-bcrypt python- python-fedora-flask python2-pillow python2-psycopg2 python-trollius \ python-trollius-redis -RUN dnf install -y python2-celery - WORKDIR /code ENTRYPOINT ["/usr/bin/python", "/code/pagure-ev/pagure_stream_server.py"] From a95a671237f4f4c6ed60ff30bc6366d4d0019053 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2017 13:56:44 +0000 Subject: [PATCH 6/250] Show the task's status on the wait page and avoid reloading the page Instead of reloading the entire page, query for its status using JS and only redirect once the task has completed. Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 3821f39..c669604 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -1814,12 +1814,13 @@ def fork_project(session, user, repo, gitfolder, session.flush() session.commit() - return tasks.fork.delay(repo.name, + task = tasks.fork.delay(repo.name, repo.namespace, repo.user.username if repo.is_fork else None, user, editbranch, - editfile).id + editfile) + return task.id def search_projects( diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index e5c7168..6a84b33 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -375,7 +375,7 @@ class Gitolite2Auth(GitAuthHelper): error_msg = ( 'The command "{0}" failed with' '\n\n out: "{1}\n\n err:"{2}"' - .format(' '.join(cmd), stdout, stderr)) + .format(cmd, stdout, stderr)) raise pagure.exceptions.PagureException(error_msg) diff --git a/pagure/templates/waiting.html b/pagure/templates/waiting.html index b3765cd..d539f41 100644 --- a/pagure/templates/waiting.html +++ b/pagure/templates/waiting.html @@ -14,13 +14,14 @@

We are waiting for your task to finish. This page should be refreshed automatically, but if not click - Here + Here +

+

+ You task is currently {{ task.status }} +

+ - {% if count >= 5 %} -

- This is taking longer than usual... Sorry for that -

- {% endif %} @@ -29,12 +30,43 @@ {% endblock %} {% block jscripts %} - {{ super() }} - {% endblock %} diff --git a/pagure/ui/app.py b/pagure/ui/app.py index 0e0a332..e20a2c3 100644 --- a/pagure/ui/app.py +++ b/pagure/ui/app.py @@ -502,36 +502,53 @@ def new_project(): @APP.route('/wait/') def wait_task(taskid): - status = pagure.lib.tasks.get_result(taskid) - if status.ready(): - result = status.get(timeout=0, propagate=False) - if status.failed(): + """ Shows a wait page until the task finishes. """ + task = pagure.lib.tasks.get_result(taskid) + + is_js = flask.request.args.get('js') + if str(is_js).lower() == '1': + is_js = True + else: + is_js = False + + prev = flask.request.args.get('prev') + if not is_safe_url(prev): + prev = flask.url_for('index') + + count = flask.request.args.get('count', 0) + try: + count = int(count) + if count < 1: + count = 0 + except ValueError: + count = 0 + + if task.ready(): + if is_js: + flask.abort(417) + + result = task.get(timeout=0, propagate=False) + if task.failed(): flask.flash('Your task failed: %s' % str(result)) - status.forget() - prev = flask.request.args.get('prev') - if not is_safe_url(prev): - prev = flask.url_for('index') + task.forget() return flask.redirect(prev) endpoint = result.pop('endpoint') - status.forget() + task.forget() return flask.redirect( flask.url_for(endpoint, **result)) else: - count = int(flask.request.args.get('count', 0)) - # First refresh in 10ms, after that, wait a second - delay = 10 if count == 0 else 1000 - prev = flask.request.args.get('prev') - if not is_safe_url(prev): - prev = flask.url_for('index') + if is_js: + return flask.jsonify({ + 'count': count + 1, + 'status': task.status, + }) + return flask.render_template( 'waiting.html', - taskid=taskid, - wait_delay=str(delay), + task=task, count=count, - wait_next=flask.url_for('wait_task', - taskid=taskid, - count=str(count + 1), - prev=prev)) + prev=prev, + ) @APP.route('/settings/', methods=('GET', 'POST')) diff --git a/pagure/ui/fork.py b/pagure/ui/fork.py index 78be8ef..1c70831 100644 --- a/pagure/ui/fork.py +++ b/pagure/ui/fork.py @@ -897,7 +897,13 @@ def fork_project(repo, username=None, namespace=None): user=flask.g.fas_user.username) SESSION.commit() - return pagure.wait_for_task(taskid) + return pagure.wait_for_task( + taskid, + prev=flask.url_for( + 'view_repo', repo=repo.name, + username=username, namespace=namespace + ) + ) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover diff --git a/tests/__init__.py b/tests/__init__.py index b0d21ad..fe49fc7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -85,7 +85,7 @@ if os.environ.get('BUILD_ID')or os.environ.get('FAITOUT_URL'): pass -WAIT_REGEX = re.compile("""window\.location = '(\/wait\/[a-z0-9-]+\?.*)'""") +WAIT_REGEX = re.compile("""var _url = '(\/wait\/[a-z0-9-]+\?.*)'""") def get_wait_target(html): """ This parses the window.location out of the HTML for the wait page. """ found = WAIT_REGEX.findall(html) From 9a0f227fe3b06632e8e59efa297cdd4398248e02 Mon Sep 17 00:00:00 2001 From: Vivek Anand Date: Jul 04 2017 14:03:28 +0000 Subject: [PATCH 7/250] Don't show '+' sign when GROUP_MNGT is off --- diff --git a/pagure/templates/index_auth.html b/pagure/templates/index_auth.html index 18078e6..d1568fe 100644 --- a/pagure/templates/index_auth.html +++ b/pagure/templates/index_auth.html @@ -186,11 +186,13 @@
My Groups {{ user.groups | length }} - - - + {% if config.get('ENABLE_GROUP_MNGT') %} + + + + {% endif %}
{% for group in user.groups %} diff --git a/tests/test_pagure_flask_ui_groups.py b/tests/test_pagure_flask_ui_groups.py index 07a51d4..36b7994 100644 --- a/tests/test_pagure_flask_ui_groups.py +++ b/tests/test_pagure_flask_ui_groups.py @@ -41,7 +41,6 @@ class PagureFlaskGroupstests(tests.Modeltests): pagure.ui.repo.SESSION = self.session pagure.ui.filters.SESSION = self.session - def test_group_lists(self): """ Test the group_lists endpoint. """ output = self.app.get('/groups') @@ -50,6 +49,27 @@ class PagureFlaskGroupstests(tests.Modeltests): ' Groups 0', output.data) + def test_add_group_index_auth(self): + """ Test the presence of the add group button on the front page. """ + user = tests.FakeUser(username='foo') + with tests.user_set(pagure.APP, user): + output = self.app.get('/') + self.assertEqual(output.status_code, 200) + self.assertIn( + 'title="Create New Group" aria-hidden="true">', + output.data) + + @patch.dict('pagure.APP.config', {'ENABLE_GROUP_MNGT': False}) + def test_not_add_group_index_auth(self): + """ Test the presence of the add group button on the front page. """ + user = tests.FakeUser(username='foo') + with tests.user_set(pagure.APP, user): + output = self.app.get('/') + self.assertEqual(output.status_code, 200) + self.assertNotIn( + 'title="Create New Group" aria-hidden="true">', + output.data) + def test_add_group(self): """ Test the add_group endpoint. """ output = self.app.get('/group/add') From 9a8ed82e68436b51387297a7cf1248696e094ba1 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2017 14:26:37 +0000 Subject: [PATCH 8/250] Release 3.1 Signed-off-by: Pierre-Yves Chibon --- diff --git a/UPGRADING.rst b/UPGRADING.rst index 2f7bbed..671ad9e 100644 --- a/UPGRADING.rst +++ b/UPGRADING.rst @@ -1,6 +1,14 @@ Upgrading Pagure ================ +From 3.0 to 3.1 +--------------- + +While not being a bug fix, this release has no database schema changes. +However, this release breaks the plugin interface for auth introduced in 3.0. If +you are using pagure-dist-git, make sure to upgrade to at least 0.3. + + From 2.15 to 3.0 ---------------- diff --git a/doc/contributors.rst b/doc/contributors.rst index 4d937d2..acf20cf 100644 --- a/doc/contributors.rst +++ b/doc/contributors.rst @@ -3,14 +3,14 @@ Contributors to pagure Pagure would be nothing without its contributors. -On June 30, 2017 (release 3.0), the list looks as follow: +On July 4, 2017 (release 3.1), the list looks as follow: ================= =========== Number of commits Contributor ================= =========== - 5311 Pierre-Yves Chibon + 5317 Pierre-Yves Chibon 191 Ryan Lerch - 140 Vivek Anand + 143 Vivek Anand 139 farhaanbukhsh 126 Patrick Uiterwijk 77 Farhaan Bukhsh diff --git a/files/pagure.spec b/files/pagure.spec index d670f4e..09633db 100644 --- a/files/pagure.spec +++ b/files/pagure.spec @@ -2,7 +2,7 @@ %distutils.sysconfig import get_python_lib; print (get_python_lib())")} Name: pagure -Version: 3.0 +Version: 3.1 Release: 1%{?dist} Summary: A git-centered forge @@ -384,6 +384,16 @@ install -m 644 pagure-loadjson/pagure_loadjson.service \ %changelog +* Tue Jul 04 2017 Pierre-Yves Chibon - 3.1-1 +- Update to 3.1 +- Allow project-less API token to create new tickets +- Tips/tricks: add info on how to validate local user account without email + verification (Vivek Anand) +- Optimize the generation of the gitolite configuration +- Improve logging and load only the plugin of interest instead of all of them +- Show the task's status on the wait page and avoid reloading the page +- Don't show '+' sign when GROUP_MNGT is off (Vivek Anand) + * Fri Jun 30 2017 Pierre-Yves Chibon - 3.0-1 - Update to 3.0 - Since 2.90 celery has become a requirement as well as one of the queueing diff --git a/pagure/__init__.py b/pagure/__init__.py index f07f7bf..2b780b9 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -12,7 +12,7 @@ __requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] import pkg_resources # noqa: E402,F401 -__version__ = '3.0' +__version__ = '3.1' __api_version__ = '0.14' From f1fbde9675be0220a3f6276fd7766fc57bb62c94 Mon Sep 17 00:00:00 2001 From: Clement Verna Date: Jul 06 2017 16:26:11 +0000 Subject: [PATCH 9/250] Use a decorator to check if a project has an issue tracker This commit wraps the logic to check if a project has an active issue tracker into a decorator to reduce code duplication Signed-off-by: Clement Verna --- diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index 34954de..47a4ede 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -19,6 +19,7 @@ import logging import os import re from collections import defaultdict +from functools import wraps from math import ceil import flask @@ -41,7 +42,21 @@ from pagure import (APP, SESSION, __get_file_in_tree, _log = logging.getLogger(__name__) -# URLs + +def has_issue_tracker(function): + """ + Decorator that checks if the current pagure project has the + issue tracker active + If not active returns a 404 page + """ + @wraps(function) + def check_issue_tracker(*args, **kwargs): + repo = flask.g.repo + if not repo.settings.get('issue_tracker', True): + flask.abort(404, 'No issue tracker found for this project') + return function(*args, **kwargs) + + return check_issue_tracker @APP.route( @@ -69,6 +84,7 @@ _log = logging.getLogger(__name__) '/fork////issue//update', methods=['GET', 'POST']) @login_required +@has_issue_tracker def update_issue(repo, issueid, username=None, namespace=None): ''' Add a comment to an issue. ''' is_js = flask.request.args.get('js', False) @@ -82,9 +98,6 @@ def update_issue(repo, issueid, username=None, namespace=None): 'view_issue', username=username, repo=repo.name, namespace=repo.namespace, issueid=issueid)) - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: @@ -379,6 +392,7 @@ def update_issue(repo, issueid, username=None, namespace=None): '/fork////tag//edit', methods=('GET', 'POST')) @login_required +@has_issue_tracker def edit_tag(repo, tag, username=None, namespace=None): """ Edit the specified tag associated with the issues of a project. """ @@ -390,9 +404,6 @@ def edit_tag(repo, tag, username=None, namespace=None): 'You are not allowed to edit tags associated with the issues of \ this project') - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - tags = pagure.lib.get_tags_of_project(SESSION, repo) if not tags: flask.abort(404, 'Project has no tags to edit') @@ -448,15 +459,13 @@ def edit_tag(repo, tag, username=None, namespace=None): @APP.route('//update/tags', methods=['POST']) @APP.route('///update/tags', methods=['POST']) @login_required +@has_issue_tracker def update_tags(repo, username=None, namespace=None): """ Update the tags of a project. """ repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - if not flask.g.repo_admin: flask.abort( 403, @@ -541,6 +550,7 @@ def update_tags(repo, username=None, namespace=None): @APP.route('/fork///droptag/', methods=['POST']) @APP.route('/fork////droptag/', methods=['POST']) @login_required +@has_issue_tracker def remove_tag(repo, username=None, namespace=None): """ Remove the specified tag, associated with the issues, from the project. """ @@ -552,9 +562,6 @@ def remove_tag(repo, username=None, namespace=None): 'You are not allowed to remove tags associated with the issues \ of this project') - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - form = pagure.forms.DeleteIssueTagForm() if form.validate_on_submit(): tags = form.tag.data @@ -590,6 +597,7 @@ def remove_tag(repo, username=None, namespace=None): @APP.route('/fork///issues') @APP.route('/fork////issues/') @APP.route('/fork////issues') +@has_issue_tracker def view_issues(repo, username=None, namespace=None): """ List all issues associated to a repo """ @@ -622,9 +630,6 @@ def view_issues(repo, username=None, namespace=None): repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - try: priority = int(priority) except (ValueError, TypeError): @@ -745,6 +750,7 @@ def view_issues(repo, username=None, namespace=None): @APP.route('/fork///roadmap') @APP.route('/fork////roadmap/') @APP.route('/fork////roadmap') +@has_issue_tracker def view_roadmap(repo, username=None, namespace=None): """ List all issues associated to a repo as roadmap """ @@ -756,9 +762,6 @@ def view_roadmap(repo, username=None, namespace=None): repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - # Hide private tickets private = False # If user is authenticated, show him/her his/her private tickets @@ -869,14 +872,12 @@ def view_roadmap(repo, username=None, namespace=None): '/fork////new_issue', methods=('GET', 'POST')) @login_required +@has_issue_tracker def new_issue(repo, username=None, namespace=None): """ Create a new issue """ repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - form = pagure.forms.IssueFormSimplied() if form.validate_on_submit(): title = form.title.data @@ -991,15 +992,13 @@ def new_issue(repo, username=None, namespace=None): @APP.route('/fork///issue/') @APP.route('/fork////issue//') @APP.route('/fork////issue/') +@has_issue_tracker def view_issue(repo, issueid, username=None, namespace=None): """ List all issues associated to a repo """ repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: @@ -1055,15 +1054,13 @@ def view_issue(repo, issueid, username=None, namespace=None): methods=['POST']) @APP.route('/fork////issue//drop', methods=['POST']) +@has_issue_tracker def delete_issue(repo, issueid, username=None, namespace=None): """ Delete the specified issue """ repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: @@ -1114,14 +1111,12 @@ def delete_issue(repo, issueid, username=None, namespace=None): @APP.route('/fork////issue//edit', methods=('GET', 'POST')) @login_required +@has_issue_tracker def edit_issue(repo, issueid, username=None, namespace=None): """ Edit the specified issue """ repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: @@ -1233,14 +1228,12 @@ def edit_issue(repo, issueid, username=None, namespace=None): @APP.route('/fork////issue//upload', methods=['POST']) @login_required +@has_issue_tracker def upload_issue(repo, issueid, username=None, namespace=None): ''' Upload a file to a ticket. ''' repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: @@ -1293,6 +1286,7 @@ def upload_issue(repo, issueid, username=None, namespace=None): @APP.route('///issue/raw/') @APP.route('/fork///issue/raw/') @APP.route('/fork////issue/raw/') +@has_issue_tracker def view_issue_raw_file( repo, filename=None, username=None, namespace=None): """ Displays the raw content of a file of a commit for the specified @@ -1303,9 +1297,6 @@ def view_issue_raw_file( repo = flask.g.repo - if not repo.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - mimetype, encoding = mimetypes.guess_type(filename) attachdir = os.path.join(APP.config['ATTACHMENTS_FOLDER'], repo.fullname) @@ -1397,6 +1388,7 @@ def view_issue_raw_file( @APP.route('/fork////issue//comment' '//edit', methods=('GET', 'POST')) @login_required +@has_issue_tracker def edit_comment_issue( repo, issueid, commentid, username=None, namespace=None): """Edit comment of an issue @@ -1405,9 +1397,6 @@ def edit_comment_issue( project = flask.g.repo - if not project.settings.get('issue_tracker', True): - flask.abort(404, 'No issue tracker found for this project') - issue = pagure.lib.search_issues(SESSION, project, issueid=issueid) if issue is None or issue.project != project: diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py index 33ef494..90109db 100644 --- a/tests/test_pagure_flask_ui_issues.py +++ b/tests/test_pagure_flask_ui_issues.py @@ -1386,9 +1386,6 @@ class PagureFlaskIssuestests(tests.Modeltests): self.session.commit() with tests.user_set(pagure.APP, user): - output = self.app.get('/test/issue/1/update') - self.assertEqual(output.status_code, 302) - # Repo not set-up for issue tracker output = self.app.post('/test/issue/1/update', data=data) self.assertEqual(output.status_code, 404) From ccb160ee3d69b017e44849c0f27c74dde2cad1ac Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 07 2017 10:14:37 +0000 Subject: [PATCH 10/250] Optimize generating the gitolite configuration for group change This allows updating just the section about groups or even a single group without re-generating the entire gitolite configuration file. Fixes https://pagure.io/pagure/issue/2399 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/git.py b/pagure/lib/git.py index 8b2f4e9..8675109 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -88,7 +88,7 @@ Subject: {subject} return patch -def generate_gitolite_acls(project=None): +def generate_gitolite_acls(project=None, group=None): """ Generate the gitolite configuration file. :arg project: the project of which to update the ACLs. This argument @@ -100,16 +100,19 @@ def generate_gitolite_acls(project=None): If project is a pagure.lib.model.Project, the configuration of this project should be updated. :type project: None, int or pagure.lib.model.Project + :kwarg group: the group to refresh the members of + :type group: None or str """ if project != -1: tasks.generate_gitolite_acls.delay( namespace=project.namespace if project else None, name=project.name if project else None, - user=project.user.user if project and project.is_fork else None + user=project.user.user if project and project.is_fork else None, + group=group ) else: - tasks.generate_gitolite_acls.delay(name=-1) + tasks.generate_gitolite_acls.delay(name=-1, group=group) def update_git(obj, repo, repofolder): diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index 6a84b33..aaa3f83 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -54,7 +54,7 @@ class GitAuthHelper(object): @classmethod @abc.abstractmethod - def generate_acls(self, project): + def generate_acls(self, project, group=None): """ This is the method that is called by pagure to generate the configuration file. @@ -63,10 +63,13 @@ class GitAuthHelper(object): If project is ``-1``, the configuration should be refreshed for *all* projects. If project is ``None``, there no specific project to refresh - but the ssh key of an user was added and updated. + but the ssh key of an user was added and updated or a group + was removed. If project is a pagure.lib.model.Project, the configuration of this project should be updated. :type project: None, int or pagure.lib.model.Project + :kwarg group: the group to refresh the members of + :type group: None or pagure.lib.model.PagureGroup (This behaviour is based on the workflow of gitolite, if you are implementing a different auth backend and need more granularity, @@ -91,7 +94,7 @@ class Gitolite2Auth(GitAuthHelper): """ A gitolite 2 authentication module. """ @classmethod - def _process_project(cls, project, config, groups, global_pr_only): + def _process_project(cls, project, config, global_pr_only): """ Generate the gitolite configuration for the specified project. :arg project: the project to generate the configuration for @@ -105,15 +108,11 @@ class Gitolite2Auth(GitAuthHelper): :arg global_pr_only: boolean on whether the pagure instance enforces the PR workflow only or not :type global_pr_only: bool - :return: a tuple containing the updated config and groups variables - :return type: tuple(list, dict(str: list)) + :return: the updated config + :return type: list """ _log.debug(' Processing project: %s', project.fullname) - for group in project.committer_groups: - if group.group_name not in groups: - groups[group.group_name] = [ - user.username for user in group.users] # Check if the project or the pagure instance enforce the PR only # development model. @@ -162,11 +161,11 @@ class Gitolite2Auth(GitAuthHelper): deploykey.id)) config.append('') - return (groups, config) + return config @classmethod def _clean_current_config( - cls, current_config, project, preconfig=None, postconfig=None): + cls, current_config, project): """ Remove the specified project from the current configuration file :arg current_config: the content of the current/actual gitolite @@ -174,71 +173,145 @@ class Gitolite2Auth(GitAuthHelper): :type current_config: list :arg project: the project to update in the configuration file :type project: pagure.lib.model.Project - :kwarg preconfig: the content of the file to place at the top of - gitolite configuration file read from the disk - :type preconfig: list - :kwarg postconfig: the content of the file to place at the bottom - of gitolite configuration file read from the disk - :type postconfig: list """ keys = [ 'repo %s%s' % (repos, project.fullname) for repos in ['', 'docs/', 'tickets/', 'requests/'] ] - prestart = preend = None - if preconfig: - preconfig = preconfig.strip().split('\n') - prestart = preconfig[0].strip() - preend = preconfig[-1].strip() - poststart = postend = None - if postconfig: - postconfig = postconfig.strip().split('\n') - poststart = postconfig[0].strip() - postend = postconfig[-1].strip() keep = True - header = footer = False config = [] for line in current_config: line = line.rstrip() - if line == prestart: - header = True - continue - - if line == poststart: - footer = True - continue if line in keys: keep = False continue - if line == preend: - header = False - continue - - if line == postend: - footer = False - continue - if keep is False and line == '': keep = True - # Avoid too many empty lines - if line == '' and ( - config and config[-1] == '' - or not config): - continue - - if keep and not header and not footer: + if keep: config.append(line) return config @classmethod + def _clean_groups(cls, config, group=None): + """ Removes the groups in the given configuration file. + + :arg config: the current configuration + :type config: list + :kwarg group: the group to refresh the members of + :type group: None or pagure.lib.model.PagureGroup + :return: the configuration without the groups + :return type: list + + """ + + if group is None: + output = [ + row.rstrip() + for row in config + if not row.startswith('@') + and row.strip() != '# end of groups'] + else: + end_grp = None + seen = False + output = [] + for idx, row in enumerate(config): + if end_grp is None and row.startswith('repo '): + end_grp = idx + + if row.startswith('@%s ' % group.group_name): + seen = True + row = '@%s = %s' % ( + group.group_name, + ' '.join(sorted( + [user.username for user in group.users]) + ) + ) + output.append(row) + + if not seen: + row = '@%s = %s' % ( + group.group_name, + ' '.join(sorted([user.username for user in group.users])) + ) + output.insert(end_grp, '') + output.insert(end_grp, row) + + return output + + @classmethod + def _generate_groups_config(cls, session): + """ Generate the gitolite configuration for all of the groups. + + :arg session: the session with which to connect to the database + :return: the gitolite configuration for the groups + :return type: list + + """ + query = session.query( + model.PagureGroup + ).order_by( + model.PagureGroup.group_name + ) + + groups = {} + for grp in query.all(): + groups[grp.group_name] = [user.username for user in grp.users] + + return groups + + @classmethod + def _get_current_config(cls, configfile, preconfig=None, postconfig=None): + """ Load the current gitolite configuration file from the disk. + + :arg configfile: the name of the configuration file to load + :type configfile: str + :kwarg preconf: the content of the file to include at the top of the + gitolite configuration file, used here to determine that a part of + the configuration file should be cleaned at the top. + :type preconf: None or str + :kwarg postconf: the content of the file to include at the bottom of the + gitolite configuration file, used here to determine that a part of + the configuration file should be cleaned at the bottom. + :type postconf: None or str + + """ + _log.info('Reading in the current configuration: %s', configfile) + with open(configfile) as stream: + current_config = stream.readlines() + + if preconfig: + idx = None + for idx, row in enumerate(current_config): + if row.strip() == '# end of header': + break + if idx is not None: + idx = idx + 1 + _log.info('Removing the first %s lines', idx) + current_config = current_config[idx:] + + if postconfig: + idx = None + for idx, row in enumerate(current_config): + if row.strip() == '# end of body': + break + if idx is not None: + _log.info( + 'Keeping the first %s lines out of %s', + idx, len(current_config)) + current_config = current_config[:idx] + + return current_config + + @classmethod def write_gitolite_acls( - cls, session, configfile, project, preconf=None, postconf=None): + cls, session, configfile, project, preconf=None, postconf=None, + group=None): """ Generate the configuration file for gitolite for all projects on the forge. @@ -251,8 +324,9 @@ class Gitolite2Auth(GitAuthHelper): file. It can be of three types/values. If it is ``-1`` or if the file does not exist on disk, the entire gitolite configuration will be re-generated. - If it is ``None``, the gitolite configuration will not be - changed but will be re-compiled. + If it is ``None``, the gitolite configuration will have its + groups information updated but not the projects and will be + re-compiled. If it is a ``pagure.lib.model.Project``, the gitolite configuration will be updated for just this project. :type project: None, int or spagure.lib.model.Project @@ -262,6 +336,8 @@ class Gitolite2Auth(GitAuthHelper): :kwarg postconf: a file to include at the bottom of the configuration file :type postconf: None or str + :kwarg group: the group to refresh the members of + :type group: None or pagure.lib.model.PagureGroup """ _log.info('Write down the gitolite configuration file') @@ -281,41 +357,63 @@ class Gitolite2Auth(GitAuthHelper): global_pr_only = pagure.APP.config.get('PR_ONLY', False) config = [] groups = {} + if group is None: + groups = cls._generate_groups_config(session) + if project == -1 or not os.path.exists(configfile): _log.info('Refreshing the configuration for all projects') query = session.query(model.Project).order_by(model.Project.id) for project in query.all(): - groups, config = cls._process_project( - project, config, groups, global_pr_only) + config = cls._process_project( + project, config, global_pr_only) elif project: _log.info('Refreshing the configuration for one project') - groups, config = cls._process_project( - project, config, groups, global_pr_only) + config = cls._process_project(project, config, global_pr_only) + + current_config = cls._get_current_config( + configfile, preconfig, postconfig) - _log.info('Reading in the current configuration') - with open(configfile) as stream: - current_config = stream.readlines() current_config = cls._clean_current_config( - current_config, - project, - preconfig, - postconfig) + current_config, project) config = current_config + config + if config: + _log.info('Cleaning the group %s from the loaded config', group) + config = cls._clean_groups(config, group=group) + + else: + current_config = cls._get_current_config( + configfile, preconfig, postconfig) + + _log.info( + 'Cleaning the group %s from the config on disk', group) + config = cls._clean_groups(current_config, group=group) + if not config: return + _log.info('Writing the configuration to: %s', configfile) with open(configfile, 'w') as stream: if preconfig: stream.write(preconfig + '\n') + stream.write('# end of header\n') - for key, users in groups.iteritems(): - stream.write('@%s = %s\n' % (key, ' '.join(users))) - stream.write('\n') + if groups: + for key, users in groups.iteritems(): + stream.write('@%s = %s\n' % (key, ' '.join(users))) + stream.write('# end of groups\n\n') + prev = None for row in config: + if prev is None: + prev = row + if prev == row == '': + continue stream.write(row + '\n') + prev = row + + stream.write('# end of body\n') if postconfig: stream.write(postconfig + '\n') @@ -336,7 +434,7 @@ class Gitolite2Auth(GitAuthHelper): return cmd @classmethod - def generate_acls(cls, project): + def generate_acls(cls, project, group=None): """ Generate the gitolite configuration file for all repos :arg project: the project to update in the gitolite configuration @@ -347,18 +445,21 @@ class Gitolite2Auth(GitAuthHelper): changed but will be re-compiled. If it is a ``pagure.lib.model.Project``, the gitolite configuration will be updated for just this project. - :type project: None, int or spagure.lib.model.Project + :type project: None, int or pagure.lib.model.Project + :kwarg group: the group to refresh the members of + :type group: None or pagure.lib.model.PagureGroup """ _log.info('Refresh gitolite configuration') - if project is not None: + if project is not None or group is not None: cls.write_gitolite_acls( pagure.SESSION, project=project, configfile=pagure.APP.config['GITOLITE_CONFIG'], preconf=pagure.APP.config.get('GITOLITE_PRE_CONFIG') or None, - postconf=pagure.APP.config.get('GITOLITE_POST_CONFIG') or None + postconf=pagure.APP.config.get('GITOLITE_POST_CONFIG') or None, + group=group, ) cmd = cls._get_gitolite_command() @@ -400,15 +501,17 @@ class GitAuthTestHelper(GitAuthHelper): """ Simple test auth module to check the auth customization system. """ @classmethod - def generate_acls(cls, project): + def generate_acls(cls, project, group=None): """ Print a statement when called, useful for debugging, only. :arg project: this variable is just printed out but not used in any real place. :type project: None, int or spagure.lib.model.Project + :kwarg group: the group to refresh the members of + :type group: None or pagure.lib.model.PagureGroup """ out = 'Called GitAuthTestHelper.generate_acls() ' \ - 'with arg %s' % project + 'with args: project=%s, group=%s' % (project, group) print(out) return out diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index fe9e1c3..38cd7f3 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -68,16 +68,18 @@ def gc_clean(): @conn.task -def generate_gitolite_acls(namespace=None, name=None, user=None): +def generate_gitolite_acls(namespace=None, name=None, user=None, group=None): """ Generate the gitolite configuration file either entirely or for a specific project. :kwarg namespace: the namespace of the project - :type namespace: str + :type namespace: None or str :kwarg name: the name of the project - :type name: str + :type name: None or str :kwarg user: the user of the project, only set if the project is a fork - :type user: str + :type user: None or str + :kwarg group: the group to refresh the members of + :type group: None or str """ session = pagure.lib.create_session() @@ -90,7 +92,12 @@ def generate_gitolite_acls(namespace=None, name=None, user=None): helper = pagure.lib.git_auth.get_git_auth_helper( APP.config['GITOLITE_BACKEND']) _log.debug('Got helper: %s', helper) - helper.generate_acls(project=project) + + group_obj = pagure.lib.search_groups(session, group_name=group) or None + _log.debug( + 'Calling helper: %s with arg: project=%s, group=%s', + helper, project, group_obj) + helper.generate_acls(project=project, group=group_obj) session.remove() gc_clean() diff --git a/pagure/ui/groups.py b/pagure/ui/groups.py index 40e6515..0691ce4 100644 --- a/pagure/ui/groups.py +++ b/pagure/ui/groups.py @@ -84,7 +84,7 @@ def view_group(group): is_admin=pagure.is_admin(), ) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls(project=-1) + pagure.lib.git.generate_gitolite_acls(project=None, group=group) flask.flash(msg) except pagure.exceptions.PagureException as err: pagure.SESSION.rollback() @@ -195,7 +195,7 @@ def group_user_delete(user, group): is_admin=pagure.is_admin() ) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls(project=-1) + pagure.lib.git.generate_gitolite_acls(project=None, group=group) flask.flash( 'User `%s` removed from the group `%s`' % (user, group)) except pagure.exceptions.PagureException as err: @@ -249,7 +249,7 @@ def group_delete(group): pagure.SESSION.delete(group_obj) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls(project=-1) + pagure.lib.git.generate_gitolite_acls(project=None) flask.flash( 'Group `%s` has been deleted' % (group)) diff --git a/tests/test_pagure_lib_git.py b/tests/test_pagure_lib_git.py index bf63316..8300527 100644 --- a/tests/test_pagure_lib_git.py +++ b/tests/test_pagure_lib_git.py @@ -81,8 +81,7 @@ class PagureLibGittests(tests.Modeltests): with open(outputconf) as stream: data = stream.read() - exp = """ -repo test + exp = """repo test R = @all RW+ = pingou RW+ = foo @@ -142,6 +141,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) @@ -172,7 +172,7 @@ repo requests/forks/pingou/test3 data = stream.read() exp = """# this is a header that is manually added - +# end of header repo test R = @all RW+ = pingou @@ -215,9 +215,10 @@ repo tickets/somenamespace/test3 repo requests/somenamespace/test3 RW+ = pingou +# end of body """ #print data - self.assertEqual(data, exp) + self.assertEqual(data.split('\n'), exp.split('\n')) os.unlink(outputconf) self.assertFalse(os.path.exists(outputconf)) @@ -251,7 +252,7 @@ repo requests/somenamespace/test3 data = stream.read() exp = """# this is a header that is manually added - +# end of header repo test R = @all RW+ = pingou @@ -294,6 +295,7 @@ repo tickets/somenamespace/test3 repo requests/somenamespace/test3 RW+ = pingou +# end of body # end of generated configuration """ #print data @@ -324,8 +326,7 @@ repo requests/somenamespace/test3 with open(outputconf) as stream: data = stream.read() - exp = """ -repo test + exp = """repo test R = @all RW+ = pingou @@ -367,6 +368,7 @@ repo tickets/somenamespace/test3 repo requests/somenamespace/test3 RW+ = pingou +# end of body # end of generated configuration """ #print data @@ -420,8 +422,7 @@ repo requests/somenamespace/test3 with open(outputconf) as stream: data = stream.read() - exp = """ -repo test + exp = """repo test R = @all RW+ = pingou R = deploykey_test_1 @@ -485,6 +486,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) @@ -531,8 +533,7 @@ repo requests/forks/pingou/test3 with open(outputconf) as stream: data = stream.read() - exp = """ -repo test + exp = """repo test R = @all RW+ = pingou @@ -588,6 +589,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) @@ -634,8 +636,7 @@ repo requests/forks/pingou/test3 with open(outputconf) as stream: data = stream.read() - exp = """ -repo test + exp = """repo test R = @all RW+ = pingou RW+ = foo @@ -695,6 +696,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) @@ -786,8 +788,9 @@ repo requests/forks/pingou/test3 with open(outputconf) as stream: data = stream.read() - exp = """@devs = pingou -@sysadmin = pingou + exp = """@devs = pingou +@sysadmin = pingou +# end of groups repo test R = @all @@ -853,6 +856,7 @@ repo tickets/forks/pingou/test2 repo requests/forks/pingou/test2 RW+ = pingou +# end of body """ #print data self.assertEqual(data.split('\n'), exp.split('\n')) @@ -946,7 +950,10 @@ repo requests/forks/pingou/test2 with open(outputconf) as stream: data = stream.read() - exp = """ + exp = """@devs = pingou +@sysadmin = pingou +# end of groups + repo test R = @all RW+ = pingou @@ -1007,6 +1014,7 @@ repo tickets/forks/pingou/test2 repo requests/forks/pingou/test2 RW+ = pingou +# end of body """ #print data self.assertEqual(data.split('\n'), exp.split('\n')) @@ -1100,8 +1108,9 @@ repo requests/forks/pingou/test2 with open(outputconf) as stream: data = stream.read() - exp = """@devs = pingou -@sysadmin = pingou + exp = """@devs = pingou +@sysadmin = pingou +# end of groups repo test R = @all @@ -1167,6 +1176,7 @@ repo tickets/forks/pingou/test2 repo requests/forks/pingou/test2 RW+ = pingou +# end of body """ #print data self.assertEqual(data.split('\n'), exp.split('\n')) @@ -1220,8 +1230,7 @@ repo requests/forks/pingou/test2 with open(outputconf) as stream: data = stream.read() - exp = """ -repo docs/test + exp = """repo docs/test R = @all RW+ = pingou RW+ = foo @@ -1276,6 +1285,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) @@ -1325,8 +1335,7 @@ repo requests/forks/pingou/test3 with open(outputconf) as stream: data = stream.read() - exp = """ -repo docs/test + exp = """repo docs/test R = @all RW+ = pingou RW+ = foo @@ -1373,6 +1382,7 @@ repo tickets/forks/pingou/test3 repo requests/forks/pingou/test3 RW+ = pingou +# end of body """ #print data self.assertEqual(data, exp) diff --git a/tests/test_pagure_lib_gitolite_config.py b/tests/test_pagure_lib_gitolite_config.py index 6105790..7219200 100644 --- a/tests/test_pagure_lib_gitolite_config.py +++ b/tests/test_pagure_lib_gitolite_config.py @@ -78,6 +78,8 @@ repo requests/somenamespace/test3 class PagureLibGitoliteConfigtests(tests.Modeltests): """ Tests for pagure.lib.git """ + maxDiff = None + def setUp(self): """ Set up the environnment, ran before every tests. """ super(PagureLibGitoliteConfigtests, self).setUp() @@ -123,16 +125,17 @@ class PagureLibGitoliteConfigtests(tests.Modeltests): self.assertTrue(os.path.exists(self.outputconf)) with open(self.outputconf) as stream: - data = stream.read() + data = stream.read().decode('utf-8') - exp = """# this is a header that is manually added + exp = u"""# this is a header that is manually added @group1 = foo bar baz @group2 = threebean puiterwijk kevin pingou - +# end of header %s +# end of body # end of generated configuration # \ó/ # end of footer @@ -180,16 +183,17 @@ class PagureLibGitoliteConfigtests(tests.Modeltests): self.assertTrue(os.path.exists(self.outputconf)) with open(self.outputconf) as stream: - data = stream.read() + data = stream.read().decode('utf-8') - exp = """# this is a header that is manually added + exp = u"""# this is a header that is manually added @group1 = foo bar baz @group2 = threebean puiterwijk kevin pingou - +# end of header %s +# end of body # end of generated configuration # \ó/ # end of footer @@ -219,14 +223,14 @@ class PagureLibGitoliteConfigtests(tests.Modeltests): self.assertTrue(os.path.exists(self.outputconf)) with open(self.outputconf) as stream: - data = stream.read() + data = stream.read().decode('utf-8') - exp = """# this is a header that is manually added + exp = u"""# this is a header that is manually added @group1 = foo bar baz @group2 = threebean puiterwijk kevin pingou - +# end of header repo test R = @all RW+ = pingou @@ -241,6 +245,7 @@ repo tickets/test repo requests/test RW+ = pingou +# end of body # end of generated configuration # \ó/ # end of footer @@ -285,14 +290,14 @@ repo requests/test self.assertTrue(os.path.exists(self.outputconf)) with open(self.outputconf) as stream: - data = stream.read() + data = stream.read().decode('utf-8') - exp = """# this is a header that is manually added + exp = u"""# this is a header that is manually added @group1 = foo bar baz @group2 = threebean puiterwijk kevin pingou - +# end of header repo test2 R = @all RW+ = pingou @@ -339,6 +344,326 @@ repo requests/test RW+ = foo RW+ = pingou +# end of body +# end of generated configuration +# \ó/ +# end of footer + +""" + #print data + self.assertEqual(data, exp) + + +class PagureLibGitoliteGroupConfigtests(tests.Modeltests): + """ Tests for generating the gitolite configuration file for a group + change + + """ + + maxDiff = None + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureLibGitoliteGroupConfigtests, self).setUp() + + pagure.lib.git.SESSION = self.session + tests.create_projects(self.session) + + pagure.lib.add_group( + self.session, + group_name='grp', + display_name='grp group', + description=None, + group_type='user', + user='pingou', + is_admin=False, + blacklist=[], + ) + pagure.lib.add_group( + self.session, + group_name='grp2', + display_name='grp2 group', + description=None, + group_type='user', + user='foo', + is_admin=False, + blacklist=[], + ) + self.session.commit() + + self.outputconf = os.path.join(self.path, 'test_gitolite.conf') + + self.preconf = os.path.join(self.path, 'header_gitolite') + with open(self.preconf, 'w') as stream: + stream.write('# this is a header that is manually added\n') + stream.write('\n') + stream.write('@group1 = foo bar baz\n') + stream.write('@group2 = threebean puiterwijk kevin pingou\n') + + self.postconf = os.path.join(self.path, 'footer_gitolite') + with open(self.postconf, 'w') as stream: + stream.write('# end of generated configuration\n') + stream.write('# \ó/\n') + stream.write('# end of footer\n') + + def tearDown(self): + """ Tearn down the environnment, ran before every tests. """ + super(PagureLibGitoliteGroupConfigtests, self).tearDown() + + if os.path.exists(self.outputconf): + os.unlink(self.outputconf) + self.assertFalse(os.path.exists(self.outputconf)) + + def test_write_gitolite_project_test_group(self): + """ Test the write_gitolite_acls when updating a single group. """ + + with open(self.outputconf, 'w') as stream: + pass + + project = pagure.lib._get_project(self.session, 'test') + group = pagure.lib.search_groups(self.session, group_name='grp') + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=project, + preconf=self.preconf, + postconf=self.postconf, + group=group, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + +# end of header +@grp = pingou + +repo test + R = @all + RW+ = pingou + +repo docs/test + R = @all + RW+ = pingou + +repo tickets/test + RW+ = pingou + +repo requests/test + RW+ = pingou + +# end of body +# end of generated configuration +# \ó/ +# end of footer + +""" + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_project_test_all_groups(self): + """ Test the write_gitolite_acls when updating all groups. """ + + with open(self.outputconf, 'w') as stream: + pass + + project = pagure.lib._get_project(self.session, 'test') + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=project, + preconf=self.preconf, + postconf=self.postconf, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + +# end of header +@grp2 = foo +@grp = pingou +# end of groups + +repo test + R = @all + RW+ = pingou + +repo docs/test + R = @all + RW+ = pingou + +repo tickets/test + RW+ = pingou + +repo requests/test + RW+ = pingou + +# end of body +# end of generated configuration +# \ó/ +# end of footer + +""" + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_project_all_projects_groups(self): + """ Test the generating the entire gitolite config. """ + + with open(self.outputconf, 'w') as stream: + pass + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=-1, + preconf=self.preconf, + postconf=self.postconf, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + +# end of header +@grp2 = foo +@grp = pingou +# end of groups + +%s + +# end of body +# end of generated configuration +# \ó/ +# end of footer + +""" % CORE_CONFIG + #print data + self.assertEqual(data, exp) + + def test_write_gitolite_project_all_projects_one_group(self): + """ Test the generating the entire gitolite config. """ + + # Generate the full gitolite config that we will update + self.test_write_gitolite_project_all_projects_groups() + + project = pagure.lib._get_project(self.session, 'test') + group = pagure.lib.search_groups(self.session, group_name='grp') + + # Let's add `foo` to `grp` so something changes + msg = pagure.lib.add_user_to_group( + self.session, + username='foo', + group=group, + user='pingou', + is_admin=False, + ) + self.session.commit() + self.assertEqual(msg, 'User `foo` added to the group `grp`.') + + # Let's add `foo` to `test` so the project changes as well + msg = pagure.lib.add_user_to_project( + self.session, + project=project, + new_user='foo', + user='pingou', + access='commit' + ) + self.assertEqual(msg, 'User added') + self.session.commit() + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=project, + group=group, + preconf=self.preconf, + postconf=self.postconf, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + +# end of header +@grp2 = foo +@grp = foo pingou +# end of groups + +repo test2 + R = @all + RW+ = pingou + +repo docs/test2 + R = @all + RW+ = pingou + +repo tickets/test2 + RW+ = pingou + +repo requests/test2 + RW+ = pingou + +repo somenamespace/test3 + R = @all + RW+ = pingou + +repo docs/somenamespace/test3 + R = @all + RW+ = pingou + +repo tickets/somenamespace/test3 + RW+ = pingou + +repo requests/somenamespace/test3 + RW+ = pingou + +repo test + R = @all + RW+ = pingou + RW+ = foo + +repo docs/test + R = @all + RW+ = pingou + RW+ = foo + +repo tickets/test + RW+ = pingou + RW+ = foo + +repo requests/test + RW+ = pingou + RW+ = foo + +# end of body # end of generated configuration # \ó/ # end of footer @@ -347,6 +672,54 @@ repo requests/test #print data self.assertEqual(data, exp) + def test_write_gitolite_delete_group(self): + """ Test the updating the gitolite config after having + deleted a group. + """ + + # Generate the full gitolite config that we will update + self.test_write_gitolite_project_all_projects_groups() + + # Delete the group `grp` + self.assertEqual(len(pagure.lib.search_groups(self.session)), 2) + group = pagure.lib.search_groups(self.session, group_name='grp') + self.session.delete(group) + self.session.commit() + self.assertEqual(len(pagure.lib.search_groups(self.session)), 1) + + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=None, + preconf=self.preconf, + postconf=self.postconf, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""# this is a header that is manually added + +@group1 = foo bar baz +@group2 = threebean puiterwijk kevin pingou + +# end of header +@grp2 = foo +# end of groups + +%s + +# end of body +# end of generated configuration +# \ó/ +# end of footer + +""" % CORE_CONFIG + #print data + self.assertEqual(data, exp) + if __name__ == '__main__': unittest.main(verbosity=2) From 109c4b00b796dd92f7a18ac130e33a5510e2cb2b Mon Sep 17 00:00:00 2001 From: Clement Verna Date: Jul 09 2017 16:42:41 +0000 Subject: [PATCH 11/250] Add a new class without broker/worker to the unit test. This commit introduce the SimplePagureTest class, this class does not set a broker/worker and should be used by test that do not require concurency. Signed-off-by: Clement Verna --- diff --git a/tests/__init__.py b/tests/__init__.py index fe49fc7..eff48a2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -140,8 +140,10 @@ def user_set(APP, user): yield -class Modeltests(unittest.TestCase): - """ Model tests. """ +class SimplePagureTest(unittest.TestCase): + """ + Simple Test class that does not set a broker/worker + """ def __init__(self, method_name='runTest'): """ Constructor. """ @@ -171,14 +173,9 @@ class Modeltests(unittest.TestCase): perfrepo.reset_stats() perfrepo.REQUESTS = [] - def setUp(self): # pylint: disable=invalid-name - """ Set up the environnment, ran before every tests. """ - # Clean up test performance info - perfrepo.reset_stats() - perfrepo.REQUESTS = [] + def setUp(self): + self.perfReset() - pagure.REDIS = None - pagure.lib.REDIS = None if self.path is not None: raise Exception('Double init?!') self.path = tempfile.mkdtemp(prefix='pagure-tests-path-') @@ -199,44 +196,9 @@ class Modeltests(unittest.TestCase): with open(os.path.join(self.path, 'config'), 'w') as f: f.write(CONFIG_TEMPLATE % config_values) - # Create a broker - broker_url = os.path.join(self.path, 'broker') - - self.broker = subprocess.Popen( - ['/usr/bin/redis-server', '--unixsocket', broker_url, '--port', - '0', '--loglevel', 'warning', '--logfile', '/dev/null'], - stdout=None, stderr=None) - self.broker.poll() - if self.broker.returncode is not None: - raise Exception('Broker failed to start') - self.session = pagure.lib.model.create_tables( self.dbpath, acls=pagure.APP.config.get('ACLS', {})) - celery_broker_url = 'redis+socket://' + broker_url - pagure.APP.config['BROKER_URL'] = celery_broker_url - reload(pagure.lib.tasks) - - # Start a worker - # Using cocurrency 2 to test with some concurrency, but not be heavy - # Using eventlet so that worker.terminate kills everything - self.workerlog = open(os.path.join(self.path, 'worker.log'), 'w') - self.worker = subprocess.Popen( - ['/usr/bin/celery', '-A', 'pagure.lib.tasks', 'worker', - '--loglevel=info', '--concurrency=2', '--pool=eventlet', - '--without-gossip', '--without-mingle', '--quiet'], - env={'PAGURE_BROKER_URL': celery_broker_url, - 'PAGURE_CONFIG': os.path.join(self.path, 'config'), - 'PYTHONPATH': '.'}, - cwd=os.path.normpath(os.path.join(os.path.dirname(__file__), - '..')), - stdout=self.workerlog, - stderr=self.workerlog) - self.worker.poll() - if self.worker.returncode is not None: - raise Exception('Worker failed to start') - time.sleep(2) - # Create a couple of users item = pagure.lib.model.User( user='pingou', @@ -282,11 +244,8 @@ class Modeltests(unittest.TestCase): pagure.APP.config['ATTACHMENTS_FOLDER'] = os.path.join( self.path, 'attachments') self.app = pagure.APP.test_client() - self.app.get = create_maybe_waiter(self.app.get, self.app.get) - self.app.post = create_maybe_waiter(self.app.post, self.app.get) - def tearDown(self): # pylint: disable=invalid-name - """ Remove the test.db database if there is one. """ + def tearDown(self): self.session.close() # Clear DB @@ -295,19 +254,6 @@ class Modeltests(unittest.TestCase): db_name = self.dbpath.rsplit('/', 1)[1] requests.get('%s/clean/%s' % (FAITOUT_URL, db_name)) - # Terminate worker and broker - # We just send a SIGKILL (kill -9), since when the test finishes, we - # don't really care about the output of either worker or broker - # anymore - self.worker.kill() - self.worker.wait() - self.worker = None - self.workerlog.close() - self.workerlog = None - self.broker.kill() - self.broker.wait() - self.broker = None - # Remove testdir shutil.rmtree(self.path) self.path = None @@ -321,6 +267,71 @@ class Modeltests(unittest.TestCase): return output.data.split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] +class Modeltests(SimplePagureTest): + """ Model tests. """ + + def setUp(self): # pylint: disable=invalid-name + """ Set up the environnment, ran before every tests. """ + # Clean up test performance info + super(Modeltests, self).setUp() + + pagure.REDIS = None + pagure.lib.REDIS = None + + # Create a broker + broker_url = os.path.join(self.path, 'broker') + + self.broker = subprocess.Popen( + ['/usr/bin/redis-server', '--unixsocket', broker_url, '--port', + '0', '--loglevel', 'warning', '--logfile', '/dev/null'], + stdout=None, stderr=None) + self.broker.poll() + if self.broker.returncode is not None: + raise Exception('Broker failed to start') + + celery_broker_url = 'redis+socket://' + broker_url + pagure.APP.config['BROKER_URL'] = celery_broker_url + reload(pagure.lib.tasks) + + # Start a worker + # Using cocurrency 2 to test with some concurrency, but not be heavy + # Using eventlet so that worker.terminate kills everything + self.workerlog = open(os.path.join(self.path, 'worker.log'), 'w') + self.worker = subprocess.Popen( + ['/usr/bin/celery', '-A', 'pagure.lib.tasks', 'worker', + '--loglevel=info', '--concurrency=2', '--pool=eventlet', + '--without-gossip', '--without-mingle', '--quiet'], + env={'PAGURE_BROKER_URL': celery_broker_url, + 'PAGURE_CONFIG': os.path.join(self.path, 'config'), + 'PYTHONPATH': '.'}, + cwd=os.path.normpath(os.path.join(os.path.dirname(__file__), + '..')), + stdout=self.workerlog, + stderr=self.workerlog) + self.worker.poll() + if self.worker.returncode is not None: + raise Exception('Worker failed to start') + time.sleep(2) + + self.app.get = create_maybe_waiter(self.app.get, self.app.get) + self.app.post = create_maybe_waiter(self.app.post, self.app.get) + + def tearDown(self): # pylint: disable=invalid-name + """ Remove the test.db database if there is one. """ + super(Modeltests, self).tearDown() + # Terminate worker and broker + # We just send a SIGKILL (kill -9), since when the test finishes, we + # don't really care about the output of either worker or broker + # anymore + self.worker.kill() + self.worker.wait() + self.worker = None + self.workerlog.close() + self.workerlog = None + self.broker.kill() + self.broker.wait() + self.broker = None + class FakeGroup(object): # pylint: disable=too-few-public-methods """ Fake object used to make the FakeUser object closer to the diff --git a/tests/test_pagure_flask.py b/tests/test_pagure_flask.py index 9157677..d8fdf7a 100644 --- a/tests/test_pagure_flask.py +++ b/tests/test_pagure_flask.py @@ -29,7 +29,7 @@ import pagure.lib.model import tests -class PagureGetRemoteRepoPath(tests.Modeltests): +class PagureGetRemoteRepoPath(tests.SimplePagureTest): """ Tests for pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_api.py b/tests/test_pagure_flask_api.py index fbee2a2..7891a4e 100644 --- a/tests/test_pagure_flask_api.py +++ b/tests/test_pagure_flask_api.py @@ -26,7 +26,7 @@ import pagure.lib import tests -class PagureFlaskApitests(tests.Modeltests): +class PagureFlaskApitests(tests.SimplePagureTest): """ Tests for flask API controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_api_auth.py b/tests/test_pagure_flask_api_auth.py index afdd070..567959f 100644 --- a/tests/test_pagure_flask_api_auth.py +++ b/tests/test_pagure_flask_api_auth.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskApiAuthtests(tests.Modeltests): +class PagureFlaskApiAuthtests(tests.SimplePagureTest): """ Tests for the authentication in the flask API of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_api_group.py b/tests/test_pagure_flask_api_group.py index 19977b7..3e267fa 100644 --- a/tests/test_pagure_flask_api_group.py +++ b/tests/test_pagure_flask_api_group.py @@ -22,7 +22,7 @@ import pagure.lib import tests -class PagureFlaskApiGroupTests(tests.Modeltests): +class PagureFlaskApiGroupTests(tests.SimplePagureTest): """ Tests for the flask API of pagure for issue """ def setUp(self): diff --git a/tests/test_pagure_flask_docs.py b/tests/test_pagure_flask_docs.py index 90077cb..b6608d0 100644 --- a/tests/test_pagure_flask_docs.py +++ b/tests/test_pagure_flask_docs.py @@ -31,7 +31,7 @@ import tests from pagure.lib.repo import PagureRepo -class PagureFlaskDocstests(tests.Modeltests): +class PagureFlaskDocstests(tests.SimplePagureTest): """ Tests for flask docs of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_form.py b/tests/test_pagure_flask_form.py index 2b16546..0b4a6a5 100644 --- a/tests/test_pagure_flask_form.py +++ b/tests/test_pagure_flask_form.py @@ -28,7 +28,7 @@ import pagure.forms import tests -class PagureFlaskFormTests(tests.Modeltests): +class PagureFlaskFormTests(tests.SimplePagureTest): """ Tests for forms of the flask application """ def setUpt(self): diff --git a/tests/test_pagure_flask_ui_app_give_project.py b/tests/test_pagure_flask_ui_app_give_project.py index c3a0416..ea5cb08 100644 --- a/tests/test_pagure_flask_ui_app_give_project.py +++ b/tests/test_pagure_flask_ui_app_give_project.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskGiveRepotests(tests.Modeltests): +class PagureFlaskGiveRepotests(tests.SimplePagureTest): """ Tests for give a project on pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_login.py b/tests/test_pagure_flask_ui_login.py index 5312a0d..d4ce901 100644 --- a/tests/test_pagure_flask_ui_login.py +++ b/tests/test_pagure_flask_ui_login.py @@ -35,7 +35,7 @@ from pagure.lib.repo import PagureRepo import pagure.ui.login -class PagureFlaskLogintests(tests.Modeltests): +class PagureFlaskLogintests(tests.SimplePagureTest): """ Tests for flask app controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_no_master_branch.py b/tests/test_pagure_flask_ui_no_master_branch.py index 9f82fa9..a410eb8 100644 --- a/tests/test_pagure_flask_ui_no_master_branch.py +++ b/tests/test_pagure_flask_ui_no_master_branch.py @@ -29,7 +29,7 @@ import tests from pagure.lib.repo import PagureRepo -class PagureFlaskNoMasterBranchtests(tests.Modeltests): +class PagureFlaskNoMasterBranchtests(tests.SimplePagureTest): """ Tests for flask application when the git repo has no master branch. """ diff --git a/tests/test_pagure_flask_ui_old_commit.py b/tests/test_pagure_flask_ui_old_commit.py index ac09026..03595a4 100644 --- a/tests/test_pagure_flask_ui_old_commit.py +++ b/tests/test_pagure_flask_ui_old_commit.py @@ -30,7 +30,7 @@ import tests from pagure.lib.repo import PagureRepo -class PagureFlaskRepoOldUrltests(tests.Modeltests): +class PagureFlaskRepoOldUrltests(tests.SimplePagureTest): """ Tests for flask app controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins.py b/tests/test_pagure_flask_ui_plugins.py index 420860b..57f0780 100644 --- a/tests/test_pagure_flask_ui_plugins.py +++ b/tests/test_pagure_flask_ui_plugins.py @@ -40,7 +40,7 @@ class FakeForm(wtforms.Form): ) -class PagureFlaskPluginstests(tests.Modeltests): +class PagureFlaskPluginstests(tests.SimplePagureTest): """ Tests for flask plugins controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_fedmsg.py b/tests/test_pagure_flask_ui_plugins_fedmsg.py index ad41463..d057793 100644 --- a/tests/test_pagure_flask_ui_plugins_fedmsg.py +++ b/tests/test_pagure_flask_ui_plugins_fedmsg.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginFedmsgtests(tests.Modeltests): +class PagureFlaskPluginFedmsgtests(tests.SimplePagureTest): """ Tests for fedmsg plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_irc.py b/tests/test_pagure_flask_ui_plugins_irc.py index ff3f74d..301f493 100644 --- a/tests/test_pagure_flask_ui_plugins_irc.py +++ b/tests/test_pagure_flask_ui_plugins_irc.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginIRCtests(tests.Modeltests): +class PagureFlaskPluginIRCtests(tests.SimplePagureTest): """ Tests for pagure_hook plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_mail.py b/tests/test_pagure_flask_ui_plugins_mail.py index 6cafe65..1ad2c5e 100644 --- a/tests/test_pagure_flask_ui_plugins_mail.py +++ b/tests/test_pagure_flask_ui_plugins_mail.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginMailtests(tests.Modeltests): +class PagureFlaskPluginMailtests(tests.SimplePagureTest): """ Tests for flask plugins controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_noff.py b/tests/test_pagure_flask_ui_plugins_noff.py index 1f90245..1b1c5df 100644 --- a/tests/test_pagure_flask_ui_plugins_noff.py +++ b/tests/test_pagure_flask_ui_plugins_noff.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginNoFFtests(tests.Modeltests): +class PagureFlaskPluginNoFFtests(tests.SimplePagureTest): """ Tests for Block non fast-forward pushes plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_pagure_ci.py b/tests/test_pagure_flask_ui_plugins_pagure_ci.py index aea146d..9c28e8b 100644 --- a/tests/test_pagure_flask_ui_plugins_pagure_ci.py +++ b/tests/test_pagure_flask_ui_plugins_pagure_ci.py @@ -24,7 +24,7 @@ import pagure.lib import tests -class PagureFlaskPluginPagureCItests(tests.Modeltests): +class PagureFlaskPluginPagureCItests(tests.SimplePagureTest): """ Tests for flask plugins controller of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_pagure_hook.py b/tests/test_pagure_flask_ui_plugins_pagure_hook.py index 66191c8..5d22a77 100644 --- a/tests/test_pagure_flask_ui_plugins_pagure_hook.py +++ b/tests/test_pagure_flask_ui_plugins_pagure_hook.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginPagureHooktests(tests.Modeltests): +class PagureFlaskPluginPagureHooktests(tests.SimplePagureTest): """ Tests for pagure_hook plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_pagure_request_hook.py b/tests/test_pagure_flask_ui_plugins_pagure_request_hook.py index 742629a..eaaacc3 100644 --- a/tests/test_pagure_flask_ui_plugins_pagure_request_hook.py +++ b/tests/test_pagure_flask_ui_plugins_pagure_request_hook.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginPagureRequestHooktests(tests.Modeltests): +class PagureFlaskPluginPagureRequestHooktests(tests.SimplePagureTest): """ Tests for pagure_hook plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py b/tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py index 0f223bf..4b3788b 100644 --- a/tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py +++ b/tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginPagureTicketHooktests(tests.Modeltests): +class PagureFlaskPluginPagureTicketHooktests(tests.SimplePagureTest): """ Tests for pagure_hook plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_rtd_hook.py b/tests/test_pagure_flask_ui_plugins_rtd_hook.py index 03258b2..bfa37a6 100644 --- a/tests/test_pagure_flask_ui_plugins_rtd_hook.py +++ b/tests/test_pagure_flask_ui_plugins_rtd_hook.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginRtdHooktests(tests.Modeltests): +class PagureFlaskPluginRtdHooktests(tests.SimplePagureTest): """ Tests for rtd_hook plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_plugins_unsigned.py b/tests/test_pagure_flask_ui_plugins_unsigned.py index 9303225..25733e3 100644 --- a/tests/test_pagure_flask_ui_plugins_unsigned.py +++ b/tests/test_pagure_flask_ui_plugins_unsigned.py @@ -27,7 +27,7 @@ import pagure.lib import tests -class PagureFlaskPluginUnsignedtests(tests.Modeltests): +class PagureFlaskPluginUnsignedtests(tests.SimplePagureTest): """ Tests for Block pushes with unsigned commit plugin of pagure """ def setUp(self): diff --git a/tests/test_pagure_flask_ui_repo_slash_name.py b/tests/test_pagure_flask_ui_repo_slash_name.py index 9d8c055..e24f167 100644 --- a/tests/test_pagure_flask_ui_repo_slash_name.py +++ b/tests/test_pagure_flask_ui_repo_slash_name.py @@ -29,7 +29,7 @@ import tests from pagure.lib.repo import PagureRepo -class PagureFlaskSlashInNametests(tests.Modeltests): +class PagureFlaskSlashInNametests(tests.SimplePagureTest): """ Tests for flask application when the project contains a '/'. """ diff --git a/tests/test_pagure_flask_ui_slash_branch_name.py b/tests/test_pagure_flask_ui_slash_branch_name.py index b8463f9..08da657 100644 --- a/tests/test_pagure_flask_ui_slash_branch_name.py +++ b/tests/test_pagure_flask_ui_slash_branch_name.py @@ -29,7 +29,7 @@ import tests from pagure.lib.repo import PagureRepo -class PagureFlaskSlashInBranchtests(tests.Modeltests): +class PagureFlaskSlashInBranchtests(tests.SimplePagureTest): """ Tests for flask application when the branch name contains a '/'. """ From b6b5f6d03160bb7a82f6da4aba63e0bf67502a9d Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 09 2017 16:43:48 +0000 Subject: [PATCH 12/250] Fix the issue_keys table for mysql It seems mysql doesn't like TEXT field to be part of an unique constraint so go for a String(255) instead. Relates to https://pagure.io/pagure/issue/2377 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 3485785..f37fd95 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -1348,7 +1348,7 @@ class IssueKeys(BASE): 'projects.id', onupdate='CASCADE', ondelete='CASCADE', ), nullable=False) - name = sa.Column(sa.Text(), nullable=False) + name = sa.Column(sa.String(255), nullable=False) key_type = sa.Column(sa.String(255), nullable=False) key_data = sa.Column(sa.Text()) key_notify = sa.Column(sa.Boolean, default=False, nullable=False) From 8fca2491f2d110cb30551dfd62ed8cbfb812e577 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 10 2017 10:23:17 +0000 Subject: [PATCH 13/250] Drop the load_from_disk script This script is quite specific to loading Fedora-specific projects into a pagure database and was meant to be used to populate the dist-git database. So this file is removed from the pagure project but will be added and adjusted to the pagure-dist-git project. Signed-off-by: Pierre-Yves Chibon --- diff --git a/files/load_from_disk.py b/files/load_from_disk.py deleted file mode 100644 index 99b3f16..0000000 --- a/files/load_from_disk.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python - -import argparse -import requests -import os - -from sqlalchemy.exc import SQLAlchemyError - -if 'PAGURE_CONFIG' not in os.environ \ - and os.path.exists('/etc/pagure/pagure.cfg'): - print 'Using configuration file `/etc/pagure/pagure.cfg`' - os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg' - -import pagure -import pagure.lib -import pagure.lib.model - - -def get_poc_of_pkgs(debug=False): - """ Retrieve a dictionary giving the point of contact of each package - in pkgdb. - """ - if debug: - print 'Querying pkgdb' - PKGDB_URL = 'https://admin.stg.fedoraproject.org/pkgdb/api/' - req = requests.get(PKGDB_URL + 'bugzilla').text - if debug: - print 'Pkgdb data retrieved, getting POC' - pkgs = {} - for line in req.split('\n'): - line = line.strip() - if not line or line.startswith('#'): - continue - line = line.split('|') - if len(line) < 4: - continue - pkgs[line[1]] = line[3] - - return pkgs - - -def main(folder, debug=False): - """ - Logic: - - Query the list of maintainer/PoC from pkgdb - - Browse the directory - - For each git in the directory, create the project with the correct POC - """ - pocs = get_poc_of_pkgs(debug=debug) - - if debug: - print 'Adding the user to the DB' - for user in sorted(set(pocs.values())): - if debug: - print user - try: - pagure.lib.set_up_user( - session=pagure.SESSION, - username=user, - fullname=user, - default_email='%s@fedoraproject.org' % user, - keydir=pagure.APP.config.get('GITOLITE_KEYDIR', None), - ) - pagure.SESSION.commit() - except SQLAlchemyError as err: - pagure.SESSION.rollback() - print 'ERROR with user %s' % user - print err - - for project in sorted(os.listdir(folder)): - if debug: - print project - - if not project.endswith('.git'): - if debug: - print ' -skip: not a git repository' - continue - - if project.split('.git')[0] not in pocs: - if debug: - print ' -skip: no pocs' - continue - - try: - name = project.split('.git')[0] - orig_name = name - name = 'rpms/%s' % name - if name in pagure.APP.config['BLACKLISTED_PROJECTS']: - raise pagure.exceptions.RepoExistsException( - 'No project "%s" are allowed to be created due to potential ' - 'conflicts in URLs with pagure itself' % name - ) - - user_obj = pagure.lib.get_user(pagure.SESSION, pocs[orig_name]) - allowed_prefix = pagure.APP.config[ - 'ALLOWED_PREFIX'] + [grp for grp in user_obj.groups] - - first_part, _, second_part = name.partition('/') - if second_part and first_part not in allowed_prefix: - raise pagure.exceptions.PagureException( - 'The prefix of your project must be in the list of allowed ' - 'prefixes set by the admins of this pagure instance, or the name ' - 'of a group of which you are a member.' - ) - - gitfolder = pagure.APP.config['GIT_FOLDER'] - docfolder = pagure.APP.config['DOCS_FOLDER'] - ticketfolder = pagure.APP.config['TICKETS_FOLDER'] - requestfolder = pagure.APP.config['REQUESTS_FOLDER'] - - gitrepo = os.path.join(gitfolder, '%s.git' % name) - - project = pagure.lib.model.Project( - name=name, - description=None, - url=None, - avatar_email=None, - user_id=user_obj.id, - parent_id=None, - hook_token=pagure.lib.login.id_generator(40) - ) - pagure.SESSION.add(project) - # Make sure we won't have SQLAlchemy error before we create the repo - pagure.SESSION.flush() - - http_clone_file = os.path.join(gitrepo, 'git-daemon-export-ok') - if not os.path.exists(http_clone_file): - with open(http_clone_file, 'w') as stream: - pass - - docrepo = os.path.join(docfolder, project.path) - if os.path.exists(docrepo): - shutil.rmtree(gitrepo) - raise pagure.exceptions.RepoExistsException( - 'The docs repo "%s" already exists' % project.path - ) - pygit2.init_repository(docrepo, bare=True) - - ticketrepo = os.path.join(ticketfolder, project.path) - if os.path.exists(ticketrepo): - shutil.rmtree(gitrepo) - shutil.rmtree(docrepo) - raise pagure.exceptions.RepoExistsException( - 'The tickets repo "%s" already exists' % project.path - ) - pygit2.init_repository( - ticketrepo, bare=True, - mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) - - requestrepo = os.path.join(requestfolder, project.path) - if os.path.exists(requestrepo): - shutil.rmtree(gitrepo) - shutil.rmtree(docrepo) - shutil.rmtree(ticketrepo) - raise pagure.exceptions.RepoExistsException( - 'The requests repo "%s" already exists' % project.path - ) - pygit2.init_repository( - requestrepo, bare=True, - mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) - - pagure.SESSION.commit() - except pagure.exceptions.PagureException as err: - print 'ERROR with project %s' % project - print err - except SQLAlchemyError as err: # pragma: no cover - pagure.SESSION.rollback() - print 'ERROR (DB) with project %s' % project - print err - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='Script creating projects on pagure based on the git ' - 'repos present in the specified folder and the pkgdb information.' - ) - parser.add_argument( - 'folder', - help='Folder containing all the git repos of the projects to create') - parser.add_argument( - '--debug', dest='debug', action='store_true', default=False, - help='Print the debugging output') - - args = parser.parse_args() - - main(args.folder, debug=args.debug) From 9ec7a7324835dea3f7d6d8fa2e6baec8e7bf7ee1 Mon Sep 17 00:00:00 2001 From: Carlos Mogas da Silva Date: Jul 10 2017 11:06:49 +0000 Subject: [PATCH 14/250] Fix next_url URL parameter on the login page not being used --- diff --git a/pagure/ui/login.py b/pagure/ui/login.py index bee6ae7..78bf4ed 100644 --- a/pagure/ui/login.py +++ b/pagure/ui/login.py @@ -84,7 +84,7 @@ def do_login(): """ Log the user in user. """ form = forms.LoginForm() - next_url = flask.request.args.get('next_url') + next_url = flask.request.form.get('next_url') if not next_url or next_url == 'None': next_url = flask.url_for('index') From 42df122cd5439f3768fa819e58539ecb25623ef8 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 11 2017 11:12:39 +0000 Subject: [PATCH 15/250] Support configuration where there are no docs folder and no tickets folder Those cases are basically for instances not wanting to run the doc server or enable issue tracking globally, so let's not create the git repositories. Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 38cd7f3..b273aed 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -160,37 +160,43 @@ def create_project(username, namespace, name, add_readme, with open(http_clone_file, 'w') as stream: pass - docrepo = os.path.join(APP.config['DOCS_FOLDER'], project.path) - if os.path.exists(docrepo): - if not ignore_existing_repo: - shutil.rmtree(gitrepo) - raise pagure.exceptions.RepoExistsException( - 'The docs repo "%s" already exists' % project.path - ) - else: - pygit2.init_repository(docrepo, bare=True) - - ticketrepo = os.path.join(APP.config['TICKETS_FOLDER'], project.path) - if os.path.exists(ticketrepo): - if not ignore_existing_repo: - shutil.rmtree(gitrepo) - shutil.rmtree(docrepo) - raise pagure.exceptions.RepoExistsException( - 'The tickets repo "%s" already exists' % project.path - ) - else: - pygit2.init_repository( - ticketrepo, bare=True, - mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) - - requestrepo = os.path.join(APP.config['REQUESTS_FOLDER'], project.path) + if APP.config['DOCS_FOLDER']: + docrepo = os.path.join(APP.config['DOCS_FOLDER'], project.path) + if os.path.exists(docrepo): + if not ignore_existing_repo: + shutil.rmtree(gitrepo) + raise pagure.exceptions.RepoExistsException( + 'The docs repo "%s" already exists' % project.path + ) + else: + pygit2.init_repository(docrepo, bare=True) + + if APP.config['TICKETS_FOLDER']: + ticketrepo = os.path.join( + APP.config['TICKETS_FOLDER'], project.path) + if os.path.exists(ticketrepo): + if not ignore_existing_repo: + shutil.rmtree(gitrepo) + shutil.rmtree(docrepo) + raise pagure.exceptions.RepoExistsException( + 'The tickets repo "%s" already exists' % + project.path + ) + else: + pygit2.init_repository( + ticketrepo, bare=True, + mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) + + requestrepo = os.path.join( + APP.config['REQUESTS_FOLDER'], project.path) if os.path.exists(requestrepo): if not ignore_existing_repo: shutil.rmtree(gitrepo) shutil.rmtree(docrepo) shutil.rmtree(ticketrepo) raise pagure.exceptions.RepoExistsException( - 'The requests repo "%s" already exists' % project.path + 'The requests repo "%s" already exists' % + project.path ) else: pygit2.init_repository( @@ -348,24 +354,28 @@ def fork(name, namespace, user_owner, user_forker, editbranch, editfile): with open(http_clone_file, 'w'): pass - docrepo = os.path.join(APP.config['DOCS_FOLDER'], repo_to.path) - if os.path.exists(docrepo): - shutil.rmtree(forkreponame) - raise pagure.exceptions.RepoExistsException( - 'The docs "%s" already exists' % repo_to.path - ) - pygit2.init_repository(docrepo, bare=True) + # Only fork the doc folder if the pagure instance supports the doc + # service/server. + if APP.config.get('DOCS_FOLDER'): + docrepo = os.path.join(APP.config['DOCS_FOLDER'], repo_to.path) + if os.path.exists(docrepo): + shutil.rmtree(forkreponame) + raise pagure.exceptions.RepoExistsException( + 'The docs "%s" already exists' % repo_to.path + ) + pygit2.init_repository(docrepo, bare=True) - ticketrepo = os.path.join(APP.config['TICKETS_FOLDER'], repo_to.path) - if os.path.exists(ticketrepo): - shutil.rmtree(forkreponame) - shutil.rmtree(docrepo) - raise pagure.exceptions.RepoExistsException( - 'The tickets repo "%s" already exists' % repo_to.path - ) - pygit2.init_repository( - ticketrepo, bare=True, - mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) + if APP.config.get('TICKETS_FOLDER'): + ticketrepo = os.path.join(APP.config['TICKETS_FOLDER'], repo_to.path) + if os.path.exists(ticketrepo): + shutil.rmtree(forkreponame) + shutil.rmtree(docrepo) + raise pagure.exceptions.RepoExistsException( + 'The tickets repo "%s" already exists' % repo_to.path + ) + pygit2.init_repository( + ticketrepo, bare=True, + mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP) requestrepo = os.path.join(APP.config['REQUESTS_FOLDER'], repo_to.path) if os.path.exists(requestrepo): From d012eed6a8d7e5b01bfa6e9db0a2cf90b74d4518 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 12 2017 13:07:01 +0000 Subject: [PATCH 16/250] Fix retrieving a group only when there was a group specified Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index b273aed..4d9ebdc 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -93,7 +93,9 @@ def generate_gitolite_acls(namespace=None, name=None, user=None, group=None): APP.config['GITOLITE_BACKEND']) _log.debug('Got helper: %s', helper) - group_obj = pagure.lib.search_groups(session, group_name=group) or None + group_obj = None + if group: + group_obj = pagure.lib.search_groups(session, group_name=group) _log.debug( 'Calling helper: %s with arg: project=%s, group=%s', helper, project, group_obj) From 4a31fb3c67b0ce8096f455b0e9a602ece212e5b0 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 12 2017 13:07:25 +0000 Subject: [PATCH 17/250] Send the group name not the group object to the task Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/ui/groups.py b/pagure/ui/groups.py index 0691ce4..54b3fce 100644 --- a/pagure/ui/groups.py +++ b/pagure/ui/groups.py @@ -84,7 +84,8 @@ def view_group(group): is_admin=pagure.is_admin(), ) pagure.SESSION.commit() - pagure.lib.git.generate_gitolite_acls(project=None, group=group) + pagure.lib.git.generate_gitolite_acls( + project=None, group=group.group_name) flask.flash(msg) except pagure.exceptions.PagureException as err: pagure.SESSION.rollback() From 7614955799225a91a838cd32cf37be1b2f38532c Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 12 2017 13:07:41 +0000 Subject: [PATCH 18/250] Typo fixes in the docstrings Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index 6252161..9d6227d 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -1678,7 +1678,7 @@ def add_deploykey(repo, username=None, namespace=None): '/fork////adduser', methods=('GET', 'POST')) @login_required def add_user(repo, username=None, namespace=None): - """ Add the specified user from the project. + """ Add the specified user to the project. """ if not pagure.APP.config.get('ENABLE_USER_MNGT', True): @@ -1821,7 +1821,7 @@ def remove_group_project(repo, groupid, username=None, namespace=None): '/fork////addgroup', methods=('GET', 'POST')) @login_required def add_group_project(repo, username=None, namespace=None): - """ Add the specified group from the project. + """ Add the specified group to the project. """ if not pagure.APP.config.get('ENABLE_USER_MNGT', True): From 10fdb943d7bb55a49c4ab67dbc078201d7f3f60a Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 13 2017 10:21:12 +0000 Subject: [PATCH 19/250] Show all the projects a group has access to Before that it was showing only projects where the group had admin access on. Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/templates/group_info.html b/pagure/templates/group_info.html index 27c1c13..7f806b0 100644 --- a/pagure/templates/group_info.html +++ b/pagure/templates/group_info.html @@ -55,8 +55,7 @@
- {{ render_repos_as_card( - group.projects_admin_groups, group.projects_admin_groups|count, 'Projects') }} + {{ render_repos_as_card(group.projects, group.projects|count, 'Projects') }}
From b31a794dafedff0b8f6711c4e4670792903e0c5e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 13 2017 10:21:19 +0000 Subject: [PATCH 20/250] Add unit-tests ensuring generate_acls is called with group=None This to ensure that when group=None, not all the groups are retrieved but that group=None is kept and passed on. Signed-off-by: Pierre-Yves Chibon --- diff --git a/tests/test_pagure_lib_gitolite_config.py b/tests/test_pagure_lib_gitolite_config.py index 7219200..e07de7f 100644 --- a/tests/test_pagure_lib_gitolite_config.py +++ b/tests/test_pagure_lib_gitolite_config.py @@ -720,6 +720,22 @@ repo requests/test #print data self.assertEqual(data, exp) + @patch('pagure.lib.git_auth.get_git_auth_helper') + def test_task_generate_gitolite_acls_one_group(self, get_helper): + """ Test the generate_gitolite_acls task to ensure if group is None + then None is passed to the helper. """ + helper = MagicMock() + get_helper.return_value = helper + pagure.lib.SESSIONMAKER = self.session.session_factory + + pagure.lib.tasks.generate_gitolite_acls( + namespace=None, name='test', user=None, group=None) + + get_helper.assert_called_with('gitolite3') + args = helper.generate_acls.call_args + self.assertIsNone(args[1].get('group')) + self.assertEqual(args[1].get('project').fullname, 'test') + if __name__ == '__main__': unittest.main(verbosity=2) From a01c25996b31278aef0c42b0bee29c8bef6474d6 Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Jul 13 2017 17:25:45 +0000 Subject: [PATCH 21/250] Add pagination to the projects API --- diff --git a/pagure/api/__init__.py b/pagure/api/__init__.py index 2d20891..8eab66f 100644 --- a/pagure/api/__init__.py +++ b/pagure/api/__init__.py @@ -88,6 +88,7 @@ class APIERROR(enum.Enum): ENOGROUP = 'Group not found' ENOTMAINADMIN = 'Only the main admin can set the main admin of a project' EMODIFYPROJECTNOTALLOWED = 'You are not allowed to modify this project' + EINVALIDPERPAGEVALUE = 'The per_page value must be between 1 and 100' def get_authorized_api_project(SESSION, repo, user=None, namespace=None): diff --git a/pagure/api/project.py b/pagure/api/project.py index 5590a1a..aea1361 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -260,6 +260,10 @@ def api_projects(): GET /api/0/projects?tags=fedora-infra + :: + + GET /api/0/projects?page=1&per_page=50 + Parameters ^^^^^^^^^^ @@ -291,6 +295,16 @@ def api_projects(): | | | | entrie project JSON | | | | | or just a sub-set | +---------------+----------+---------------+--------------------------+ + | ``page`` | int | Optional | | Specifies that | + | | | | pagination should be | + | | | | turned on and that | + | | | | this specific page | + | | | | should be displayed | + +---------------+----------+---------------+--------------------------+ + | ``per_page`` | int | Optional | | The number of projects | + | | | | to return per page. | + | | | | The maximum is 100. | + +---------------+----------+---------------+--------------------------+ Sample response ^^^^^^^^^^^^^^^ @@ -378,6 +392,98 @@ def api_projects(): ] } + Sample Response With Pagination + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + :: + + { + "args": { + "fork": null, + "namespace": null, + "owner": null, + "page": 1, + "pattern": null, + "per_page": 2, + "short": false, + "tags": [], + "username": null + }, + "pagination": { + "first": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=1", + "last": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=500", + "next": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=2", + "page": 1, + "pages": 500, + "per_page": 2, + "prev": null + }, + "projects": [ + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": [ + "mprahl" + ], + "ticket": [] + }, + "close_status": [], + "custom_keys": [], + "date_created": "1498841289", + "description": "test1", + "fullname": "test1", + "id": 1, + "milestones": {}, + "name": "test1", + "namespace": null, + "parent": null, + "priorities": {}, + "tags": [], + "user": { + "fullname": "Matt Prahl", + "name": "mprahl" + } + }, + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": [ + "mprahl" + ], + "ticket": [] + }, + "close_status": [], + "custom_keys": [], + "date_created": "1499795310", + "description": "test2", + "fullname": "test2", + "id": 2, + "milestones": {}, + "name": "test2", + "namespace": null, + "parent": null, + "priorities": {}, + "tags": [], + "user": { + "fullname": "Matt Prahl", + "name": "mprahl" + } + } + ], + "total_projects": 1000 + } """ tags = flask.request.values.getlist('tags') username = flask.request.values.get('username', None) @@ -386,6 +492,8 @@ def api_projects(): owner = flask.request.values.get('owner', None) pattern = flask.request.values.get('pattern', None) short = flask.request.values.get('short', None) + page = flask.request.values.get('page', None) + per_page = flask.request.values.get('per_page', None) if str(fork).lower() in ['1', 'true']: fork = True @@ -400,9 +508,46 @@ def api_projects(): if authenticated() and username == flask.g.fas_user.username: private = flask.g.fas_user.username + project_count = pagure.lib.search_projects( + SESSION, username=username, fork=fork, tags=tags, pattern=pattern, + private=private, namespace=namespace, owner=owner, count=True) + # Pagination code inspired by Flask-SQLAlchemy + pagination_metadata = None + query_start = None + query_limit = None + if page: + try: + page = int(page) + except (TypeError, ValueError): + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDREQ) + + if page < 1: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDREQ) + + if per_page: + try: + per_page = int(per_page) + except (TypeError, ValueError): + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDREQ) + + if per_page < 1 or per_page > 100: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDPERPAGEVALUE) + else: + per_page = 20 + + pagination_metadata = pagure.lib.get_pagination_metadata( + flask.request, page, per_page, project_count) + query_start = (page - 1) * per_page + query_limit = per_page + projects = pagure.lib.search_projects( SESSION, username=username, fork=fork, tags=tags, pattern=pattern, - private=private, namespace=namespace, owner=owner) + private=private, namespace=namespace, owner=owner, limit=query_limit, + start=query_start) if not projects: raise pagure.exceptions.APIError( @@ -422,8 +567,8 @@ def api_projects(): for p in projects ] - jsonout = flask.jsonify({ - 'total_projects': len(projects), + jsonout = { + 'total_projects': project_count, 'projects': projects, 'args': { 'tags': tags, @@ -434,8 +579,12 @@ def api_projects(): 'owner': owner, 'short': short, } - }) - return jsonout + } + if pagination_metadata: + jsonout['args']['page'] = page + jsonout['args']['per_page'] = per_page + jsonout['pagination'] = pagination_metadata + return flask.jsonify(jsonout) @API.route('/') diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index c669604..b67068b 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -32,6 +32,8 @@ import urlparse import uuid import markdown import werkzeug +from math import ceil +import copy import bleach import redis @@ -43,6 +45,7 @@ from sqlalchemy import asc from sqlalchemy.orm import aliased from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import scoped_session +from flask import url_for import pagure import pagure.exceptions @@ -4271,3 +4274,56 @@ def set_project_owner(session, project, user): ''' project.user = user session.add(project) + + +def get_pagination_metadata(flask_request, page, per_page, total): + """ + Returns pagination metadata for an API. The code was inspired by + Flask-SQLAlchemy. + :param flask_request: flask.request object + :param page: int of the current page + :param per_page: int of results per page + :param total: int of total results + :return: dictionary of pagination metadata + """ + pages = int(ceil(total / float(per_page))) + request_args_wo_page = dict(copy.deepcopy(flask_request.args)) + # Remove pagination related args because those are handled elsewhere + # Also, remove any args that url_for accepts in case the user entered + # those in + for key in ['page', 'per_page', 'endpoint']: + if key in request_args_wo_page: + request_args_wo_page.pop(key) + for key in flask_request.args: + if key.startswith('_'): + request_args_wo_page.pop(key) + + next_page = None + if page < pages: + next_page = url_for( + flask_request.endpoint, page=page + 1, per_page=per_page, + _external=True, **request_args_wo_page) + + prev_page = None + if page > 1: + prev_page = url_for( + flask_request.endpoint, page=page - 1, per_page=per_page, + _external=True, **request_args_wo_page) + + first_page = url_for( + flask_request.endpoint, page=1, per_page=per_page, _external=True, + **request_args_wo_page) + + last_page = url_for( + flask_request.endpoint, page=pages, per_page=per_page, + _external=True, **request_args_wo_page) + + return { + 'page': page, + 'pages': pages, + 'per_page': per_page, + 'prev': prev_page, + 'next': next_page, + 'first': first_page, + 'last': last_page + } diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index 636d028..0747f8a 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -830,6 +830,266 @@ class PagureFlaskApiProjecttests(tests.Modeltests): } self.assertDictEqual(data, expected_data) + def test_api_projects_pagination(self): + """ Test the api_projects method of the flask api with pagination. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=1') + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + data['projects'][0]['date_created'] = "1436527638" + data['projects'][1]['date_created'] = "1436527638" + data['projects'][2]['date_created'] = "1436527638" + expected_data = { + "args": { + "fork": None, + "namespace": None, + "owner": None, + "page": 1, + "per_page": 20, + "pattern": None, + "short": False, + "tags": [], + "username": None + }, + "pagination": { + "first": "http://localhost/api/0/projects?per_page=20&page=1", + "last": "http://localhost/api/0/projects?per_page=20&page=1", + "next": None, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": None + }, + "projects": [ + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": []}, + "access_users": { + "admin": [], + "commit": [], + "owner": ["pingou"], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1436527638", + "description": "test project #1", + "fullname": "test", + "id": 1, + "milestones": {}, + "name": "test", + "namespace": None, + "parent": None, + "priorities": {}, + "tags": [], + "user": { + "fullname": "PY C", + "name": "pingou" + } + }, + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": ["pingou"], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1436527638", + "description": "test project #2", + "fullname": "test2", + "id": 2, + "milestones": {}, + "name": "test2", + "namespace": None, + "parent": None, + "priorities": {}, + "tags": [], + "user": { + "fullname": "PY C", + "name": "pingou" + } + }, + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": []}, + "access_users": { + "admin": [], + "commit": [], + "owner": ["pingou"], + "ticket": []}, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1436527638", + "description": "namespaced test project", + "fullname": "somenamespace/test3", + "id": 3, + "milestones": {}, + "name": "test3", + "namespace": "somenamespace", + "parent": None, + "priorities": {}, + "tags": [], + "user": { + "fullname": "PY C", + "name": "pingou" + } + } + ], + "total_projects": 3 + } + self.assertDictEqual(data, expected_data) + + def test_api_projects_pagination_per_page(self): + """ Test the api_projects method of the flask api with pagination and + the `per_page` argument set. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=2&per_page=2') + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + data['projects'][0]['date_created'] = "1436527638" + expected_data = { + "args": { + "fork": None, + "namespace": None, + "owner": None, + "page": 2, + "per_page": 2, + "pattern": None, + "short": False, + "tags": [], + "username": None + }, + "pagination": { + "first": "http://localhost/api/0/projects?per_page=2&page=1", + "last": "http://localhost/api/0/projects?per_page=2&page=2", + "next": None, + "page": 2, + "pages": 2, + "per_page": 2, + "prev": "http://localhost/api/0/projects?per_page=2&page=1", + }, + "projects": [ + { + "access_groups": { + "admin": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [], + "commit": [], + "owner": ["pingou"], + "ticket": [] + }, + "close_status": [ + "Invalid", + "Insufficient data", + "Fixed", + "Duplicate" + ], + "custom_keys": [], + "date_created": "1436527638", + "description": "namespaced test project", + "fullname": "somenamespace/test3", + "id": 3, + "milestones": {}, + "name": "test3", + "namespace": "somenamespace", + "parent": None, + "priorities": {}, + "tags": [], + "user": { + "fullname": "PY C", + "name": "pingou" + } + } + ], + "total_projects": 3 + } + self.assertDictEqual(data, expected_data) + + def test_api_projects_pagination_invalid_page(self): + """ Test the api_projects method of the flask api when an invalid page + value is entered. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=-3') + self.assertEqual(output.status_code, 400) + + def test_api_projects_pagination_invalid_page_str(self): + """ Test the api_projects method of the flask api when an invalid type + for the page value is entered. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=abcd') + self.assertEqual(output.status_code, 400) + + def test_api_projects_pagination_invalid_per_page_too_low(self): + """ Test the api_projects method of the flask api when a per_page + value is below 1. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=1&per_page=0') + self.assertEqual(output.status_code, 400) + error = json.loads(output.data) + self.assertEqual( + error['error'], 'The per_page value must be between 1 and 100') + + def test_api_projects_pagination_invalid_per_page_too_high(self): + """ Test the api_projects method of the flask api when a per_page + value is above 100. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=1&per_page=101') + self.assertEqual(output.status_code, 400) + error = json.loads(output.data) + self.assertEqual( + error['error'], 'The per_page value must be between 1 and 100') + + def test_api_projects_pagination_invalid_per_page_str(self): + """ Test the api_projects method of the flask api when an invalid type + for the per_page value is entered. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=1&per_page=abcd') + self.assertEqual(output.status_code, 400) + + def test_api_projects_pagination_beyond_last_page(self): + """ Test the api_projects method of the flask api when a page value + that is larger than the last page is entered. """ + tests.create_projects(self.session) + + output = self.app.get('/api/0/projects?page=99999') + self.assertEqual(output.status_code, 404) + def test_api_modify_project_main_admin(self): """ Test the api_modify_project method of the flask api when the request is to change the main_admin of the project. """ From dbd6dd1dedd6c6ff1c90dd48f66bea2b91b57621 Mon Sep 17 00:00:00 2001 From: Carlos Mogas da Silva Date: Jul 13 2017 20:58:16 +0000 Subject: [PATCH 22/250] Simplify diff calculation The old code had 2 diff atributions and I could not see any reason for them. Also, instead of getting the parent via revparse_single, just get it from the attributes. Merges https://pagure.io/pagure/pull-request/2422 --- diff --git a/pagure/lib/git.py b/pagure/lib/git.py index 8675109..dcf9fc3 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -51,10 +51,7 @@ def commit_to_patch(repo_obj, commits): patch = "" for cnt, commit in enumerate(commits): if commit.parents: - diff = commit.tree.diff_to_tree() - - parent = repo_obj.revparse_single('%s^' % commit.oid.hex) - diff = repo_obj.diff(parent, commit) + diff = repo_obj.diff(commit.parents[0], commit) else: # First commit in the repo diff = commit.tree.diff_to_tree(swap=True) diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index 9d6227d..7c273bb 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -620,8 +620,8 @@ def view_raw_file( data = repo_obj[content.oid].data else: if commit.parents: - diff = commit.tree.diff_to_tree() - + # We need to take this not so nice road to ensure that the + # identifier retrieved from the URL is actually valid try: parent = repo_obj.revparse_single('%s^' % identifier) diff = repo_obj.diff(parent, commit) @@ -750,10 +750,7 @@ def view_commit(repo, commitid, username=None, namespace=None): flask.abort(404, 'Commit not found') if commit.parents: - diff = commit.tree.diff_to_tree() - - parent = repo_obj.revparse_single('%s^' % commitid) - diff = repo_obj.diff(parent, commit) + diff = repo_obj.diff(commit.parents[0], commit) else: # First commit in the repo diff = commit.tree.diff_to_tree(swap=True) From 92dd7b064dc74dca2b04d00d5a1f70a9185bcab6 Mon Sep 17 00:00:00 2001 From: Clement Verna Date: Jul 13 2017 21:01:21 +0000 Subject: [PATCH 23/250] Show the inline comment in the PR's comments by default. Fixes #1685 Merges https://pagure.io/pagure/pull-request/2402 Signed-off-by: Clement Verna --- diff --git a/pagure/templates/pull_request.html b/pagure/templates/pull_request.html index 25771e4..a6f2891 100644 --- a/pagure/templates/pull_request.html +++ b/pagure/templates/pull_request.html @@ -535,7 +535,7 @@ {{ comment.date_created | humanize }}
- (Show) + (Hide)
{{ comment.comment }}
@@ -966,13 +966,13 @@ function show_merge_status(){ $(document).ready(function() { - $(".inline-comments").hide(); + $(".inline-comments").show(); $( ".inline-btn" ).click(function() { var msgid = $( this ).attr('data-id'); - if ($( this ).html() == 'Show') { - $( this ).html('Hide'); - } else { + if ($( this ).html() == 'Hide') { $( this ).html('Show'); + } else { + $( this ).html('Hide'); } $( '#inline-comment-' + msgid).toggle(); }); From e7ba58f3742a974a3a38d0c9c57ea3f0a87f72f0 Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Jul 14 2017 15:48:41 +0000 Subject: [PATCH 24/250] Fix the URL in the API documentation for creating a new project --- diff --git a/pagure/api/project.py b/pagure/api/project.py index aea1361..7b8ee4f 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -643,7 +643,7 @@ def api_new_project(): :: - POST /api/0//new + POST /api/0/new Input From 2e5a5bc25a5454cfa03f22f4f7400c1a25883f16 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 19:54:57 +0000 Subject: [PATCH 25/250] Flake8 fixes Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index aaa3f83..939e053 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -275,9 +275,9 @@ class Gitolite2Auth(GitAuthHelper): gitolite configuration file, used here to determine that a part of the configuration file should be cleaned at the top. :type preconf: None or str - :kwarg postconf: the content of the file to include at the bottom of the - gitolite configuration file, used here to determine that a part of - the configuration file should be cleaned at the bottom. + :kwarg postconf: the content of the file to include at the bottom of + the gitolite configuration file, used here to determine that a part + of the configuration file should be cleaned at the bottom. :type postconf: None or str """ diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 4d9ebdc..41a84f5 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -368,7 +368,8 @@ def fork(name, namespace, user_owner, user_forker, editbranch, editfile): pygit2.init_repository(docrepo, bare=True) if APP.config.get('TICKETS_FOLDER'): - ticketrepo = os.path.join(APP.config['TICKETS_FOLDER'], repo_to.path) + ticketrepo = os.path.join( + APP.config['TICKETS_FOLDER'], repo_to.path) if os.path.exists(ticketrepo): shutil.rmtree(forkreponame) shutil.rmtree(docrepo) From f341aa15222f4f2f69876d93ae5a5bd7a3139dfe Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 19:54:57 +0000 Subject: [PATCH 26/250] Let's check if there are no results rather than restricting on one result but be more flexible --- diff --git a/tests/__init__.py b/tests/__init__.py index eff48a2..daebe20 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -85,13 +85,13 @@ if os.environ.get('BUILD_ID')or os.environ.get('FAITOUT_URL'): pass -WAIT_REGEX = re.compile("""var _url = '(\/wait\/[a-z0-9-]+\?.*)'""") +WAIT_REGEX = re.compile("""var _url = '(\/wait\/[a-z0-9-]+\??.*)'""") def get_wait_target(html): """ This parses the window.location out of the HTML for the wait page. """ found = WAIT_REGEX.findall(html) - if len(found) != 1: + if len(found) == 0: raise Exception("Not able to get wait target in %s" % html) - return found[0] + return found[-1] def create_maybe_waiter(method, getter): From c58866a4ae4ad65caa01af10df88049b0f80e030 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 19:54:57 +0000 Subject: [PATCH 27/250] Rework how the count variable is included in the URL called --- diff --git a/pagure/templates/waiting.html b/pagure/templates/waiting.html index d539f41..61f50b5 100644 --- a/pagure/templates/waiting.html +++ b/pagure/templates/waiting.html @@ -58,7 +58,9 @@ function check_task_status(){ window.setTimeout(check_task_status, _delay); }, error: function() { - var _url = '{{ url_for("wait_task", taskid=task.id, prev=prev) }}&count=' + _cnt; + var _url = '{{ url_for("wait_task", taskid=task.id, prev=prev) | safe }}'; + _url += _url.contains('?') ? '&' : '?'; + _url += 'count=' + _cnt; console.log('Sending to ' + _url); window.location = _url; } From 636a73fe08c3c16a6a1dfb76798b0705cadd13ab Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 19:54:57 +0000 Subject: [PATCH 28/250] When forking a project, use the entire url instead of just a piece of it --- diff --git a/pagure/ui/fork.py b/pagure/ui/fork.py index 1c70831..1b5111f 100644 --- a/pagure/ui/fork.py +++ b/pagure/ui/fork.py @@ -901,7 +901,8 @@ def fork_project(repo, username=None, namespace=None): taskid, prev=flask.url_for( 'view_repo', repo=repo.name, - username=username, namespace=namespace + username=username, namespace=namespace, + _external=True ) ) except pagure.exceptions.PagureException as err: From e49b79c8cead734a8ca71eaf009acf5566d91682 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 19:54:57 +0000 Subject: [PATCH 29/250] Ugly but works, try to delete the folder twice Signed-off-by: Pierre-Yves Chibon --- diff --git a/tests/__init__.py b/tests/__init__.py index daebe20..9c962ed 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -255,7 +255,13 @@ class SimplePagureTest(unittest.TestCase): requests.get('%s/clean/%s' % (FAITOUT_URL, db_name)) # Remove testdir - shutil.rmtree(self.path) + try: + shutil.rmtree(self.path) + except: + # Sometimes there is a race condition that makes deleting the folder + # fail during the first attempt. So just try a second time if that's + # the case. + shutil.rmtree(self.path) self.path = None def get_csrf(self, url='/new', output=None): From 9f4434483ee2c000791cd1ca009d1cd2159e38be Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 20:07:37 +0000 Subject: [PATCH 30/250] Release 3.2 --- diff --git a/UPGRADING.rst b/UPGRADING.rst index 671ad9e..3fac462 100644 --- a/UPGRADING.rst +++ b/UPGRADING.rst @@ -1,6 +1,16 @@ Upgrading Pagure ================ +From 3.1 to 3.2 +--------------- + +While not being a bug fix, this release has no database schema changes. +However, this release breaks the plugin interface for auth introduced in 3.1 and +changed in 3.1. If you are using pagure-dist-git, make sure to upgrade to at +least 0.4. This interface will be considered stable in 3.4 and announced as +such. + + From 3.0 to 3.1 --------------- diff --git a/doc/contributors.rst b/doc/contributors.rst index acf20cf..a87b8c2 100644 --- a/doc/contributors.rst +++ b/doc/contributors.rst @@ -3,23 +3,23 @@ Contributors to pagure Pagure would be nothing without its contributors. -On July 4, 2017 (release 3.1), the list looks as follow: +On July 14, 2017 (release 3.2), the list looks as follow: ================= =========== Number of commits Contributor ================= =========== - 5317 Pierre-Yves Chibon + 5332 Pierre-Yves Chibon 191 Ryan Lerch 143 Vivek Anand 139 farhaanbukhsh 126 Patrick Uiterwijk 77 Farhaan Bukhsh - 62 Clement Verna + 65 Clement Verna 59 Johan Cwiklinski 47 Mark Reynolds 32 Pradeep CE (cep) 30 Lubomír Sedlář - 26 Matt Prahl + 28 Matt Prahl 22 rahul Bajaj 20 Jeremy Cline 19 Gaurav Kumar @@ -57,6 +57,7 @@ Number of commits Contributor 3 cep 3 skrzepto 3 tenstormavi + 2 Carlos Mogas da Silva 2 Daniel Mach 2 Michael Watters 2 Nuno Maltez diff --git a/files/pagure.spec b/files/pagure.spec index 09633db..f69cc1d 100644 --- a/files/pagure.spec +++ b/files/pagure.spec @@ -2,7 +2,7 @@ %distutils.sysconfig import get_python_lib; print (get_python_lib())")} Name: pagure -Version: 3.1 +Version: 3.2 Release: 1%{?dist} Summary: A git-centered forge @@ -384,6 +384,21 @@ install -m 644 pagure-loadjson/pagure_loadjson.service \ %changelog +* Fri Jul 14 2017 Pierre-Yves Chibon - 3.2-1 +- Update to 3.2 +- Use a decorator to check if a project has an issue tracker (Clement Verna) +- Optimize generating the gitolite configuration for group change +- Fix the issue_keys table for mysql +- Drop the load_from_disk script +- Fix next_url URL parameter on the login page not being used (Carlos Mogas da + Silva) +- Support configuration where there are no docs folder and no tickets folder +- Show all the projects a group has access to +- Add pagination to the projects API (Matt Prahl) +- Simplify diff calculation (Carlos Mogas da Silva) +- Show the inline comment in the PR's comments by default (Clement Verna) +- Fix the URL in the API documentation for creating a new project (Matt Prahl) + * Tue Jul 04 2017 Pierre-Yves Chibon - 3.1-1 - Update to 3.1 - Allow project-less API token to create new tickets diff --git a/pagure/__init__.py b/pagure/__init__.py index 2b780b9..078c5b0 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - (c) 2014-2015 - Copyright Red Hat Inc + (c) 2014-2017 - Copyright Red Hat Inc Authors: Pierre-Yves Chibon @@ -12,8 +12,8 @@ __requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] import pkg_resources # noqa: E402,F401 -__version__ = '3.1' -__api_version__ = '0.14' +__version__ = '3.2' +__api_version__ = '0.15' import datetime # noqa: E402 From e920d735bed335cb733df95fb87d84dd9b467e31 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 20:36:33 +0000 Subject: [PATCH 31/250] Fix the JS syntax on the wait page Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/templates/waiting.html b/pagure/templates/waiting.html index 61f50b5..e183e73 100644 --- a/pagure/templates/waiting.html +++ b/pagure/templates/waiting.html @@ -59,7 +59,7 @@ function check_task_status(){ }, error: function() { var _url = '{{ url_for("wait_task", taskid=task.id, prev=prev) | safe }}'; - _url += _url.contains('?') ? '&' : '?'; + _url += _url.includes('?') ? '&' : '?'; _url += 'count=' + _cnt; console.log('Sending to ' + _url); window.location = _url; From e460d34a63140a854d52ed07e8ecda5acbdb93a0 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 14 2017 20:45:09 +0000 Subject: [PATCH 32/250] Release 3.2.1 --- diff --git a/doc/contributors.rst b/doc/contributors.rst index a87b8c2..34506f9 100644 --- a/doc/contributors.rst +++ b/doc/contributors.rst @@ -3,12 +3,12 @@ Contributors to pagure Pagure would be nothing without its contributors. -On July 14, 2017 (release 3.2), the list looks as follow: +On July 14, 2017 (release 3.2.1), the list looks as follow: ================= =========== Number of commits Contributor ================= =========== - 5332 Pierre-Yves Chibon + 5334 Pierre-Yves Chibon 191 Ryan Lerch 143 Vivek Anand 139 farhaanbukhsh diff --git a/files/pagure.spec b/files/pagure.spec index f69cc1d..55a6289 100644 --- a/files/pagure.spec +++ b/files/pagure.spec @@ -2,7 +2,7 @@ %distutils.sysconfig import get_python_lib; print (get_python_lib())")} Name: pagure -Version: 3.2 +Version: 3.2.1 Release: 1%{?dist} Summary: A git-centered forge @@ -384,6 +384,9 @@ install -m 644 pagure-loadjson/pagure_loadjson.service \ %changelog +* Fri Jul 14 2017 Pierre-Yves Chibon - 3.2.1-1 +- Fix a syntax error on the JS in the wait page + * Fri Jul 14 2017 Pierre-Yves Chibon - 3.2-1 - Update to 3.2 - Use a decorator to check if a project has an issue tracker (Clement Verna) diff --git a/pagure/__init__.py b/pagure/__init__.py index 078c5b0..821d2e2 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -12,7 +12,7 @@ __requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] import pkg_resources # noqa: E402,F401 -__version__ = '3.2' +__version__ = '3.2.1' __api_version__ = '0.15' From c92108097e8ae4702c115ae4702b63d960838e75 Mon Sep 17 00:00:00 2001 From: Stefan Bühler Date: Jul 24 2017 10:26:46 +0000 Subject: [PATCH 33/250] Hide private repos in ssh too '@all' shouldn't have access to private repos, otherwise every user sees all private repositories. Add the corresponding unit-tests This commit fixes CVE-2017-1002151 Merges https://pagure.io/pagure/pull-request/2426 --- diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index 939e053..577b668 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -126,7 +126,7 @@ class Gitolite2Auth(GitAuthHelper): repos = '' config.append('repo %s%s' % (repos, project.fullname)) - if repos not in ['tickets/', 'requests/']: + if not project.private and repos not in ['tickets/', 'requests/']: config.append(' R = @all') if project.committer_groups: config.append(' RW+ = @%s' % ' @'.join( diff --git a/tests/test_pagure_lib_gitolite_config.py b/tests/test_pagure_lib_gitolite_config.py index e07de7f..184256d 100644 --- a/tests/test_pagure_lib_gitolite_config.py +++ b/tests/test_pagure_lib_gitolite_config.py @@ -736,6 +736,77 @@ repo requests/test self.assertIsNone(args[1].get('group')) self.assertEqual(args[1].get('project').fullname, 'test') + def test_write_gitolite_project_test_private(self): + """ Test the write_gitolite_acls function of pagure.lib.git with + a postconf set """ + + # Make the test project private + project = pagure.lib._get_project(self.session, 'test') + project.private = True + self.session.add(project) + self.session.commit() + + # Re-generate the gitolite config just for this project + helper = pagure.lib.git_auth.get_git_auth_helper('gitolite3') + helper.write_gitolite_acls( + self.session, + self.outputconf, + project=None, + ) + self.assertTrue(os.path.exists(self.outputconf)) + + with open(self.outputconf) as stream: + data = stream.read().decode('utf-8') + + exp = u"""@grp2 = foo +@grp = pingou +# end of groups + +repo test + RW+ = pingou + +repo docs/test + RW+ = pingou + +repo tickets/test + RW+ = pingou + +repo requests/test + RW+ = pingou + +repo test2 + R = @all + RW+ = pingou + +repo docs/test2 + R = @all + RW+ = pingou + +repo tickets/test2 + RW+ = pingou + +repo requests/test2 + RW+ = pingou + +repo somenamespace/test3 + R = @all + RW+ = pingou + +repo docs/somenamespace/test3 + R = @all + RW+ = pingou + +repo tickets/somenamespace/test3 + RW+ = pingou + +repo requests/somenamespace/test3 + RW+ = pingou + +# end of body +""" + #print data + self.assertEqual(data, exp) + if __name__ == '__main__': unittest.main(verbosity=2) From cd6e301af40b1c5f06d7e881463aceb78858f4e5 Mon Sep 17 00:00:00 2001 From: Clement Verna Date: Jul 24 2017 12:06:23 +0000 Subject: [PATCH 34/250] Add the date_modified to projects API This commit add the date_modified timestamp to the projects API, the date_modified timestamp is updated whenever the project's settings, project's group settings or the project's user settings are modified. Fixes #2412 Signed-off-by: Clement Verna Signed-off-by: Pierre-Yves Chibon --- diff --git a/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py b/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py new file mode 100644 index 0000000..aa43da6 --- /dev/null +++ b/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py @@ -0,0 +1,36 @@ +"""Add date_modified for project + +Revision ID: 8a5d68f74beb +Revises: 27a79ff0fb41 +Create Date: 2017-07-18 19:28:09.566997 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '8a5d68f74beb' +down_revision = '27a79ff0fb41' + + +def upgrade(): + ''' Add the column date_modified to the table projects. + ''' + op.add_column( + 'projects', + sa.Column('date_modified', sa.DateTime, nullable=True, + default=sa.func.now()) + ) + op.execute("UPDATE projects SET date_modified=date_created;") + + op.alter_column( + 'projects', + column_name='date_modified', + nullable=False, + exisiting_nullable=True) + + +def downgrade(): + ''' Remove the column date_modified from the table projects. + ''' + op.drop_column('projects', 'date_modified') diff --git a/pagure/api/project.py b/pagure/api/project.py index 7b8ee4f..a63b981 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -333,6 +333,7 @@ def api_projects(): "close_status": [], "custom_keys": [], "date_created": "1427441537", + "date_modified": "1427441537", "description": "A web-based calendar for Fedora", "milestones": {}, "namespace": null, diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index b67068b..b84d6d9 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -976,7 +976,9 @@ def add_user_to_project( if new_user_obj in project.users: access_obj = get_obj_access(session, project, new_user_obj) access_obj.access = access + project.date_modified = datetime.datetime.utcnow() session.add(access_obj) + session.add(project) session.flush() pagure.lib.notify.log( @@ -998,7 +1000,9 @@ def add_user_to_project( user_id=new_user_obj.id, access=access, ) + project.date_modified = datetime.datetime.utcnow() session.add(project_user) + session.add(project) # Make sure we won't have SQLAlchemy error before we continue session.flush() @@ -1067,6 +1071,8 @@ def add_group_to_project( access_obj = get_obj_access(session, project, group_obj) access_obj.access = access session.add(access_obj) + project.date_modified = datetime.datetime.utcnow() + session.add(project) session.flush() pagure.lib.notify.log( @@ -1090,6 +1096,8 @@ def add_group_to_project( ) session.add(project_group) # Make sure we won't have SQLAlchemy error before we continue + project.date_modified = datetime.datetime.utcnow() + session.add(project) session.flush() pagure.lib.notify.log( @@ -1738,6 +1746,7 @@ def update_project_settings(session, repo, settings, user): return 'No settings to change' else: repo.settings = new_settings + repo.date_modified = datetime.datetime.utcnow() session.add(repo) session.flush() diff --git a/pagure/lib/model.py b/pagure/lib/model.py index f37fd95..5191a91 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -371,7 +371,8 @@ class Project(BASE): date_created = sa.Column(sa.DateTime, nullable=False, default=datetime.datetime.utcnow) - + date_modified = sa.Column(sa.DateTime, nullable=False, + default=datetime.datetime.utcnow) parent = relation('Project', remote_side=[id], backref='forks') user = relation('User', foreign_keys=[user_id], remote_side=[User.id], backref='projects') @@ -821,6 +822,7 @@ class Project(BASE): 'parent': self.parent.to_json( public=public, api=api) if self.parent else None, 'date_created': self.date_created.strftime('%s'), + 'date_modified': self.date_modified.strftime('%s'), 'user': self.user.to_json(public=public), 'access_users': self.access_users_json, 'access_groups': self.access_groups_json, diff --git a/tests/test_pagure_flask_api_fork.py b/tests/test_pagure_flask_api_fork.py index 47b1719..1de1c79 100644 --- a/tests/test_pagure_flask_api_fork.py +++ b/tests/test_pagure_flask_api_fork.py @@ -89,7 +89,9 @@ class PagureFlaskApiForktests(tests.Modeltests): data['requests'][0]['date_created'] = '1431414800' data['requests'][0]['updated_on'] = '1431414800' data['requests'][0]['project']['date_created'] = '1431414800' + data['requests'][0]['project']['date_modified'] = '1431414800' data['requests'][0]['repo_from']['date_created'] = '1431414800' + data['requests'][0]['repo_from']['date_modified'] = '1431414800' data['requests'][0]['uid'] = '1431414800' data['requests'][0]['last_updated'] = '1431414800' expected_data = { @@ -131,6 +133,7 @@ class PagureFlaskApiForktests(tests.Modeltests): ], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project #1", "fullname": "test", "id": 1, @@ -165,6 +168,7 @@ class PagureFlaskApiForktests(tests.Modeltests): ], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project #1", "fullname": "test", "id": 1, @@ -201,7 +205,9 @@ class PagureFlaskApiForktests(tests.Modeltests): data2['requests'][0]['date_created'] = '1431414800' data2['requests'][0]['updated_on'] = '1431414800' data2['requests'][0]['project']['date_created'] = '1431414800' + data2['requests'][0]['project']['date_modified'] = '1431414800' data2['requests'][0]['repo_from']['date_created'] = '1431414800' + data2['requests'][0]['repo_from']['date_modified'] = '1431414800' data2['requests'][0]['uid'] = '1431414800' data2['requests'][0]['last_updated'] = '1431414800' self.assertDictEqual(data, data2) @@ -262,7 +268,9 @@ class PagureFlaskApiForktests(tests.Modeltests): data['date_created'] = '1431414800' data['updated_on'] = '1431414800' data['project']['date_created'] = '1431414800' + data['project']['date_modified'] = '1431414800' data['repo_from']['date_created'] = '1431414800' + data['repo_from']['date_modified'] = '1431414800' data['uid'] = '1431414800' data['last_updated'] = '1431414800' expected_data = { @@ -298,6 +306,7 @@ class PagureFlaskApiForktests(tests.Modeltests): ], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project #1", "fullname": "test", "id": 1, @@ -330,6 +339,7 @@ class PagureFlaskApiForktests(tests.Modeltests): "Duplicate"], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project #1", "fullname": "test", "id": 1, @@ -363,7 +373,9 @@ class PagureFlaskApiForktests(tests.Modeltests): data2 = json.loads(output.data) data2['date_created'] = '1431414800' data2['project']['date_created'] = '1431414800' + data2['project']['date_modified'] = '1431414800' data2['repo_from']['date_created'] = '1431414800' + data2['repo_from']['date_modified'] = '1431414800' data2['uid'] = '1431414800' data2['date_created'] = '1431414800' data2['updated_on'] = '1431414800' diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index 0747f8a..4191c89 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -243,6 +243,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -276,6 +277,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -367,6 +369,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -395,6 +398,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -417,8 +421,11 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" data['projects'][1]['date_created'] = "1436527638" + data['projects'][1]['date_modified'] = "1436527638" data['projects'][2]['date_created'] = "1436527638" + data['projects'][2]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -450,6 +457,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -484,6 +492,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #2", "fullname": "test2", "id": 2, @@ -518,6 +527,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, @@ -541,8 +551,11 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" data['projects'][1]['date_created'] = "1436527638" + data['projects'][1]['date_modified'] = "1436527638" data['projects'][2]['date_created'] = "1436527638" + data['projects'][2]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -573,6 +586,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -607,6 +621,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #2", "fullname": "test2", "id": 2, @@ -639,6 +654,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, @@ -662,6 +678,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -690,6 +707,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): "Duplicate"], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -712,6 +730,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -741,6 +760,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, @@ -795,6 +815,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['date_created'] = "1436527638" + data['date_modified'] = "1436527638" expected_data ={ "access_groups": { "admin": [], @@ -814,6 +835,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -837,9 +859,9 @@ class PagureFlaskApiProjecttests(tests.Modeltests): output = self.app.get('/api/0/projects?page=1') self.assertEqual(output.status_code, 200) data = json.loads(output.data) - data['projects'][0]['date_created'] = "1436527638" - data['projects'][1]['date_created'] = "1436527638" - data['projects'][2]['date_created'] = "1436527638" + for i in range(3): + data['projects'][i]['date_created'] = "1436527638" + data['projects'][i]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -881,6 +903,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #1", "fullname": "test", "id": 1, @@ -915,6 +938,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project #2", "fullname": "test2", "id": 2, @@ -947,6 +971,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, @@ -975,6 +1000,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" expected_data = { "args": { "fork": None, @@ -1017,6 +1043,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, @@ -1106,6 +1133,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['date_created'] = '1496338274' + data['date_modified'] = '1496338274' expected_output = { "access_groups": { "admin": [], @@ -1128,6 +1156,7 @@ class PagureFlaskApiProjecttests(tests.Modeltests): ], "custom_keys": [], "date_created": "1496338274", + "date_modified": "1496338274", "description": "test project #1", "fullname": "test", "id": 1, diff --git a/tests/test_pagure_flask_api_ui_private_repo.py b/tests/test_pagure_flask_api_ui_private_repo.py index bfed6b4..c218435 100644 --- a/tests/test_pagure_flask_api_ui_private_repo.py +++ b/tests/test_pagure_flask_api_ui_private_repo.py @@ -1021,6 +1021,7 @@ class PagurePrivateRepotest(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.get_data(as_text=True)) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" self.assertDictEqual( data, { @@ -1053,6 +1054,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project description", "id": 1, "milestones": {}, @@ -1075,6 +1077,7 @@ class PagurePrivateRepotest(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.get_data(as_text=True)) data['projects'][0]['date_created'] = "1436527638" + data['projects'][0]['date_modified'] = "1436527638" self.assertDictEqual( data, { @@ -1106,6 +1109,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1436527638", + "date_modified": "1436527638", "description": "test project description", "id": 1, "milestones": {}, @@ -1181,7 +1185,9 @@ class PagurePrivateRepotest(tests.Modeltests): data['requests'][0]['date_created'] = '1431414800' data['requests'][0]['updated_on'] = '1431414800' data['requests'][0]['project']['date_created'] = '1431414800' + data['requests'][0]['project']['date_modified'] = '1431414800' data['requests'][0]['repo_from']['date_created'] = '1431414800' + data['requests'][0]['repo_from']['date_modified'] = '1431414800' data['requests'][0]['uid'] = '1431414800' data['requests'][0]['last_updated'] = '1431414800' self.assertDictEqual( @@ -1223,6 +1229,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project description", "id": 1, "milestones": {}, @@ -1255,6 +1262,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project description", "id": 1, "milestones": {}, @@ -1292,7 +1300,9 @@ class PagurePrivateRepotest(tests.Modeltests): data2['requests'][0]['date_created'] = '1431414800' data2['requests'][0]['updated_on'] = '1431414800' data2['requests'][0]['project']['date_created'] = '1431414800' + data2['requests'][0]['project']['date_modified'] = '1431414800' data2['requests'][0]['repo_from']['date_created'] = '1431414800' + data2['requests'][0]['repo_from']['date_modified'] = '1431414800' data2['requests'][0]['uid'] = '1431414800' data2['requests'][0]['last_updated'] = '1431414800' self.assertDictEqual(data, data2) @@ -1304,7 +1314,9 @@ class PagurePrivateRepotest(tests.Modeltests): data['date_created'] = '1431414800' data['updated_on'] = '1431414800' data['project']['date_created'] = '1431414800' + data['project']['date_modified'] = '1431414800' data['repo_from']['date_created'] = '1431414800' + data['repo_from']['date_modified'] = '1431414800' data['uid'] = '1431414800' data['last_updated'] = '1431414800' self.assertDictEqual( @@ -1339,6 +1351,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project description", "id": 1, "milestones": {}, @@ -1371,6 +1384,7 @@ class PagurePrivateRepotest(tests.Modeltests): "close_status": [], "custom_keys": [], "date_created": "1431414800", + "date_modified": "1431414800", "description": "test project description", "id": 1, "milestones": {}, @@ -1404,7 +1418,9 @@ class PagurePrivateRepotest(tests.Modeltests): data2 = json.loads(output.get_data(as_text=True)) data2['date_created'] = '1431414800' data2['project']['date_created'] = '1431414800' + data2['project']['date_modified'] = '1431414800' data2['repo_from']['date_created'] = '1431414800' + data2['repo_from']['date_modified'] = '1431414800' data2['uid'] = '1431414800' data2['date_created'] = '1431414800' data2['updated_on'] = '1431414800' diff --git a/tests/test_pagure_flask_api_user.py b/tests/test_pagure_flask_api_user.py index ba487b2..572eec7 100644 --- a/tests/test_pagure_flask_api_user.py +++ b/tests/test_pagure_flask_api_user.py @@ -88,8 +88,11 @@ class PagureFlaskApiUSertests(tests.Modeltests): self.assertEqual(output.status_code, 200) data = json.loads(output.data) data['repos'][0]['date_created'] = "1490272832" + data['repos'][0]['date_modified'] = "1490272832" data['repos'][1]['date_created'] = "1490272832" + data['repos'][1]['date_modified'] = "1490272832" data['repos'][2]['date_created'] = "1490272832" + data['repos'][2]['date_modified'] = "1490272832" expected_data = { "forks": [], "repos": [ @@ -113,6 +116,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): ], "custom_keys": [], "date_created": "1490272832", + "date_modified": "1490272832", "description": "test project #1", "fullname": "test", "id": 1, @@ -160,6 +164,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): ], "custom_keys": [], "date_created": "1490272832", + "date_modified": "1490272832", "description": "test project #2", "fullname": "test2", "id": 2, @@ -206,6 +211,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): ], "custom_keys": [], "date_created": "1490272832", + "date_modified": "1490272832", "description": "namespaced test project", "fullname": "somenamespace/test3", "id": 3, diff --git a/tests/test_pagure_lib_git.py b/tests/test_pagure_lib_git.py index 8300527..d226b43 100644 --- a/tests/test_pagure_lib_git.py +++ b/tests/test_pagure_lib_git.py @@ -1850,7 +1850,7 @@ new file mode 100644 index 0000000..60f7480 --- /dev/null +++ b/456 -@@ -0,0 +1,126 @@ +@@ -0,0 +1,128 @@ +{ + "assignee": null, + "branch": "master", @@ -1881,6 +1881,7 @@ index 0000000..60f7480 + "close_status": [], + "custom_keys": [], + "date_created": null, ++ "date_modified": null, + "description": "test project for ticket", + "fullname": "test_ticket_repo", + "id": 1, @@ -1931,6 +1932,7 @@ index 0000000..60f7480 + "close_status": [], + "custom_keys": [], + "date_created": null, ++ "date_modified": null, + "description": "test project for ticket", + "fullname": "test_ticket_repo", + "id": 1, @@ -1998,6 +2000,9 @@ index 0000000..60f7480 elif 'date_created' in row: t = row.split(': ')[0] row = '%s: null,' % t + elif 'date_modified' in row: + t = row.split(': ')[0] + row = '%s: null,' % t elif 'last_updated' in row: t = row.split(': ')[0] row = '%s: null,' % t @@ -2010,7 +2015,7 @@ index 0000000..60f7480 row = '+++ b/456' npatch.append(row) patch = '\n'.join(npatch) - # print patch + print patch self.assertEqual(patch, exp) def test_update_ticket_from_git_no_priority(self): From f5d66fd3867f0d6e3551b362445d15c6d19e43a0 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 24 2017 12:21:16 +0000 Subject: [PATCH 35/250] Release 3.3 --- diff --git a/UPGRADING.rst b/UPGRADING.rst index 3fac462..b95c1a2 100644 --- a/UPGRADING.rst +++ b/UPGRADING.rst @@ -1,6 +1,23 @@ Upgrading Pagure ================ +From 3.2 to 3.3 +--------------- + +[SECURITY FIX]: The 3.3 release contains an important security fix. +If you are using the private project feature of pagure, the gitolite +configuration generated was still granting access to the private projects. This +made the private projects visible and accessible. +After updating to 3.3, ensure your gitolite configuration gets re-generated +(pagure-admin refresh-gitolite can help you with this). + + +The 3.3 release brings some adjustments to the database scheme. + +* Update the database schame using alembic: ``alembic upgrade head`` + + + From 3.1 to 3.2 --------------- diff --git a/doc/contributors.rst b/doc/contributors.rst index 34506f9..afed912 100644 --- a/doc/contributors.rst +++ b/doc/contributors.rst @@ -3,18 +3,18 @@ Contributors to pagure Pagure would be nothing without its contributors. -On July 14, 2017 (release 3.2.1), the list looks as follow: +On July 24, 2017 (release 3.3), the list looks as follow: ================= =========== Number of commits Contributor ================= =========== - 5334 Pierre-Yves Chibon + 5335 Pierre-Yves Chibon 191 Ryan Lerch 143 Vivek Anand 139 farhaanbukhsh 126 Patrick Uiterwijk 77 Farhaan Bukhsh - 65 Clement Verna + 66 Clement Verna 59 Johan Cwiklinski 47 Mark Reynolds 32 Pradeep CE (cep) @@ -88,6 +88,7 @@ Number of commits Contributor 1 Snehal Karale 1 Stanislav Laznicka 1 Stanislav Ochotnicky + 1 Stefan Bühler 1 Stephen Gallagher 1 Tiago M. Vieira 1 Vyacheslav Anzhiganov diff --git a/files/pagure.spec b/files/pagure.spec index 55a6289..5042acd 100644 --- a/files/pagure.spec +++ b/files/pagure.spec @@ -2,7 +2,7 @@ %distutils.sysconfig import get_python_lib; print (get_python_lib())")} Name: pagure -Version: 3.2.1 +Version: 3.3 Release: 1%{?dist} Summary: A git-centered forge @@ -384,6 +384,11 @@ install -m 644 pagure-loadjson/pagure_loadjson.service \ %changelog +* Mon Jul 24 2017 Pierre-Yves Chibon - 3.3-1 +- [SECURITY FIX] block private repo (read) access via ssh due to a bug on how we + generated the gitolite config - CVE-2017-1002151 (Stefan Bühler) +- Add the date_modified to projects (Clement Verna) + * Fri Jul 14 2017 Pierre-Yves Chibon - 3.2.1-1 - Fix a syntax error on the JS in the wait page diff --git a/pagure/__init__.py b/pagure/__init__.py index 821d2e2..a7afee0 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -12,7 +12,7 @@ __requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] import pkg_resources # noqa: E402,F401 -__version__ = '3.2.1' +__version__ = '3.3' __api_version__ = '0.15' From e440750ed8d4c44d66a8cf52a4835f2a1b8fb19b Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 24 2017 13:12:07 +0000 Subject: [PATCH 36/250] Fix typo in the alembic migration Signed-off-by: Pierre-Yves Chibon --- diff --git a/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py b/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py index aa43da6..d97d8c8 100644 --- a/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py +++ b/alembic/versions/8a5d68f74beb_add_date_modified_for_project.py @@ -27,7 +27,7 @@ def upgrade(): 'projects', column_name='date_modified', nullable=False, - exisiting_nullable=True) + existing_nullable=True) def downgrade(): From 86b4d24d15ad6d9c00ed27ab40715521abfdb0fc Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 24 2017 13:49:41 +0000 Subject: [PATCH 37/250] Release 3.3.1 --- diff --git a/doc/contributors.rst b/doc/contributors.rst index afed912..5a82d92 100644 --- a/doc/contributors.rst +++ b/doc/contributors.rst @@ -3,12 +3,12 @@ Contributors to pagure Pagure would be nothing without its contributors. -On July 24, 2017 (release 3.3), the list looks as follow: +On July 24, 2017 (release 3.3.1), the list looks as follow: ================= =========== Number of commits Contributor ================= =========== - 5335 Pierre-Yves Chibon + 5337 Pierre-Yves Chibon 191 Ryan Lerch 143 Vivek Anand 139 farhaanbukhsh diff --git a/files/pagure.spec b/files/pagure.spec index 5042acd..49c5491 100644 --- a/files/pagure.spec +++ b/files/pagure.spec @@ -2,7 +2,7 @@ %distutils.sysconfig import get_python_lib; print (get_python_lib())")} Name: pagure -Version: 3.3 +Version: 3.3.1 Release: 1%{?dist} Summary: A git-centered forge @@ -384,6 +384,10 @@ install -m 644 pagure-loadjson/pagure_loadjson.service \ %changelog +* Mon Jul 24 2017 Pierre-Yves Chibon - 3.3.1-1 +- Update to 3.3.1 +- Fix typo in the alembic migration present in 3.3 + * Mon Jul 24 2017 Pierre-Yves Chibon - 3.3-1 - [SECURITY FIX] block private repo (read) access via ssh due to a bug on how we generated the gitolite config - CVE-2017-1002151 (Stefan Bühler) diff --git a/pagure/__init__.py b/pagure/__init__.py index a7afee0..6383759 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -12,7 +12,7 @@ __requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] import pkg_resources # noqa: E402,F401 -__version__ = '3.3' +__version__ = '3.3.1' __api_version__ = '0.15' From c669d2b55d0b1b9608d160317770bad559839f34 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 25 2017 12:53:36 +0000 Subject: [PATCH 38/250] Fix layout breakage in the doc Signed-off-by: Pierre-Yves Chibon --- diff --git a/doc/usage/theming.rst b/doc/usage/theming.rst index 3e7b78e..f44d9ce 100644 --- a/doc/usage/theming.rst +++ b/doc/usage/theming.rst @@ -56,6 +56,8 @@ from. So what you want to do is replace this ``master.html`` by your own. * Adjust pagure's configuration file: +:: + + THEME_TEMPLATE_FOLDER='/var/www/mypaguretheme/templates' + THEME_STATIC_FOLDER='/var/www/mypaguretheme/static' From f7666ddf5a85b79f163867c4c41e142b1d265dd8 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 25 2017 16:47:32 +0000 Subject: [PATCH 39/250] Stop using readlines() to drop the trailing new line character Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index 577b668..b98d4ac 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -283,7 +283,7 @@ class Gitolite2Auth(GitAuthHelper): """ _log.info('Reading in the current configuration: %s', configfile) with open(configfile) as stream: - current_config = stream.readlines() + current_config = [line for line in stream] if preconfig: idx = None From de7a1a451a39b130da4540962d08541c27dea201 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 25 2017 16:47:32 +0000 Subject: [PATCH 40/250] Fix logging by properly formatting the message Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 41a84f5..22a376e 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -455,7 +455,7 @@ def merge_pull_request(name, namespace, user, requestid, user_merger): request = pagure.lib.search_pull_requests( session, project_id=project.id, requestid=requestid) _log.debug( - 'Merging pull-request: %/#%s', request.project.fullname, + 'Merging pull-request: %s/#%s', request.project.fullname, request.id) pagure.lib.git.merge_pull_request( session, request, user_merger, APP.config['REQUESTS_FOLDER']) From 3faca4cfd1acde7bd8e06403b02d6276856dcb50 Mon Sep 17 00:00:00 2001 From: Vivek Anand Date: Jul 27 2017 08:47:59 +0000 Subject: [PATCH 41/250] my_issues: fix issue count Signed-off-by: Vivek Anand --- diff --git a/pagure/templates/user_issues.html b/pagure/templates/user_issues.html index 28ec0f9..c25a0ad 100644 --- a/pagure/templates/user_issues.html +++ b/pagure/templates/user_issues.html @@ -19,10 +19,11 @@
{% for issues in (user.assigned_issues, user.issues) %} - {% if loop.index == 1 %} - Assigned Issues {{ issues | length }} + {% set outer_loop = loop %} + {% if outer_loop.index == 1 %} + Assigned Issues {% else %} - Opened Issues {{ issues | length }} + Opened Issues {% endif %} @@ -42,7 +43,7 @@ {% for issue in issues %} - \n ' - 'Create a new token\n', output.data) + 'Create a new token\n', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -1521,13 +1522,13 @@ class PagureFlaskApptests(tests.Modeltests): output = self.app.post('/settings/token/new', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Create token - Pagure', output.data) + 'Create token - Pagure', output.data.decode('utf-8')) self.assertIn( '
\n ' - 'Create a new token\n', output.data) + 'Create a new token\n', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) data = { 'acls': ['new_project'], @@ -1538,13 +1539,13 @@ class PagureFlaskApptests(tests.Modeltests): output = self.app.post('/settings/token/new', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Create token - Pagure', output.data) + 'Create token - Pagure', output.data.decode('utf-8')) self.assertIn( '
\n ' - 'Create a new token\n', output.data) + 'Create a new token\n', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) data = { 'acls': ['create_project', 'fork_project'], @@ -1556,12 +1557,12 @@ class PagureFlaskApptests(tests.Modeltests): '/settings/token/new', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'foo\'s settings - Pagure', output.data) + 'foo\'s settings - Pagure', output.data.decode('utf-8')) self.assertIn( '\n Token created\n', - output.data) + output.data.decode('utf-8')) self.assertEqual( - output.data.count( + output.data.decode('utf-8').count( 'Valid' ' until: '), 1) @@ -1580,7 +1581,7 @@ class PagureFlaskApptests(tests.Modeltests): # Token doesn't exist output = self.app.post('/settings/token/revoke/foobar') self.assertEqual(output.status_code, 404) - self.assertTrue('

Page not found (404)

' in output.data) + self.assertTrue('

Page not found (404)

' in output.data.decode('utf-8')) # Create the foobar API token but associated w/ the user 'foo' item = pagure.lib.model.Token( @@ -1595,7 +1596,7 @@ class PagureFlaskApptests(tests.Modeltests): # Token not associated w/ this user output = self.app.post('/settings/token/revoke/foobar') self.assertEqual(output.status_code, 404) - self.assertTrue('

Page not found (404)

' in output.data) + self.assertTrue('

Page not found (404)

' in output.data.decode('utf-8')) user.username = 'foo' with tests.user_set(pagure.APP, user): @@ -1604,13 +1605,13 @@ class PagureFlaskApptests(tests.Modeltests): '/settings/token/revoke/foobar', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - "foo's settings - Pagure", output.data) + "foo's settings - Pagure", output.data.decode('utf-8')) self.assertEqual( - output.data.count( + output.data.decode('utf-8').count( 'Valid' ' until: '), 1) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -1623,9 +1624,9 @@ class PagureFlaskApptests(tests.Modeltests): follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - "foo's settings - Pagure", output.data) + "foo's settings - Pagure", output.data.decode('utf-8')) self.assertEqual( - output.data.count( + output.data.decode('utf-8').count( 'Valid' ' until: '), 0) @@ -1639,9 +1640,9 @@ class PagureFlaskApptests(tests.Modeltests): follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - "foo's settings - Pagure", output.data) + "foo's settings - Pagure", output.data.decode('utf-8')) self.assertEqual( - output.data.count( + output.data.decode('utf-8').count( 'Valid' ' until: '), 0) diff --git a/tests/test_pagure_flask_ui_app_give_project.py b/tests/test_pagure_flask_ui_app_give_project.py index 2335694..8787d1a 100644 --- a/tests/test_pagure_flask_ui_app_give_project.py +++ b/tests/test_pagure_flask_ui_app_give_project.py @@ -80,7 +80,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertEqual(output.status_code, 200) self.assertIn( 'Overview - test - Pagure', - output.data) + output.data.decode('utf-8')) self._check_user() @@ -105,7 +105,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertEqual(output.status_code, 404) self.assertIn( '

No such user foobar found

', - output.data) + output.data.decode('utf-8')) self._check_user() @@ -130,7 +130,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertEqual(output.status_code, 403) self.assertIn( '

You are not allowed to change the settings for this ' - 'project

', output.data) + 'project

', output.data.decode('utf-8')) self._check_user() @@ -155,7 +155,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertEqual(output.status_code, 403) self.assertIn( '

You are not allowed to change the settings for this ' - 'project

', output.data) + 'project

', output.data.decode('utf-8')) self._check_user() @@ -191,7 +191,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertEqual(output.status_code, 403) self.assertIn( '

You are not allowed to give this project

', - output.data) + output.data.decode('utf-8')) self._check_user() @@ -225,7 +225,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertIn( '\n The project has been ' 'transferred to foo\n', - output.data) + output.data.decode('utf-8')) self._check_user('foo') @@ -253,7 +253,7 @@ class PagureFlaskGiveRepotests(tests.SimplePagureTest): self.assertIn( '\n The project has been ' 'transferred to foo\n', - output.data) + output.data.decode('utf-8')) self._check_user('foo') # Make sure that the user giving the project is still an admin diff --git a/tests/test_pagure_flask_ui_fork.py b/tests/test_pagure_flask_ui_fork.py index 62d52d5..9a6aa1c 100644 --- a/tests/test_pagure_flask_ui_fork.py +++ b/tests/test_pagure_flask_ui_fork.py @@ -270,16 +270,16 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n

', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 2a552b">sources', output.data) + 'title="View file as of 2a552b">sources', output.data.decode('utf-8')) # Test if the `open changed file icon` is displayed. self.assertIn( 'class="open_changed_file_icon_wrap">' - '', output.data) + '', output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_merge_request_pull_FF(self, send_email): @@ -301,12 +301,12 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n

', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 2a552b">sources', output.data) + 'title="View file as of 2a552b">sources', output.data.decode('utf-8')) # Wrong project data = { @@ -360,14 +360,14 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n This request must be ' - 'assigned to be merged', output.data) + 'assigned to be merged', output.data.decode('utf-8')) # PR assigned but not to this user repo = pagure.get_authorized_project(self.session, 'test') @@ -382,10 +382,10 @@ class PagureFlaskForktests(tests.Modeltests): self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Only the assignee can ' - 'merge this review', output.data) + 'merge this review', output.data.decode('utf-8')) # Project w/ minimal PR score settings['Only_assignee_can_merge_pull-request'] = False @@ -400,11 +400,11 @@ class PagureFlaskForktests(tests.Modeltests): self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n This request does not ' 'have the minimum review score necessary to be merged', - output.data) + output.data.decode('utf-8')) # Merge settings['Minimum_score_to_merge_pull-request'] = -1 @@ -415,13 +415,13 @@ class PagureFlaskForktests(tests.Modeltests): '/test/pull-request/1/merge', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( - 'A commit on branch feature', output.data) + 'A commit on branch feature', output.data.decode('utf-8')) self.assertNotIn( - 'Merge #1 `PR from the feature branch`', output.data) + 'Merge #1 `PR from the feature branch`', output.data.decode('utf-8')) # Ensure we have the new commit - commits = _get_commits(output.data) + commits = _get_commits(output.data.decode('utf-8')) self.assertEqual( commits, [ @@ -434,7 +434,7 @@ class PagureFlaskForktests(tests.Modeltests): output = self.app.get('/test/pull-request/1') self.assertIn( '

Pull-Request has been merged by pingou

', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_merge_request_pull_merge(self, send_email): @@ -464,13 +464,13 @@ class PagureFlaskForktests(tests.Modeltests): '/test/pull-request/1/merge', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) # Check if the closing notification was added output = self.app.get('/test/pull-request/1') self.assertIn( '

Pull-Request has been merged by pingou

', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_merge_request_pull_conflicts(self, send_email): @@ -502,8 +502,8 @@ class PagureFlaskForktests(tests.Modeltests): self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) - self.assertIn('Merge conflicts!', output.data) + output.data.decode('utf-8')) + self.assertIn('Merge conflicts!', output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_merge_request_pull_nochange(self, send_email): @@ -535,15 +535,15 @@ class PagureFlaskForktests(tests.Modeltests): self.assertIn( '

PR#1\n' ' Merged', - output.data) + output.data.decode('utf-8')) self.assertIn('Nothing to do, changes were already merged', - output.data) + output.data.decode('utf-8')) # Check if the closing notification was added output = self.app.get('/test/pull-request/1') self.assertIn( '

Pull-Request has been merged by pingou

', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_request_pull_close(self, send_email): @@ -556,10 +556,10 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

PR#1\n' - ' ', output.data) - self.assertIn('
Merged by\n', output.data) + ' ', output.data.decode('utf-8')) + self.assertIn('
Merged by\n', output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 2a552b">sources', output.data) + 'title="View file as of 2a552b">sources', output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_request_pull_disabled(self, send_email): @@ -660,9 +660,9 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n

', output.data) + ' PR from the feature branch\n

', output.data.decode('utf-8')) self.assertTrue( - output.data.count('Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n No branch from which to pull ' - 'or local PR reference were found', output.data) + 'or local PR reference were found', output.data.decode('utf-8')) shutil.rmtree(newpath) @@ -745,14 +745,14 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

\n 0 Pull Requests (of 0)\n

', - output.data) + output.data.decode('utf-8')) # Open is primary self.assertIn( 'Open', output.data) + 'href="/test/pull-requests">Open', output.data.decode('utf-8')) self.assertIn( 'Closed', output.data) + 'href="/test/pull-requests?status=0">Closed', output.data.decode('utf-8')) self.set_up_git_repo(new_project=None, branch_from='feature') @@ -760,40 +760,40 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

\n 1 Pull Requests (of 1)\n

', - output.data) + output.data.decode('utf-8')) # Open is primary self.assertIn( 'Open', output.data) + 'href="/test/pull-requests">Open', output.data.decode('utf-8')) self.assertIn( 'Closed', output.data) + 'href="/test/pull-requests?status=0">Closed', output.data.decode('utf-8')) output = self.app.get('/test/pull-requests?status=Closed') self.assertEqual(output.status_code, 200) self.assertIn( '

\n 0 Closed Pull Requests (of 0)\n

', - output.data) + output.data.decode('utf-8')) # Close is primary self.assertIn( 'Open', output.data) + 'href="/test/pull-requests">Open', output.data.decode('utf-8')) self.assertIn( 'Closed', output.data) + 'href="/test/pull-requests?status=0">Closed', output.data.decode('utf-8')) output = self.app.get('/test/pull-requests?status=0') self.assertEqual(output.status_code, 200) self.assertIn( '

\n 0 Closed/Merged Pull Requests (of 0)\n

', - output.data) + output.data.decode('utf-8')) # Close is primary self.assertIn( 'Open', output.data) + 'href="/test/pull-requests">Open', output.data.decode('utf-8')) self.assertIn( 'Closed', output.data) + 'href="/test/pull-requests?status=0">Closed', output.data.decode('utf-8')) # Project w/o pull-request repo = pagure.get_authorized_project(self.session, 'test') @@ -827,7 +827,7 @@ class PagureFlaskForktests(tests.Modeltests): self.assertEqual(output.status_code, 200) npatch = [] - for row in output.data.split('\n'): + for row in output.data.decode('utf-8').split('\n'): if row.startswith('Date:'): continue if row.startswith('From '): @@ -890,7 +890,7 @@ index 9f44358..2a552bb 100644 self.assertEqual(output.status_code, 200) npatch = [] - for row in output.data.split('\n'): + for row in output.data.decode('utf-8').split('\n'): if row.startswith('Date:'): continue if row.startswith('From '): @@ -1004,7 +1004,7 @@ index 9f44358..2a552bb 100644 self.assertEqual(output.status_code, 200) npatch = [] - for row in output.data.split('\n'): + for row in output.data.decode('utf-8').split('\n'): if row.startswith('Date:'): continue if row.startswith('From '): @@ -1093,10 +1093,10 @@ index 0000000..2a552bb output = self.app.get('/test/pull-request/1.patch', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n No branch from which to pull ' - 'or local PR reference were found', output.data) + 'or local PR reference were found', output.data.decode('utf-8')) shutil.rmtree(newpath) @@ -1120,10 +1120,10 @@ index 0000000..2a552bb '/test/pull-request/cancel/1', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n Invalid input submitted', - output.data) + output.data.decode('utf-8')) output = self.app.get('/test/pull-request/1') self.assertEqual(output.status_code, 200) @@ -1180,10 +1180,10 @@ index 0000000..2a552bb follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n Pull request canceled!', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_set_assignee_requests(self, send_email): @@ -1211,13 +1211,13 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertNotIn( '\n Request assigned', - output.data) + output.data.decode('utf-8')) output = self.app.get('/test/pull-request/1') self.assertEqual(output.status_code, 200) @@ -1235,13 +1235,13 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertNotIn( '\n Request assigned', - output.data) + output.data.decode('utf-8')) # Invalid assignee data = { @@ -1255,13 +1255,13 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertIn( '\n No user "bar" found', - output.data) + output.data.decode('utf-8')) # Assign the PR data = { @@ -1284,13 +1284,13 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n', output.data) + ' PR from the feature branch\n', output.data.decode('utf-8')) self.assertIn( '\n Request assigned', - output.data) + output.data.decode('utf-8')) # Pull-Request closed repo = pagure.get_authorized_project(self.session, 'test') @@ -1336,7 +1336,7 @@ index 0000000..2a552bb output = self.app.get('/new/') self.assertEqual(output.status_code, 200) - self.assertIn('Create new Project', output.data) + self.assertIn('Create new Project', output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1393,18 +1393,18 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'Diff from master to feature - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( - '

No commits found

', output.data) + '

No commits found

', output.data.decode('utf-8')) output = self.app.get('/test/diff/master..feature') self.assertEqual(output.status_code, 200) self.assertIn( 'Diff from feature to master - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertNotIn( '', output.data) + 'value="Create">', output.data.decode('utf-8')) user.username = 'pingou' with tests.user_set(pagure.APP, user): @@ -1412,10 +1412,10 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'Create new Pull Request for master - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1432,15 +1432,15 @@ index 0000000..2a552bb self.assertIn( 'PR#2: foo bar PR - test\n - Pagure', output.data) - self.assertIn('

Test Initial Comment

', output.data) - self.assertEqual(output.data.count('title="PY C (pingou)"'), 1) + self.assertIn('

Test Initial Comment

', output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('title="PY C (pingou)"'), 1) # Test if the `open changed file icon` is displayed. self.assertIn( 'class="open_changed_file_icon_wrap">' - '', output.data) + '', output.data.decode('utf-8')) # Case 2 - Add an empty initial comment data = { @@ -1454,8 +1454,8 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#3: foo bar PR - test\n - Pagure', - output.data) - self.assertNotIn('
Create new Pull Request for master - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1499,8 +1499,8 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#2: foo bar PR - test\n - Pagure', - output.data) - self.assertIn('

Test Initial Comment

', output.data) + output.data.decode('utf-8')) + self.assertIn('

Test Initial Comment

', output.data.decode('utf-8')) # Check if commit start and stop have been set for PR#2 request = pagure.lib.search_pull_requests( @@ -1539,7 +1539,7 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 400) self.assertIn( '

No branch from which to pull or local PR reference ' - 'were found

', output.data) + 'were found

', output.data.decode('utf-8')) output = self.app.get('/test/new_issue') csrf_token = self.get_csrf(output=output) @@ -1554,7 +1554,7 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 400) self.assertIn( '

No branch from which to pull or local PR reference ' - 'were found

', output.data) + 'were found

', output.data.decode('utf-8')) shutil.rmtree(newpath) @@ -1589,7 +1589,7 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 400) self.assertIn( '

No branch from which to pull or local PR reference ' - 'were found

', output.data) + 'were found

', output.data.decode('utf-8')) shutil.rmtree(newpath) @@ -1612,7 +1612,7 @@ index 0000000..2a552bb output = self.app.post('/test/pull-request/1/comment') self.assertEqual(output.status_code, 200) self.assertTrue( - output.data.startswith('\n
')) + output.data.decode('utf-8').startswith('\n
')) csrf_token = self.get_csrf(output=output) @@ -1626,11 +1626,11 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '\n Comment added', output.data) - self.assertEqual(output.data.count('title="PY C (pingou)"'), 2) + self.assertEqual(output.data.decode('utf-8').count('title="PY C (pingou)"'), 2) # Project w/o pull-request repo = pagure.get_authorized_project(self.session, 'test') @@ -1673,11 +1673,11 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n

', output.data) - #self.assertIn('href="#comment-1">¶', output.data) + ' PR from the feature branch\n

', output.data.decode('utf-8')) + #self.assertIn('href="#comment-1">¶', output.data.decode('utf-8')) self.assertIn( '

This look alright but we can do better

', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1707,10 +1707,10 @@ index 0000000..2a552bb self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment removed', - output.data) + output.data.decode('utf-8')) # Project w/o pull-request repo = pagure.get_authorized_project(self.session, 'test') @@ -1747,7 +1747,7 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) # Creating comment to play with self.assertTrue( - output.data.startswith('\n
')) + output.data.decode('utf-8').startswith('\n
')) csrf_token = self.get_csrf(output=output) @@ -1763,20 +1763,20 @@ index 0000000..2a552bb self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment added', - output.data) + output.data.decode('utf-8')) # Check if the comment is there self.assertIn( - '

This look alright but we can do better

', output.data) + '

This look alright but we can do better

', output.data.decode('utf-8')) output = self.app.get('/test/pull-request/1/comment/1/edit') self.assertEqual(output.status_code, 200) - self.assertIn('
', output.data) + self.assertIn('
', output.data.decode('utf-8')) # Checking if the comment is there in the update page self.assertIn( - 'This look alright but we can do better', output.data) + 'This look alright but we can do better', output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1789,20 +1789,20 @@ index 0000000..2a552bb follow_redirects=True) # Checking if the comment is updated in the main page self.assertIn( - '

This look alright but we can do better than this.

', output.data) + '

This look alright but we can do better than this.

', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' ' PR from the feature branch\n ', - output.data) + output.data.decode('utf-8')) # Checking if Edited by User is there or not self.assertTrue( 'Edited just now by pingou ' - in output.data + in output.data.decode('utf-8') or 'Edited seconds ago by pingou ' - in output.data) + in output.data.decode('utf-8')) self.assertIn( - '\n Comment updated', output.data) + '\n Comment updated', output.data.decode('utf-8')) # Project w/o pull-request repo = pagure.get_authorized_project(self.session, 'test') @@ -1839,12 +1839,12 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'PR#1: PR from the feature branch - test\n - ' - 'Pagure', output.data) + 'Pagure', output.data.decode('utf-8')) self.assertIn( '

PR#1\n' - ' PR from the feature branch\n

', output.data) + ' PR from the feature branch\n

', output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 2a552b">sources', output.data) + 'title="View file as of 2a552b">sources', output.data.decode('utf-8')) # Wrong project data = { @@ -1886,13 +1886,13 @@ index 0000000..2a552bb '/test/pull-request/1/merge', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( - 'Merge #1 `PR from the feature branch`', output.data) + 'Merge #1 `PR from the feature branch`', output.data.decode('utf-8')) self.assertIn( - 'A commit on branch feature', output.data) + 'A commit on branch feature', output.data.decode('utf-8')) # Ensure we have the merge commit - commits = _get_commits(output.data) + commits = _get_commits(output.data.decode('utf-8')) self.assertEqual(commits, [ 'Merge #1 `PR from the feature branch`', 'Add sources file for testing', @@ -1903,7 +1903,7 @@ index 0000000..2a552bb output = self.app.get('/test/pull-request/1') self.assertIn( '

Pull-Request has been merged by pingou

', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_internal_endpoint_main_ahead(self, send_email): @@ -2012,16 +2012,14 @@ index 0000000..2a552bb user.username = 'foo' with tests.user_set(pagure.APP, user): - output = self.app.get('/new') - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf() output = self.app.post( '/pv/pull-request/ready', data={'repo': 'test', 'csrf_token': csrf_token} ) self.assertEqual(output.status_code, 200) - data = json.loads(output.data) + data = json.loads(output.data.decode('utf-8')) self.assertEqual( data, { @@ -2063,7 +2061,7 @@ index 0000000..2a552bb output = self.app.get('/new/') self.assertEqual(output.status_code, 200) - self.assertIn('Create new Project', output.data) + self.assertIn('Create new Project', output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2105,14 +2103,14 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'Fork and Edit\n \n', - output.data) + output.data.decode('utf-8')) # Check fork-edit doesn't show for binary files output = self.app.get('/test/blob/master/f/test.jpg') self.assertEqual(output.status_code, 200) self.assertNotIn( 'Fork and Edit\n \n', - output.data) + output.data.decode('utf-8')) # Check for edit panel output = self.app.post('fork_edit/test/edit/master/f/sources', @@ -2124,23 +2122,23 @@ index 0000000..2a552bb '  master' '
  • ' '  sources
  • ', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # View what's supposed to be an image output = self.app.post('fork_edit/test/edit/master/f/test.jpg', data=data, follow_redirects=True) self.assertEqual(output.status_code, 400) - self.assertIn('

    Cannot edit binary files

    ', output.data) + self.assertIn('

    Cannot edit binary files

    ', output.data.decode('utf-8')) # Check fork-edit shows when user is not logged in output = self.app.get('/test/blob/master/f/sources') self.assertEqual(output.status_code, 200) self.assertIn( 'Fork and Edit\n \n', - output.data) + output.data.decode('utf-8')) # Check if fork-edit shows for different user user.username = 'pingou' @@ -2151,14 +2149,14 @@ index 0000000..2a552bb self.assertEqual(output.status_code, 200) self.assertIn( 'Fork and Edit\n \n', - output.data) + output.data.decode('utf-8')) # Check fork-edit doesn't show for binary output = self.app.get('/test/blob/master/f/test.jpg') self.assertEqual(output.status_code, 200) self.assertNotIn( 'Fork and Edit\n \n', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.notify.send_email') def test_fork_without_main_repo(self, send_email): @@ -2210,12 +2208,12 @@ index 0000000..2a552bb output = self.app.get('/fork/foo/test') self.assertEqual(output.status_code, 200) print output.data - self.assertIn('Fork from a deleted repository\n', output.data) + self.assertIn('Fork from a deleted repository\n', output.data.decode('utf-8')) # Testing commit endpoint output = self.app.get('/fork/foo/test/commits/master') self.assertEqual(output.status_code, 200) - self.assertIn('Commits 2\n

    \n', output.data) + self.assertIn('Commits 2\n

    \n', output.data.decode('utf-8')) # Test pull-request endpoint output = self.app.get('/fork/foo/test/pull-requests') diff --git a/tests/test_pagure_flask_ui_groups.py b/tests/test_pagure_flask_ui_groups.py index 36b7994..601b3c0 100644 --- a/tests/test_pagure_flask_ui_groups.py +++ b/tests/test_pagure_flask_ui_groups.py @@ -47,7 +47,7 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertIn( '

    \n' ' Groups 0', - output.data) + output.data.decode('utf-8')) def test_add_group_index_auth(self): """ Test the presence of the add group button on the front page. """ @@ -84,11 +84,11 @@ class PagureFlaskGroupstests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/group/add') self.assertEqual(output.status_code, 200) - self.assertIn('Create new group', output.data) + self.assertIn('Create new group', output.data.decode('utf-8')) self.assertNotIn( - '', output.data) + '', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -97,8 +97,8 @@ class PagureFlaskGroupstests(tests.Modeltests): # Insufficient input output = self.app.post('/group/add', data=data) self.assertEqual(output.status_code, 200) - self.assertIn('Create new group', output.data) - self.assertEqual(output.data.count( + self.assertIn('Create new group', output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( 'This field is required.'), 3) data = { @@ -110,8 +110,8 @@ class PagureFlaskGroupstests(tests.Modeltests): # Missing CSRF output = self.app.post('/group/add', data=data) self.assertEqual(output.status_code, 200) - self.assertIn('Create new group', output.data) - self.assertEqual(output.data.count( + self.assertIn('Create new group', output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( 'This field is required.'), 0) data['csrf_token'] = csrf_token @@ -122,14 +122,14 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n User `pingou` added to ' - 'the group `test_group`.', output.data) + 'the group `test_group`.', output.data.decode('utf-8')) self.assertIn( '\n Group `test_group` created.', - output.data) + output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 1', - output.data) + output.data.decode('utf-8')) user = tests.FakeUser( username='pingou', @@ -137,8 +137,8 @@ class PagureFlaskGroupstests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/group/add') self.assertEqual(output.status_code, 200) - self.assertIn('Create new group', output.data) - self.assertIn('', output.data) + self.assertIn('Create new group', output.data.decode('utf-8')) + self.assertIn('', output.data.decode('utf-8')) data = { 'group_name': 'test_admin_group', @@ -154,14 +154,14 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n User `pingou` added to ' - 'the group `test_admin_group`.', output.data) + 'the group `test_admin_group`.', output.data.decode('utf-8')) self.assertIn( '\n Group `test_admin_group` ' - 'created.',output.data) + 'created.',output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 2', - output.data) + output.data.decode('utf-8')) def test_edit_group(self): """ Test the edit_group endpoint. """ @@ -173,7 +173,7 @@ class PagureFlaskGroupstests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/group/test_group/edit') self.assertEqual(output.status_code, 404) - self.assertIn('

    Group not found

    ', output.data) + self.assertIn('

    Group not found

    ', output.data.decode('utf-8')) self.test_add_group() @@ -181,21 +181,21 @@ class PagureFlaskGroupstests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/group/foo/edit') self.assertEqual(output.status_code, 404) - self.assertIn('

    Group not found

    ', output.data) + self.assertIn('

    Group not found

    ', output.data.decode('utf-8')) output = self.app.get('/group/test_group/edit') self.assertEqual(output.status_code, 200) self.assertIn( 'Edit group: test_group - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '', output.data) + '', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] # Missing CSRF @@ -207,17 +207,17 @@ class PagureFlaskGroupstests(tests.Modeltests): output = self.app.post( '/group/test_group/edit', data=data, follow_redirects=True) - #print output.data + #print output.data.decode('utf-8') self.assertEqual(output.status_code, 200) self.assertIn( 'Edit group: test_group - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( '', output.data) + '', output.data.decode('utf-8')) # User not allowed data['csrf_token'] = csrf_token @@ -227,13 +227,13 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Group test_group - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n You are not ' - 'allowed to edit this group', output.data) + 'allowed to edit this group', output.data.decode('utf-8')) self.assertIn( ' ' - ' Test Group', output.data) + ' Test Group', output.data.decode('utf-8')) user.username = 'pingou' with tests.user_set(pagure.APP, user): @@ -241,19 +241,19 @@ class PagureFlaskGroupstests(tests.Modeltests): output = self.app.post( '/group/bar/edit', data=data, follow_redirects=True) self.assertEqual(output.status_code, 404) - self.assertIn('

    Group not found

    ', output.data) + self.assertIn('

    Group not found

    ', output.data.decode('utf-8')) output = self.app.post( '/group/test_group/edit', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Group test_group - Pagure', output.data) + 'Group test_group - Pagure', output.data.decode('utf-8')) self.assertIn( ' ' - ' Test Group', output.data) + ' Test Group', output.data.decode('utf-8')) self.assertIn( 'Group "Test Group edited" (test_group) edited', - output.data) + output.data.decode('utf-8')) def test_group_delete(self): """ Test the group_delete endpoint. """ @@ -266,11 +266,11 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '

    No groups have been created on this pagure instance ' - 'yet

    ', output.data) + 'yet

    ', output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 0', - output.data) + output.data.decode('utf-8')) self.test_add_group() @@ -280,10 +280,10 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertIn( '

    \n' ' Groups 1', - output.data) + output.data.decode('utf-8')) output = self.app.get('/new/') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] user.username = 'foo' @@ -297,22 +297,22 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n No group `bar` found', - output.data) + output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 1', - output.data) + output.data.decode('utf-8')) output = self.app.post( '/group/test_group/delete', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( '\n You are not allowed to ' - 'delete the group test_group', output.data) + 'delete the group test_group', output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 1', - output.data) + output.data.decode('utf-8')) user.username = 'bar' with tests.user_set(pagure.APP, user): @@ -329,11 +329,11 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n Group `test_group` has ' - 'been deleted', output.data) + 'been deleted', output.data.decode('utf-8')) self.assertIn( '

    \n' ' Groups 0', - output.data) + output.data.decode('utf-8')) def test_view_group(self): """ Test the view_group endpoint. """ @@ -348,7 +348,7 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '  ' - 'Test Group', output.data) + 'Test Group', output.data.decode('utf-8')) output = self.app.get('/group/test_admin_group') self.assertEqual(output.status_code, 404) @@ -362,10 +362,10 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '  ' - 'Test Admin Group', output.data) - self.assertEqual(output.data.count('')[0] # No CSRF @@ -377,8 +377,8 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '  ' - 'Test Admin Group', output.data) - self.assertEqual(output.data.count('\n No user `bar` found', - output.data) + output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Admin Group', output.data) - self.assertEqual(output.data.count('\n User `foo` added to the ' - 'group `test_admin_group`.', output.data) + 'group `test_admin_group`.', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Admin Group', output.data) - self.assertEqual(output.data.count('  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('')[0] data = {'csrf_token': csrf_token} @@ -445,22 +445,22 @@ class PagureFlaskGroupstests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n No user `bar` found', - output.data) + output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n Could not find user ' - 'username', output.data) + 'username', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n User `foo` could not be ' - 'found in the group `test_group`', output.data) + 'found in the group `test_group`', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n User `foo` could not be ' - 'found in the group `test_group`', output.data) + 'found in the group `test_group`', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n User `foo` added to the ' - 'group `test_group`.', output.data) + 'group `test_group`.', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n User `foo` removed from ' - 'the group `test_group`', output.data) + 'the group `test_group`', output.data.decode('utf-8')) self.assertIn( '  ' - 'Test Group', output.data) - self.assertEqual(output.data.count('\n New issue' - in output.data) + in output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -86,8 +87,8 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) - self.assertEqual(output.data.count( + in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( 'This field is required.'), 2) data['title'] = 'Test issue' @@ -95,8 +96,8 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) - self.assertEqual(output.data.count( + in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( 'This field is required.'), 1) data['issue_content'] = 'We really should improve on this issue' @@ -105,8 +106,8 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) - self.assertEqual(output.data.count( + in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( '\n This field is required.'), 0) @@ -116,7 +117,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 404) self.assertIn( '

    No such user found in the database: username

    ', - output.data) + output.data.decode('utf-8')) # User not logged in output = self.app.get('/test/new_issue') @@ -129,11 +130,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) # Project w/o issue tracker repo = pagure.get_authorized_project(self.session, 'test') @@ -167,11 +168,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) + in output.data.decode('utf-8')) csrf_token = self.get_csrf() - with open(os.path.join(tests.HERE, 'placebo.png'), 'r') as stream: + with open(os.path.join(tests.HERE, 'placebo.png'), 'rb') as stream: data = { 'title': 'Test issue', 'issue_content': 'We really should improve on this issue\n' @@ -188,17 +189,17 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Check the image was uploaded self.assertIn( 'href="/test/issue/raw/' '8a06845923010b27bfd8e7e75acff7badc40d1021b4' '994e01f5e11ca40bc3abe', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.git.update_git') @patch('pagure.lib.notify.send_email') @@ -222,7 +223,7 @@ class PagureFlaskIssuestests(tests.Modeltests): user = tests.FakeUser() user.username = 'pingou' with tests.user_set(pagure.APP, user): - with open(os.path.join(tests.HERE, 'placebo.png'), 'r') as stream: + with open(os.path.join(tests.HERE, 'placebo.png'), 'rb') as stream: data = { 'title': 'Test issue', 'issue_content': 'We really should improve on this issue', @@ -257,11 +258,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) + in output.data.decode('utf-8')) csrf_token = self.get_csrf() - with open(os.path.join(tests.HERE, 'placebo.png'), 'r') as stream: + with open(os.path.join(tests.HERE, 'placebo.png'), 'rb') as stream: data = { 'title': 'Test issue3', 'issue_content': 'We really should improve on this issue\n' @@ -278,18 +279,18 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue3 - test3 - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Check the image was uploaded self.assertIn( 'href="/somenamespace/test3/issue/raw/' '8a06845923010b27bfd8e7e75acff7badc40d1021b4' '994e01f5e11ca40bc3abe', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.git.update_git') @patch('pagure.lib.notify.send_email') @@ -311,7 +312,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n New issue' - in output.data) + in output.data.decode('utf-8')) csrf_token = self.get_csrf() @@ -337,22 +338,22 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Check the image was uploaded self.assertIn( 'href="/test/issue/raw/' '8a06845923010b27bfd8e7e75acff7badc40d1021b4' '994e01f5e11ca40bc3abe', - output.data) + output.data.decode('utf-8')) self.assertIn( 'href="/test/issue/raw/' '6498a2de405546200b6144da56fc25d0a3976ae688d' 'bfccaca609c8b4480523e', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.git.update_git') @patch('pagure.lib.notify.send_email') @@ -375,7 +376,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '', output.data) + '
    ', output.data.decode('utf-8')) self.assertTrue( - '

    \n 0 Open Issues' in output.data) + '

    \n 0 Open Issues' in output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') # Create some custom fields to play with @@ -517,49 +518,49 @@ class PagureFlaskIssuestests(tests.Modeltests): # Whole list output = self.app.get('/test/issues') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) self.assertTrue( - '

    \n 2 Open Issues' in output.data) + '

    \n 2 Open Issues' in output.data.decode('utf-8')) self.assertIn( '
    ', output.data) + 'closed issues of total 3 issues">', output.data.decode('utf-8')) self.assertIn( '', output.data) + '3 issues">', output.data.decode('utf-8')) # Status = closed (all but open) output = self.app.get('/test/issues?status=cloSED') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) self.assertTrue( - '

    \n 1 Closed Issues' in output.data) + '

    \n 1 Closed Issues' in output.data.decode('utf-8')) self.assertIn( '
    ', output.data) + 'title="67% of open issues of total 3 issues">', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'of total 3 issues">', output.data.decode('utf-8')) # Status = fixed output = self.app.get('/test/issues?status=fixed') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) self.assertTrue( - '

    \n 0 Closed Issues' in output.data) + '

    \n 0 Closed Issues' in output.data.decode('utf-8')) # Status = Invalid output = self.app.get('/test/issues?status=Invalid') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) self.assertTrue( - '

    \n 1 Closed Issues' in output.data) + '

    \n 1 Closed Issues' in output.data.decode('utf-8')) # All tickets output = self.app.get('/test/issues?status=all') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) self.assertTrue( - '

    \n 3 Issues' in output.data) + '

    \n 3 Issues' in output.data.decode('utf-8')) # Unicode search pattern output = self.app.get( @@ -572,49 +573,61 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.get( '/test/issues?status=all&search_pattern=test1:firstissue') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('1 Issues', output.data) + self.assertIn( + 'Issues - test - Pagure', + output.data.decode('utf-8')) + self.assertIn('1 Issues', output.data.decode('utf-8')) # Custom key searching with space output = self.app.get( '/test/issues?status=all&search_pattern=test1:"second issue"') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('1 Issues', output.data) + self.assertIn( + 'Issues - test - Pagure', + output.data.decode('utf-8')) + self.assertIn('1 Issues', output.data.decode('utf-8')) # All tickets - different pagination before = pagure.APP.config['ITEM_PER_PAGE'] pagure.APP.config['ITEM_PER_PAGE'] = 1 output = self.app.get('/test/issues?status=all') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('

    \n 1 Issues (of 3)', output.data) self.assertIn( - '
  • page 1 of 3
  • ', output.data) + 'Issues - test - Pagure', + output.data.decode('utf-8')) + self.assertIn( + '

    \n 1 Issues (of 3)', + output.data.decode('utf-8')) + self.assertIn( + '
  • page 1 of 3
  • ', output.data.decode('utf-8')) # All tickets - filtered for 1 - checking the pagination output = self.app.get( '/test/issues?status=all&search_pattern=invalid') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('

    \n 1 Issues (of 1)', output.data) + self.assertIn('Issues - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    \n 1 Issues (of 1)', output.data.decode('utf-8')) self.assertIn( - '
  • page 1 of 1
  • ', output.data) + '
  • page 1 of 1
  • ', output.data.decode('utf-8')) pagure.APP.config['ITEM_PER_PAGE'] = before # Search for issues with no milestone MARK output = self.app.get( '/test/issues?milestone=none') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('1 Open Issues (of 1)', output.data) + self.assertIn( + 'Issues - test - Pagure', + output.data.decode('utf-8')) + self.assertIn('1 Open Issues (of 1)', output.data.decode('utf-8')) # Search for issues with no milestone and milestone 1.1 output = self.app.get( '/test/issues?milestone=none&milestone=1.1') self.assertEqual(output.status_code, 200) - self.assertIn('Issues - test - Pagure', output.data) - self.assertIn('2 Open Issues (of 2)', output.data) + self.assertIn( + 'Issues - test - Pagure', + output.data.decode('utf-8')) + self.assertIn('2 Open Issues (of 2)', output.data.decode('utf-8')) # New issue button is shown user = tests.FakeUser() @@ -623,7 +636,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'class="btn btn-success btn-sm">New Issue
    ', - output.data) + output.data.decode('utf-8')) # Project w/o issue tracker repo = pagure.get_authorized_project(self.session, 'test') @@ -641,7 +654,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertNotIn( 'class="btn btn-success btn-sm">New Issue', - output.data) + output.data.decode('utf-8')) @patch('pagure.lib.git.update_git') @patch('pagure.lib.notify.send_email') @@ -730,11 +743,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertNotIn( '', - output.data) + output.data.decode('utf-8')) self.assertTrue( '' 'Login\n to comment on this ticket.' - in output.data) + in output.data.decode('utf-8')) user = tests.FakeUser() with tests.user_set(pagure.APP, user): @@ -744,20 +757,20 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertNotIn( '', - output.data) + output.data.decode('utf-8')) self.assertNotIn( '\n ' 'Issue close_status updated to: Fixed\n', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n ' 'Issue status updated to: Closed (was: Open)\n', - output.data) + output.data.decode('utf-8')) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # FIXME: There is likely something going wrong in the html # below self.assertIn( @@ -1271,7 +1287,7 @@ class PagureFlaskIssuestests(tests.Modeltests): ''' @pingou:
    - Issue close_status updated to: Fixed
    - Issue status updated to: Closed (was: Open)

    ''', - output.data) + output.data.decode('utf-8')) # Add new comment data = { @@ -1285,25 +1301,25 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment added', - output.data) + output.data.decode('utf-8')) self.assertNotIn( '\n No changes to edit', - output.data) + output.data.decode('utf-8')) self.assertTrue( - '

    Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # 2: one for the original comment, one for the new comment - self.assertEqual(output.data.count('title="PY C (pingou)"'), 2) + self.assertEqual(output.data..decode('utf-8')count('title="PY C (pingou)"'), 2) # Add new tag data = { @@ -1317,17 +1333,17 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertTrue( - '

    Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # Assign issue to an non-existent user data = { @@ -1341,20 +1357,20 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n No user "ralph" found', - output.data) + output.data.decode('utf-8')) self.assertTrue( - '

    Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # Assign issue properly data = { @@ -1368,22 +1384,22 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Issue assigned to pingou\n', - output.data) + output.data.decode('utf-8')) self.assertTrue( - 'Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # Create another issue with a dependency repo = pagure.get_authorized_project(self.session, 'test') @@ -1418,18 +1434,18 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #2: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n You cannot close a ticket ' 'that has ticket depending that are still open.', - output.data) + output.data.decode('utf-8')) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) # Create private issue repo = pagure.get_authorized_project(self.session, 'test') @@ -1494,11 +1510,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1512,17 +1528,17 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment added', - output.data) + output.data.decode('utf-8')) self.assertTrue( - '

    Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -1554,14 +1570,14 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment removed', - output.data) + output.data.decode('utf-8')) # Drop non-existant comment output = self.app.post( @@ -1615,11 +1631,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1633,11 +1649,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Add an invalid dependent ticket data = { @@ -1649,15 +1665,15 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertNotIn( '\n ' 'Successfully edited issue #1', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -1720,7 +1736,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1734,7 +1750,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -1748,11 +1764,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1766,11 +1782,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Add an invalid dependent ticket data = { @@ -1782,15 +1798,15 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertNotIn( '\n ' 'Successfully edited issue #1', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -1830,11 +1846,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -1851,7 +1867,7 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post( '/test/issue/1/upload', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - json_data = json.loads(output.data) + json_data = json.loads(output.data.decode('utf-8')) exp = {'output': 'notok'} self.assertDictEqual(json_data, exp) @@ -1865,7 +1881,7 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post( '/test/issue/1/upload', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - json_data = json.loads(output.data) + json_data = json.loads(output.data.decode('utf-8')) folder = os.path.dirname( os.path.abspath(__file__))[1:].replace('/', '_') @@ -1939,7 +1955,7 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post( '/test/issue/1/upload', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - json_data = json.loads(output.data) + json_data = json.loads(output.data.decode('utf-8')) exp = { 'output': 'notok', } @@ -1987,7 +2003,7 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post( '/test/issue/1/upload', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - json_data = json.loads(output.data) + json_data = json.loads(output.data.decode('utf-8')) folder = os.path.dirname( os.path.abspath(__file__))[1:].replace('/', '_') @@ -2139,7 +2155,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n Edit ' - 'issue #1\n
    ' in output.data) + 'issue #1\n

    ' in output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2151,11 +2167,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n Edit ' - 'issue #1\n
    ' in output.data) - self.assertEqual(output.data.count( + 'issue #1\n

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( '\n This field is required. \n' ' '), 1) - self.assertEqual(output.data.count( + self.assertEqual(output.data.decode('utf-8').count( '\n Not a valid choice ' '\n '), 1) @@ -2165,11 +2181,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '
    \n Edit ' - 'issue #1\n
    ' in output.data) - self.assertEqual(output.data.count( + 'issue #1\n

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( '\n This field is required. \n' ' '), 0) - self.assertEqual(output.data.count( + self.assertEqual(output.data.decode('utf-8').count( '\n Not a valid choice ' '\n '), 0) @@ -2180,11 +2196,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertIn( '#1\n' ' Test issue #1', - output.data) - self.assertEqual(output.data.count( + output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count( ''), 1) - self.assertEqual(output.data.count('comment_body">'), 1) - self.assertEqual(output.data.count( + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 1) + self.assertEqual(output.data.decode('utf-8').count( '

    We should work on this!

    '), 1) # Project w/o issue tracker @@ -2314,7 +2330,7 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.get('/test/tag/tag1/edit') self.assertEqual(output.status_code, 200) - self.assertTrue('Edit tag: tag1' in output.data) + self.assertTrue('Edit tag: tag1' in output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2324,18 +2340,18 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post('/test/tag/tag1/edit', data=data) self.assertEqual(output.status_code, 200) - self.assertTrue('Edit tag: tag1' in output.data) + self.assertTrue('Edit tag: tag1' in output.data.decode('utf-8')) data['csrf_token'] = csrf_token output = self.app.post( '/test/tag/tag1/edit', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n ' 'Edited tag: tag1()[DeepSkyBlue] to tag2(lorem ipsum)[DeepSkyBlue]', - output.data) + output.data.decode('utf-8')) # update tag with empty description data['tag_description'] = '' @@ -2343,11 +2359,11 @@ class PagureFlaskIssuestests(tests.Modeltests): '/test/tag/tag2/edit', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n ' 'Edited tag: tag2(lorem ipsum)[DeepSkyBlue] to tag2()[DeepSkyBlue]', - output.data) + output.data.decode('utf-8')) # After edit, list tags tags = pagure.lib.get_tags_of_project(self.session, repo) @@ -2414,8 +2430,8 @@ class PagureFlaskIssuestests(tests.Modeltests): '/test/droptag/', data={}, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertTrue( - 'Settings - test - Pagure' in output.data) - self.assertTrue("

    Settings for test

    " in output.data) + 'Settings - test - Pagure' in output.data.decode('utf-8')) + self.assertTrue("

    Settings for test

    " in output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2424,16 +2440,16 @@ class PagureFlaskIssuestests(tests.Modeltests): output = self.app.post( '/test/droptag/', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertTrue("

    Settings for test

    " in output.data) + self.assertTrue("

    Settings for test

    " in output.data.decode('utf-8')) data['csrf_token'] = csrf_token output = self.app.post( '/test/droptag/', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertTrue("

    Settings for test

    " in output.data) + self.assertTrue("

    Settings for test

    " in output.data.decode('utf-8')) self.assertIn( '\n ' - 'Issue **un**tagged with: tag1', output.data) + 'Issue **un**tagged with: tag1', output.data.decode('utf-8')) @patch('pagure.lib.git.update_git') @patch('pagure.lib.notify.send_email') @@ -2480,7 +2496,7 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2493,17 +2509,17 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) data['csrf_token'] = csrf_token output = self.app.post( '/test/issue/1/drop', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Issues - test - Pagure', output.data) + 'Issues - test - Pagure', output.data.decode('utf-8')) self.assertIn( '\n Issue deleted', - output.data) + output.data.decode('utf-8')) # Project w/o issue tracker repo = pagure.get_authorized_project(self.session, 'test') @@ -2547,11 +2563,11 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) csrf_token = self.get_csrf(output=output) @@ -2565,17 +2581,17 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment added', - output.data) + output.data.decode('utf-8')) self.assertTrue( - '

    Woohoo a second comment !

    ' in output.data) - self.assertEqual(output.data.count('comment_body">'), 2) + '

    Woohoo a second comment !

    ' in output.data.decode('utf-8')) + self.assertEqual(output.data.decode('utf-8').count('comment_body">'), 2) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -2611,14 +2627,14 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Issue #1: Test issue - test - Pagure', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    ', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Comment updated', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') issue = pagure.lib.search_issues(self.session, repo, issueid=1) @@ -2628,12 +2644,12 @@ class PagureFlaskIssuestests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/test/issue/1/comment/1/edit') self.assertIn( - 'test - Pagure', output.data) - self.assertTrue('
    ' in output.data) - self.assertTrue('
    ' in output.data) + 'test - Pagure', output.data.decode('utf-8')) + self.assertTrue('
    ' in output.data.decode('utf-8')) + self.assertTrue('
    ' in output.data.decode('utf-8')) self.assertTrue( '', output.data) + self.assertIn(u'>Ship it!', output.data.decode('utf-8')) def test_update_quick_replies_multiple(self): with tests.user_set(pagure.APP, self.admin): @@ -139,8 +139,8 @@ class PagureFlaskQuickReplytest(tests.Modeltests): output, notice=u'quick replies updated') self.assertQuickReplies([u'Ship it!', u'Nah.']) # Check page has filled in textarea. - self.assertIn(u'>Ship it!', output.data) - self.assertIn(u'>Nah.', output.data) + self.assertIn(u'>Ship it!', output.data.decode('utf-8')) + self.assertIn(u'>Nah.', output.data.decode('utf-8')) def test_update_quick_replies_empty_to_reset(self): # Set some quick replies @@ -176,7 +176,7 @@ class PagureFlaskQuickReplytest(tests.Modeltests): with tests.user_set(pagure.APP, self.admin): output = self.app.get('/test/settings') - self.assertNotIn('Quick replies', output.data) + self.assertNotIn('Quick replies', output.data.decode('utf-8')) def test_no_submit_with_disabled_issues_and_pull_requests(self): self.disable_issues_and_pull_requests() diff --git a/tests/test_pagure_flask_ui_repo.py b/tests/test_pagure_flask_ui_repo.py index 64a00d4..363bba9 100644 --- a/tests/test_pagure_flask_ui_repo.py +++ b/tests/test_pagure_flask_ui_repo.py @@ -79,7 +79,7 @@ class PagureFlaskRepotests(tests.Modeltests): #just get the csrf token pagure.APP.config['ENABLE_USER_MNGT'] = True output = self.app.get('/test/adduser') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] pagure.APP.config['ENABLE_USER_MNGT'] = False @@ -139,7 +139,7 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n Action canceled, try it ' - 'again',output.data) + 'again', output.data.decode('utf-8')) ast.return_value = False @@ -147,9 +147,9 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/test/adddeploykey') self.assertEqual(output.status_code, 200) - self.assertIn('Add deploy key to the', output.data) + self.assertIn('Add deploy key to the', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -160,22 +160,22 @@ class PagureFlaskRepotests(tests.Modeltests): # No CSRF token output = self.app.post('/test/adddeploykey', data=data) self.assertEqual(output.status_code, 200) - self.assertTrue('Add deploy key to the' in output.data) + self.assertIn('Add deploy key to the', output.data.decode('utf-8')) data['csrf_token'] = csrf_token # First, invalid SSH key output = self.app.post('/test/adddeploykey', data=data) self.assertEqual(output.status_code, 200) - self.assertIn('Add deploy key to the', output.data) - self.assertIn('Deploy key invalid', output.data) + self.assertIn('Add deploy key to the', output.data.decode('utf-8')) + self.assertIn('Deploy key invalid', output.data.decode('utf-8')) # Next up, multiple SSH keys data['ssh_key'] = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==' output = self.app.post( '/test/adddeploykey', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertIn('Deploy key can only be single keys.', output.data) + self.assertIn('Deploy key can only be single keys.', output.data.decode('utf-8')) # Now, a valid SSH key data['ssh_key'] = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==' @@ -183,16 +183,16 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/adddeploykey', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertIn('Deploy key added', output.data) - self.assertNotIn('PUSH ACCESS', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertIn('Deploy key added', output.data.decode('utf-8')) + self.assertNotIn('PUSH ACCESS', output.data.decode('utf-8')) # And now, adding the same key output = self.app.post( '/test/adddeploykey', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertIn('Deploy key already exists', output.data) + self.assertIn('Deploy key already exists', output.data.decode('utf-8')) # And next, a key with push access data['ssh_key'] = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9Xwc2RDzPBhlEDARfHldGjudIVoa04tqT1JVKGQmyllTFz7Rb8CngQL3e7zyNzotnhwYKHdoiLlPkVEiDee4dWMUe48ilqId+FJZQGhyv8fu4BoFdE1AJUVylzmltbLg14VqG5gjTpXgtlrEva9arKwBMHJjRYc8ScaSn3OgyQw==' @@ -201,10 +201,10 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/adddeploykey', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertIn('Deploy key added', output.data) - self.assertIn('PUSH ACCESS', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertIn('Deploy key added', output.data.decode('utf-8')) + self.assertIn('PUSH ACCESS', output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') @patch.dict('pagure.APP.config', {'DEPLOY_KEY': False}) @@ -258,7 +258,7 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n Action canceled, try it ' - 'again',output.data) + 'again',output.data.decode('utf-8')) ast.return_value = False @@ -266,9 +266,9 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/test/adduser') self.assertEqual(output.status_code, 200) - self.assertIn('Add user to the', output.data) + self.assertIn('Add user to the', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -279,42 +279,42 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/adduser', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add user - test - Pagure', output.data) - self.assertTrue('Add user to the' in output.data) + 'Add user - test - Pagure', output.data.decode('utf-8')) + self.assertTrue('Add user to the' in output.data.decode('utf-8')) # No CSRF output = self.app.post('/test/adduser', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add user - test - Pagure', output.data) + 'Add user - test - Pagure', output.data.decode('utf-8')) # Missing access data['csrf_token'] = csrf_token output = self.app.post('/test/adduser', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add user - test - Pagure', output.data) - self.assertIn('Add user to the', output.data) + 'Add user - test - Pagure', output.data.decode('utf-8')) + self.assertIn('Add user to the', output.data.decode('utf-8')) # Unknown user data['access'] = 'commit' output = self.app.post('/test/adduser', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add user - test - Pagure', output.data) - self.assertIn('Add user to the', output.data) + 'Add user - test - Pagure', output.data.decode('utf-8')) + self.assertIn('Add user to the', output.data.decode('utf-8')) self.assertIn( '\n No user "ralph" found\n', - output.data) + output.data.decode('utf-8')) # All correct data['user'] = 'foo' output = self.app.post( '/test/adduser', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertIn('

    Settings for test

    ', output.data) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( - '\n User added', output.data) + '\n User added', output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') def test_add_group_project_when_user_mngt_off(self, ast): @@ -353,7 +353,7 @@ class PagureFlaskRepotests(tests.Modeltests): pagure.APP.config['ENABLE_USER_MNGT'] = True output = self.app.get('/test/addgroup') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] pagure.APP.config['ENABLE_USER_MNGT'] = False @@ -411,7 +411,7 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '\n Action canceled, try it ' - 'again', output.data) + 'again', output.data.decode('utf-8')) ast.return_value = False @@ -432,9 +432,9 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.get('/test/addgroup') self.assertEqual(output.status_code, 200) - self.assertTrue('Add group to the' in output.data) + self.assertTrue('Add group to the' in output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -445,27 +445,27 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/addgroup', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add group - test - Pagure', output.data) - self.assertIn('Add group to the', output.data) + 'Add group - test - Pagure', output.data.decode('utf-8')) + self.assertIn('Add group to the', output.data.decode('utf-8')) # Missing access data['csrf_token'] = csrf_token output = self.app.post('/test/addgroup', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add group - test - Pagure', output.data) - self.assertIn('Add group to the', output.data) + 'Add group - test - Pagure', output.data.decode('utf-8')) + self.assertIn('Add group to the', output.data.decode('utf-8')) # Unknown group data['access'] = 'ticket' output = self.app.post('/test/addgroup', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Add group - test - Pagure', output.data) - self.assertIn('Add group to the', output.data) + 'Add group - test - Pagure', output.data.decode('utf-8')) + self.assertIn('Add group to the', output.data.decode('utf-8')) self.assertIn( '\n No group ralph found.', - output.data) + output.data.decode('utf-8')) # All good data['group'] = 'foo' @@ -473,10 +473,10 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/addgroup', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( - '\n Group added', output.data) + '\n Group added', output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') def test_remove_user_when_user_mngt_off(self, ast): @@ -496,7 +496,7 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/settings') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {'csrf_token': csrf_token} @@ -564,7 +564,7 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.post('/test/settings') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {'csrf_token': csrf_token} @@ -573,9 +573,9 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/dropdeploykey/1', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertIn('Deploy key does not exist in project', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertIn('Deploy key does not exist in project', output.data.decode('utf-8')) # Add a deploy key to a project repo = pagure.get_authorized_project(self.session, 'test') @@ -593,18 +593,18 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/dropdeploykey/1', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertNotIn('Deploy key removed', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertNotIn('Deploy key removed', output.data.decode('utf-8')) data = {'csrf_token': csrf_token} output = self.app.post( '/test/dropdeploykey/1', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertIn('Deploy key removed', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertIn('Deploy key removed', output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') @patch.dict('pagure.APP.config', {'DEPLOY_KEY': False}) @@ -654,7 +654,7 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.post('/test/settings') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {'csrf_token': csrf_token} @@ -663,11 +663,11 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/dropuser/2', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n User does not have any ' - 'access on the repo', output.data) + 'access on the repo', output.data.decode('utf-8')) # Add an user to a project repo = pagure.get_authorized_project(self.session, 'test') @@ -686,11 +686,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/dropuser/2', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertNotIn( - '\n User removed', output.data) - self.assertIn('action="/test/dropuser/2">', output.data) + '\n User removed', output.data.decode('utf-8')) + self.assertIn('action="/test/dropuser/2">', output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertEqual(len(repo.users), 1) @@ -699,11 +699,11 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/dropuser/2', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( - '\n User removed', output.data) - self.assertNotIn('action="/test/dropuser/2">', output.data) + '\n User removed', output.data.decode('utf-8')) + self.assertNotIn('action="/test/dropuser/2">', output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertEqual(len(repo.users), 0) @@ -731,7 +731,7 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.post('/test/settings') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {'csrf_token': csrf_token} @@ -810,7 +810,7 @@ class PagureFlaskRepotests(tests.Modeltests): with tests.user_set(pagure.APP, user): output = self.app.post('/test/settings') - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {'csrf_token': csrf_token} @@ -819,12 +819,12 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/dropgroup/2', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n ' 'Group does not seem to be part of this project', - output.data) + output.data.decode('utf-8')) # Create the new group msg = pagure.lib.add_group( @@ -858,12 +858,12 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/dropgroup/1', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) - self.assertIn('action="/test/dropgroup/1">', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) + self.assertIn('action="/test/dropgroup/1">', output.data.decode('utf-8')) self.assertNotIn( '\n Group removed', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertEqual(len(repo.groups), 1) @@ -872,12 +872,12 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/dropgroup/1', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n Group removed', - output.data) - self.assertNotIn('action="/test/dropgroup/1">', output.data) + output.data.decode('utf-8')) + self.assertNotIn('action="/test/dropgroup/1">', output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertEqual(len(repo.groups), 0) @@ -918,10 +918,10 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.post('/test/update', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = { @@ -932,13 +932,13 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( - '', output.data) + '', output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) # Edit the avatar_email data = { @@ -950,14 +950,14 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) # Reset the avatar_email data = { @@ -969,13 +969,13 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( - '', output.data) + '', output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') def test_update_project_update_tag(self, ast): @@ -998,10 +998,10 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/settings') self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] # Add tag to a project so that they are added to the database @@ -1014,11 +1014,11 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) # Remove two of the tags of the project, they will still be in # the DB but not associated to this project @@ -1031,11 +1031,11 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) # Try re-adding the two tags, this used to fail before we fixed # it @@ -1048,11 +1048,11 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/update', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '\n Project updated', - output.data) + output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') def test_view_settings(self, ast): @@ -1088,18 +1088,18 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/settings') self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) # Both checkbox checked before self.assertIn( '', output.data) + 'name="pull_requests" checked=""/>', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="issue_tracker" checked=""/>', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] data = {} @@ -1107,47 +1107,47 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/settings', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) # Both checkbox are still checked output = self.app.get('/test/settings', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="pull_requests" checked=""/>', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="issue_tracker" checked=""/>', output.data.decode('utf-8')) data = {'csrf_token': csrf_token} output = self.app.post( '/test/settings', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1
    ', output.data.decode('utf-8')) self.assertIn( '\n Edited successfully ' - 'settings of repo: test', output.data) + 'settings of repo: test', output.data.decode('utf-8')) # Both checkbox are now un-checked output = self.app.get('/test/settings', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="pull_requests" />', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="issue_tracker" />', output.data.decode('utf-8')) data = { 'csrf_token': csrf_token, @@ -1158,26 +1158,26 @@ class PagureFlaskRepotests(tests.Modeltests): '/test/settings', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Overview - test - Pagure', output.data) + 'Overview - test - Pagure', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1
    ', output.data.decode('utf-8')) self.assertIn( '\n Edited successfully ' - 'settings of repo: test', output.data) + 'settings of repo: test', output.data.decode('utf-8')) # Both checkbox are again checked output = self.app.get('/test/settings', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="pull_requests" checked=""/>', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'name="issue_tracker" checked=""/>', output.data.decode('utf-8')) @patch('pagure.ui.repo.admin_session_timedout') def test_fields_in_view_settings(self, ast): @@ -1220,8 +1220,8 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/settings') self.assertEqual(output.status_code, 200) self.assertIn( - 'Settings - test - Pagure', output.data) - self.assertIn('

    Settings for test

    ', output.data) + 'Settings - test - Pagure', output.data.decode('utf-8')) + self.assertIn('

    Settings for test

    ', output.data.decode('utf-8')) # Check that the priorities have their empty fields self.assertIn( '''
    @@ -1235,7 +1235,7 @@ class PagureFlaskRepotests(tests.Modeltests): value="" class="form-control"/>
    -
    ''', output.data) +
    ''', output.data.decode('utf-8')) # Check that the milestones have their empty fields self.assertIn( @@ -1249,7 +1249,7 @@ class PagureFlaskRepotests(tests.Modeltests):
    -

    ''', output.data) +
    ''', output.data.decode('utf-8')) # Check that the close_status have its empty field self.assertIn( @@ -1259,7 +1259,7 @@ class PagureFlaskRepotests(tests.Modeltests): - ''', output.data) + ''', output.data.decode('utf-8')) # Check that the custom fields have their empty fields self.assertIn( @@ -1285,7 +1285,7 @@ class PagureFlaskRepotests(tests.Modeltests): - ''', output.data) + ''', output.data.decode('utf-8')) def test_view_forks(self): """ Test the view_forks endpoint. """ @@ -1298,7 +1298,7 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/forks', follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertTrue('This project has not been forked.' in output.data) + self.assertTrue('This project has not been forked.' in output.data.decode('utf-8')) @patch.dict('pagure.APP.config', {'CASE_SENSITIVE': True}) def test_view_repo_case_sensitive(self): @@ -1333,19 +1333,19 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test') self.assertEqual(output.status_code, 200) - self.assertTrue('

    This repo is brand new!

    ' in output.data) + self.assertTrue('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) self.perfMaxWalks(0, 0) self.perfReset() output = self.app.get('/test/') self.assertEqual(output.status_code, 200) - self.assertTrue('

    This repo is brand new!

    ' in output.data) + self.assertTrue('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) self.perfMaxWalks(0, 0) self.perfReset() @@ -1357,11 +1357,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) - self.assertFalse('Forked from' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) + self.assertFalse('Forked from' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) self.perfMaxWalks(3, 8) # Target: (1, 3) self.perfReset() @@ -1384,11 +1384,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) - self.assertTrue('Forked from' in output.data) + 'test project #1 ', output.data.decode('utf-8')) + self.assertTrue('Forked from' in output.data.decode('utf-8')) self.perfMaxWalks(1, 3) self.perfReset() @@ -1414,11 +1414,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test3') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #3
    ', output.data) - self.assertTrue('Forked from' in output.data) + 'test project #3 ', output.data.decode('utf-8')) + self.assertTrue('Forked from' in output.data.decode('utf-8')) self.perfMaxWalks(3, 18) # Ideal: (1, 3) self.perfReset() @@ -1460,13 +1460,13 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) - self.assertFalse('Forked from' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) + self.assertFalse('Forked from' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) self.assertEqual( - output.data.count(''), 0) + output.data.decode('utf-8').count(''), 0) shutil.rmtree(newpath) @@ -1495,11 +1495,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/branch/master') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) - self.assertFalse('Forked from' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) + self.assertFalse('Forked from' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) # Turn that repo into a fork repo = pagure.get_authorized_project(self.session, 'test') @@ -1520,11 +1520,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test/branch/master') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) - self.assertTrue('Forked from' in output.data) + 'test project #1 ', output.data.decode('utf-8')) + self.assertTrue('Forked from' in output.data.decode('utf-8')) # Add a fork of a fork item = pagure.lib.model.Project( @@ -1548,11 +1548,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test3/branch/master') self.assertEqual(output.status_code, 200) - self.assertFalse('

    This repo is brand new!

    ' in output.data) + self.assertFalse('

    This repo is brand new!

    ' in output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #3
    ', output.data) - self.assertTrue('Forked from' in output.data) + 'test project #3 ', output.data.decode('utf-8')) + self.assertTrue('Forked from' in output.data.decode('utf-8')) def test_view_commits(self): """ Test the view_commits endpoint. """ @@ -1570,10 +1570,10 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/commits') self.assertEqual(output.status_code, 200) - self.assertIn('

    This repo is brand new!

    ', output.data) + self.assertIn('

    This repo is brand new!

    ', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) # Add some content to the git repo tests.add_content_git_repo(os.path.join(self.path, 'repos', @@ -1582,20 +1582,20 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/commits') self.assertEqual(output.status_code, 200) - self.assertNotIn('

    This repo is brand new!

    ', output.data) - self.assertNotIn('Forked from', output.data) + self.assertNotIn('

    This repo is brand new!

    ', output.data.decode('utf-8')) + self.assertNotIn('Forked from', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) - self.assertIn('Commits - test - Pagure', output.data) + 'test project #1 ', output.data.decode('utf-8')) + self.assertIn('Commits - test - Pagure', output.data.decode('utf-8')) output = self.app.get('/test/commits/master') self.assertEqual(output.status_code, 200) - self.assertNotIn('

    This repo is brand new!

    ', output.data) - self.assertNotIn('Forked from', output.data) + self.assertNotIn('

    This repo is brand new!

    ', output.data.decode('utf-8')) + self.assertNotIn('Forked from', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) + 'test project #1 ', output.data.decode('utf-8')) # Turn that repo into a fork repo = pagure.get_authorized_project(self.session, 'test') @@ -1616,11 +1616,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test/commits?page=abc') self.assertEqual(output.status_code, 200) - self.assertNotIn('

    This repo is brand new!

    ', output.data) + self.assertNotIn('

    This repo is brand new!

    ', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #1
    ', output.data) - self.assertIn('Forked from', output.data) + 'test project #1 ', output.data.decode('utf-8')) + self.assertIn('Forked from', output.data.decode('utf-8')) # Add a fork of a fork item = pagure.lib.model.Project( @@ -1647,11 +1647,11 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test3/commits') self.assertEqual(output.status_code, 200) - self.assertNotIn('

    This repo is brand new!

    ', output.data) + self.assertNotIn('

    This repo is brand new!

    ', output.data.decode('utf-8')) self.assertIn( '
    \n' - 'test project #3
    ', output.data) - self.assertIn('Forked from', output.data) + 'test project #3 ', output.data.decode('utf-8')) + self.assertIn('Forked from', output.data.decode('utf-8')) def test_compare_commits(self): """ Test the compare_commits endpoint. """ @@ -1664,40 +1664,40 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertIn( 'Diff from %s to %s - test\n - Pagure' % (c2.oid.hex, c1.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( '
    %s .. %s
    ' % (c2.oid.hex, c1.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( 'Commits \n ' + '' + '\n 2\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '- ' + - 'Row 0', output.data) + 'Row 0', output.data.decode('utf-8')) # View inverse commits comparison output = self.app.get('/test/c/%s..%s' % (c1.oid.hex, c2.oid.hex)) self.assertEqual(output.status_code, 200) self.assertIn( 'Diff from %s to %s - test\n - Pagure' % (c1.oid.hex, c2.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( 'Commits \n ' + '' + '\n 2\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( '
    %s .. %s
    ' % (c1.oid.hex, c2.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( '' + - '+ Row 0', output.data) + '+ Row 0', output.data.decode('utf-8')) def compare_all(c1, c3): # View commits comparison @@ -1705,16 +1705,16 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Diff from %s to %s - test\n - Pagure' % - (c1.oid.hex, c3.oid.hex), output.data) + (c1.oid.hex, c3.oid.hex), output.data.decode('utf-8')) self.assertIn( '
    %s .. %s
    ' % (c1.oid.hex, c3.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( '+ Row 0', output.data) + '#ddffdd">+ Row 0
    ', output.data.decode('utf-8')) self.assertEqual( - output.data.count( + output.data.decode('utf-8').count( '+ Row 0'), 2) self.assertIn( @@ -1722,11 +1722,14 @@ class PagureFlaskRepotests(tests.Modeltests): '' + '\n 3\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 4829cf">Šource
    ', output.data) + u'title="View file as of 4829cf">Šource'.encode('utf-8'), + output.data + ) self.assertIn( - '
    file added
    ', output.data) + '
    file added
    ', + output.data.decode('utf-8')) # View inverse commits comparison output = self.app.get( @@ -1734,27 +1737,30 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( 'Diff from %s to %s - test\n - Pagure' % - (c3.oid.hex, c1.oid.hex), output.data) + (c3.oid.hex, c1.oid.hex), output.data.decode('utf-8')) self.assertIn( '
    %s .. %s
    ' % (c3.oid.hex, c1.oid.hex), - output.data) + output.data.decode('utf-8')) self.assertIn( '@@ -1,2 +1,1' + - ' @@', output.data) + ' @@', output.data.decode('utf-8')) self.assertIn( '- ' + - 'Row 0', output.data) + 'Row 0', output.data.decode('utf-8')) self.assertIn( 'Commits \n ' + '' + '\n 3\n ', - output.data) + output.data.decode('utf-8')) self.assertIn( - 'title="View file as of 000000">Šource', output.data) + u'title="View file as of 000000">Šource'.encode('utf-8'), + output.data + ) self.assertIn( - '
    file removed
    ', output.data) + '
    file removed
    ', + output.data.decode('utf-8')) output = self.app.get('/foo/bar') # No project registered in the DB @@ -1830,12 +1836,12 @@ class PagureFlaskRepotests(tests.Modeltests): # View in a branch output = self.app.get('/test/blob/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertTrue('
    ' in output.data) + self.assertTrue('
    ' in output.data.decode('utf-8')) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) self.assertTrue( - '' in output.data) + '' in output.data.decode('utf-8')) # Empty files should also be displayed tests.add_content_to_git( @@ -1847,19 +1853,19 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertIn( 'Raw', output.data) + 'title="View as raw">Raw', output.data.decode('utf-8')) self.assertIn( '
    \n' - ' \n
    ', output.data) + ' \n ', output.data.decode('utf-8')) # View what's supposed to be an image output = self.app.get('/test/blob/master/f/test.jpg') self.assertEqual(output.status_code, 200) self.assertIn( - 'Binary files cannot be rendered.
    ', output.data) + 'Binary files cannot be rendered.
    ', output.data.decode('utf-8')) self.assertIn( 'view the raw version', - output.data) + output.data.decode('utf-8')) # View by commit id repo = pygit2.Repository(os.path.join(self.path, 'repos', 'test.git')) @@ -1868,33 +1874,33 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/blob/%s/f/test.jpg' % commit.oid.hex) self.assertEqual(output.status_code, 200) self.assertIn( - 'Binary files cannot be rendered.
    ', output.data) - self.assertIn('/f/test.jpg">view the raw version', output.data) + 'Binary files cannot be rendered.
    ', output.data.decode('utf-8')) + self.assertIn('/f/test.jpg">view the raw version', output.data.decode('utf-8')) # View by image name -- somehow we support this output = self.app.get('/test/blob/sources/f/test.jpg') self.assertEqual(output.status_code, 200) self.assertIn( - 'Binary files cannot be rendered.
    ', output.data) - self.assertIn('/f/test.jpg">view the raw version', output.data) + 'Binary files cannot be rendered.
    ', output.data.decode('utf-8')) + self.assertIn('/f/test.jpg">view the raw version', output.data.decode('utf-8')) # View binary file output = self.app.get('/test/blob/sources/f/test_binary') self.assertEqual(output.status_code, 200) - self.assertIn('/f/test_binary">view the raw version', output.data) + self.assertIn('/f/test_binary">view the raw version', output.data.decode('utf-8')) self.assertTrue( 'Binary files cannot be rendered.
    ' - in output.data) + in output.data.decode('utf-8')) # View folder output = self.app.get('/test/blob/master/f/folder1') self.assertEqual(output.status_code, 200) self.assertIn( '', - output.data) - self.assertIn('Tree - test - Pagure', output.data) + output.data.decode('utf-8')) + self.assertIn('Tree - test - Pagure', output.data.decode('utf-8')) self.assertIn( - '
    ', output.data) + '', output.data.decode('utf-8')) # Verify the nav links correctly when viewing a nested folder/file. output = self.app.get('/test/blob/master/f/folder1/folder2/file') @@ -1903,7 +1909,7 @@ class PagureFlaskRepotests(tests.Modeltests): '
  • \n' ' ' '  folder2\n' - '
  • ', output.data) + ' ', output.data.decode('utf-8')) # View by image name -- with a non-existant file output = self.app.get('/test/blob/sources/f/testfoo.jpg') @@ -1919,16 +1925,18 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'text/html; charset=utf-8') - self.assertIn('  Šource', output.data) - self.assertIn('
     bar
     bar
    ', output.data) + self.assertIn( + u'  Šource'.encode('utf-8'), + output.data) + self.assertIn('
    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'data-line-number="1">', output.data.decode('utf-8')) self.assertTrue( '' - in output.data + in output.data.decode('utf-8') or - '' in output.data + '' in output.data.decode('utf-8') ) # Add a fork of a fork @@ -1958,17 +1966,17 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertIn( '
  • \n' '   ' - 'folder2\n
  • ', output.data) + 'folder2\n ', output.data.decode('utf-8')) output = self.app.get('/fork/pingou/test3/blob/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertIn('
    Row 0
    Row 0
    Row 0
    ', output.data) + self.assertIn('
    ', output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) self.assertIn( - '', output.data) + '', output.data.decode('utf-8')) @patch( 'pagure.lib.encoding_utils.decode', @@ -1991,7 +1999,7 @@ class PagureFlaskRepotests(tests.Modeltests): # View file output = self.app.get('/test/blob/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertIn('Binary files cannot be rendered.
    ', output.data) + self.assertIn('Binary files cannot be rendered.
    ', output.data.decode('utf-8')) def test_view_raw_file(self): """ Test the view_raw_file endpoint. """ @@ -2018,7 +2026,7 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'text/plain; charset=ascii') - self.assertTrue(':Author: Pierre-Yves Chibon' in output.data) + self.assertTrue(':Author: Pierre-Yves Chibon' in output.data.decode('utf-8')) # Add some more content to the repo tests.add_content_git_repo(os.path.join(self.path, 'repos', @@ -2036,12 +2044,12 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.headers['Content-Type'].lower(), 'text/plain; charset=ascii') self.assertEqual(output.status_code, 200) - self.assertTrue('foo\n bar' in output.data) + self.assertTrue('foo\n bar' in output.data.decode('utf-8')) # View what's supposed to be an image output = self.app.get('/test/raw/master/f/test.jpg') self.assertEqual(output.status_code, 200) - self.assertTrue(output.data.startswith('\x00\x00\x01\x00')) + self.assertTrue(output.data.startswith(b'\x00\x00\x01\x00')) # View by commit id repo = pygit2.Repository(os.path.join(self.path, 'repos', 'test.git')) @@ -2049,19 +2057,19 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/raw/%s/f/test.jpg' % commit.oid.hex) self.assertEqual(output.status_code, 200) - self.assertTrue(output.data.startswith('\x00\x00\x01\x00')) + self.assertTrue(output.data.startswith(b'\x00\x00\x01\x00')) # View by image name -- somehow we support this output = self.app.get('/test/raw/sources/f/test.jpg') self.assertEqual(output.status_code, 200) - self.assertTrue(output.data.startswith('\x00\x00\x01\x00')) + self.assertTrue(output.data.startswith(b'\x00\x00\x01\x00')) # View binary file output = self.app.get('/test/raw/sources/f/test_binary') self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'application/octet-stream') - self.assertTrue(output.data.startswith('\x00\x00\x01\x00')) + self.assertTrue(output.data.startswith(b'\x00\x00\x01\x00')) # View folder output = self.app.get('/test/raw/master/f/folder1') @@ -2080,12 +2088,12 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'text/plain; charset=ascii') - self.assertTrue(output.data.startswith( + self.assertTrue(output.data.decode('utf-8').startswith( 'diff --git a/test_binary b/test_binary\n')) output = self.app.get('/test/raw/%s' % commit.oid.hex) self.assertEqual(output.status_code, 200) - self.assertTrue(output.data.startswith( + self.assertTrue(output.data.decode('utf-8').startswith( 'diff --git a/test_binary b/test_binary\n')) # Add a fork of a fork @@ -2112,7 +2120,7 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'text/plain; charset=ascii') - self.assertTrue('foo\n bar' in output.data) + self.assertTrue('foo\n bar' in output.data.decode('utf-8')) def test_view_blame_file(self): """ Test the view_blame_file endpoint. """ @@ -2150,25 +2158,25 @@ class PagureFlaskRepotests(tests.Modeltests): # View in master branch output = self.app.get('/test/blame/sources') self.assertEqual(output.status_code, 200) - self.assertIn(b'
     barRow 0
     barRow 0
    ', output.data) + self.assertIn('
    ', output.data.decode('utf-8')) self.assertIn( - b'', output.data) + '', output.data.decode('utf-8')) self.assertIn( - b'', output.data) - data = regex.findall(output.data) + b'', output.data.decode('utf-8')) + data = regex.findall(output.data.decode('utf-8')) self.assertEqual(len(data), 2) # View in feature branch output = self.app.get('/test/blame/sources?identifier=feature') self.assertEqual(output.status_code, 200) - self.assertIn(b'
     bar
     bar
    ', output.data) + self.assertIn(b'
    ', output.data.decode('utf-8')) self.assertIn( b'', output.data) + b'data-line-number="1">', output.data.decode('utf-8')) self.assertIn( - b'', output.data) - data2 = regex.findall(output.data) + b'', output.data.decode('utf-8')) + data2 = regex.findall(output.data.decode('utf-8')) self.assertEqual(len(data2), 2) self.assertNotEqual(data, data2) @@ -2176,16 +2184,16 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/test/blame/test.jpg') self.assertEqual(output.status_code, 400) self.assertIn( - b'400 Bad Request', output.data) + '400 Bad Request', output.data.decode('utf-8')) self.assertIn( - b'

    Binary files cannot be blamed

    ', output.data) + '

    Binary files cannot be blamed

    ', output.data.decode('utf-8')) # View folder output = self.app.get('/test/blame/folder1') self.assertEqual(output.status_code, 404) - self.assertIn("Page not found :'( - Pagure", output.data) + self.assertIn("Page not found :'( - Pagure", output.data.decode('utf-8')) self.assertIn( - '

    Page not found (404)

    ', output.data) + '

    Page not found (404)

    ', output.data.decode('utf-8')) # View by image name -- with a non-existant file output = self.app.get('/test/blame/testfoo.jpg') @@ -2201,16 +2209,21 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertEqual(output.headers['Content-Type'].lower(), 'text/html; charset=utf-8') - self.assertIn('  Šource', output.data) - self.assertIn('
     bar
     bar
    ', output.data) + self.assertIn( + u'  Šource'.encode('utf-8'), + output.data + ) + self.assertIn( + '
    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'data-line-number="1">', output.data.decode('utf-8')) self.assertTrue( '' - in output.data + in output.data.decode('utf-8') or - '' in output.data + '' + in output.data.decode('utf-8') ) # Add a fork of a fork @@ -2238,12 +2251,12 @@ class PagureFlaskRepotests(tests.Modeltests): output = self.app.get('/fork/pingou/test3/blame/sources') self.assertEqual(output.status_code, 200) - self.assertIn('
    Row 0
    Row 0
    Row 0
    ', output.data) + self.assertIn('
    ', output.data.decode('utf-8')) self.assertIn( '', output.data) + 'data-line-number="1">', output.data.decode('utf-8')) self.assertIn( - '', output.data) + '', output.data.decode('utf-8')) def test_view_commit(self): """ Test the view_commit endpoint. """ @@ -2272,15 +2285,15 @@ class PagureFlaskRepotests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertTrue( '', output.data.decode('utf-8')) + self.assertIn('Tree - test3 - Pagure', output.data.decode('utf-8')) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) self.assertTrue( '' - in output.data) + in output.data.decode('utf-8')) self.assertFalse( - 'No content found in this repository' in output.data) + 'No content found in this repository' in output.data.decode('utf-8')) output = self.app.get( '/fork/pingou/test3/blob/master/f/folder1/folder2') self.assertEqual(output.status_code, 200) self.assertTrue( '' in output.data) + 'f/folder1/folder2/file%C5%A0">' in output.data.decode('utf-8')) @patch.dict('pagure.APP.config', {'ENABLE_DEL_PROJECTS': False}) @patch('pagure.lib.notify.send_email') @@ -2682,10 +2695,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 6', output.data) + 'class="label label-default">6', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # add issues repo = pagure.get_authorized_project(self.session, 'test') @@ -2774,10 +2787,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 6', output.data) + 'class="label label-default">6', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) output = self.app.post('/test/delete', follow_redirects=True) self.assertEqual(output.status_code, 404) @@ -2811,10 +2824,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 6', output.data) + 'class="label label-default">6', output.data.decode('utf-8')) self.assertIn( 'Forks 1', - output.data) + output.data.decode('utf-8')) output = self.app.post( '/fork/pingou/test3/delete', follow_redirects=True) @@ -2917,10 +2930,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Only git repo item = pagure.lib.model.Project( @@ -2938,10 +2951,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Only git and doc repo item = pagure.lib.model.Project( @@ -2959,10 +2972,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # All repo there item = pagure.lib.model.Project( @@ -2988,10 +3001,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 3', output.data) + 'class="label label-default">3', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # add issues repo = pagure.get_authorized_project(self.session, 'test') @@ -3080,19 +3093,19 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 3', output.data) + 'class="label label-default">3', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) output = self.app.post('/test/delete', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertEqual(repo, None) @@ -3124,20 +3137,20 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 1', - output.data) + output.data.decode('utf-8')) output = self.app.post( '/fork/pingou/test3/delete', follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 2', output.data) + 'class="label label-default">2', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) @patch.dict('pagure.APP.config', {'TICKETS_FOLDER': None}) @patch('pagure.lib.notify.send_email', MagicMock(return_value=True)) @@ -3214,10 +3227,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # add user repo = pagure.get_authorized_project(self.session, 'test') @@ -3242,10 +3255,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertNotEqual(repo, None) repo = pagure.get_authorized_project(self.session, 'test2') @@ -3256,10 +3269,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 0', output.data) + 'class="label label-default">0', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Check after repo = pagure.get_authorized_project(self.session, 'test') @@ -3302,10 +3315,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Create group msg = pagure.lib.add_group( @@ -3348,10 +3361,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertNotEqual(repo, None) @@ -3360,10 +3373,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 0', output.data) + 'class="label label-default">0', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Check after repo = pagure.get_authorized_project(self.session, 'test') @@ -3404,10 +3417,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Create the issue repo = pagure.get_authorized_project(self.session, 'test') @@ -3439,10 +3452,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n My Projects 1', output.data) + 'class="label label-default">1', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) repo = pagure.get_authorized_project(self.session, 'test') self.assertNotEqual(repo, None) repo = pagure.get_authorized_project(self.session, 'test2') @@ -3453,10 +3466,10 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Projects 0', output.data) + 'class="label label-default">0', output.data.decode('utf-8')) self.assertIn( 'Forks 0', - output.data) + output.data.decode('utf-8')) # Check after repo = pagure.get_authorized_project(self.session, 'test') @@ -3479,9 +3492,9 @@ index 0000000..fb7093d pagure.APP.config['WEBHOOK'] = True output = self.app.get('/new/') self.assertEqual(output.status_code, 200) - self.assertIn('Create new Project', output.data) + self.assertIn('Create new Project', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] output = self.app.post('/foo/hook_token') @@ -3516,7 +3529,7 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '\n New hook token generated', - output.data) + output.data.decode('utf-8')) pagure.APP.config['WEBHOOK'] = False repo = pagure.get_authorized_project(self.session, 'test') @@ -3537,9 +3550,9 @@ index 0000000..fb7093d with tests.user_set(pagure.APP, user): output = self.app.get('/new/') self.assertEqual(output.status_code, 200) - self.assertIn('Create new Project', output.data) + self.assertIn('Create new Project', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] output = self.app.post('/foo/regenerate') @@ -3586,7 +3599,7 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '\n Tickets git repo updated', - output.data) + output.data.decode('utf-8')) # Create a request to play with repo = pagure.get_authorized_project(self.session, 'test') @@ -3609,7 +3622,7 @@ index 0000000..fb7093d self.assertEqual(output.status_code, 200) self.assertIn( '\n Requests git repo updated', - output.data) + output.data.decode('utf-8')) def test_view_tags(self): """ Test the view_tags endpoint. """ @@ -3627,7 +3640,7 @@ index 0000000..fb7093d output = self.app.get('/test/releases') self.assertEqual(output.status_code, 200) - self.assertIn('This project has not been tagged.', output.data) + self.assertIn('This project has not been tagged.', output.data.decode('utf-8')) # Add a README to the git repo - First commit tests.add_readme_git_repo(os.path.join(self.path, 'repos', 'test.git')) @@ -3640,9 +3653,9 @@ index 0000000..fb7093d output = self.app.get('/test/releases') self.assertEqual(output.status_code, 200) - self.assertIn('0.0.1', output.data) - self.assertIn('', output.data) - self.assertTrue(output.data.count('tagid'), 1) + self.assertIn('0.0.1', output.data.decode('utf-8')) + self.assertIn('', output.data.decode('utf-8')) + self.assertTrue(output.data.decode('utf-8').count('tagid'), 1) def test_edit_file(self): """ Test the edit_file endpoint. """ @@ -3697,10 +3710,10 @@ index 0000000..fb7093d '  master' '
  • ' '  sources
  • ', - output.data) + output.data.decode('utf-8')) self.assertIn( '', - output.data) + output.data.decode('utf-8')) # Verify the nav links correctly when editing a file. output = self.app.get('/test/blob/master/f/folder1/folder2/file') @@ -3709,20 +3722,20 @@ index 0000000..fb7093d '
  • \n' ' ' '  folder2\n' - '
  • ', output.data) + ' ', output.data.decode('utf-8')) - csrf_token = output.data.split( + csrf_token = output.data.decode('utf-8').split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] # View what's supposed to be an image output = self.app.get('/test/edit/master/f/test.jpg') self.assertEqual(output.status_code, 400) - self.assertIn('

    Cannot edit binary files

    ', output.data) + self.assertIn('

    Cannot edit binary files

    ', output.data.decode('utf-8')) # Check file before the commit: output = self.app.get('/test/raw/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertEqual(output.data, 'foo\n bar') + self.assertEqual(output.data.decode('utf-8'), 'foo\n bar') # No CSRF Token data = { @@ -3733,25 +3746,25 @@ index 0000000..fb7093d output = self.app.post('/test/edit/master/f/sources', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Edit - test - Pagure', output.data) + 'Edit - test - Pagure', output.data.decode('utf-8')) # Check that nothing changed output = self.app.get('/test/raw/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertEqual(output.data, 'foo\n bar') + self.assertEqual(output.data.decode('utf-8'), 'foo\n bar') # Missing email data['csrf_token'] = csrf_token output = self.app.post('/test/edit/master/f/sources', data=data) self.assertEqual(output.status_code, 200) self.assertIn( - 'Edit - test - Pagure', output.data) + 'Edit - test - Pagure', output.data.decode('utf-8')) # Invalid email data['email'] = 'pingou@fp.o' output = self.app.post('/test/edit/master/f/sources', data=data) self.assertIn( - 'Edit - test - Pagure', output.data) + 'Edit - test - Pagure', output.data.decode('utf-8')) # Works data['email'] = 'bar@pingou.com' @@ -3761,13 +3774,13 @@ index 0000000..fb7093d follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertIn( - 'Commits - test - Pagure', output.data) - self.assertIn('test commit', output.data) + 'Commits - test - Pagure', output.data.decode('utf-8')) + self.assertIn('test commit', output.data.decode('utf-8')) # Check file after the commit: output = self.app.get('/test/raw/master/f/sources') self.assertEqual(output.status_code, 200) - self.assertEqual(output.data, 'foo\n bar\n baz') + self.assertEqual(output.data.decode('utf-8'), 'foo\n bar\n baz') # Add a fork of a fork item = pagure.lib.model.Project( @@ -3797,7 +3810,7 @@ index 0000000..fb7093d '
  •   ' - 'folder2\n
  • ', output.data) + 'folder2\n ', output.data.decode('utf-8')) output = self.app.get('/fork/pingou/test3/edit/master/f/sources') self.assertEqual(output.status_code, 200) @@ -3806,10 +3819,10 @@ index 0000000..fb7093d '  master' '
  • ' '  sources' - '
  • ', output.data) + '', output.data.decode('utf-8')) self.assertIn( '', output.data) + '', output.data.decode('utf-8')) @patch.dict('pagure.APP.config', {'LOCAL_SSH_KEY': False}) @patch('pagure.ui.app.admin_session_timedout') @@ -608,10 +608,10 @@ class PagureFlaskApptests(tests.Modeltests): self.assertEqual(output.status_code, 200) self.assertIn( '
    \n Basic Information\n' - '
    ', output.data) + '
    ', output.data.decode('utf-8')) self.assertNotIn( '', output.data) + '', output.data.decode('utf-8')) # Before user = pagure.lib.get_user(self.session, 'foo') @@ -631,13 +631,14 @@ class PagureFlaskApptests(tests.Modeltests): output = self.app.post( '/settings/', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) - self.assertNotIn('Public ssh key updated', output.data) + self.assertNotIn('Public ssh key updated', + output.data.decode('utf-8')) self.assertIn( '
    \n Basic Information\n' - '
    ', output.data) + '
    ', output.data.decode('utf-8')) self.assertNotIn( '
     barRow 0
     barRow 0