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

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

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>

"""

from __future__ import unicode_literals, absolute_import

import unittest
import shutil
import sys
import os

import json
import pygit2
from mock import patch, MagicMock

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

import pagure.api
import pagure.flask_app
import pagure.lib.query
import tests


def set_projects_up(self):
    tests.create_projects(self.session)
    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"))
    tests.create_tokens(self.session)
    tests.create_tokens_acl(self.session)

    tag = pagure.lib.model.TagColored(
        tag="dev", tag_color="DeepBlueSky", project_id=1
    )
    self.session.add(tag)
    tag = pagure.lib.model.TagColored(
        tag="infra", tag_color="DeepGreen", project_id=1
    )
    self.session.add(tag)
    self.session.commit()


def set_up_board(self):
    headers = {
        "Authorization": "token aaabbbcccddd",
        "Content-Type": "application/json",
    }

    data = json.dumps({"dev": {"active": True, "tag": "dev"}})
    output = self.app.post("/api/0/test/boards", headers=headers, data=data)
    self.assertEqual(output.status_code, 200)
    data = json.loads(output.get_data(as_text=True))
    self.assertDictEqual(
        data,
        {
            "boards": [
                {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            ]
        },
    )


class PagureFlaskApiBoardstests(tests.SimplePagureTest):
    """ Tests for flask API Boards controller of pagure """

    maxDiff = None

    def setUp(self):
        super(PagureFlaskApiBoardstests, self).setUp()

        set_projects_up(self)

    def test_api_boards_view_no_project(self):
        output = self.app.get("/api/0/invalid/boards")
        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_boards_view_empty(self):
        output = self.app.get("/api/0/test/boards")
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"boards": [], "total_requests": 0})

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

    def test_api_board_create_expired_token(self):
        headers = {"Authorization": "token expired_token"}
        data = {}
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or expired token. Please visit "
                "http://localhost.localdomain/settings#nav-api-tab to get or renew "
                "your API token.",
                "error_code": "EINVALIDTOK",
                "errors": "Expired token",
            },
        )

    def test_api_board_create_invalid_token_project(self):
        headers = {"Authorization": "token aaabbbcccddd"}
        data = {}
        output = self.app.post(
            "/api/0/test2/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 401)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or expired token. Please visit "
                "http://localhost.localdomain/settings#nav-api-tab to get or renew "
                "your API token.",
                "error_code": "EINVALIDTOK",
            },
        )

    def test_api_board_create_no_data(self):
        headers = {"Authorization": "token aaabbbcccddd"}
        data = {}
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        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": "No (JSON) data provided",
            },
        )

    def test_api_board_create_no_contenttype(self):
        headers = {"Authorization": "token aaabbbcccddd"}
        data = {"name": "board", "active": True, "Tag": "not found"}
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        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": "No (JSON) data provided",
            },
        )

    def test_api_board_create_html_data(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        data = {"name": "board", "active": True, "tag": "not found"}
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        self.assertIn(
            "The browser (or proxy) sent a request that this server could not understand.",
            output.get_data(as_text=True),
        )

    def test_api_board_create_invalid_json(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        data = json.dumps(
            {"name": "board", "active": True, "tag": "not found"}
        )
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        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": "No tag associated with at least one of the boards",
            },
        )

    def test_api_board_create_invalid_tag(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        data = json.dumps({"dev": {"active": True, "tag": "not found"}})
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "No tag found with the name not found",
                "error_code": "ENOCODE",
            },
        )

    def test_api_board_create(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        data = json.dumps({"dev": {"active": True, "tag": "dev"}})
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "boards": [
                    {
                        "active": True,
                        "full_url": "http://localhost.localdomain/test/boards/dev",
                        "name": "dev",
                        "status": [],
                        "tag": {
                            "tag": "dev",
                            "tag_color": "DeepBlueSky",
                            "tag_description": "",
                        },
                    }
                ]
            },
        )

    def test_api_board_edit_board_delete_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        # Add 2 boards to the project
        data = json.dumps(
            {
                "dev": {"active": True, "tag": "dev"},
                "infra": {"active": True, "tag": "infra"},
            }
        )
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "boards": [
                    {
                        "active": True,
                        "full_url": "http://localhost.localdomain/test/boards/dev",
                        "name": "dev",
                        "status": [],
                        "tag": {
                            "tag": "dev",
                            "tag_color": "DeepBlueSky",
                            "tag_description": "",
                        },
                    },
                    {
                        "active": True,
                        "full_url": "http://localhost.localdomain/test/boards/infra",
                        "name": "infra",
                        "status": [],
                        "tag": {
                            "tag": "infra",
                            "tag_color": "DeepGreen",
                            "tag_description": "",
                        },
                    },
                ]
            },
        )

        # Remove one of the 2 boards
        data = json.dumps(
            {
                "dev": {"active": True, "tag": "dev"},
            }
        )
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "boards": [
                    {
                        "active": True,
                        "full_url": "http://localhost.localdomain/test/boards/dev",
                        "name": "dev",
                        "status": [],
                        "tag": {
                            "tag": "dev",
                            "tag_color": "DeepBlueSky",
                            "tag_description": "",
                        },
                    }
                ]
            },
        )

        # Removing the last board doesn't work (no JSON data provided)
        data = json.dumps({})
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)

    def test_api_board_delete_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"name": ["dev"]})
        output = self.app.post(
            "/api/0/test/boards/delete", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(data, {"boards": []})

    def test_api_board_api_board_status_no_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"foo": "bar"})
        output = self.app.post(
            "/api/0/test/boards/invalid/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": "Board not found",
            },
        )


class PagureFlaskApiBoardsWithBoardtests(tests.SimplePagureTest):
    """Tests for flask API Boards controller of pagure for the tests
    requiring a pre-existing board.
    """

    maxDiff = None

    def setUp(self):
        super(PagureFlaskApiBoardsWithBoardtests, self).setUp()

        set_projects_up(self)

        set_up_board(self)

    def test_api_board_edit_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        # Make the board inactive
        data = json.dumps({"dev": {"active": False, "tag": "dev"}})
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "boards": [
                    {
                        "active": False,
                        "full_url": "http://localhost.localdomain/test/boards/dev",
                        "name": "dev",
                        "status": [],
                        "tag": {
                            "tag": "dev",
                            "tag_color": "DeepBlueSky",
                            "tag_description": "",
                        },
                    }
                ]
            },
        )

    def test_api_board_edit_board_invalid_tag(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        # Associate the existing board with an invalid tag
        data = json.dumps(
            {
                "dev": {"active": True, "tag": "invalid"},
            }
        )
        output = self.app.post(
            "/api/0/test/boards", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "No tag found with the name invalid",
                "error_code": "ENOCODE",
            },
        )

    def test_api_board_delete_invalid_json_input(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        # Remove this board
        data = json.dumps({})
        output = self.app.post(
            "/api/0/test/boards/delete", headers=headers, data=data
        )
        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"]},
            },
        )

    def test_api_board_delete_invalid_html_input(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
        }

        # Remove the board
        headers = {
            "Authorization": "token aaabbbcccddd",
        }
        data = {}
        output = self.app.post(
            "/api/0/test/boards/delete", headers=headers, data=data
        )
        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"]},
            },
        )

    def test_api_board_delete_json_input(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        # Remove the board
        data = json.dumps({"name": ["dev"]})
        output = self.app.post(
            "/api/0/test/boards/delete", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {"boards": []},
        )

    def test_api_board_delete_html_input(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
        }

        # Remove the board
        data = {"name": ["dev"]}
        output = self.app.post(
            "/api/0/test/boards/delete", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {"boards": []},
        )

    def test_api_board_api_board_status_no_data(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({})
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": "No (JSON) data provided",
            },
        )

    def test_api_board_api_board_status_invalid_tag_name(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "-Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": True,
                    "rank": 1,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": [
                        "Invalid status name provided, it should match: "
                        "^[a-zA-Z0-9][a-zA-Z0-9-_ .:]+$."
                    ]
                },
            },
        )

    def test_api_board_api_board_status_missing_rank(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": True,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": "The 'rank' and 'default' fields are mandatory.",
            },
        )

    def test_api_board_api_board_status_missing_default(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "rank": 1,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": "The 'rank' and 'default' fields are mandatory.",
            },
        )

    def test_api_board_api_board_status_no_default(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": False,
                    "rank": 1,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": "There must be one and only one default.",
            },
        )

    def test_api_board_api_board_status_multiple_default(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": True,
                    "rank": 1,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        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": "There must be one and only one default.",
            },
        )

    def test_api_board_api_board_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": False,
                    "rank": 2,
                },
                "Done": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 4,
                },
                "  ": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 5,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0dcd",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "Triaged",
                        },
                        {
                            "name": "Done",
                            "close": True,
                            "close_status": "Fixed",
                            "default": False,
                            "bg_color": "#34d240",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )

    def test_api_board_api_board_status_no_close_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": None,
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "Triaged": {
                    "close": False,
                    "close_status": None,
                    "bg_color": "#ca0dcd",
                    "default": False,
                    "rank": 2,
                },
                "Done": {
                    "close": True,
                    "close_status": None,
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 4,
                },
                "  ": {
                    "close": True,
                    "close_status": None,
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 5,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0dcd",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "Triaged",
                        },
                        {
                            "name": "Done",
                            "close": True,
                            "close_status": None,
                            "default": False,
                            "bg_color": "#34d240",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )

    def test_api_board_api_board_status_adding_removing(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "Triaged": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0dcd",
                    "default": False,
                    "rank": 2,
                },
                "Done": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 4,
                },
                "  ": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 5,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0dcd",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "Triaged",
                        },
                        {
                            "name": "Done",
                            "close": True,
                            "close_status": "Fixed",
                            "default": False,
                            "bg_color": "#34d240",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )

        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "In Progress": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0eef",
                    "default": False,
                    "rank": 2,
                },
                "Done": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 4,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0eef",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "In Progress",
                        },
                        {
                            "name": "Done",
                            "close": True,
                            "close_status": "Fixed",
                            "default": False,
                            "bg_color": "#34d240",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )


class PagureFlaskApiBoardsWithBoardAndIssuetests(tests.SimplePagureTest):
    """Tests for flask API Boards controller of pagure for the tests
    requiring a pre-existing board and issues.
    """

    maxDiff = None

    def setUp(self):
        super(PagureFlaskApiBoardsWithBoardAndIssuetests, self).setUp()

        set_projects_up(self)

        set_up_board(self)

        # Set up the ticket repo
        tests.create_projects_git(
            os.path.join(self.path, "repos", "tickets"), bare=True
        )

        # Set up some status to the board
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }
        data = json.dumps(
            {
                "Backlog": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#FFB300",
                    "default": True,
                    "rank": 1,
                },
                "In Progress": {
                    "close": False,
                    "close_status": "",
                    "bg_color": "#ca0eef",
                    "default": False,
                    "rank": 2,
                },
                "Done": {
                    "close": True,
                    "close_status": "Fixed",
                    "bg_color": "#34d240",
                    "default": False,
                    "rank": 4,
                },
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/status", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)

        # Create two issues to play with
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        msg = pagure.lib.query.new_issue(
            session=self.session,
            repo=repo,
            title="Test issue",
            content="We should work on this",
            user="pingou",
        )
        self.session.commit()
        self.assertEqual(msg.title, "Test issue")
        self.assertEqual(repo.open_tickets, 1)
        self.assertEqual(repo.open_tickets_public, 1)

        msg = pagure.lib.query.new_issue(
            session=self.session,
            repo=repo,
            title="Test issue #2",
            content="We should work on this for the second time",
            user="foo",
            status="Open",
        )
        self.session.commit()
        self.assertEqual(msg.title, "Test issue #2")
        self.assertEqual(repo.open_tickets, 2)
        self.assertEqual(repo.open_tickets_public, 2)
        self.tickets_uid = [t.uid for t in repo.issues]

    def test_api_board_ticket_add_status_invalid_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "12": {
                    "status": "In Progress",
                    "rank": 2,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/invalid/add_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": "Board not found",
            },
        )

    def test_api_board_ticket_add_status_no_data(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({})
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        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": "No (JSON) data provided",
            },
        )

    def test_api_board_ticket_add_status_no_rank(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"12": {"status": "In Progress"}})
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        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": "The 'rank' and 'status' fields are mandatory.",
            },
        )

    def test_api_board_ticket_add_status_no_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "12": {
                    "rank": 2,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        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": "The 'rank' and 'status' fields are mandatory.",
            },
        )

    def test_api_board_ticket_add_status_invalid_ticket_id(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"12": {"status": "In Progress", "rank": 2}})
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "No ticket found with this identifier",
                "error_code": "ENOCODE",
            },
        )

    def test_api_board_ticket_add_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "2": {"status": "In Progress", "rank": 2},
                "  ": {"status": "In Progress", "rank": 4},
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0eef",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "In Progress",
                        },
                        {
                            "bg_color": "#34d240",
                            "close": True,
                            "close_status": "Fixed",
                            "default": False,
                            "name": "Done",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )

    def test_api_board_ticket_update_status_invalid_board(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "12": {
                    "status": "In Progress",
                    "rank": 2,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/invalid/update_issue",
            headers=headers,
            data=data,
        )
        self.assertEqual(output.status_code, 404)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "Invalid or incomplete input submitted",
                "error_code": "EINVALIDREQ",
                "errors": "Board not found",
            },
        )

    def test_api_board_ticket_update_status_no_data(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({})
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        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": "No (JSON) data provided",
            },
        )

    def test_api_board_ticket_update_status_no_rank(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"12": {"status": "In Progress"}})
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        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": "The 'rank' and 'status' fields are mandatory.",
            },
        )

    def test_api_board_ticket_update_status_no_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "12": {
                    "rank": 2,
                }
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        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": "The 'rank' and 'status' fields are mandatory.",
            },
        )

    def test_api_board_ticket_update_status_invalid_ticket_id(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps({"12": {"status": "In Progress", "rank": 2}})
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 400)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "error": "No ticket found with this identifier",
                "error_code": "ENOCODE",
            },
        )

    def test_api_board_ticket_update_status(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                self.tickets_uid[1]: {"status": "In Progress", "rank": 2},
                "  ": {"status": "In Progress", "rank": 4},
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))
        self.assertDictEqual(
            data,
            {
                "board": {
                    "active": True,
                    "full_url": "http://localhost.localdomain/test/boards/dev",
                    "name": "dev",
                    "status": [
                        {
                            "bg_color": "#FFB300",
                            "close": False,
                            "close_status": None,
                            "default": True,
                            "name": "Backlog",
                        },
                        {
                            "bg_color": "#ca0eef",
                            "close": False,
                            "close_status": None,
                            "default": False,
                            "name": "In Progress",
                        },
                        {
                            "bg_color": "#34d240",
                            "close": True,
                            "close_status": "Fixed",
                            "default": False,
                            "name": "Done",
                        },
                    ],
                    "tag": {
                        "tag": "dev",
                        "tag_color": "DeepBlueSky",
                        "tag_description": "",
                    },
                }
            },
        )

    def test_api_board_ticket_update_status_close_re_opend(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                self.tickets_uid[1]: {"status": "Done", "rank": 2},
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))

        self.session = pagure.lib.query.create_session(self.dbpath)
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(repo.issues[1].status, "Closed")
        self.assertEqual(repo.issues[1].close_status, "Fixed")

        data = json.dumps(
            {
                self.tickets_uid[1]: {"status": "In Progress", "rank": 2},
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/update_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)
        data = json.loads(output.get_data(as_text=True))

        self.session = pagure.lib.query.create_session(self.dbpath)
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(repo.issues[0].status, "Open")
        self.assertEqual(repo.issues[1].status, "Open")
        self.assertEqual(repo.issues[1].close_status, None)

    @patch("pagure.lib.notify.send_email", new=MagicMock(return_value=True))
    def test_ticket_representation_in_git(self):
        headers = {
            "Authorization": "token aaabbbcccddd",
            "Content-Type": "application/json",
        }

        data = json.dumps(
            {
                "2": {"status": "In Progress", "rank": 2},
            }
        )
        output = self.app.post(
            "/api/0/test/boards/dev/add_issue", headers=headers, data=data
        )
        self.assertEqual(output.status_code, 200)

        self.session = pagure.lib.query.create_session(self.dbpath)
        repo = pagure.lib.query.get_authorized_project(self.session, "test")
        self.assertEqual(repo.issues[1].status, "Open")
        self.assertEqual(repo.issues[1].close_status, None)

        # Clone the repo so it isn't a bare repo
        pygit2.clone_repository(
            os.path.join(self.path, "repos", "tickets", "test.git"),
            os.path.join(self.path, "repos", "tickets", "test"),
        )

        exp = {
            "assignee": None,
            "blocks": [],
            "boards": [
                {
                    "board": {
                        "active": True,
                        "full_url": "http://localhost.localdomain/test/boards/dev",
                        "name": "dev",
                        "status": [
                            {
                                "bg_color": "#FFB300",
                                "close": False,
                                "close_status": None,
                                "default": True,
                                "name": "Backlog",
                            },
                            {
                                "bg_color": "#ca0eef",
                                "close": False,
                                "close_status": None,
                                "default": False,
                                "name": "In Progress",
                            },
                            {
                                "bg_color": "#34d240",
                                "close": True,
                                "close_status": "Fixed",
                                "default": False,
                                "name": "Done",
                            },
                        ],
                        "tag": {
                            "tag": "dev",
                            "tag_color": "DeepBlueSky",
                            "tag_description": "",
                        },
                    },
                    "rank": 2,
                    "status": {
                        "bg_color": "#ca0eef",
                        "close": False,
                        "close_status": None,
                        "default": False,
                        "name": "In Progress",
                    },
                }
            ],
            "close_status": None,
            "closed_at": None,
            "closed_by": None,
            "comments": [
                {
                    "comment": "Issue tagged with: dev",
                    "date_created": "1594654596",
                    "edited_on": None,
                    "editor": None,
                    "id": 1,
                    "notification": True,
                    "parent": None,
                    "reactions": {},
                    "user": {
                        "default_email": "bar@pingou.com",
                        "emails": ["bar@pingou.com", "foo@pingou.com"],
                        "full_url": "http://localhost.localdomain/user/pingou",
                        "fullname": "PY C",
                        "name": "pingou",
                        "url_path": "user/pingou",
                    },
                }
            ],
            "content": "We should work on this for the second time",
            "custom_fields": [],
            "date_created": "1594654596",
            "depends": [],
            "full_url": "http://localhost.localdomain/test/issue/2",
            "id": 2,
            "last_updated": "1594654596",
            "milestone": None,
            "priority": None,
            "private": False,
            "related_prs": [],
            "status": "Open",
            "tags": ["dev"],
            "title": "Test issue #2",
            "user": {
                "default_email": "foo@bar.com",
                "emails": ["foo@bar.com"],
                "full_url": "http://localhost.localdomain/user/foo",
                "fullname": "foo bar",
                "name": "foo",
                "url_path": "user/foo",
            },
        }

        with open(
            os.path.join(
                self.path, "repos", "tickets", "test", repo.issues[1].uid
            )
        ) as stream:
            data = json.load(stream)

        # Make the date fix
        for idx, com in enumerate(data["comments"]):
            com["date_created"] = "1594654596"
            data["comments"][idx] = com
        data["date_created"] = "1594654596"
        data["last_updated"] = "1594654596"

        self.assertDictEqual(data, exp)


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