# -*- coding: utf-8 -*-

"""
 (c) 2015-2018 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>
   Karsten Hopp <karsten@redhat.com>

"""

from __future__ import unicode_literals, absolute_import

import datetime
import json
import unittest
import shutil
import sys
import tempfile
import os

import pygit2
from celery.result import EagerResult
from mock import patch, Mock

sys.path.insert(
    0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
)

import pagure.flask_app
import pagure.lib.query
import tests
from pagure.lib.repo import PagureRepo


class PagureFlaskApiProjecttests(tests.Modeltests):
    """ Tests for the flask API of pagure for issue """

    def setUp(self):
        super(PagureFlaskApiProjecttests, self).setUp()
        self.gga_patcher = patch(
            "pagure.lib.tasks.generate_gitolite_acls.delay"
        )
        self.mock_gen_acls = self.gga_patcher.start()
        task_result = EagerResult("abc-1234", True, "SUCCESS")
        self.mock_gen_acls.return_value = task_result

    def tearDown(self):
        self.gga_patcher.stop()
        super(PagureFlaskApiProjecttests, self).tearDown()

    def test_api_git_tags(self):
        """ Test the api_git_tags method of the flask api. """
        tests.create_projects(self.session)

        # Create a git repo to play with
        gitrepo = os.path.join(self.path, "repos", "test.git")
        repo = pygit2.init_repository(gitrepo, bare=True)

        newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
        repopath = os.path.join(newpath, "test")
        clone_repo = pygit2.clone_repository(gitrepo, repopath)

        # Create a file in that git repo
        with open(os.path.join(repopath, "sources"), "w") as stream:
            stream.write("foo\n bar")
        clone_repo.index.add("sources")
        clone_repo.index.write()

        # Commits the files added
        tree = clone_repo.index.write_tree()
        author = pygit2.Signature("Alice Author", "alice@authors.tld")
        committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
        clone_repo.create_commit(
            "refs/heads/master",  # the name of the reference to update
            author,
            committer,
            "Add sources file for testing",
            # binary string representing the tree object ID
            tree,
            # list of binary strings representing parents of the new commit
            [],
        )
        refname = "refs/heads/master:refs/heads/master"
        ori_remote = clone_repo.remotes[0]
        PagureRepo.push(ori_remote, refname)

        # Tag our first commit
        first_commit = repo.revparse_single("HEAD")
        tagger = pygit2.Signature("Alice Doe", "adoe@example.com", 12347, 0)
        repo.create_tag(
            "0.0.1",
            first_commit.oid.hex,
            pygit2.GIT_OBJ_COMMIT,
            tagger,
            "Release 0.0.1",
        )

        # Check tags
        output = self.app.get("/api/0/test/git/tags")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"tags": ["0.0.1"], "total_tags": 1})

        # Check tags with commits
        output = self.app.get("/api/0/test/git/tags?with_commits=True")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["tags"]["0.0.1"] = "bb8fa2aa199da08d6085e1c9badc3d83d188d38c"
        self.assertDictEqual(
            data,
            {
                "tags": {"0.0.1": "bb8fa2aa199da08d6085e1c9badc3d83d188d38c"},
                "total_tags": 1,
            },
        )

        shutil.rmtree(newpath)

    def test_api_git_branches(self):
        """ Test the api_git_branches method of the flask api. """
        # Create a git repo to add branches to
        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos", "test.git")
        tests.add_content_git_repo(repo_path)
        new_repo_path = tempfile.mkdtemp(prefix="pagure-api-git-branches-test")
        clone_repo = pygit2.clone_repository(repo_path, new_repo_path)

        # Create two other branches based on master
        for branch in ["pats-win-49", "pats-win-51"]:
            clone_repo.create_branch(branch, clone_repo.head.peel())
            refname = "refs/heads/{0}:refs/heads/{0}".format(branch)
            PagureRepo.push(clone_repo.remotes[0], refname)

        # Check that the branches show up on the API
        output = self.app.get("/api/0/test/git/branches")
        # Delete the cloned git repo after the API call
        shutil.rmtree(new_repo_path)

        # Verify the API data
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "branches": ["master", "pats-win-49", "pats-win-51"],
                "total_branches": 3,
            },
        )

    def test_api_git_branches_empty_repo(self):
        """ Test the api_git_branches method of the flask api when the repo is
        empty.
        """
        # Create a git repo without any branches
        tests.create_projects(self.session)
        repo_base_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repo_base_path)

        # Check that no branches show up on the API
        output = self.app.get("/api/0/test/git/branches")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"branches": [], "total_branches": 0})

    def test_api_git_branches_no_repo(self):
        """ Test the api_git_branches method of the flask api when there is no
        repo on a project.
        """
        tests.create_projects(self.session)
        output = self.app.get("/api/0/test/git/branches")
        self.assertEqual(output.status_code, 404)

    def test_api_git_urls(self):
        """ Test the api_project_git_urls method of the flask api.
        """
        tests.create_projects(self.session)
        output = self.app.get("/api/0/test/git/urls")
        self.assertEqual(output.status_code, 200)
        expected_rv = {
            "urls": {
                "git": "git://localhost.localdomain/test.git",
                "ssh": "ssh://git@localhost.localdomain/test.git",
            },
            "total_urls": 2,
        }
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, expected_rv)

    def test_api_git_urls_no_project(self):
        """ Test the api_project_git_urls method of the flask api when there is
        no project.
        """
        output = self.app.get("/api/0/test1234/git/urls")
        self.assertEqual(output.status_code, 404)
        expected_rv = {
            "error": "Project not found",
            "error_code": "ENOPROJECT",
        }
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, expected_rv)

    @patch.dict("pagure.config.config", {"PRIVATE_PROJECTS": True})
    def test_api_git_urls_private_project(self):
        """ Test the api_project_git_urls method of the flask api when the
        project is private.
        """
        tests.create_projects(self.session)
        tests.create_tokens(self.session)
        tests.create_tokens_acl(self.session, "aaabbbcccddd")
        headers = {"Authorization": "token aaabbbcccddd"}

        test_project = pagure.lib.query._get_project(self.session, "test")
        test_project.private = True
        self.session.add(test_project)
        self.session.commit()

        output = self.app.get("/api/0/test/git/urls", headers=headers)
        self.assertEqual(output.status_code, 200)
        expected_rv = {
            "urls": {
                "git": "git://localhost.localdomain/test.git",
                "ssh": "ssh://git@localhost.localdomain/test.git",
            },
            "total_urls": 2,
        }
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, expected_rv)

    @patch.dict("pagure.config.config", {"PRIVATE_PROJECTS": True})
    def test_api_git_urls_private_project_no_login(self):
        """ Test the api_project_git_urls method of the flask api when the
        project is private and the user is not logged in.
        """
        tests.create_projects(self.session)
        test_project = pagure.lib.query._get_project(self.session, "test")
        test_project.private = True
        self.session.add(test_project)
        self.session.commit()

        output = self.app.get("/api/0/test/git/urls")
        self.assertEqual(output.status_code, 404)
        expected_rv = {
            "error": "Project not found",
            "error_code": "ENOPROJECT",
        }
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, expected_rv)

    def test_api_projects_pattern(self):
        """ Test the api_projects method of the flask api. """
        tests.create_projects(self.session)

        output = self.app.get("/api/0/projects?pattern=test")
        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"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "pattern": "test",
                "per_page": 20,
                "short": False,
                "tags": [],
                "username": 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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "test",
                    "id": 1,
                    "milestones": {},
                    "name": "test",
                    "namespace": None,
                    "parent": None,
                    "priorities": {},
                    "tags": [],
                    "user": {"fullname": "PY C", "name": "pingou"},
                }
            ],
            "total_projects": 1,
        }
        self.assertDictEqual(data, expected_data)

    def test_api_projects_pattern_short(self):
        """ Test the api_projects method of the flask api. """
        tests.create_projects(self.session)

        output = self.app.get("/api/0/projects?pattern=te*&short=1")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "pattern": "te*",
                "per_page": 20,
                "short": True,
                "tags": [],
                "username": None,
            },
            "projects": [
                {
                    "description": "test project #1",
                    "fullname": "test",
                    "name": "test",
                    "namespace": None,
                },
                {
                    "description": "test project #2",
                    "fullname": "test2",
                    "name": "test2",
                    "namespace": None,
                },
                {
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "name": "test3",
                    "namespace": "somenamespace",
                },
            ],
            "total_projects": 3,
        }
        self.maxDiff = None
        self.assertDictEqual(data, expected_data)

    def test_api_projects_owner(self):
        """ Test the api_projects method of the flask api. """
        tests.create_projects(self.session)

        output = self.app.get("/api/0/projects?owner=foo")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": "foo",
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": False,
                "tags": [],
                "username": None,
            },
            "projects": [],
            "total_projects": 0,
        }
        self.maxDiff = None
        self.assertDictEqual(data, expected_data)

    def test_api_projects_not_owner(self):
        """ Test the api_projects method of the flask api. """
        tests.create_projects(self.session)

        output = self.app.get("/api/0/projects?owner=!foo&short=1")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": "!foo",
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": True,
                "tags": [],
                "username": None,
            },
            "projects": [
                {
                    "description": "test project #1",
                    "fullname": "test",
                    "name": "test",
                    "namespace": None,
                },
                {
                    "description": "test project #2",
                    "fullname": "test2",
                    "name": "test2",
                    "namespace": None,
                },
                {
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "name": "test3",
                    "namespace": "somenamespace",
                },
            ],
            "total_projects": 3,
        }
        self.maxDiff = None
        self.assertDictEqual(data, expected_data)

    def test_api_projects(self):
        """ Test the api_projects method of the flask api. """
        tests.create_projects(self.session)

        # Check before adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(repo.tags, [])

        # Adding a tag
        output = pagure.lib.query.update_tags(
            self.session, repo, "infra", "pingou"
        )
        self.assertEqual(output, ["Project tagged with: infra"])

        # Check after adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(len(repo.tags), 1)
        self.assertEqual(repo.tags_text, ["infra"])

        # Check the API
        output = self.app.get("/api/0/projects?tags=inf")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        null = None
        del data["pagination"]
        self.assertDictEqual(
            data,
            {
                "total_projects": 0,
                "projects": [],
                "args": {
                    "fork": None,
                    "namespace": None,
                    "owner": None,
                    "page": 1,
                    "pattern": None,
                    "per_page": 20,
                    "short": False,
                    "tags": ["inf"],
                    "username": None,
                },
            },
        )
        output = self.app.get("/api/0/projects?tags=infra")
        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"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": False,
                "tags": ["infra"],
                "username": 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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "test",
                    "id": 1,
                    "milestones": {},
                    "name": "test",
                    "namespace": None,
                    "parent": None,
                    "priorities": {},
                    "tags": ["infra"],
                    "user": {"fullname": "PY C", "name": "pingou"},
                }
            ],
            "total_projects": 1,
        }
        self.assertDictEqual(data, expected_data)

        output = self.app.get("/api/0/projects?owner=pingou")
        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"
        data["projects"][1]["date_created"] = "1436527638"
        data["projects"][1]["date_modified"] = "1436527638"
        data["projects"][2]["date_created"] = "1436527638"
        data["projects"][2]["date_modified"] = "1436527638"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": "pingou",
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": False,
                "tags": [],
                "username": 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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "test",
                    "id": 1,
                    "milestones": {},
                    "name": "test",
                    "namespace": None,
                    "parent": None,
                    "priorities": {},
                    "tags": ["infra"],
                    "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",
                    "date_modified": "1436527638",
                    "description": "test project #2",
                    "fullname": "test2",
                    "url_path": "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",
                    "date_modified": "1436527638",
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "url_path": "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)

        output = self.app.get("/api/0/projects?username=pingou")
        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"
        data["projects"][1]["date_created"] = "1436527638"
        data["projects"][1]["date_modified"] = "1436527638"
        data["projects"][2]["date_created"] = "1436527638"
        data["projects"][2]["date_modified"] = "1436527638"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": False,
                "tags": [],
                "username": "pingou",
            },
            "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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "test",
                    "id": 1,
                    "milestones": {},
                    "name": "test",
                    "namespace": None,
                    "parent": None,
                    "priorities": {},
                    "tags": ["infra"],
                    "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",
                    "date_modified": "1436527638",
                    "description": "test project #2",
                    "fullname": "test2",
                    "url_path": "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",
                    "date_modified": "1436527638",
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "url_path": "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)

        output = self.app.get("/api/0/projects?username=pingou&tags=infra")
        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"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "pattern": None,
                "per_page": 20,
                "short": False,
                "tags": ["infra"],
                "username": "pingou",
            },
            "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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "test",
                    "id": 1,
                    "milestones": {},
                    "name": "test",
                    "namespace": None,
                    "parent": None,
                    "priorities": {},
                    "tags": ["infra"],
                    "user": {"fullname": "PY C", "name": "pingou"},
                }
            ],
            "total_projects": 1,
        }
        self.assertDictEqual(data, expected_data)

        output = self.app.get("/api/0/projects?namespace=somenamespace")
        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"
        del data["pagination"]
        expected_data = {
            "args": {
                "fork": None,
                "owner": None,
                "page": 1,
                "namespace": "somenamespace",
                "per_page": 20,
                "pattern": None,
                "short": False,
                "tags": [],
                "username": 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",
                    "date_modified": "1436527638",
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "url_path": "somenamespace/test3",
                    "id": 3,
                    "milestones": {},
                    "name": "test3",
                    "namespace": "somenamespace",
                    "parent": None,
                    "priorities": {},
                    "tags": [],
                    "user": {"fullname": "PY C", "name": "pingou"},
                }
            ],
            "total_projects": 1,
        }
        self.assertDictEqual(data, expected_data)

    def test_api_project(self):
        """ Test the api_project method of the flask api. """
        tests.create_projects(self.session)

        # Check before adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(repo.tags, [])

        # Adding a tag
        output = pagure.lib.query.update_tags(
            self.session, repo, "infra", "pingou"
        )
        self.assertEqual(output, ["Project tagged with: infra"])

        # Check after adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(len(repo.tags), 1)
        self.assertEqual(repo.tags_text, ["infra"])

        # Check the API

        # Non-existing project
        output = self.app.get("/api/0/random")
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"error_code": "ENOPROJECT", "error": "Project not found"}
        )

        # Existing project
        output = self.app.get("/api/0/test")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1436527638"
        data["date_modified"] = "1436527638"
        expected_data = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1436527638",
            "date_modified": "1436527638",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": ["infra"],
            "user": {"fullname": "PY C", "name": "pingou"},
        }
        self.assertDictEqual(data, expected_data)

    def test_api_project_group(self):
        """ Test the api_project method of the flask api. """
        tests.create_projects(self.session)
        repo = pagure.lib.query.get_authorized_project(self.session, "test")

        # Adding a tag
        output = pagure.lib.query.update_tags(
            self.session, repo, "infra", "pingou"
        )
        self.assertEqual(output, ["Project tagged with: infra"])

        # Check after adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(len(repo.tags), 1)
        self.assertEqual(repo.tags_text, ["infra"])

        # Add a group to the project
        msg = pagure.lib.query.add_group(
            self.session,
            group_name="some_group",
            display_name="Some Group",
            description=None,
            group_type="bar",
            user="foo",
            is_admin=False,
            blacklist=[],
        )
        self.session.commit()

        project = pagure.lib.query.get_authorized_project(self.session, "test")
        group = pagure.lib.query.search_groups(
            self.session, group_name="some_group"
        )

        pagure.lib.query.add_group_to_project(
            self.session,
            project,
            new_group="some_group",
            user="pingou",
            access="commit",
            create=False,
            is_admin=True,
        )
        self.session.commit()

        # Check the API

        # Existing project
        output = self.app.get("/api/0/test?expand_group=1")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1436527638"
        data["date_modified"] = "1436527638"
        expected_data = {
            "access_groups": {
                "admin": [],
                "commit": ["some_group"],
                "ticket": [],
            },
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1436527638",
            "date_modified": "1436527638",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "group_details": {"some_group": ["foo"]},
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": ["infra"],
            "user": {"fullname": "PY C", "name": "pingou"},
        }
        self.assertDictEqual(data, expected_data)

    def test_api_project_group_but_no_group(self):
        """ Test the api_project method of the flask api when asking for
        group details while there are none associated.
        """
        tests.create_projects(self.session)
        repo = pagure.lib.query.get_authorized_project(self.session, "test")

        # Adding a tag
        output = pagure.lib.query.update_tags(
            self.session, repo, "infra", "pingou"
        )
        self.assertEqual(output, ["Project tagged with: infra"])

        # Check after adding
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(len(repo.tags), 1)
        self.assertEqual(repo.tags_text, ["infra"])

        # Check the API

        # Existing project
        output = self.app.get("/api/0/test?expand_group=0")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1436527638"
        data["date_modified"] = "1436527638"
        expected_data = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1436527638",
            "date_modified": "1436527638",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": ["infra"],
            "user": {"fullname": "PY C", "name": "pingou"},
        }
        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.get_data(as_text=True))
        for i in range(3):
            data["projects"][i]["date_created"] = "1436527638"
            data["projects"][i]["date_modified"] = "1436527638"
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 1,
                "per_page": 20,
                "pattern": None,
                "short": False,
                "tags": [],
                "username": None,
            },
            "pagination": {
                "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",
                    "date_modified": "1436527638",
                    "description": "test project #1",
                    "fullname": "test",
                    "url_path": "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",
                    "date_modified": "1436527638",
                    "description": "test project #2",
                    "fullname": "test2",
                    "url_path": "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",
                    "date_modified": "1436527638",
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "url_path": "somenamespace/test3",
                    "id": 3,
                    "milestones": {},
                    "name": "test3",
                    "namespace": "somenamespace",
                    "parent": None,
                    "priorities": {},
                    "tags": [],
                    "user": {"fullname": "PY C", "name": "pingou"},
                },
            ],
            "total_projects": 3,
        }
        # Test URLs
        self.assertURLEqual(
            data["pagination"].pop("first"),
            "http://localhost/api/0/projects?per_page=20&page=1",
        )
        self.assertURLEqual(
            data["pagination"].pop("last"),
            "http://localhost/api/0/projects?per_page=20&page=1",
        )
        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.get_data(as_text=True))
        data["projects"][0]["date_created"] = "1436527638"
        data["projects"][0]["date_modified"] = "1436527638"
        expected_data = {
            "args": {
                "fork": None,
                "namespace": None,
                "owner": None,
                "page": 2,
                "per_page": 2,
                "pattern": None,
                "short": False,
                "tags": [],
                "username": None,
            },
            "pagination": {"next": None, "page": 2, "pages": 2, "per_page": 2},
            "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",
                    "date_modified": "1436527638",
                    "description": "namespaced test project",
                    "fullname": "somenamespace/test3",
                    "url_path": "somenamespace/test3",
                    "id": 3,
                    "milestones": {},
                    "name": "test3",
                    "namespace": "somenamespace",
                    "parent": None,
                    "priorities": {},
                    "tags": [],
                    "user": {"fullname": "PY C", "name": "pingou"},
                }
            ],
            "total_projects": 3,
        }
        self.assertURLEqual(
            data["pagination"].pop("first"),
            "http://localhost/api/0/projects?per_page=2&page=1",
        )
        self.assertURLEqual(
            data["pagination"].pop("prev"),
            "http://localhost/api/0/projects?per_page=2&page=1",
        )
        self.assertURLEqual(
            data["pagination"].pop("last"),
            "http://localhost/api/0/projects?per_page=2&page=2",
        )
        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.get_data(as_text=True))
        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.get_data(as_text=True))
        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, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertURLEqual(
            data["pagination"].pop("first"),
            "http://localhost/api/0/projects?per_page=20&page=1",
        )
        self.assertURLEqual(
            data["pagination"].pop("last"),
            "http://localhost/api/0/projects?per_page=20&page=1",
        )
        self.assertURLEqual(
            data["pagination"].pop("prev"),
            "http://localhost/api/0/projects?per_page=20&page=99998",
        )
        self.assertEqual(
            data,
            {
                "args": {
                    "fork": None,
                    "namespace": None,
                    "owner": None,
                    "page": 99999,
                    "pattern": None,
                    "per_page": 20,
                    "short": False,
                    "tags": [],
                    "username": None,
                },
                "pagination": {
                    "next": None,
                    "page": 99999,
                    "pages": 1,
                    "per_page": 20,
                },
                "projects": [],
                "total_projects": 3,
            },
        )

    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. """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"main_admin": "foo"}
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1496338274"
        data["date_modified"] = "1496338274"
        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["foo"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1496338274",
            "date_modified": "1496338274",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "fullname": "foo bar",
                "name": "foo",
            },
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_project_main_admin_retain_access(self):
        """ Test the api_modify_project method of the flask api when the
        request is to change the main_admin of the project and retain_access
        is true. """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test",
            headers=headers,
            data={"main_admin": "foo", "retain_access": True},
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1496338274"
        data["date_modified"] = "1496338274"
        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": ["pingou"],
                "commit": [],
                "owner": ["foo"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1496338274",
            "date_modified": "1496338274",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "fullname": "foo bar",
                "name": "foo",
            },
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_project_main_admin_retain_access_already_user(self):
        """ Test the api_modify_project method of the flask api when the
        request is to change the main_admin of the project and retain_access
        is true and the user becoming the main_admin already has access. """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        project = pagure.lib.query._get_project(self.session, "test")
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="commit",
        )
        self.session.commit()

        output = self.app.patch(
            "/api/0/test",
            headers=headers,
            data={"main_admin": "foo", "retain_access": True},
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1496338274"
        data["date_modified"] = "1496338274"
        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": ["pingou"],
                "commit": [],
                "owner": ["foo"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1496338274",
            "date_modified": "1496338274",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "fullname": "foo bar",
                "name": "foo",
            },
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_project_main_admin_json(self):
        """ Test the api_modify_project method of the flask api when the
        request is to change the main_admin of the project using JSON. """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        output = self.app.patch(
            "/api/0/test",
            headers=headers,
            data=json.dumps({"main_admin": "foo"}),
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1496338274"
        data["date_modified"] = "1496338274"
        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["foo"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1496338274",
            "date_modified": "1496338274",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "fullname": "foo bar",
                "name": "foo",
            },
        }
        self.assertEqual(data, expected_output)

    @patch.dict("pagure.config.config", {"PAGURE_ADMIN_USERS": "foo"})
    def test_api_modify_project_main_admin_as_site_admin(self):
        """ Test the api_modify_project method of the flask api when the
        request is to change the main_admin of the project and the user is a
        Pagure site admin. """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, user_id=2, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        # date before:
        project = pagure.lib.query.get_authorized_project(self.session, "test")
        date_before = project.date_modified
        self.assertIsNotNone(date_before)

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"main_admin": "foo"}
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1496338274"
        data["date_modified"] = "1496338274"
        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["foo"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1496338274",
            "date_modified": "1496338274",
            "description": "test project #1",
            "fullname": "test",
            "url_path": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "fullname": "foo bar",
                "name": "foo",
            },
        }
        self.assertEqual(data, expected_output)

        # date after:
        self.session = pagure.lib.query.create_session(self.dbpath)
        project = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertNotEqual(date_before, project.date_modified)

    def test_api_modify_project_main_admin_not_main_admin(self):
        """ Test the api_modify_project method of the flask api when the
        requester is not the main_admin of the project and requests to change
        the main_admin.
        """
        tests.create_projects(self.session)
        project_user = pagure.lib.query.model.ProjectUser(
            project_id=1, user_id=2, access="admin"
        )
        self.session.add(project_user)
        self.session.commit()
        tests.create_tokens(self.session, project_id=None, user_id=2)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"main_admin": "foo"}
        )
        self.assertEqual(output.status_code, 401)
        expected_error = {
            "error": (
                "Only the main admin can set the main admin of a " "project"
            ),
            "error_code": "ENOTMAINADMIN",
        }
        self.assertEqual(
            json.loads(output.get_data(as_text=True)), expected_error
        )

    def test_api_modify_project_not_admin(self):
        """ Test the api_modify_project method of the flask api when the
        requester is not an admin of the project.
        """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None, user_id=2)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"main_admin": "foo"}
        )
        self.assertEqual(output.status_code, 401)
        expected_error = {
            "error": "You are not allowed to modify this project",
            "error_code": "EMODIFYPROJECTNOTALLOWED",
        }
        self.assertEqual(
            json.loads(output.get_data(as_text=True)), expected_error
        )

    def test_api_modify_project_invalid_request(self):
        """ Test the api_modify_project method of the flask api when the
        request data is invalid.
        """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch("/api/0/test", headers=headers, data="invalid")
        self.assertEqual(output.status_code, 400)
        expected_error = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
        }
        self.assertEqual(
            json.loads(output.get_data(as_text=True)), expected_error
        )

    def test_api_modify_project_invalid_keys(self):
        """ Test the api_modify_project method of the flask api when the
        request data contains an invalid key.
        """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"invalid": "invalid"}
        )
        self.assertEqual(output.status_code, 400)
        expected_error = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
        }
        self.assertEqual(
            json.loads(output.get_data(as_text=True)), expected_error
        )

    def test_api_modify_project_invalid_new_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 to a main_admin
        that doesn't exist.
        """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")
        headers = {"Authorization": "token aaabbbcccddd"}

        output = self.app.patch(
            "/api/0/test", headers=headers, data={"main_admin": "tbrady"}
        )
        self.assertEqual(output.status_code, 400)
        expected_error = {
            "error": "No such user found",
            "error_code": "ENOUSER",
        }
        self.assertEqual(
            json.loads(output.get_data(as_text=True)), expected_error
        )

    def test_api_project_watchers(self):
        """ Test the api_project_watchers method of the flask api. """
        tests.create_projects(self.session)
        # The user is not logged in and the owner is watching issues implicitly
        output = self.app.get("/api/0/test/watchers")
        self.assertEqual(output.status_code, 200)
        expected_data = {
            "total_watchers": 1,
            "watchers": {"pingou": ["issues"]},
        }
        self.assertDictEqual(
            json.loads(output.get_data(as_text=True)), expected_data
        )

        user = tests.FakeUser(username="pingou")
        with tests.user_set(self.app.application, user):
            # Non-existing project
            output = self.app.get("/api/0/random/watchers")
            self.assertEqual(output.status_code, 404)
            data = json.loads(output.get_data(as_text=True))
            self.assertDictEqual(
                data,
                {"error_code": "ENOPROJECT", "error": "Project not found"},
            )

            # The owner is watching issues implicitly
            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 1,
                "watchers": {"pingou": ["issues"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            project = pagure.lib.query.get_authorized_project(
                self.session, "test"
            )

            # The owner is watching issues and commits explicitly
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "3"
            )
            self.session.commit()
            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 1,
                "watchers": {"pingou": ["issues", "commits"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner is watching issues explicitly
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "1"
            )
            self.session.commit()
            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 1,
                "watchers": {"pingou": ["issues"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner is watching commits explicitly
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "2"
            )
            self.session.commit()
            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 1,
                "watchers": {"pingou": ["commits"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner is watching commits explicitly and foo is watching
            # issues implicitly
            project_user = pagure.lib.model.ProjectUser(
                project_id=project.id, user_id=2, access="commit"
            )
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "2"
            )
            self.session.add(project_user)
            self.session.commit()

            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 2,
                "watchers": {"foo": ["issues"], "pingou": ["commits"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner and foo are watching issues implicitly
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "-1"
            )
            self.session.commit()

            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 2,
                "watchers": {"foo": ["issues"], "pingou": ["issues"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner and foo through group membership are watching issues
            # implicitly
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "-1"
            )
            project_membership = (
                self.session.query(pagure.lib.model.ProjectUser)
                .filter_by(user_id=2, project_id=project.id)
                .one()
            )
            self.session.delete(project_membership)
            self.session.commit()

            msg = pagure.lib.query.add_group(
                self.session,
                group_name="some_group",
                display_name="Some Group",
                description=None,
                group_type="bar",
                user="pingou",
                is_admin=False,
                blacklist=[],
            )
            self.session.commit()

            project = pagure.lib.query.get_authorized_project(
                self.session, "test"
            )
            group = pagure.lib.query.search_groups(
                self.session, group_name="some_group"
            )
            pagure.lib.query.add_user_to_group(
                self.session, "foo", group, "pingou", False
            )

            pagure.lib.query.add_group_to_project(
                self.session,
                project,
                new_group="some_group",
                user="pingou",
                access="commit",
                create=False,
                is_admin=True,
            )
            self.session.commit()

            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 2,
                "watchers": {"@some_group": ["issues"], "pingou": ["issues"]},
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

            # The owner is watching issues implicitly and foo will be watching
            # commits explicitly but is in a group with commit access
            pagure.lib.query.update_watch_status(
                self.session, project, "pingou", "-1"
            )
            pagure.lib.query.update_watch_status(
                self.session, project, "foo", "2"
            )
            self.session.commit()

            output = self.app.get("/api/0/test/watchers")
            self.assertEqual(output.status_code, 200)
            expected_data = {
                "total_watchers": 3,
                "watchers": {
                    "@some_group": ["issues"],
                    "foo": ["commits"],
                    "pingou": ["issues"],
                },
            }
            self.assertDictEqual(
                json.loads(output.get_data(as_text=True)), expected_data
            )

    def test_api_new_project(self):
        """ Test the api_new_project method of the flask api. """

        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)

        headers = {"Authorization": "token foo_token"}

        # Invalid token
        output = self.app.post("/api/0/new", headers=headers)
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            sorted(data.keys()), ["error", "error_code", "errors"]
        )
        self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
        self.assertEqual(
            pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
        )
        self.assertEqual(data["errors"], "Missing ACLs: create_project")

        headers = {"Authorization": "token aaabbbcccddd"}

        # No input
        output = self.app.post("/api/0/new", headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {
                    "name": ["This field is required."],
                    "description": ["This field is required."],
                },
            },
        )

        data = {"name": "test"}

        # Incomplete request
        output = self.app.post("/api/0/new", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"description": ["This field is required."]},
            },
        )

        data = {"name": "test", "description": "Just a small test project"}

        # Valid request but repo already exists
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'It is not possible to create the repo "test"',
                "error_code": "ENOCODE",
            },
        )

        data = {
            "name": "api1",
            "description": "Mighty mighty description",
            "avatar_email": 123,
        }

        # invalid avatar_email - number
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"avatar_email": ["avatar_email must be an email"]},
            },
        )

        data = {
            "name": "api1",
            "description": "Mighty mighty description",
            "avatar_email": [1, 2, 3],
        }

        # invalid avatar_email - list
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"avatar_email": ["avatar_email must be an email"]},
            },
        )

        data = {
            "name": "api1",
            "description": "Mighty mighty description",
            "avatar_email": True,
        }

        # invalid avatar_email - boolean
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"avatar_email": ["avatar_email must be an email"]},
            },
        )

        data = {
            "name": "api1",
            "description": "Mighty mighty description",
            "avatar_email": "mighty@email.com",
        }

        # valid avatar_email
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"message": 'Project "api1" created'})

        data = {
            "name": "test_42",
            "description": "Just another small test project",
        }

        # Valid request
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"message": 'Project "test_42" created'})

    @patch.dict(
        "pagure.config.config",
        {
            "PAGURE_ADMIN_USERS": ["pingou"],
            "ALLOW_ADMIN_IGNORE_EXISTING_REPOS": True,
        },
    )
    def test_adopt_repos(self):
        """ Test the new_project endpoint with existing git repo. """
        # Before
        projects = pagure.lib.query.search_projects(self.session)
        self.assertEqual(len(projects), 0)

        tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
        tests.add_content_git_repo(
            os.path.join(self.path, "repos", "test.git")
        )
        item = pagure.lib.model.Token(
            id="aaabbbcccddd",
            user_id=1,
            project_id=None,
            expiration=datetime.datetime.utcnow()
            + datetime.timedelta(days=10),
        )
        self.session.add(item)
        self.session.commit()
        tests.create_tokens_acl(self.session)

        headers = {"Authorization": "token aaabbbcccddd"}

        user = tests.FakeUser(username="pingou")
        with tests.user_set(self.app.application, user):
            input_data = {"name": "test", "description": "Project #1"}

            # Valid request
            output = self.app.post(
                "/api/0/new/", data=input_data, headers=headers
            )
            self.assertEqual(output.status_code, 400)
            data = json.loads(output.get_data(as_text=True))
            self.assertDictEqual(
                data,
                {
                    "error": "The main repo test.git already exists",
                    "error_code": "ENOCODE",
                },
            )

            input_data["ignore_existing_repos"] = "y"
            # Valid request
            output = self.app.post(
                "/api/0/new/", data=input_data, headers=headers
            )
            self.assertEqual(output.status_code, 200)
            data = json.loads(output.get_data(as_text=True))
            self.assertDictEqual(data, {"message": 'Project "test" created'})

    @patch.dict("pagure.config.config", {"PRIVATE_PROJECTS": True})
    def test_api_new_project_private(self):
        """ Test the api_new_project method of the flask api to create
        a private project. """

        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)

        headers = {"Authorization": "token aaabbbcccddd"}

        data = {
            "name": "test",
            "description": "Just a small test project",
            "private": True,
        }

        # Valid request
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Project "pingou/test" created'}
        )

    def test_api_new_project_user_token(self):
        """ Test the api_new_project method of the flask api. """
        tests.create_projects(self.session)
        tests.create_projects_git(os.path.join(self.path, "tickets"))
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session)

        headers = {"Authorization": "token foo_token"}

        # Invalid token
        output = self.app.post("/api/0/new", headers=headers)
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            sorted(data.keys()), ["error", "error_code", "errors"]
        )
        self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
        self.assertEqual(
            pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
        )
        self.assertEqual(data["errors"], "Missing ACLs: create_project")

        headers = {"Authorization": "token aaabbbcccddd"}

        # No input
        output = self.app.post("/api/0/new", headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {
                    "name": ["This field is required."],
                    "description": ["This field is required."],
                },
            },
        )

        data = {"name": "test"}

        # Incomplete request
        output = self.app.post("/api/0/new", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"description": ["This field is required."]},
            },
        )

        data = {"name": "test", "description": "Just a small test project"}

        # Valid request but repo already exists
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'It is not possible to create the repo "test"',
                "error_code": "ENOCODE",
            },
        )

        data = {
            "name": "test_42",
            "description": "Just another small test project",
        }

        # Valid request
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"message": 'Project "test_42" created'})

        # Project with a namespace
        pagure.config.config["ALLOWED_PREFIX"] = ["rpms"]
        data = {
            "name": "test_42",
            "namespace": "pingou",
            "description": "Just another small test project",
        }

        # Invalid namespace
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"namespace": ["Not a valid choice"]},
            },
        )

        data = {
            "name": "test_42",
            "namespace": "rpms",
            "description": "Just another small test project",
        }

        # All good
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Project "rpms/test_42" created'}
        )

    @patch.dict("pagure.config.config", {"USER_NAMESPACE": True})
    def test_api_new_project_user_ns(self):
        """ Test the api_new_project method of the flask api. """
        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)

        headers = {"Authorization": "token aaabbbcccddd"}

        # Create a project with the user namespace feature on
        data = {
            "name": "testproject",
            "description": "Just another small test project",
        }

        # Valid request
        output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Project "pingou/testproject" created'}
        )

        # Create a project with a namespace and the user namespace feature on
        data = {
            "name": "testproject2",
            "namespace": "testns",
            "description": "Just another small test project",
        }

        # Valid request
        with patch.dict(
            "pagure.config.config", {"ALLOWED_PREFIX": ["testns"]}
        ):
            output = self.app.post("/api/0/new/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Project "testns/testproject2" created'}
        )

    def test_api_fork_project(self):
        """ Test the api_fork_project method of the flask api. """
        tests.create_projects(self.session)
        for folder in ["docs", "tickets", "requests", "repos"]:
            tests.create_projects_git(
                os.path.join(self.path, folder), bare=True
            )
        tests.create_tokens(self.session)
        tests.create_tokens_acl(self.session)

        headers = {"Authorization": "token foo_token"}

        # Invalid token
        output = self.app.post("/api/0/fork", headers=headers)
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            sorted(data.keys()), ["error", "error_code", "errors"]
        )
        self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
        self.assertEqual(
            pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
        )
        self.assertEqual(data["errors"], "Missing ACLs: fork_project")

        headers = {"Authorization": "token aaabbbcccddd"}

        # No input
        output = self.app.post("/api/0/fork", headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"repo": ["This field is required."]},
            },
        )

        data = {"name": "test"}

        # Incomplete request
        output = self.app.post("/api/0/fork", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"repo": ["This field is required."]},
            },
        )

        data = {"repo": "test"}

        # Valid request
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Repo "test" cloned to "pingou/test"'}
        )

        data = {"repo": "test"}

        # project already forked
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'Repo "forks/pingou/test" already exists',
                "error_code": "ENOCODE",
            },
        )

        data = {"repo": "test", "username": "pingou"}

        # Fork already exists
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'Repo "forks/pingou/test" already exists',
                "error_code": "ENOCODE",
            },
        )

        data = {"repo": "test", "namespace": "pingou"}

        # Repo does not exists
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"error": "Project not found", "error_code": "ENOPROJECT"}
        )

    def test_api_fork_project_user_token(self):
        """ Test the api_fork_project method of the flask api. """
        tests.create_projects(self.session)
        for folder in ["docs", "tickets", "requests", "repos"]:
            tests.create_projects_git(
                os.path.join(self.path, folder), bare=True
            )
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session)

        headers = {"Authorization": "token foo_token"}

        # Invalid token
        output = self.app.post("/api/0/fork", headers=headers)
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            sorted(data.keys()), ["error", "error_code", "errors"]
        )
        self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
        self.assertEqual(
            pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
        )
        self.assertEqual(data["errors"], "Missing ACLs: fork_project")

        headers = {"Authorization": "token aaabbbcccddd"}

        # No input
        output = self.app.post("/api/0/fork", headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"repo": ["This field is required."]},
            },
        )

        data = {"name": "test"}

        # Incomplete request
        output = self.app.post("/api/0/fork", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": {"repo": ["This field is required."]},
            },
        )

        data = {"repo": "test"}

        # Valid request
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"message": 'Repo "test" cloned to "pingou/test"'}
        )

        data = {"repo": "test"}

        # project already forked
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'Repo "forks/pingou/test" already exists',
                "error_code": "ENOCODE",
            },
        )

        data = {"repo": "test", "username": "pingou"}

        # Fork already exists
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": 'Repo "forks/pingou/test" already exists',
                "error_code": "ENOCODE",
            },
        )

        data = {"repo": "test", "namespace": "pingou"}

        # Repo does not exists
        output = self.app.post("/api/0/fork/", data=data, headers=headers)
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data, {"error": "Project not found", "error_code": "ENOPROJECT"}
        )

    def test_api_generate_acls(self):
        """ Test the api_generate_acls method of the flask api """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(
            self.session, "aaabbbcccddd", "generate_acls_project"
        )
        headers = {"Authorization": "token aaabbbcccddd"}

        user = pagure.lib.query.get_user(self.session, "pingou")
        output = self.app.post(
            "/api/0/test/git/generateacls",
            headers=headers,
            data={"wait": False},
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "message": "Project ACL generation queued",
            "taskid": "abc-1234",
        }
        self.assertEqual(data, expected_output)
        self.mock_gen_acls.assert_called_once_with(
            name="test", namespace=None, user=None, group=None
        )

    def test_api_generate_acls_json(self):
        """ Test the api_generate_acls method of the flask api using JSON """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(
            self.session, "aaabbbcccddd", "generate_acls_project"
        )
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        user = pagure.lib.query.get_user(self.session, "pingou")

        output = self.app.post(
            "/api/0/test/git/generateacls",
            headers=headers,
            data=json.dumps({"wait": False}),
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "message": "Project ACL generation queued",
            "taskid": "abc-1234",
        }
        self.assertEqual(data, expected_output)
        self.mock_gen_acls.assert_called_once_with(
            name="test", namespace=None, user=None, group=None
        )

    def test_api_generate_acls_wait_true(self):
        """ Test the api_generate_acls method of the flask api when wait is
        set to True """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(
            self.session, "aaabbbcccddd", "generate_acls_project"
        )
        headers = {"Authorization": "token aaabbbcccddd"}

        task_result = Mock()
        task_result.id = "abc-1234"
        self.mock_gen_acls.return_value = task_result

        user = pagure.lib.query.get_user(self.session, "pingou")
        output = self.app.post(
            "/api/0/test/git/generateacls",
            headers=headers,
            data={"wait": True},
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {"message": "Project ACLs generated"}
        self.assertEqual(data, expected_output)
        self.mock_gen_acls.assert_called_once_with(
            name="test", namespace=None, user=None, group=None
        )
        self.assertTrue(task_result.get.called)

    def test_api_generate_acls_no_project(self):
        """ Test the api_generate_acls method of the flask api when the project
        doesn't exist """
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(
            self.session, "aaabbbcccddd", "generate_acls_project"
        )
        headers = {"Authorization": "token aaabbbcccddd"}

        user = pagure.lib.query.get_user(self.session, "pingou")
        output = self.app.post(
            "/api/0/test12345123/git/generateacls",
            headers=headers,
            data={"wait": False},
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error_code": "ENOPROJECT",
            "error": "Project not found",
        }
        self.assertEqual(data, expected_output)

    def test_api_new_git_branch(self):
        """ Test the api_new_branch method of the flask api """
        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repo_path, bare=True)
        tests.add_content_git_repo(os.path.join(repo_path, "test.git"))
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")
        headers = {"Authorization": "token aaabbbcccddd"}
        args = {"branch": "test123"}
        output = self.app.post(
            "/api/0/test/git/branch", headers=headers, data=args
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {"message": "Project branch was created"}
        self.assertEqual(data, expected_output)
        git_path = os.path.join(self.path, "repos", "test.git")
        repo_obj = pygit2.Repository(git_path)
        self.assertIn("test123", repo_obj.listall_branches())

    def test_api_new_git_branch_json(self):
        """ Test the api_new_branch method of the flask api """
        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repo_path, bare=True)
        tests.add_content_git_repo(os.path.join(repo_path, "test.git"))
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        args = {"branch": "test123"}
        output = self.app.post(
            "/api/0/test/git/branch", headers=headers, data=json.dumps(args)
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {"message": "Project branch was created"}
        self.assertEqual(data, expected_output)
        git_path = os.path.join(self.path, "repos", "test.git")
        repo_obj = pygit2.Repository(git_path)
        self.assertIn("test123", repo_obj.listall_branches())

    def test_api_new_git_branch_from_branch(self):
        """ Test the api_new_branch method of the flask api """
        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repo_path, bare=True)
        tests.add_content_git_repo(os.path.join(repo_path, "test.git"))
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")
        git_path = os.path.join(self.path, "repos", "test.git")
        repo_obj = pygit2.Repository(git_path)
        parent = pagure.lib.git.get_branch_ref(repo_obj, "master").peel()
        repo_obj.create_branch("dev123", parent)
        headers = {"Authorization": "token aaabbbcccddd"}
        args = {"branch": "test123", "from_branch": "dev123"}
        output = self.app.post(
            "/api/0/test/git/branch", headers=headers, data=args
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {"message": "Project branch was created"}
        self.assertEqual(data, expected_output)
        self.assertIn("test123", repo_obj.listall_branches())

    def test_api_new_git_branch_already_exists(self):
        """ Test the api_new_branch method of the flask api when branch already
        exists """
        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repo_path, bare=True)
        tests.add_content_git_repo(os.path.join(repo_path, "test.git"))
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")
        headers = {"Authorization": "token aaabbbcccddd"}
        args = {"branch": "master"}
        output = self.app.post(
            "/api/0/test/git/branch", headers=headers, data=args
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": 'The branch "master" already exists',
            "error_code": "ENOCODE",
        }
        self.assertEqual(data, expected_output)

    def test_api_new_git_branch_from_commit(self):
        """ Test the api_new_branch method of the flask api """
        tests.create_projects(self.session)
        repos_path = os.path.join(self.path, "repos")
        tests.create_projects_git(repos_path, bare=True)
        git_path = os.path.join(repos_path, "test.git")
        tests.add_content_git_repo(git_path)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")
        repo_obj = pygit2.Repository(git_path)
        from_commit = repo_obj.revparse_single("HEAD").oid.hex
        headers = {"Authorization": "token aaabbbcccddd"}
        args = {"branch": "test123", "from_commit": from_commit}
        output = self.app.post(
            "/api/0/test/git/branch", headers=headers, data=args
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {"message": "Project branch was created"}
        self.assertEqual(data, expected_output)
        self.assertIn("test123", repo_obj.listall_branches())


class PagureFlaskApiProjectFlagtests(tests.Modeltests):
    """ Tests for the flask API of pagure for flagging commit in project
    """

    def setUp(self):
        """ Set up the environnment, ran before every tests. """
        super(PagureFlaskApiProjectFlagtests, self).setUp()

        tests.create_projects(self.session)
        repo_path = os.path.join(self.path, "repos")
        self.git_path = os.path.join(repo_path, "test.git")
        tests.create_projects_git(repo_path, bare=True)
        tests.add_content_git_repo(self.git_path)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "commit_flag")

    def test_flag_commit_missing_status(self):
        """ Test flagging a commit with missing precentage. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "uid": "jenkins_build_pagure_100+seed",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {"status": ["Not a valid choice"]},
        }
        self.assertEqual(data, expected_output)

    def test_flag_commit_missing_username(self):
        """ Test flagging a commit with missing username. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "uid": "jenkins_build_pagure_100+seed",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {"username": ["This field is required."]},
        }
        self.assertEqual(data, expected_output)

    def test_flag_commit_missing_comment(self):
        """ Test flagging a commit with missing comment. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "uid": "jenkins_build_pagure_100+seed",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {"comment": ["This field is required."]},
        }
        self.assertEqual(data, expected_output)

    def test_flag_commit_missing_url(self):
        """ Test flagging a commit with missing url. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "uid": "jenkins_build_pagure_100+seed",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {"url": ["This field is required."]},
        }
        self.assertEqual(data, expected_output)

    def test_flag_commit_invalid_token(self):
        """ Test flagging a commit with missing info. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token 123"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "uid": "jenkins_build_pagure_100+seed",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            sorted(data.keys()), ["error", "error_code", "errors"]
        )
        self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
        self.assertEqual(
            pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
        )
        self.assertEqual(data["errors"], "Invalid token")

    def test_flag_commit_invalid_status(self):
        """ Test flagging a commit with an invalid status. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "status": "foobar",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "errors": {"status": ["Not a valid choice"]},
                "error_code": "EINVALIDREQ",
                "error": "Invalid or incomplete input submitted",
            },
        )

    def test_flag_commit_with_uid(self):
        """ Test flagging a commit with provided uid. """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "uid": "jenkins_build_pagure_100+seed",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["flag"]["date_created"] = "1510742565"
        data["flag"]["date_updated"] = "1510742565"
        data["flag"]["commit_hash"] = "62b49f00d489452994de5010565fab81"
        expected_output = {
            "flag": {
                "comment": "Tests passed",
                "commit_hash": "62b49f00d489452994de5010565fab81",
                "date_created": "1510742565",
                "date_updated": "1510742565",
                "percent": 100,
                "status": "success",
                "url": "http://jenkins.cloud.fedoraproject.org/",
                "user": {
                    "default_email": "bar@pingou.com",
                    "emails": ["bar@pingou.com", "foo@pingou.com"],
                    "fullname": "PY C",
                    "name": "pingou",
                },
                "username": "Jenkins",
            },
            "message": "Flag added",
            "uid": "jenkins_build_pagure_100+seed",
        }

        self.assertEqual(data, expected_output)

    @patch("pagure.lib.notify.send_email")
    def test_flag_commit_without_uid(self, mock_email):
        """ Test flagging a commit with missing info.

        Also ensure notifications aren't sent when they are not asked for.
        """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertNotEqual(data["uid"], "jenkins_build_pagure_100+seed")
        data["flag"]["date_created"] = "1510742565"
        data["flag"]["date_updated"] = "1510742565"
        data["uid"] = "b1de8f80defd4a81afe2e09f39678087"
        expected_output = {
            "flag": {
                "comment": "Tests passed",
                "commit_hash": commit.oid.hex,
                "date_created": "1510742565",
                "date_updated": "1510742565",
                "percent": 100,
                "status": "success",
                "url": "http://jenkins.cloud.fedoraproject.org/",
                "user": {
                    "default_email": "bar@pingou.com",
                    "emails": ["bar@pingou.com", "foo@pingou.com"],
                    "fullname": "PY C",
                    "name": "pingou",
                },
                "username": "Jenkins",
            },
            "message": "Flag added",
            "uid": "b1de8f80defd4a81afe2e09f39678087",
        }
        self.assertEqual(data, expected_output)

        mock_email.assert_not_called()

    @patch("pagure.lib.notify.send_email")
    def test_flag_commit_with_notification(self, mock_email):
        """ Test flagging a commit with notification enabled. """

        # Enable commit notifications
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        settings = repo.settings
        settings["notify_on_commit_flag"] = True
        repo.settings = settings
        self.session.add(repo)
        self.session.commit()

        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "status": "success",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertNotEqual(data["uid"], "jenkins_build_pagure_100+seed")
        data["flag"]["date_created"] = "1510742565"
        data["flag"]["date_updated"] = "1510742565"
        data["uid"] = "b1de8f80defd4a81afe2e09f39678087"
        expected_output = {
            "flag": {
                "comment": "Tests passed",
                "commit_hash": commit.oid.hex,
                "date_created": "1510742565",
                "date_updated": "1510742565",
                "percent": 100,
                "status": "success",
                "url": "http://jenkins.cloud.fedoraproject.org/",
                "user": {
                    "default_email": "bar@pingou.com",
                    "emails": ["bar@pingou.com", "foo@pingou.com"],
                    "fullname": "PY C",
                    "name": "pingou",
                },
                "username": "Jenkins",
            },
            "message": "Flag added",
            "uid": "b1de8f80defd4a81afe2e09f39678087",
        }
        self.assertEqual(data, expected_output)

        mock_email.assert_called_once_with(
            "\nJenkins flagged the commit "
            "`" + commit.oid.hex + "` as success: "
            "Tests passed\n\n"
            "http://localhost.localdomain/test/c/" + commit.oid.hex + "\n",
            "Commit #" + commit.oid.hex + " - Jenkins: success",
            "bar@pingou.com",
            in_reply_to="test-project-1",
            mail_id="test-commit-1-1",
            project_name="test",
            user_from="Jenkins",
        )

    @patch.dict(
        "pagure.config.config",
        {
            "FLAG_STATUSES_LABELS": {
                "pend!": "label-info",
                "succeed!": "label-success",
                "fail!": "label-danger",
                "what?": "label-warning",
            },
            "FLAG_PENDING": "pend!",
            "FLAG_SUCCESS": "succeed!",
            "FLAG_FAILURE": "fail!",
        },
    )
    def test_flag_commit_with_custom_flags(self):
        """ Test flagging when custom flags are set up
        """
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        headers = {"Authorization": "token aaabbbcccddd"}
        send_data = {
            "username": "Jenkins",
            "percent": 100,
            "comment": "Tests passed",
            "url": "http://jenkins.cloud.fedoraproject.org/",
            "status": "succeed!",
        }
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=send_data,
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(data["flag"]["status"], "succeed!")

        # Try invalid flag status
        send_data["status"] = "nooooo...."
        output = self.app.post(
            "/api/0/test/c/%s/flag" % commit.oid.hex,
            headers=headers,
            data=send_data,
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "errors": {"status": ["Not a valid choice"]},
                "error_code": "EINVALIDREQ",
                "error": "Invalid or incomplete input submitted",
            },
        )

    def test_commit_flags(self):
        """ Test retrieving commit flags. """
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        repo_obj = pygit2.Repository(self.git_path)
        commit = repo_obj.revparse_single("HEAD")

        # test with no flags
        output = self.app.get("/api/0/test/c/%s/flag" % commit.oid.hex)
        self.assertEqual(
            json.loads(output.get_data(as_text=True)),
            {"total_flags": 0, "flags": []},
        )
        self.assertEqual(output.status_code, 200)

        # add some flags and retrieve them
        pagure.lib.query.add_commit_flag(
            session=self.session,
            repo=repo,
            commit_hash=commit.oid.hex,
            username="simple-koji-ci",
            status="pending",
            percent=None,
            comment="Build is running",
            url="https://koji.fp.o/koji...",
            uid="uid",
            user="foo",
            token="aaabbbcccddd",
        )

        pagure.lib.query.add_commit_flag(
            session=self.session,
            repo=repo,
            commit_hash=commit.oid.hex,
            username="complex-koji-ci",
            status="success",
            percent=None,
            comment="Build succeeded",
            url="https://koji.fp.o/koji...",
            uid="uid2",
            user="foo",
            token="aaabbbcccddd",
        )
        self.session.commit()

        output = self.app.get("/api/0/test/c/%s/flag" % commit.oid.hex)
        data = json.loads(output.get_data(as_text=True))

        for f in data["flags"]:
            f["date_created"] = "1510742565"
            f["date_updated"] = "1510742565"
            f["commit_hash"] = "62b49f00d489452994de5010565fab81"
        expected_output = {
            "flags": [
                {
                    "comment": "Build is running",
                    "commit_hash": "62b49f00d489452994de5010565fab81",
                    "date_created": "1510742565",
                    "date_updated": "1510742565",
                    "percent": None,
                    "status": "pending",
                    "url": "https://koji.fp.o/koji...",
                    "user": {"fullname": "foo bar", "name": "foo"},
                    "username": "simple-koji-ci",
                },
                {
                    "comment": "Build succeeded",
                    "commit_hash": "62b49f00d489452994de5010565fab81",
                    "date_created": "1510742565",
                    "date_updated": "1510742565",
                    "percent": None,
                    "status": "success",
                    "url": "https://koji.fp.o/koji...",
                    "user": {"fullname": "foo bar", "name": "foo"},
                    "username": "complex-koji-ci",
                },
            ],
            "total_flags": 2,
        }

        self.assertEqual(data, expected_output)


