4
4
"""Tests for the FakeEmailAnalyzer heuristic."""
5
5
6
6
7
- from collections .abc import Generator
8
- from unittest .mock import MagicMock , patch
7
+ from unittest .mock import MagicMock
9
8
10
9
import pytest
11
- from email_validator import EmailNotValidError
12
10
13
11
from macaron .malware_analyzer .pypi_heuristics .heuristics import HeuristicResult
14
12
from macaron .malware_analyzer .pypi_heuristics .metadata .fake_email import FakeEmailAnalyzer
@@ -22,20 +20,13 @@ def analyzer_() -> FakeEmailAnalyzer:
22
20
23
21
24
22
@pytest .fixture (name = "pypi_package_json_asset_mock" )
25
- def pypi_package_json_asset_mock_fixture () -> MagicMock :
23
+ def pypi_package_json_asset_mock_ () -> MagicMock :
26
24
"""Pytest fixture for a mock PyPIPackageJsonAsset."""
27
25
mock_asset = MagicMock (spec = PyPIPackageJsonAsset )
28
26
mock_asset .package_json = {}
29
27
return mock_asset
30
28
31
29
32
- @pytest .fixture (name = "mock_validate_email" )
33
- def mock_validate_email_fixture () -> Generator [MagicMock ]:
34
- """Patch validate_email and mock its behavior."""
35
- with patch ("macaron.malware_analyzer.pypi_heuristics.metadata.fake_email.validate_email" ) as mock :
36
- yield mock
37
-
38
-
39
30
def test_analyze_skip_no_emails_present (analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock ) -> None :
40
31
"""Test the analyzer skips if no author_email or maintainer_email is present."""
41
32
pypi_package_json_asset_mock .package_json = {"info" : {"author_email" : None , "maintainer_email" : None }}
@@ -52,91 +43,65 @@ def test_analyze_skip_no_info_key(analyzer: FakeEmailAnalyzer, pypi_package_json
52
43
assert info ["message" ] == "No package info available."
53
44
54
45
55
- def test_analyze_fail_invalid_email (
56
- analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock , mock_validate_email : MagicMock
57
- ) -> None :
46
+ def test_analyze_fail_invalid_email (analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock ) -> None :
58
47
"""Test analyzer fails for an invalid email format."""
59
48
invalid_email = "invalid-email"
60
49
pypi_package_json_asset_mock .package_json = {"info" : {"author_email" : invalid_email , "maintainer_email" : None }}
61
- mock_validate_email .side_effect = EmailNotValidError ("Invalid email." )
62
50
63
51
result , info = analyzer .analyze (pypi_package_json_asset_mock )
64
-
65
52
assert result == HeuristicResult .FAIL
66
53
assert info == {"email" : invalid_email }
67
- mock_validate_email .assert_called_once_with (invalid_email , check_deliverability = True )
68
54
69
55
70
56
def test_analyze_pass_only_maintainer_email_valid (
71
- analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock , mock_validate_email : MagicMock
57
+ analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock
72
58
) -> None :
73
- """Test analyzer passes when only maintainer_email is present and valid ."""
59
+ """Test the analyzer's behavior when only a valid maintainer_email is present."""
74
60
email = "maintainer@example.net"
75
61
pypi_package_json_asset_mock .package_json = {"info" : {"author_email" : None , "maintainer_email" : email }}
62
+ result , info = analyzer .analyze (pypi_package_json_asset_mock )
76
63
77
- mock_email_info = MagicMock ()
78
- mock_email_info .normalized = "maintainer@example.net"
79
- mock_email_info .local_part = "maintainer"
80
- mock_email_info .domain = "example.net"
81
- mock_validate_email .return_value = mock_email_info
64
+ if analyzer .check_deliverability :
65
+ assert result == HeuristicResult .FAIL
66
+ assert info == {"email" : email }
67
+ return
82
68
83
- result , info = analyzer .analyze (pypi_package_json_asset_mock )
84
69
assert result == HeuristicResult .PASS
85
70
assert info ["validated_emails" ] == [
86
71
{"normalized" : "maintainer@example.net" , "local_part" : "maintainer" , "domain" : "example.net" }
87
72
]
88
- mock_validate_email .assert_called_once_with (email , check_deliverability = True )
89
73
90
74
91
- def test_analyze_pass_both_emails_valid (
92
- analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock , mock_validate_email : MagicMock
93
- ) -> None :
94
- """Test the analyzer passes when both emails are present and valid."""
95
-
96
- def side_effect (email : str , check_deliverability : bool ) -> MagicMock : # pylint: disable=unused-argument
97
- local_part , domain = email .split ("@" )
98
- mock_email_info = MagicMock ()
99
- mock_email_info .normalized = email
100
- mock_email_info .local_part = local_part
101
- mock_email_info .domain = domain
102
- return mock_email_info
103
-
104
- mock_validate_email .side_effect = side_effect
75
+ def test_analyze_pass_both_emails_valid (analyzer : FakeEmailAnalyzer , pypi_package_json_asset_mock : MagicMock ) -> None :
76
+ """Test the analyzer's behavior when both author and maintainer emails are valid."""
77
+ author_email = "example@gmail.com"
78
+ author_local_part , author_domain = author_email .split ("@" )
79
+ maintainer_email = "maintainer@example.net"
80
+ maintainer_local_part , maintainer_domain = maintainer_email .split ("@" )
105
81
106
82
pypi_package_json_asset_mock .package_json = {
107
- "info" : {"author_email" : "author@example.com" , "maintainer_email" : "maintainer@example.net" }
83
+ "info" : {"author_email" : author_email , "maintainer_email" : maintainer_email }
108
84
}
109
85
result , info = analyzer .analyze (pypi_package_json_asset_mock )
86
+ if analyzer .check_deliverability :
87
+ assert result == HeuristicResult .FAIL
88
+ assert info == {"email" : maintainer_email }
89
+ return
90
+
110
91
assert result == HeuristicResult .PASS
111
- assert mock_validate_email .call_count == 2
112
92
113
93
validated_emails = info .get ("validated_emails" )
114
94
assert isinstance (validated_emails , list )
115
95
assert len (validated_emails ) == 2
116
- assert {"normalized" : "author@example.com" , "local_part" : "author" , "domain" : "example.com" } in validated_emails
96
+ assert {"normalized" : author_email , "local_part" : author_local_part , "domain" : author_domain } in validated_emails
117
97
assert {
118
- "normalized" : "maintainer@example.net" ,
119
- "local_part" : "maintainer" ,
120
- "domain" : "example.net" ,
98
+ "normalized" : maintainer_email ,
99
+ "local_part" : maintainer_local_part ,
100
+ "domain" : maintainer_domain ,
121
101
} in validated_emails
122
102
123
103
124
- def test_is_valid_email_success (analyzer : FakeEmailAnalyzer , mock_validate_email : MagicMock ) -> None :
125
- """Test is_valid_email returns the validation object on success."""
126
- mock_validated_email = MagicMock ()
127
- mock_validated_email .normalized = "test@example.com"
128
- mock_validated_email .local_part = "test"
129
- mock_validated_email .domain = "example.com"
130
-
131
- mock_validate_email .return_value = mock_validated_email
132
- result = analyzer .is_valid_email ("test@example.com" )
133
- assert result == mock_validated_email
134
- mock_validate_email .assert_called_once_with ("test@example.com" , check_deliverability = True )
135
-
136
-
137
- def test_is_valid_email_failure (analyzer : FakeEmailAnalyzer , mock_validate_email : MagicMock ) -> None :
104
+ def test_is_valid_email_failure (analyzer : FakeEmailAnalyzer ) -> None :
138
105
"""Test is_valid_email returns None on failure."""
139
- mock_validate_email .side_effect = EmailNotValidError ("The email address is not valid." )
140
106
result = analyzer .is_valid_email ("invalid-email" )
141
107
assert result is None
142
- mock_validate_email .assert_called_once_with ("invalid-email" , check_deliverability = True )
0 commit comments