# -*- encoding: UTF8 -*-
# Test harness for SwarmReviewTemplate.py

from __future__ import print_function

import sys
import unittest
import os

from p4testutils import TestCase, P4Server, localDirectory, create_file, append_to_file

parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
from SwarmReviewTemplate import SwarmReviewTemplate

os.environ["LOGS"] = "."
LOGGER_NAME = "TestSwarmReviewTemplate"
LOG_FILE = "log-SwarmReviewTemplate.log"

python3 = sys.version_info[0] >= 3

if python3:
    from unittest.mock import patch, Mock
else:
    from mock import patch, Mock

class TestSwarmReviewTemplate(TestCase):
    def __init__(self, methodName='runTest'):
        super(TestSwarmReviewTemplate, self).__init__(LOGGER_NAME, LOG_FILE, methodName=methodName)

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def testHasRequiredReviewers(self):
        """Make sure can parse json appropriately"""
        trig = SwarmReviewTemplate('1234')
        
        review = {u'updated': 1524722029, u'description': u'VWMEB-1864/HeKa: some desc', 
            u'author': u'uid04885',
            u'commits': [], u'created': 1524722029, u'deployDetails': [], 
            u'state': u'needsReview', u'comments': [0, 0], u'testDetails': [], u'testStatus': None, u'commitStatus': [], 
            u'participants': {u'uid03351': [], u'uid04885': [], u'uidl9595': [],
                              u'uid94250': {u'required': True}}}
        self.assertTrue(trig.has_required_reviewers(review))
        review = {u'updated': 1524722029, u'description': u'VWMEB-1864/HeKa: some desc', 
            u'author': u'uid04885',
            u'commits': [], u'created': 1524722029, u'deployDetails': [], 
            u'state': u'needsReview', u'comments': [0, 0], u'testDetails': [], u'testStatus': None, u'commitStatus': [], 
            u'participants': {u'uid03351': [], u'uid04885': [], u'uidl9595': [],
                              u'uid94250': []}}
        self.assertFalse(trig.has_required_reviewers(review))
     
    @patch('SwarmReviewTemplate.requests')
    def testSwarmReviewTemplate(self, requests_mock):
        """trigger fires and sends expected info to Swarm"""
        self.server = P4Server()
        config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "~test_config.yaml")
        p4 = self.server.p4
        p4.logger = self.logger
        orig_user = p4.user

        inside = localDirectory(self.server.client_root, "inside")
        outside = localDirectory(self.server.client_root, "outside")
        inside_file1 = os.path.join(inside, "inside_file1")
        outside_file1 = os.path.join(outside, "outside_file1")
        create_file(inside_file1, 'Test content')
        create_file(outside_file1, 'Test content')

        with open(config_path, "w") as f:
            f.write("""
api: "api/v6"
user: swarmtest
swarm_user: swarm
ticket: A123453
review_description:
  - "Please review me!"
  - "Don't forget to check YYYY"
projects:
  - name:           ProjectA
    post_submit_create_review:  n
    pre_submit_require_review:  y
    depot_paths:
    - //depot/inside/...
""")

        get_call_count = 0
        patch_call_count = 0

        # Create a change and shelve for review
        # Then as user swarm we create a review shelved change - duplicating swarm actions
        # Then run trigger and see that it performs correct API calls to Swarm

        p4.run('add', outside_file1)
        chg = p4.fetch_change()
        chg['Description'] = "test change"
        p4.save_change(chg)

        p4.run_shelve('-c', '1')
        p4.run_revert('//...')

        p4.user = 'swarm'
        p4.run('add', outside_file1)
        chg = p4.fetch_change()
        chg['Description'] = "test change"
        p4.save_change(chg)

        p4.run_shelve('-c', '2')
        p4.run_revert('//...')

        trig_args = ["-p", p4.port, "-u", p4.user, "-c", config_path, "-L", "p4triggers.log", "--test-mode", "1"]

        # For non Swarm user change should be ignored
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        # Hasn't been called
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)

        # For swarm user but outside any project change is ignored
        trig_args[-1] = "2"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        # Hasn't been called
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)

        #----------------------------------------
        # For a path config file defines as inside, Swarm should be called

        p4.user = orig_user
        p4.run('add', inside_file1)
        chg = p4.fetch_change()
        chg['Description'] = "test change2"
        p4.save_change(chg)

        p4.run_shelve('-c', '3')
        p4.run_revert('//...')

        p4.user = 'swarm'
        p4.run('add', inside_file1)
        chg = p4.fetch_change()
        chg['Description'] = "test change2"
        p4.save_change(chg)

        p4.run_shelve('-c', '4')
        p4.run_revert('//...')

        trig_args[-1] = "4"

        mock_get_response = Mock()
        mock_get_response.json.return_value = {"lastSeen": 843, "reviews": [{"author": "perforce",
            "changes": [3], "comments": [0, 0], "commits": [],
            "description": "my change\n", "groups": ["longlived"],
            "id": 4, "participants": {"perforce": [], "super": []},
            "pending": False, "projects": [], "state": "needsReview",
            "stateLabel": "Needs Review", "testDetails": [], "testStatus": None,
            "type": "default", "updateDate": "2017-11-09T15:16:55+00:00",
            "updated": 1510240615}],
            "totalCount": 1}


        # Assign our mock response as the result of our patched function
        requests_mock.get.return_value = mock_get_response

        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        get_call_count += 1    # Expect an update
        patch_call_count += 1
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'data': [('description', "Please review me!\nDon't forget to check YYYY")]}, kwargs)

        #----------------------------------------
        # Check formatting with a job associated

        with open(config_path, "w") as f:
            f.write("""
api: "api/v6"
user: swarmtest
swarm_user: swarm
ticket: A123453
review_description:
  - "$jobDescription"
  - "$changeDescription"
  - "Please review me!"
  - "Don't forget to check YYYY"
projects:
  - name:           ProjectA
    post_submit_create_review:  n
    pre_submit_require_review:  y
    depot_paths:
    - //depot/inside/...
""")

        job = p4.fetch_job()
        job['Description'] = 'test job'
        p4.save_job(job)
        p4.run('fix', '-c', '4', 'job000001')

        trig_args[-1] = "4"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        get_call_count += 1    # Expect an update
        patch_call_count += 1
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'data': [('description', "test job\ntest change2\nPlease review me!\nDon't forget to check YYYY")]}, kwargs)

        #----------------------------------------
        # Check formatting when 'swarm_review_template' only is set to 'y'

        with open(config_path, "w") as f:
            f.write("""
api: "api/v6"
user: swarmtest
swarm_user: swarm
ticket: A123453
review_description:
  - "$jobDescription"
  - "$changeDescription"
  - "Please review me!"
  - "Don't forget to check YYYY"
projects:
  - name:           ProjectA
    post_submit_create_review:  n
    pre_submit_require_review:  n
    swarm_review_template:      y
    depot_paths:
    - //depot/inside/...
""")

        trig_args[-1] = "4"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        get_call_count += 1    # Expect an update
        patch_call_count += 1
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'data': [('description', "test job\ntest change2\nPlease review me!\nDon't forget to check YYYY")]}, kwargs)

        # --------------------------------------
        # Handle no reviews found
        mock_get_response = Mock()
        mock_get_response.json.return_value = {"lastSeen": None, "reviews": [], "totalCount": 0}

        requests_mock.get.return_value = mock_get_response
        trig_args[-1] = "4"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)

        get_call_count += 1
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)

        # ---------------
        # Handle required reviewers being reset
        mock_get_response.json.return_value = {"lastSeen": 843, "reviews": [{"author": "perforce",
            "changes": [3], "comments": [0, 0], "commits": [],
            "description": "my change\n", "groups": ["longlived"],
            "id": 4, "participants": {"perforce": {'required': True}, "super": []},
            "pending": False, "projects": [], "state": "needsReview",
            "stateLabel": "Needs Review", "testDetails": [], "testStatus": None,
            "type": "default", "updateDate": "2017-11-09T15:16:55+00:00",
            "updated": 1510240615}],
            "totalCount": 1}
        trig_args[-1] = "4"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        get_call_count += 1    # Expect an update
        patch_call_count += 2
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'data': [('description', "test job\ntest change2\nPlease review me!\nDon't forget to check YYYY")]}, kwargs)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 2]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'json': {'requiredReviewers': []}}, kwargs)


        # ---------------
        # Handle multiple changes being specified, with the first change no longer present
        p4.run_shelve("-d", "-c", "2", "//...")
        p4.run_change("-d", "2")
        mock_get_response.json.return_value = {"lastSeen": 843, "reviews": [{"author": "perforce",
            "changes": [2, 3], "comments": [0, 0], "commits": [],
            "description": "my change\n", "groups": ["longlived"],
            "id": 4, "participants": {"perforce": {'required': True}, "super": []},
            "pending": False, "projects": [], "state": "needsReview",
            "stateLabel": "Needs Review", "testDetails": [], "testStatus": None,
            "type": "default", "updateDate": "2017-11-09T15:16:55+00:00",
            "updated": 1510240615}],
            "totalCount": 1}
        trig_args[-1] = "4"
        trig = SwarmReviewTemplate(*trig_args)
        result = trig.run()
        self.assertEqual(0, result)
        get_call_count += 1    # Expect an update
        patch_call_count += 1
        self.assertEqual(get_call_count, requests_mock.get.call_count)
        self.assertEqual(patch_call_count, requests_mock.patch.call_count)
        args, kwargs = requests_mock.patch.call_args_list[patch_call_count - 1]
        self.assertEqual(('http://swarm.dev/api/v6/reviews/4',), args)
        self.assertDictEqual({'auth': ('swarmtest', 'A123453'),
                              'json': {'requiredReviewers': []}}, kwargs)


if __name__ == '__main__':
    unittest.main()
