-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathtest_sdk_methods.py
More file actions
290 lines (238 loc) · 10.7 KB
/
Copy pathtest_sdk_methods.py
File metadata and controls
290 lines (238 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import pytest
from socketdev.fullscans import FullScanParams
from socketsecurity.config import CliConfig
from socketsecurity.core import Core
from socketsecurity.core.socket_config import SocketConfig
@pytest.fixture
def core(mock_sdk_with_responses):
config = SocketConfig(api_key="test_key")
return Core(config=config, sdk=mock_sdk_with_responses)
def make_cli_config(*extra_args):
return CliConfig.from_args(["--api-token", "test-token", "--repo", "test", *extra_args])
def make_full_scan_params(**overrides):
values = {
"repo": "test",
"branch": "main",
"commit_hash": "head123",
"scan_type": "socket",
"workspace": None,
}
values.update(overrides)
return FullScanParams(**values)
def test_get_repo_info(core, mock_sdk_with_responses):
"""Test getting repository information"""
repo_info = core.get_repo_info("test")
# Assert SDK called correctly
mock_sdk_with_responses.repos.repo.assert_called_once_with(
core.config.org_slug,
"test",
use_types=True,
)
# Assert response processed correctly
assert repo_info.id == "f639d6c9-acc3-4d8a-9fb5-2090ad651c7e"
assert repo_info.head_full_scan_id == "head"
def test_get_head_scan_for_repo(core, mock_sdk_with_responses):
"""Test getting head scan ID for a repository"""
head_scan_id = core.get_head_scan_for_repo("test")
# Assert SDK method called correctly
mock_sdk_with_responses.repos.repo.assert_called_once_with(
core.config.org_slug,
"test",
use_types=True,
)
# Assert we got the expected head scan ID
assert head_scan_id == "head"
def test_get_head_scan_for_repo_no_head(core, mock_sdk_with_responses):
"""Test getting head scan ID for repo with no head scan"""
head_scan_id = core.get_head_scan_for_repo("no-head")
assert head_scan_id is None
def test_get_full_scan_id_by_commit(core, mock_sdk_with_responses):
"""Looks up the newest full scan for a repo + commit via the list endpoint"""
mock_sdk_with_responses.fullscans.get.return_value = {
"results": [{"id": "base-scan-id", "commit_hash": "abc123"}],
"nextPage": None,
}
scan_id = core.get_full_scan_id_by_commit("test", "abc123")
assert scan_id == "base-scan-id"
mock_sdk_with_responses.fullscans.get.assert_called_once_with(
core.config.org_slug,
{
"repo": "test",
"commit_hash": "abc123",
"sort": "created_at",
"direction": "desc",
"per_page": 1,
},
)
def test_get_full_scan_id_by_commit_scopes_to_workspace_and_scan_type(core, mock_sdk_with_responses):
"""Looks up a baseline scan from the same workspace and scan type as the new scan"""
mock_sdk_with_responses.fullscans.get.return_value = {
"results": [{"id": "workspace-reach-base", "commit_hash": "abc123"}],
"nextPage": None,
}
scan_id = core.get_full_scan_id_by_commit(
"test",
"abc123",
workspace="customer-a",
scan_type="socket_tier1",
)
assert scan_id == "workspace-reach-base"
mock_sdk_with_responses.fullscans.get.assert_called_once_with(
core.config.org_slug,
{
"repo": "test",
"commit_hash": "abc123",
"sort": "created_at",
"direction": "desc",
"per_page": 1,
"workspace": "customer-a",
"scan_type": "socket_tier1",
},
)
def test_get_full_scan_id_by_commit_not_found(core, mock_sdk_with_responses):
"""No scan for the commit returns None (empty results and SDK error dict)"""
mock_sdk_with_responses.fullscans.get.return_value = {"results": [], "nextPage": None}
assert core.get_full_scan_id_by_commit("test", "abc123") is None
mock_sdk_with_responses.fullscans.get.return_value = {}
assert core.get_full_scan_id_by_commit("test", "abc123") is None
def test_resolve_base_full_scan_id_defaults_to_head_scan(core):
"""Without base overrides the repository head scan is the baseline"""
assert core.resolve_base_full_scan_id(make_full_scan_params()) == "head"
def test_resolve_base_full_scan_id_uses_base_scan_id(core):
"""--base-scan-id is used verbatim, without touching the repo endpoint"""
core.cli_config = make_cli_config("--base-scan-id", "explicit-base")
assert core.resolve_base_full_scan_id(make_full_scan_params()) == "explicit-base"
core.sdk.repos.repo.assert_not_called()
def test_resolve_base_full_scan_id_uses_base_commit_sha(core):
"""--base-commit-sha resolves through the full-scans list endpoint"""
core.cli_config = make_cli_config("--base-commit-sha", "abc123")
core.sdk.fullscans.get.return_value = {
"results": [{"id": "merge-base-scan"}],
"nextPage": None,
}
params = make_full_scan_params(workspace="customer-a", scan_type="socket_tier1")
assert core.resolve_base_full_scan_id(params) == "merge-base-scan"
core.sdk.repos.repo.assert_not_called()
core.sdk.fullscans.get.assert_called_once_with(
core.config.org_slug,
{
"repo": "test",
"commit_hash": "abc123",
"sort": "created_at",
"direction": "desc",
"per_page": 1,
"workspace": "customer-a",
"scan_type": "socket_tier1",
},
)
def test_resolve_base_full_scan_id_commit_sha_not_found_exits(core):
"""A --base-commit-sha with no scan is a hard error (exit_code_on_api_error)"""
core.cli_config = make_cli_config("--base-commit-sha", "abc123")
core.sdk.fullscans.get.return_value = {"results": [], "nextPage": None}
with pytest.raises(SystemExit) as exc_info:
core.resolve_base_full_scan_id(make_full_scan_params())
assert exc_info.value.code == core.cli_config.exit_code_on_api_error
def test_resolve_base_full_scan_id_commit_sha_not_found_disable_blocking(core):
"""--disable-blocking keeps the missing-base error from failing the build"""
core.cli_config = make_cli_config("--base-commit-sha", "abc123", "--disable-blocking")
core.sdk.fullscans.get.return_value = {"results": [], "nextPage": None}
with pytest.raises(SystemExit) as exc_info:
core.resolve_base_full_scan_id(make_full_scan_params())
assert exc_info.value.code == 0
def test_get_full_scan(core, mock_sdk_with_responses, head_scan_metadata, head_scan_stream):
"""Test getting an existing full scan"""
full_scan = core.get_full_scan("head")
# Assert SDK methods called correctly
mock_sdk_with_responses.fullscans.metadata.assert_called_once_with(
core.config.org_slug,
"head",
use_types=True,
)
mock_sdk_with_responses.fullscans.stream.assert_called_once_with(
core.config.org_slug,
"head",
use_types=True,
)
# Assert response processed correctly
assert full_scan.id == head_scan_metadata["data"]["id"]
assert len(full_scan.sbom_artifacts) == len(head_scan_stream.artifacts)
assert len(full_scan.packages) == len(head_scan_stream.artifacts)
assert full_scan.packages["dp1"].transitives == 2
def test_create_full_scan(core, mock_sdk_with_responses, new_scan_metadata):
"""Test creating a new full scan"""
# Setup test data
files = ["requirements.txt"]
params = FullScanParams(
repo="test-repo",
branch="main",
commit_hash="abc123",
)
# Create the full scan
full_scan = core.create_full_scan(files, params)
# Verify the response
assert full_scan.id == new_scan_metadata["data"]["id"]
mock_sdk_with_responses.fullscans.post.assert_called_once_with(
files,
params,
use_types=True,
use_lazy_loading=True,
max_open_files=50,
base_paths=None,
)
def test_get_added_and_removed_packages(core):
"""Test getting added and removed packages between two scans"""
# Get two different scans to compare
added, removed, all_packages = core.get_added_and_removed_packages("head", "new")
# Verify SDK was called correctly: the comparison goes through the diff-scans
# endpoints (create + poll) rather than the legacy streaming diff, so no
# connection is left idle while the backend computes.
create_args = core.sdk.diffscans.create_from_ids.call_args
assert create_args[0][0] == core.config.org_slug
create_params = create_args[0][1]
assert create_params["before"] == "head"
assert create_params["after"] == "new"
assert "on_duplicate" not in create_params
# cached=true is the polling contract (202 while computing, 200 when ready).
# No omit_license_details param: the API ignores it for cached reads (cached
# results always embed license details), so sending it would only suggest a
# leanness guarantee this path doesn't have.
core.sdk.diffscans.get.assert_called_once_with(
core.config.org_slug,
"diff-scan-123",
params={"cached": "true"},
)
core.sdk.fullscans.stream_diff.assert_not_called()
# Verify the results
# Added packages
assert len(added) > 0 # We should have some added packages
assert "dp3" in added # Verify specific package we know was added
assert "dp4" in added
# Removed packages
assert len(removed) > 0 # We should have some removed packages
assert "dp2" in removed # Verify specific package we know was removed
assert "dp2_t1" in removed # Verify transitive dependencies are also tracked
assert "pypi/direct_package_1@1.6.0" in all_packages # Unchanged package is in full package map
def test_get_added_and_removed_packages_license_override(core):
"""include_license_details only governs the legacy fallback path now: the
diff-scans path always receives embedded license details (the API ignores
omit_license_details for cached reads), so the seam must survive through to
the stream_diff call when the primary path is unavailable."""
from socketdev.exceptions import APIFailure
core.sdk.diffscans.create_from_ids.side_effect = APIFailure("forbidden", status_code=403)
core.get_added_and_removed_packages("head", "new", include_license_details=True)
core.sdk.fullscans.stream_diff.assert_called_once_with(
core.config.org_slug,
"head",
"new",
use_types=True,
include_license_details="true",
)
def test_empty_alerts_preserved(core):
"""Test that empty alerts arrays stay as empty arrays and don't become None"""
# Get the scan that contains dp2 (which has empty alerts array)
head_scan = core.get_full_scan("head")
# Check the raw artifact first
artifacts = core.get_sbom_data("head")
assert artifacts["dp2"].alerts == [] # Should be empty list, not None
# Check the final package
assert head_scan.packages["dp2"].alerts == [] # Should still be empty list