11import json
22from importlib import reload
33from unittest import mock
4- from unittest .mock import MagicMock
4+ from unittest .mock import MagicMock , Mock
55
66from flask import g
77from werkzeug .datastructures import Headers
88
99from mod_auth .models import Role
10- from mod_ci .controllers import get_info_for_pr_comment , start_platforms
10+ from mod_ci .controllers import (Workflow_builds , get_info_for_pr_comment ,
11+ start_platforms )
1112from mod_ci .models import BlockedUsers
1213from mod_customized .models import CustomizedTest
1314from mod_home .models import CCExtractorVersion , GeneralData
@@ -675,9 +676,8 @@ def __init__(self):
675676
676677 @mock .patch ('mod_ci.controllers.BlockedUsers' )
677678 @mock .patch ('mod_ci.controllers.GitHub' )
678- @mock .patch ('mod_ci.controllers.queue_test' )
679679 @mock .patch ('requests.get' , side_effect = mock_api_request_github )
680- def test_webhook_pr_opened_blocked (self , mock_request , mock_queue_test , mock_github , mock_blocked ):
680+ def test_webhook_pr_opened_blocked (self , mock_request , mock_github , mock_blocked ):
681681 """Test webhook triggered with pull_request event with opened action for blocked user."""
682682 class MockTest :
683683 def __init__ (self ):
@@ -712,6 +712,180 @@ def test_webhook_pr_opened(self, mock_request, mock_add_test_entry, mock_github,
712712 mock_blocked .query .filter .assert_called_once_with (mock_blocked .user_id == 'test' )
713713 mock_add_test_entry .assert_called_once ()
714714
715+ @mock .patch ('mod_ci.controllers.GitHub' )
716+ @mock .patch ('mod_ci.controllers.schedule_test' )
717+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
718+ def test_webhook_workflow_run_event_requested_action_with_valid_workflow_name (self , mock_request ,
719+ mock_schedule_test , mock_github ):
720+ """Test webhook triggered with workflow run event with action requested with a valid workflow name."""
721+ data = {'action' : 'requested' , 'workflow_run' : {
722+ 'name' : 'Build CCExtractor on Linux' , 'head_sha' : 'abcd1234' }}
723+ with self .app .test_client () as c :
724+ response = c .post (
725+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
726+ data = json .dumps (data ), headers = self .generate_header (data , 'workflow_run' ))
727+ self .assertEqual (response .data , b'{"msg": "EOL"}' )
728+ mock_schedule_test .assert_called_once ()
729+
730+ @mock .patch ('mod_ci.controllers.queue_test' )
731+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
732+ def test_webhook_workflow_run_event_completed_action_successful (self , mock_request , mock_queue_test ):
733+ """Test webhook triggered with workflow run event with action completed and status success."""
734+ data = {'action' : 'completed' ,
735+ 'workflow_run' : {'event' : 'push' ,
736+ 'name' : 'Build CCExtractor on Linux' , 'head_sha' : '1' ,
737+ 'head_branch' : 'master' }, 'sender' : {'login' : 'test_owner' }}
738+ fakedata = {'workflow_runs' : [
739+ {'head_sha' : '1' , 'status' : 'completed' ,
740+ 'conclusion' : 'success' , 'name' : Workflow_builds .LINUX },
741+ {'head_sha' : '1' , 'status' : 'completed' ,
742+ 'conclusion' : 'success' , 'name' : Workflow_builds .WINDOWS }
743+ ]}
744+
745+ class MockedRepository :
746+ def statuses (self , * args ):
747+ return None
748+
749+ class actions :
750+ class runs :
751+ def get (* args , ** kwargs ):
752+ return fakedata
753+
754+ class MockedGitHub :
755+ def repos (self , * args ):
756+ return MockedRepository
757+
758+ with self .app .test_client () as c :
759+ from github import GitHub
760+ GitHub .repos = Mock (return_value = MockedGitHub .repos )
761+
762+ response = c .post (
763+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
764+ data = json .dumps (data ), headers = self .generate_header (data , 'workflow_run' ))
765+ mock_queue_test .assert_called_once ()
766+
767+ @mock .patch ('mod_ci.controllers.deschedule_test' )
768+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
769+ def test_webhook_workflow_run_event_completed_action_failure (self , mock_request , mock_deschedule_test ):
770+ """Test webhook triggered with workflow run event with action completed and status failure."""
771+ data = {'action' : 'completed' ,
772+ 'workflow_run' : {'event' : 'push' ,
773+ 'name' : Workflow_builds .LINUX , 'head_sha' : '1' ,
774+ 'head_branch' : 'master' }, 'sender' : {'login' : 'test_owner' }}
775+ fakedata = {'workflow_runs' : [
776+ {'head_sha' : '1' , 'status' : 'completed' ,
777+ 'conclusion' : 'failure' , 'name' : Workflow_builds .LINUX }
778+ ]}
779+
780+ class MockedRepository :
781+ def statuses (self , * args ):
782+ return None
783+
784+ class actions :
785+ class runs :
786+ def get (* args , ** kwargs ):
787+ return fakedata
788+
789+ class MockedGitHub :
790+ def repos (self , * args ):
791+ return MockedRepository
792+
793+ with self .app .test_client () as c :
794+ from github import GitHub
795+ GitHub .repos = Mock (return_value = MockedGitHub .repos )
796+
797+ response = c .post (
798+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
799+ data = json .dumps (data ), headers = self .generate_header (data , 'workflow_run' ))
800+ mock_deschedule_test .assert_called_once ()
801+
802+ @mock .patch ('mod_ci.controllers.GitHub' )
803+ @mock .patch ('mod_ci.controllers.schedule_test' )
804+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
805+ def test_webhook_workflow_run_requested_action_with_invalid_workflow_name (self , mock_request ,
806+ mock_schedule_test , mock_github ):
807+ """Test webhook triggered with workflow run event with an invalid action."""
808+ data = {'action' : 'requested' , 'workflow_run' : {
809+ 'name' : 'Invalid' , 'head_sha' : 'abcd1234' }}
810+ with self .app .test_client () as c :
811+ response = c .post (
812+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
813+ data = json .dumps (data ), headers = self .generate_header (data , 'workflow_run' ))
814+ mock_schedule_test .assert_not_called ()
815+ self .assertEqual (response .data , b'{"msg": "EOL"}' )
816+
817+ @mock .patch ('mod_ci.controllers.schedule_test' )
818+ @mock .patch ('mod_ci.controllers.deschedule_test' )
819+ @mock .patch ('mod_ci.controllers.add_test_entry' )
820+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
821+ def test_webhook_with_unrecognized_event (self , mock_github , mock_schedule_test ,
822+ mock_deschedule_test , mock_add_test_entry ):
823+ """Test webhook with an unrecognised event triggered via GitHub Actions."""
824+ with self .app .test_client () as c :
825+ response = c .post (
826+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
827+ data = json .dumps ({}), headers = self .generate_header ({}, 'workflow_job' ))
828+ mock_schedule_test .assert_not_called ()
829+ mock_deschedule_test .assert_not_called ()
830+ mock_add_test_entry .assert_not_called ()
831+ self .assertEqual (response .data , b'{"msg": "EOL"}' )
832+
833+ @mock .patch ('flask.g.log.warning' )
834+ @mock .patch ('requests.get' , side_effect = mock_api_request_github )
835+ def test_webhook_with_invalid_ci_signature (self , mock_github , mock_warning ):
836+ """Test webhook if an invalid X-Hub-Signature is passed within headers."""
837+ with self .app .test_client () as c :
838+ response = c .post (
839+ '/start-ci' , environ_overrides = WSGI_ENVIRONMENT ,
840+ data = json .dumps ({}), headers = self .generate_header ({}, 'workflow_run' , "1" ))
841+ mock_warning .assert_called_once ()
842+
843+ def test_start_ci_with_a_get_request (self ):
844+ """Test start_ci function with a request method other than post."""
845+ from mod_ci .controllers import start_ci
846+ response = start_ci ()
847+ self .assertEqual (response , 'OK' )
848+
849+ @mock .patch ('github.GitHub' )
850+ @mock .patch ('run.log.debug' )
851+ def test_queue_test_with_pull_request (self , mock_debug , git_mock ):
852+ """Check queue_test function with pull request as test type."""
853+ from mod_ci .controllers import add_test_entry , queue_test
854+ repository = git_mock (access_token = g .github ['bot_token' ]).repos (
855+ g .github ['repository_owner' ])(g .github ['repository' ])
856+ add_test_entry (g .db , repository .statuses ("1" ), 'customizedcommitcheck' , TestType .pull_request )
857+ mock_debug .assert_called_once_with ('pull request test type detected' )
858+ queue_test (g .db , repository .statuses ("1" ), 'customizedcommitcheck' , TestType .pull_request )
859+ mock_debug .assert_called_with ('Created tests, waiting for cron...' )
860+
861+ @mock .patch ('run.log.critical' )
862+ @mock .patch ('run.log.debug' )
863+ @mock .patch ('github.GitHub' )
864+ def test_schedule_test_function (self , git_mock , mock_debug , mock_critical ):
865+ """Check the functioning of schedule_test function."""
866+ from mod_ci .controllers import schedule_test
867+ repository = git_mock (access_token = g .github ['bot_token' ]).repos (
868+ g .github ['repository_owner' ])(g .github ['repository' ])
869+ schedule_test (repository .statuses (1 ), "1" , TestType .commit )
870+ mock_debug .assert_not_called ()
871+ schedule_test (None , None , TestType .commit )
872+ mock_debug .assert_not_called ()
873+ schedule_test (repository .statuses (1 ), "1" , TestType .pull_request )
874+ mock_debug .assert_called_once_with ('pull request test type detected' )
875+
876+ @mock .patch ('run.log.critical' )
877+ @mock .patch ('run.log.debug' )
878+ @mock .patch ('github.GitHub' )
879+ def test_deschedule_test_function (self , git_mock , mock_debug , mock_critical ):
880+ """Check the functioning of deschedule_test function."""
881+ from mod_ci .controllers import deschedule_test
882+ repository = git_mock (access_token = g .github ['bot_token' ]).repos (
883+ g .github ['repository_owner' ])(g .github ['repository' ])
884+ deschedule_test (repository .statuses (1 ), "1" , TestType .commit )
885+ mock_debug .assert_not_called ()
886+ deschedule_test (None , None , TestType .commit )
887+ mock_debug .assert_not_called ()
888+
715889 @mock .patch ('mod_ci.controllers.inform_mailing_list' )
716890 @mock .patch ('requests.get' , side_effect = mock_api_request_github )
717891 @mock .patch ('mod_ci.controllers.Issue' )
@@ -1143,7 +1317,7 @@ def test_in_maintenance_mode_windows(self):
11431317 self .assertIsNotNone (response .data )
11441318
11451319 @staticmethod
1146- def generate_header (data , event ):
1320+ def generate_header (data , event , ci_key = None ):
11471321 """
11481322 Generate headers for various REST methods.
11491323
@@ -1152,6 +1326,7 @@ def generate_header(data, event):
11521326 :param event: the GitHub event to be triggered
11531327 :type event: str
11541328 """
1155- sig = generate_signature (str (json .dumps (data )).encode ('utf-8' ), g .github ['ci_key' ])
1329+ sig = generate_signature (str (json .dumps (data )).encode (
1330+ 'utf-8' ), g .github ['ci_key' ] if ci_key is None else ci_key )
11561331 headers = generate_git_api_header (event , sig )
11571332 return headers
0 commit comments