class PagureFlaskApiProjectModifyAclTests(tests.Modeltests):
    """ Tests for the flask API of pagure for modifying ACLs in a project
    """

    maxDiff = None

    def setUp(self):
        """ Set up the environnment, ran before every tests. """
        super(PagureFlaskApiProjectModifyAclTests, self).setUp()
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")

        project = pagure.lib.query._get_project(self.session, "test")
        self.assertEquals(
            project.access_users, {"admin": [], "commit": [], "ticket": []}
        )

    def test_api_modify_acls_no_project(self):
        """ Test the api_modify_acls method of the flask api when the project
        doesn't exist """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "user", "name": "bar", "acl": "commit"}
        output = self.app.post(
            "/api/0/test12345123/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error_code": "ENOPROJECT",
            "error": "Project not found",
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_no_user(self):
        """ Test the api_modify_acls method of the flask api when the user
        doesn't exist """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "user", "name": "nosuchuser", "acl": "commit"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "No such user found",
            "error_code": "ENOUSER",
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_no_group(self):
        """ Test the api_modify_acls method of the flask api when the group
        doesn't exist """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "group", "name": "nosuchgroup", "acl": "commit"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Group not found",
            "error_code": "ENOGROUP",
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_no_permission(self):
        """ Test the api_modify_acls method of the flask api when the user
        doesn't have permissions """
        item = pagure.lib.model.Token(
            id="foo_token2",
            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, "foo_token2", "modify_project")

        headers = {"Authorization": "token foo_token2"}

        data = {"user_type": "user", "name": "foo", "acl": "commit"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "You are not allowed to modify this project",
            "error_code": "EMODIFYPROJECTNOTALLOWED",
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_neither_user_nor_group(self):
        """ Test the api_modify_acls method of the flask api when neither
        user nor group was set """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"acl": "commit"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {
                "name": ["This field is required."],
                "user_type": ["Not a valid choice"],
            },
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_invalid_acl(self):
        """ Test the api_modify_acls method of the flask api when the ACL
        doesn't exist. Must be one of ticket, commit or admin. """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "user", "name": "bar", "acl": "invalidacl"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": {"acl": ["Not a valid choice"]},
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_user(self):
        """ Test the api_modify_acls method of the flask api for
        setting an ACL for a user. """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "user", "name": "foo", "acl": "commit"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1510742565"
        data["date_modified"] = "1510742566"

        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": ["foo"],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1510742565",
            "date_modified": "1510742566",
            "description": "test project #1",
            "fullname": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "url_path": "test",
            "user": {"fullname": "PY C", "name": "pingou"},
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_group(self):
        """ Test the api_modify_acls method of the flask api for
        setting an ACL for a group. """
        headers = {"Authorization": "token aaabbbcccddd"}

        # Create a group
        msg = pagure.lib.query.add_group(
            self.session,
            group_name="baz",
            display_name="baz group",
            description=None,
            group_type="bar",
            user="foo",
            is_admin=False,
            blacklist=[],
        )
        self.session.commit()
        self.assertEqual(msg, "User `foo` added to the group `baz`.")

        data = {"user_type": "group", "name": "baz", "acl": "ticket"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )

        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1510742565"
        data["date_modified"] = "1510742566"

        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": ["baz"]},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1510742565",
            "date_modified": "1510742566",
            "description": "test project #1",
            "fullname": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "url_path": "test",
            "user": {"fullname": "PY C", "name": "pingou"},
        }
        self.assertEqual(data, expected_output)

    def test_api_modify_acls_no_acl(self):
        """ Test the api_modify_acls method of the flask api when no ACL
        are specified. """
        headers = {"Authorization": "token aaabbbcccddd"}

        project = pagure.lib.query._get_project(self.session, "test")
        self.assertEquals(
            project.access_users, {"admin": [], "commit": [], "ticket": []}
        )

        data = {"user_type": "user", "name": "foo"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": "User does not have any access on the repo",
        }

        self.assertEqual(data, expected_output)

    def test_api_modify_acls_remove_own_acl_no_access(self):
        """ Test the api_modify_acls method of the flask api when no ACL
        are specified, so the user tries to remove their own access but the
        user is the project owner. """
        headers = {"Authorization": "token aaabbbcccddd"}

        data = {"user_type": "user", "name": "pingou"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        expected_output = {
            "error": "Invalid or incomplete input submitted",
            "error_code": "EINVALIDREQ",
            "errors": "User does not have any access on the repo",
        }

        self.assertEqual(data, expected_output)

    def test_api_modify_acls_remove_own_acl_(self):
        """ Test the api_modify_acls method of the flask api when no ACL
        are specified, so the user tries to remove their own access but the
        user is the project owner. """
        # Add the user `foo` to the project
        self.test_api_modify_acls_user()

        # Ensure `foo` was properly added:
        project = pagure.lib.query._get_project(self.session, "test")
        user_foo = pagure.lib.query.search_user(self.session, username="foo")
        self.assertEquals(
            project.access_users,
            {"admin": [], "commit": [user_foo], "ticket": []},
        )

        # Create an API token for `foo` for the project `test`
        item = pagure.lib.model.Token(
            id="foo_test_token",
            user_id=2,  # foo
            project_id=1,  # test
            expiration=datetime.datetime.utcnow()
            + datetime.timedelta(days=10),
        )
        self.session.add(item)
        self.session.commit()
        tests.create_tokens_acl(
            self.session, "foo_test_token", "modify_project"
        )

        headers = {"Authorization": "token foo_test_token"}

        data = {"user_type": "user", "name": "foo"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1510742565"
        data["date_modified"] = "1510742566"

        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1510742565",
            "date_modified": "1510742566",
            "description": "test project #1",
            "fullname": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "url_path": "test",
            "user": {"fullname": "PY C", "name": "pingou"},
        }

        self.assertEqual(data, expected_output)

        # Ensure `foo` was properly removed
        self.session = pagure.lib.query.create_session(self.dbpath)
        project = pagure.lib.query._get_project(self.session, "test")
        self.assertEquals(
            project.access_users, {"admin": [], "commit": [], "ticket": []}
        )

    def test_api_modify_acls_remove_someone_else_acl(self):
        """ Test the api_modify_acls method of the flask api an admin tries
        to remove access from someone else. """
        # Add the user `foo` to the project
        self.test_api_modify_acls_user()

        # Ensure `foo` was properly added:
        project = pagure.lib.query._get_project(self.session, "test")
        user_foo = pagure.lib.query.search_user(self.session, username="foo")
        self.assertEquals(
            project.access_users,
            {"admin": [], "commit": [user_foo], "ticket": []},
        )

        headers = {"Authorization": "token aaabbbcccddd"}
        data = {"user_type": "user", "name": "foo"}
        output = self.app.post(
            "/api/0/test/git/modifyacls", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        data["date_created"] = "1510742565"
        data["date_modified"] = "1510742566"

        expected_output = {
            "access_groups": {"admin": [], "commit": [], "ticket": []},
            "access_users": {
                "admin": [],
                "commit": [],
                "owner": ["pingou"],
                "ticket": [],
            },
            "close_status": [
                "Invalid",
                "Insufficient data",
                "Fixed",
                "Duplicate",
            ],
            "custom_keys": [],
            "date_created": "1510742565",
            "date_modified": "1510742566",
            "description": "test project #1",
            "fullname": "test",
            "id": 1,
            "milestones": {},
            "name": "test",
            "namespace": None,
            "parent": None,
            "priorities": {},
            "tags": [],
            "url_path": "test",
            "user": {"fullname": "PY C", "name": "pingou"},
        }

        self.assertEqual(data, expected_output)

        # Ensure `foo` was properly removed
        self.session = pagure.lib.query.create_session(self.dbpath)
        project = pagure.lib.query._get_project(self.session, "test")
        self.assertEquals(
            project.access_users, {"admin": [], "commit": [], "ticket": []}
        )


class PagureFlaskApiProjectOptionsTests(tests.Modeltests):
    """ Tests for the flask API of pagure for modifying options ofs a project
    """

    maxDiff = None

    def setUp(self):
        """ Set up the environnment, ran before every tests. """
        super(PagureFlaskApiProjectOptionsTests, self).setUp()
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")

        project = pagure.lib.query._get_project(self.session, "test")
        self.assertEquals(
            project.access_users, {"admin": [], "commit": [], "ticket": []}
        )

    def test_api_get_project_options_wrong_project(self):
        """ Test accessing api_get_project_options w/o auth header. """

        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/unknown/options", headers=headers)
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data, {"error": "Project not found", "error_code": "ENOPROJECT"}
        )

    def test_api_get_project_options_wo_header(self):
        """ Test accessing api_get_project_options w/o auth header. """

        output = self.app.get("/api/0/test/options")
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "error": "Invalid or expired token. Please visit "
                "http://localhost.localdomain/settings#api-keys to get "
                "or renew your API token.",
                "error_code": "EINVALIDTOK",
                "errors": "Invalid token",
            },
        )

    def test_api_get_project_options_w_header(self):
        """ Test accessing api_get_project_options w/ auth header. """

        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/options", headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "settings": {
                    "Enforce_signed-off_commits_in_pull-request": False,
                    "Minimum_score_to_merge_pull-request": -1,
                    "Only_assignee_can_merge_pull-request": False,
                    "Web-hooks": None,
                    "always_merge": False,
                    "disable_non_fast-forward_merges": False,
                    "fedmsg_notifications": True,
                    "issue_tracker": True,
                    "issue_tracker_read_only": False,
                    "issues_default_to_private": False,
                    "mqtt_notifications": True,
                    "notify_on_commit_flag": False,
                    "notify_on_pull-request_flag": False,
                    "open_metadata_access_to_all": False,
                    "project_documentation": False,
                    "pull_request_access_only": False,
                    "pull_requests": True,
                    "stomp_notifications": True,
                },
                "status": "ok",
            },
        )

    def test_api_modify_project_options_wrong_project(self):
        """ Test accessing api_modify_project_options w/ an invalid project.
        """

        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.post(
            "/api/0/unknown/options/update", headers=headers
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data, {"error": "Project not found", "error_code": "ENOPROJECT"}
        )

    def test_api_modify_project_options_wo_header(self):
        """ Test accessing api_modify_project_options w/o auth header. """

        output = self.app.post("/api/0/test/options/update")
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "error": "Invalid or expired token. Please visit "
                "http://localhost.localdomain/settings#api-keys to get "
                "or renew your API token.",
                "error_code": "EINVALIDTOK",
                "errors": "Invalid token",
            },
        )

    def test_api_modify_project_options_no_data(self):
        """ Test accessing api_modify_project_options w/ auth header. """

        # check before
        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/options", headers=headers)
        self.assertEqual(output.status_code, 200)
        before = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            before,
            {
                "settings": {
                    "Enforce_signed-off_commits_in_pull-request": False,
                    "Minimum_score_to_merge_pull-request": -1,
                    "Only_assignee_can_merge_pull-request": False,
                    "Web-hooks": None,
                    "always_merge": False,
                    "disable_non_fast-forward_merges": False,
                    "fedmsg_notifications": True,
                    "issue_tracker": True,
                    "issue_tracker_read_only": False,
                    "issues_default_to_private": False,
                    "mqtt_notifications": True,
                    "notify_on_commit_flag": False,
                    "notify_on_pull-request_flag": False,
                    "open_metadata_access_to_all": False,
                    "project_documentation": False,
                    "pull_request_access_only": False,
                    "pull_requests": True,
                    "stomp_notifications": True,
                },
                "status": "ok",
            },
        )

        # Do not update anything
        data = {}
        output = self.app.post(
            "/api/0/test/options/update", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data, {"message": "No settings to change", "status": "ok"}
        )

        # check after
        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/options", headers=headers)
        self.assertEqual(output.status_code, 200)
        after = json.loads(output.get_data(as_text=True))
        self.assertEqual(after, before)

    def test_api_modify_project_options(self):
        """ Test accessing api_modify_project_options w/ auth header. """

        # check before
        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/options", headers=headers)
        self.assertEqual(output.status_code, 200)
        before = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            before,
            {
                "settings": {
                    "Enforce_signed-off_commits_in_pull-request": False,
                    "Minimum_score_to_merge_pull-request": -1,
                    "Only_assignee_can_merge_pull-request": False,
                    "Web-hooks": None,
                    "always_merge": False,
                    "disable_non_fast-forward_merges": False,
                    "fedmsg_notifications": True,
                    "issue_tracker": True,
                    "issue_tracker_read_only": False,
                    "issues_default_to_private": False,
                    "mqtt_notifications": True,
                    "notify_on_commit_flag": False,
                    "notify_on_pull-request_flag": False,
                    "open_metadata_access_to_all": False,
                    "project_documentation": False,
                    "pull_request_access_only": False,
                    "pull_requests": True,
                    "stomp_notifications": True,
                },
                "status": "ok",
            },
        )

        # Update: `issues_default_to_private`.
        data = {"issues_default_to_private": True}
        output = self.app.post(
            "/api/0/test/options/update", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "message": "Edited successfully settings of repo: test",
                "status": "ok",
            },
        )

        # check after
        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/options", headers=headers)
        self.assertEqual(output.status_code, 200)
        after = json.loads(output.get_data(as_text=True))
        self.assertNotEqual(before, after)
        before["settings"]["issues_default_to_private"] = True
        self.assertEqual(after, before)


