11import pytest
22from socketdev .fullscans import FullScanParams
33
4+ from socketsecurity .config import CliConfig
45from socketsecurity .core import Core
56from socketsecurity .core .socket_config import SocketConfig
67
@@ -10,6 +11,23 @@ def core(mock_sdk_with_responses):
1011 config = SocketConfig (api_key = "test_key" )
1112 return Core (config = config , sdk = mock_sdk_with_responses )
1213
14+
15+ def make_cli_config (* extra_args ):
16+ return CliConfig .from_args (["--api-token" , "test-token" , "--repo" , "test" , * extra_args ])
17+
18+
19+ def make_full_scan_params (** overrides ):
20+ values = {
21+ "repo" : "test" ,
22+ "branch" : "main" ,
23+ "commit_hash" : "head123" ,
24+ "scan_type" : "socket" ,
25+ "workspace" : None ,
26+ }
27+ values .update (overrides )
28+ return FullScanParams (** values )
29+
30+
1331def test_get_repo_info (core , mock_sdk_with_responses ):
1432 """Test getting repository information"""
1533 repo_info = core .get_repo_info ("test" )
@@ -44,6 +62,119 @@ def test_get_head_scan_for_repo_no_head(core, mock_sdk_with_responses):
4462 head_scan_id = core .get_head_scan_for_repo ("no-head" )
4563 assert head_scan_id is None
4664
65+ def test_get_full_scan_id_by_commit (core , mock_sdk_with_responses ):
66+ """Looks up the newest full scan for a repo + commit via the list endpoint"""
67+ mock_sdk_with_responses .fullscans .get .return_value = {
68+ "results" : [{"id" : "base-scan-id" , "commit_hash" : "abc123" }],
69+ "nextPage" : None ,
70+ }
71+
72+ scan_id = core .get_full_scan_id_by_commit ("test" , "abc123" )
73+
74+ assert scan_id == "base-scan-id"
75+ mock_sdk_with_responses .fullscans .get .assert_called_once_with (
76+ core .config .org_slug ,
77+ {
78+ "repo" : "test" ,
79+ "commit_hash" : "abc123" ,
80+ "sort" : "created_at" ,
81+ "direction" : "desc" ,
82+ "per_page" : 1 ,
83+ },
84+ )
85+
86+
87+ def test_get_full_scan_id_by_commit_scopes_to_workspace_and_scan_type (core , mock_sdk_with_responses ):
88+ """Looks up a baseline scan from the same workspace and scan type as the new scan"""
89+ mock_sdk_with_responses .fullscans .get .return_value = {
90+ "results" : [{"id" : "workspace-reach-base" , "commit_hash" : "abc123" }],
91+ "nextPage" : None ,
92+ }
93+
94+ scan_id = core .get_full_scan_id_by_commit (
95+ "test" ,
96+ "abc123" ,
97+ workspace = "customer-a" ,
98+ scan_type = "socket_tier1" ,
99+ )
100+
101+ assert scan_id == "workspace-reach-base"
102+ mock_sdk_with_responses .fullscans .get .assert_called_once_with (
103+ core .config .org_slug ,
104+ {
105+ "repo" : "test" ,
106+ "commit_hash" : "abc123" ,
107+ "sort" : "created_at" ,
108+ "direction" : "desc" ,
109+ "per_page" : 1 ,
110+ "workspace" : "customer-a" ,
111+ "scan_type" : "socket_tier1" ,
112+ },
113+ )
114+
115+
116+ def test_get_full_scan_id_by_commit_not_found (core , mock_sdk_with_responses ):
117+ """No scan for the commit returns None (empty results and SDK error dict)"""
118+ mock_sdk_with_responses .fullscans .get .return_value = {"results" : [], "nextPage" : None }
119+ assert core .get_full_scan_id_by_commit ("test" , "abc123" ) is None
120+
121+ mock_sdk_with_responses .fullscans .get .return_value = {}
122+ assert core .get_full_scan_id_by_commit ("test" , "abc123" ) is None
123+
124+ def test_resolve_base_full_scan_id_defaults_to_head_scan (core ):
125+ """Without base overrides the repository head scan is the baseline"""
126+ assert core .resolve_base_full_scan_id (make_full_scan_params ()) == "head"
127+
128+ def test_resolve_base_full_scan_id_uses_base_scan_id (core ):
129+ """--base-scan-id is used verbatim, without touching the repo endpoint"""
130+ core .cli_config = make_cli_config ("--base-scan-id" , "explicit-base" )
131+
132+ assert core .resolve_base_full_scan_id (make_full_scan_params ()) == "explicit-base"
133+ core .sdk .repos .repo .assert_not_called ()
134+
135+ def test_resolve_base_full_scan_id_uses_base_commit_sha (core ):
136+ """--base-commit-sha resolves through the full-scans list endpoint"""
137+ core .cli_config = make_cli_config ("--base-commit-sha" , "abc123" )
138+ core .sdk .fullscans .get .return_value = {
139+ "results" : [{"id" : "merge-base-scan" }],
140+ "nextPage" : None ,
141+ }
142+
143+ params = make_full_scan_params (workspace = "customer-a" , scan_type = "socket_tier1" )
144+
145+ assert core .resolve_base_full_scan_id (params ) == "merge-base-scan"
146+ core .sdk .repos .repo .assert_not_called ()
147+ core .sdk .fullscans .get .assert_called_once_with (
148+ core .config .org_slug ,
149+ {
150+ "repo" : "test" ,
151+ "commit_hash" : "abc123" ,
152+ "sort" : "created_at" ,
153+ "direction" : "desc" ,
154+ "per_page" : 1 ,
155+ "workspace" : "customer-a" ,
156+ "scan_type" : "socket_tier1" ,
157+ },
158+ )
159+
160+ def test_resolve_base_full_scan_id_commit_sha_not_found_exits (core ):
161+ """A --base-commit-sha with no scan is a hard error (exit_code_on_api_error)"""
162+ core .cli_config = make_cli_config ("--base-commit-sha" , "abc123" )
163+ core .sdk .fullscans .get .return_value = {"results" : [], "nextPage" : None }
164+
165+ with pytest .raises (SystemExit ) as exc_info :
166+ core .resolve_base_full_scan_id (make_full_scan_params ())
167+ assert exc_info .value .code == core .cli_config .exit_code_on_api_error
168+
169+ def test_resolve_base_full_scan_id_commit_sha_not_found_disable_blocking (core ):
170+ """--disable-blocking keeps the missing-base error from failing the build"""
171+ core .cli_config = make_cli_config ("--base-commit-sha" , "abc123" , "--disable-blocking" )
172+ core .sdk .fullscans .get .return_value = {"results" : [], "nextPage" : None }
173+
174+ with pytest .raises (SystemExit ) as exc_info :
175+ core .resolve_base_full_scan_id (make_full_scan_params ())
176+ assert exc_info .value .code == 0
177+
47178def test_get_full_scan (core , mock_sdk_with_responses , head_scan_metadata , head_scan_stream ):
48179 """Test getting an existing full scan"""
49180 full_scan = core .get_full_scan ("head" )
@@ -98,7 +229,7 @@ def test_get_added_and_removed_packages(core):
98229 # Verify SDK was called correctly.
99230 # include_license_details defaults to "false": the diff path never consumes
100231 # embedded license data (license artifacts come from the PURL endpoint), so
101- # requesting it only bloats the response and risks the CE-224 truncation
232+ # requesting it only bloats the response and risks the truncation
102233 # crash on large repos.
103234 core .sdk .fullscans .stream_diff .assert_called_once_with (
104235 core .config .org_slug ,
0 commit comments