33from collections import OrderedDict
44from datetime import date
55from typing import Any , Dict , List , Optional , Type
6- from unittest .mock import Mock , patch
6+ from unittest .mock import patch
77
88import pytest
9+ import responses
910from marshmallow import ValidationError
10- from requests . models import Response
11+ from responses import matchers
1112
1213from pygitguardian import GGClient
1314from pygitguardian .client import is_ok , load_detail
@@ -518,10 +519,8 @@ def test_assert_content_type():
518519 ),
519520 ],
520521)
521- @patch ("requests.Session.request" )
522- @my_vcr .use_cassette
522+ @responses .activate
523523def test_extra_headers (
524- request_mock : Mock ,
525524 client : GGClient ,
526525 session_headers : Any ,
527526 extra_headers : Optional [Dict [str , str ]],
@@ -534,70 +533,72 @@ def test_extra_headers(
534533 """
535534 client .session .headers = session_headers
536535
537- mock_response = Mock (spec = Response )
538- mock_response .headers = {"content-type" : "text" }
539- mock_response .text = "some error"
540- mock_response .status_code = 400
541- request_mock .return_value = mock_response
536+ mock_response = responses .post (
537+ url = client ._url_from_endpoint ("multiscan" , "v1" ),
538+ content_type = "text/plain" ,
539+ body = "some error" ,
540+ status = 400 ,
541+ match = [matchers .header_matcher (extra_headers )] if extra_headers else [],
542+ )
542543
543544 client .multi_content_scan (
544545 [{"filename" : FILENAME , "document" : DOCUMENT }],
545546 extra_headers = extra_headers ,
546547 )
547- assert request_mock .called
548- _ , kwargs = request_mock .call_args
549- assert expected_headers == kwargs ["headers" ]
550-
548+ assert mock_response .call_count == 1
549+
550+ # Same test for content_scan
551+ mock_response = responses .post (
552+ url = client ._url_from_endpoint ("scan" , "v1" ),
553+ content_type = "text/plain" ,
554+ body = "some error" ,
555+ status = 400 ,
556+ match = [matchers .header_matcher (extra_headers )] if extra_headers else [],
557+ )
551558 client .content_scan ("some_string" , extra_headers = extra_headers )
552- assert request_mock .called
553- _ , kwargs = request_mock .call_args
554- assert expected_headers == kwargs ["headers" ]
559+ assert mock_response .call_count == 1
555560
556561
557- @patch ( "requests.Session.request" )
562+ @responses . activate
558563def test_multiscan_parameters (
559- request_mock : Mock ,
560564 client : GGClient ,
561565):
562566 """
563567 GIVEN a ggclient
564568 WHEN calling multi_content_scan with parameters
565569 THEN the parameters are passed in the request
566570 """
567- mock_response = Mock (spec = Response )
568- mock_response .headers = {"content-type" : "application/json" }
569- mock_response .status_code = 200
570- mock_response .json .return_value = [
571- {
572- "policy_break_count" : 1 ,
573- "policies" : ["pol" ],
574- "policy_breaks" : [
575- {
576- "type" : "break" ,
577- "policy" : "mypol" ,
578- "matches" : [
579- {
580- "match" : "hello" ,
581- "type" : "hello" ,
582- }
583- ],
584- }
585- ],
586- }
587- ]
588571
589- request_mock .return_value = mock_response
590-
591- params = {"ignore_known_secrets" : True }
572+ mock_response = responses .post (
573+ url = client ._url_from_endpoint ("multiscan" , "v1" ),
574+ status = 200 ,
575+ match = [matchers .query_param_matcher ({"ignore_known_secrets" : True })],
576+ json = [
577+ {
578+ "policy_break_count" : 1 ,
579+ "policies" : ["pol" ],
580+ "policy_breaks" : [
581+ {
582+ "type" : "break" ,
583+ "policy" : "mypol" ,
584+ "matches" : [
585+ {
586+ "match" : "hello" ,
587+ "type" : "hello" ,
588+ }
589+ ],
590+ }
591+ ],
592+ }
593+ ],
594+ )
592595
593596 client .multi_content_scan (
594597 [{"filename" : FILENAME , "document" : DOCUMENT }],
595598 ignore_known_secrets = True ,
596599 )
597600
598- assert request_mock .called
599- # 1 is for kwargs
600- assert request_mock .call_args [1 ]["params" ] == params
601+ assert mock_response .call_count == 1
601602
602603
603604def test_quota_overview (client : GGClient ):
@@ -620,109 +621,118 @@ def test_quota_overview(client: GGClient):
620621 assert type (json .loads (quota_response_json )) == dict
621622
622623
623- @pytest .mark .parametrize ("method" , ["get" , "post" ])
624- @patch ("requests.Session.request" )
625- def test_versions_from_headers (request_mock : Mock , client : GGClient , method ):
624+ @pytest .mark .parametrize ("method" , ["GET" , "POST" ])
625+ @responses .activate
626+ def test_versions_from_headers (client : GGClient , method ):
627+ """
628+ GIVEN a GGClient instance
629+ WHEN an HTTP request to GitGuardian API is made
630+ THEN the app_version and secrets_engine_version fields are set from the headers of
631+ the HTTP response
632+ """
633+ url = client ._url_from_endpoint ("endpoint" , "v1" )
626634 app_version_value = "1.0"
627635 secrets_engine_version_value = "2.0"
628636
629- mock_response = Mock (spec = Response )
630- mock_response .headers = {
631- "X-App-Version" : app_version_value ,
632- "X-Secrets-Engine-Version" : secrets_engine_version_value ,
633- }
634- request_mock .return_value = mock_response
637+ mock_response = responses .add (
638+ method = method ,
639+ url = url ,
640+ headers = {
641+ "X-App-Version" : app_version_value ,
642+ "X-Secrets-Engine-Version" : secrets_engine_version_value ,
643+ },
644+ )
635645
636646 client .request (method = method , endpoint = "endpoint" )
637- assert request_mock .called
638-
639- assert client .app_version is app_version_value
640- assert client .secrets_engine_version is secrets_engine_version_value
647+ assert mock_response .call_count == 1
641648
642- mock_response = Mock (spec = Response )
643- mock_response .headers = {}
644- request_mock .return_value = mock_response
649+ assert client .app_version == app_version_value
650+ assert client .secrets_engine_version == secrets_engine_version_value
645651
652+ # WHEN making another HTTP call whose response headers does not contain the version
653+ # fields
654+ # THEN known version fields remain set
655+ mock_response = responses .add (method = method , url = url )
646656 client .request (method = method , endpoint = "endpoint" )
647- assert request_mock . called
657+ assert mock_response . call_count == 1
648658
649- assert client .app_version is app_version_value
650- assert client .secrets_engine_version is secrets_engine_version_value
659+ assert client .app_version == app_version_value
660+ assert client .secrets_engine_version == secrets_engine_version_value
651661
662+ # WHEN creating another GGClient instance
663+ # THEN it already has the fields set
652664 other_client = GGClient (api_key = "" )
653- assert other_client .app_version is app_version_value
654- assert other_client .secrets_engine_version is secrets_engine_version_value
665+ assert other_client .app_version == app_version_value
666+ assert other_client .secrets_engine_version == secrets_engine_version_value
655667
656668
657- @patch ( "requests.Session.request" )
669+ @responses . activate
658670def test_create_honeytoken (
659- request_mock : Mock ,
660671 client : GGClient ,
661672):
662673 """
663674 GIVEN a ggclient
664675 WHEN calling create_honeytoken with parameters
665- THEN the parameters are passed in the request and the returned honeytoken use the parameters
676+ THEN the parameters are passed in the request
677+ AND the returned honeytoken use the parameters
666678 """
667- mock_response = Mock ( spec = Response )
668- mock_response . headers = { "content-type" : "application/json" }
669- mock_response . status_code = 201
670- mock_response . json . return_value = {
671- "id" : "d45a123f-b15d-4fea-abf6-ff2a8479de5b" ,
672- "name " : "honeytoken A " ,
673- "description " : "honeytoken used in the repository AA " ,
674- "created_at " : "2019-08-22T14:15:22Z " ,
675- "gitguardian_url " : "https://dashboard.gitguardian.com/workspace/1/honeytokens/d45a123f-b15d-4fea-abf6-ff2a8479de5b" , # noqa: E501
676- "status " : "active" ,
677- "triggered_at " : "2019-08-22T14:15:22Z " ,
678- "revoked_at " : None ,
679- "open_events_count " : 2 ,
680- "type " : "AWS" ,
681- "creator_id " : 122 ,
682- "revoker_id " : None ,
683- "creator_api_token_id " : None ,
684- "revoker_api_token_id " : None ,
685- "token" : { "access_token_id" : "AAAA" , "secret_key " : "BBB" } ,
686- "tags " : [ "publicly_exposed" ] ,
687- }
688-
689- request_mock . return_value = mock_response
679+ mock_response = responses . post (
680+ url = client . _url_from_endpoint ( "honeytokens" , "v1" ),
681+ content_type = "application/json" ,
682+ status = 201 ,
683+ json = {
684+ "id " : "d45a123f-b15d-4fea-abf6-ff2a8479de5b " ,
685+ "name " : "honeytoken A " ,
686+ "description " : "honeytoken used in the repository AA " ,
687+ "created_at " : "2019-08-22T14:15:22Z" ,
688+ "gitguardian_url " : "https://dashboard.gitguardian.com/workspace/1/honeytokens/d45a123f-b15d-4fea-abf6-ff2a8479de5b" , # noqa: E501
689+ "status " : "active " ,
690+ "triggered_at " : "2019-08-22T14:15:22Z" ,
691+ "revoked_at " : None ,
692+ "open_events_count " : 2 ,
693+ "type " : "AWS" ,
694+ "creator_id " : 122 ,
695+ "revoker_id " : None ,
696+ "creator_api_token_id " : None ,
697+ "revoker_api_token_id " : None ,
698+ "token " : { "access_token_id" : "AAAA" , "secret_key" : "BBB" } ,
699+ "tags" : [ "publicly_exposed" ],
700+ },
701+ )
690702
691703 result = client .create_honeytoken (
692704 name = "honeytoken A" ,
693705 description = "honeytoken used in the repository AA" ,
694706 type_ = "AWS" ,
695707 )
696708
697- assert request_mock . called
709+ assert mock_response . call_count == 1
698710 assert isinstance (result , HoneytokenResponse )
699711
700712
701- @patch ( "requests.Session.request" )
713+ @responses . activate
702714def test_create_honeytoken_error (
703- request_mock : Mock ,
704715 client : GGClient ,
705716):
706717 """
707718 GIVEN a ggclient
708719 WHEN calling create_honeytoken with parameters without the right access
709- THEN I get a Detail objects containing the error detail
720+ THEN I get a Detail object containing the error detail
710721 """
711- mock_response = Mock ( spec = Response )
712- mock_response . headers = { "content-type" : "application/json" }
713- mock_response . status_code = 400
714- mock_response . json . return_value = {
715- "detail" : "Not authorized" ,
716- }
717-
718- request_mock . return_value = mock_response
722+ mock_response = responses . post (
723+ url = client . _url_from_endpoint ( "honeytokens" , "v1" ),
724+ content_type = "application/json" ,
725+ status = 400 ,
726+ json = {
727+ "detail" : "Not authorized" ,
728+ },
729+ )
719730
720731 result = client .create_honeytoken (
721732 name = "honeytoken A" ,
722733 description = "honeytoken used in the repository AA" ,
723734 type_ = "AWS" ,
724735 )
725736
726- assert request_mock . called
737+ assert mock_response . call_count == 1
727738 assert isinstance (result , Detail )
728- result .status_code == 400
0 commit comments