class PagureFlaskApiProjectCreateAPITokenTests(tests.Modeltests):
    """ Tests for the flask API of pagure for creating user project API token
    """

    maxDiff = None

    def setUp(self):
        """ Set up the environnment, ran before every tests. """
        super(PagureFlaskApiProjectCreateAPITokenTests, self).setUp()
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")

    def test_api_createapitoken_as_owner(self):
        """ Test accessing api_project_create_token as owner. """

        headers = {"Authorization": "token aaabbbcccddd"}
        project = pagure.lib.query._get_project(self.session, "test")
        tdescription = "my new token"

        # Call the api with pingou user token and verify content
        data = {
            "description": tdescription,
            "acls": ["pull_request_merge", "pull_request_comment"],
        }
        output = self.app.post(
            "/api/0/test/token/new", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        tid = pagure.lib.query.search_token(
            self.session, None, description=tdescription
        )[0].id
        self.assertEqual(
            data, {"token": {"description": tdescription, "id": tid}}
        )
        # Create a second token but with faulty acl
        # Call the api with pingou user token and error code
        data = {"description": tdescription, "acl": ["foo", "bar"]}
        output = self.app.post(
            "/api/0/test/token/new", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)

    def test_api_createapitoken_as_admin(self):
        """ Test accessing api_project_create_token as admin. """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="admin",
        )
        self.session.commit()

        # Create modify_project token for foo user
        token = pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["modify_project"], username="foo"
        )

        # Call the connector with foo user token and verify content
        headers = {"Authorization": "token %s" % token.id}
        tdescription = "my new token"

        # Call the api with pingou user token and verify content
        data = {
            "description": tdescription,
            "acls": ["pull_request_merge", "pull_request_comment"],
        }
        output = self.app.post(
            "/api/0/test/token/new", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        tid = pagure.lib.query.search_token(
            self.session, None, user="foo", description=tdescription
        )[0].id
        self.assertEqual(
            data, {"token": {"description": tdescription, "id": tid}}
        )

    def test_api_createapitoken_as_unauthorized(self):
        """ Test accessing api_project_create_token as project admin
        but with unauthorized token ACL.
        """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="admin",
        )
        self.session.commit()

        # Create modify_project token for foo user
        pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["create_branch"], username="foo"
        )
        mtoken = pagure.lib.query.search_token(
            self.session, ["create_branch"], user="foo"
        )[0]

        # Call the connector with foo user token and verify content
        headers = {"Authorization": "token %s" % mtoken.id}
        tdescription = "my new token"

        # Call the api with pingou user token and verify content
        data = {
            "description": tdescription,
            "acls": ["pull_request_merge", "pull_request_comment"],
        }
        output = self.app.post(
            "/api/0/test/token/new", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 401)

    def test_api_createapitoken_as_unauthorized_2(self):
        """ Test accessing api_project_create_token as project user
        with unauthorized token ACL.
        """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="commit",
        )
        self.session.commit()

        # Create modify_project token for foo user
        pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["modify_project"], username="foo"
        )
        mtoken = pagure.lib.query.search_token(
            self.session, ["modify_project"], user="foo"
        )[0]

        # Call the connector with foo user token and verify content
        headers = {"Authorization": "token %s" % mtoken.id}
        tdescription = "my new token"

        # Call the api with pingou user token and verify content
        data = {
            "description": tdescription,
            "acls": ["pull_request_merge", "pull_request_comment"],
        }
        output = self.app.post(
            "/api/0/test/token/new", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 401)


