3
3
from collections import OrderedDict
4
4
from datetime import date
5
5
from typing import Any , Dict , List , Optional , Type
6
- from unittest .mock import Mock , patch
6
+ from unittest .mock import patch
7
7
8
8
import pytest
9
+ import responses
9
10
from marshmallow import ValidationError
10
- from requests . models import Response
11
+ from responses import matchers
11
12
12
13
from pygitguardian import GGClient
13
14
from pygitguardian .client import is_ok , load_detail
@@ -518,10 +519,8 @@ def test_assert_content_type():
518
519
),
519
520
],
520
521
)
521
- @patch ("requests.Session.request" )
522
- @my_vcr .use_cassette
522
+ @responses .activate
523
523
def test_extra_headers (
524
- request_mock : Mock ,
525
524
client : GGClient ,
526
525
session_headers : Any ,
527
526
extra_headers : Optional [Dict [str , str ]],
@@ -534,70 +533,72 @@ def test_extra_headers(
534
533
"""
535
534
client .session .headers = session_headers
536
535
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
+ )
542
543
543
544
client .multi_content_scan (
544
545
[{"filename" : FILENAME , "document" : DOCUMENT }],
545
546
extra_headers = extra_headers ,
546
547
)
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
+ )
551
558
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
555
560
556
561
557
- @patch ( "requests.Session.request" )
562
+ @responses . activate
558
563
def test_multiscan_parameters (
559
- request_mock : Mock ,
560
564
client : GGClient ,
561
565
):
562
566
"""
563
567
GIVEN a ggclient
564
568
WHEN calling multi_content_scan with parameters
565
569
THEN the parameters are passed in the request
566
570
"""
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
- ]
588
571
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
+ )
592
595
593
596
client .multi_content_scan (
594
597
[{"filename" : FILENAME , "document" : DOCUMENT }],
595
598
ignore_known_secrets = True ,
596
599
)
597
600
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
601
602
602
603
603
604
def test_quota_overview (client : GGClient ):
@@ -620,109 +621,118 @@ def test_quota_overview(client: GGClient):
620
621
assert type (json .loads (quota_response_json )) == dict
621
622
622
623
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" )
626
634
app_version_value = "1.0"
627
635
secrets_engine_version_value = "2.0"
628
636
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
+ )
635
645
636
646
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
641
648
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
645
651
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 )
646
656
client .request (method = method , endpoint = "endpoint" )
647
- assert request_mock . called
657
+ assert mock_response . call_count == 1
648
658
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
651
661
662
+ # WHEN creating another GGClient instance
663
+ # THEN it already has the fields set
652
664
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
655
667
656
668
657
- @patch ( "requests.Session.request" )
669
+ @responses . activate
658
670
def test_create_honeytoken (
659
- request_mock : Mock ,
660
671
client : GGClient ,
661
672
):
662
673
"""
663
674
GIVEN a ggclient
664
675
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
666
678
"""
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
+ )
690
702
691
703
result = client .create_honeytoken (
692
704
name = "honeytoken A" ,
693
705
description = "honeytoken used in the repository AA" ,
694
706
type_ = "AWS" ,
695
707
)
696
708
697
- assert request_mock . called
709
+ assert mock_response . call_count == 1
698
710
assert isinstance (result , HoneytokenResponse )
699
711
700
712
701
- @patch ( "requests.Session.request" )
713
+ @responses . activate
702
714
def test_create_honeytoken_error (
703
- request_mock : Mock ,
704
715
client : GGClient ,
705
716
):
706
717
"""
707
718
GIVEN a ggclient
708
719
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
710
721
"""
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
+ )
719
730
720
731
result = client .create_honeytoken (
721
732
name = "honeytoken A" ,
722
733
description = "honeytoken used in the repository AA" ,
723
734
type_ = "AWS" ,
724
735
)
725
736
726
- assert request_mock . called
737
+ assert mock_response . call_count == 1
727
738
assert isinstance (result , Detail )
728
- result .status_code == 400
0 commit comments