class PagureFlaskApiProjectConnectorTests(tests.Modeltests):
    """ Tests for the flask API of pagure for getting connector of a project
    """

    maxDiff = None

    def setUp(self):
        """ Set up the environnment, ran before every tests. """
        super(PagureFlaskApiProjectConnectorTests, self).setUp()
        tests.create_projects(self.session)
        tests.create_tokens(self.session, project_id=None)
        tests.create_tokens_acl(self.session, "aaabbbcccddd", "modify_project")

    def test_api_get_project_connector_as_owner(self):
        """ Test accessing api_get_project_connector as project owner. """

        project = pagure.lib.query._get_project(self.session, "test")

        # Create witness project Token for pingou user
        pagure.lib.query.add_token_to_user(
            self.session,
            project=project,
            acls=["pull_request_merge"],
            username="pingou",
        )
        ctokens = pagure.lib.query.search_token(
            self.session, ["pull_request_merge"], user="pingou"
        )
        self.assertEqual(len(ctokens), 1)

        # Call the connector with pingou user token and verify content
        headers = {"Authorization": "token aaabbbcccddd"}
        output = self.app.get("/api/0/test/connector", headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "connector": {
                    "hook_token": project.hook_token,
                    "api_tokens": [
                        {
                            "description": t.description,
                            "id": t.id,
                            "expired": False,
                        }
                        for t in ctokens
                    ],
                },
                "status": "ok",
            },
        )

    def test_api_get_project_connector_as_admin(self):
        """ Test accessing api_get_project_connector as project admin """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="admin",
        )
        self.session.commit()

        # Create modify_project token for foo user
        pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["modify_project"], username="foo"
        )
        mtoken = pagure.lib.query.search_token(
            self.session, ["modify_project"], user="foo"
        )[0]

        # Create witness project Token for foo user
        pagure.lib.query.add_token_to_user(
            self.session,
            project=project,
            acls=["pull_request_merge"],
            username="foo",
        )
        ctokens = pagure.lib.query.search_token(
            self.session, ["pull_request_merge"], user="foo"
        )
        self.assertEqual(len(ctokens), 1)

        # Call the connector with foo user token and verify content
        headers = {"Authorization": "token %s" % mtoken.id}
        output = self.app.get("/api/0/test/connector", headers=headers)
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertEqual(
            data,
            {
                "connector": {
                    "hook_token": project.hook_token,
                    "api_tokens": [
                        {
                            "description": t.description,
                            "id": t.id,
                            "expired": False,
                        }
                        for t in ctokens
                    ],
                },
                "status": "ok",
            },
        )

    def test_api_get_project_connector_as_unauthorized(self):
        """ Test accessing api_get_project_connector as project admin
        but with unauthorized token ACL
        """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="admin",
        )
        self.session.commit()

        # Create modify_project token for foo user
        pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["create_project"], username="foo"
        )
        mtoken = pagure.lib.query.search_token(
            self.session, ["create_project"], user="foo"
        )[0]

        # Call the connector with foo user token and verify unauthorized
        headers = {"Authorization": "token %s" % mtoken.id}
        output = self.app.get("/api/0/test/connector", headers=headers)
        self.assertEqual(output.status_code, 401)

    def test_api_get_project_connector_as_unauthorized_2(self):
        """ Test accessing api_get_project_connector as project
        but with unauthorized token ACL
        """

        project = pagure.lib.query._get_project(self.session, "test")

        # Set the foo user as test project admin
        pagure.lib.query.add_user_to_project(
            self.session,
            project,
            new_user="foo",
            user="pingou",
            access="commit",
        )
        self.session.commit()

        # Create modify_project token for foo user
        pagure.lib.query.add_token_to_user(
            self.session, project=None, acls=["modify_project"], username="foo"
        )
        mtoken = pagure.lib.query.search_token(
            self.session, ["modify_project"], user="foo"
        )[0]

        # Call the connector with foo user token and verify unauthorized
        headers = {"Authorization": "token %s" % mtoken.id}
        output = self.app.get("/api/0/test/connector", headers=headers)
        self.assertEqual(output.status_code, 401)


if __name__ == "__main__":
    unittest.main(verbosity=2)
