@@ -19,14 +19,15 @@ import (
19
19
"bytes"
20
20
"context"
21
21
_ "embed"
22
+ "encoding/json"
23
+ "fmt"
22
24
"io"
23
25
"net/http"
24
26
"testing"
25
27
"time"
26
28
27
- "github.com/google/uuid"
28
-
29
29
"github.com/golang/mock/gomock"
30
+ "github.com/google/uuid"
30
31
"github.com/rs/zerolog"
31
32
"github.com/stretchr/testify/assert"
32
33
"github.com/stretchr/testify/mock"
@@ -35,10 +36,84 @@ import (
35
36
confMocks "github.com/snyk/code-client-go/config/mocks"
36
37
httpmocks "github.com/snyk/code-client-go/http/mocks"
37
38
"github.com/snyk/code-client-go/internal/analysis"
39
+ v20241221 "github.com/snyk/code-client-go/internal/api/test/2024-12-21"
40
+ mocks2 "github.com/snyk/code-client-go/internal/bundle/mocks"
38
41
"github.com/snyk/code-client-go/observability/mocks"
42
+ "github.com/snyk/code-client-go/sarif"
43
+ "github.com/snyk/code-client-go/scan"
39
44
trackerMocks "github.com/snyk/code-client-go/scan/mocks"
40
45
)
41
46
47
+ func mockDeriveErrorFromStatusCode (statusCode int ) error {
48
+ if statusCode >= http .StatusOK && statusCode < http .StatusBadRequest {
49
+ return nil
50
+ }
51
+
52
+ return fmt .Errorf ("Statuscode: %d" , statusCode )
53
+ }
54
+
55
+ func mockGetDocumentResponse (t * testing.T , sarifResponse sarif.SarifDocument , expectedDocumentPath string , mockHTTPClient * httpmocks.MockHTTPClient , responseCode int ) {
56
+ t .Helper ()
57
+ responseBodyBytes , err := json .Marshal (sarifResponse )
58
+ assert .NoError (t , err )
59
+ expectedDocumentUrl := fmt .Sprintf ("http://localhost/hidden%s?version=%s" , expectedDocumentPath , v20241221 .DocumentApiVersion )
60
+ mockHTTPClient .EXPECT ().Do (mock .MatchedBy (func (i interface {}) bool {
61
+ req := i .(* http.Request )
62
+ return req .URL .String () == expectedDocumentUrl
63
+ })).Times (1 ).Return (& http.Response {
64
+ StatusCode : responseCode ,
65
+ Header : http.Header {
66
+ "Content-Type" : []string {"application/json" },
67
+ },
68
+ Body : io .NopCloser (bytes .NewReader (responseBodyBytes )),
69
+ }, mockDeriveErrorFromStatusCode (responseCode ))
70
+ }
71
+
72
+ func mockResultCompletedResponse (t * testing.T , mockHTTPClient * httpmocks.MockHTTPClient , expectedWebuilink string , projectId uuid.UUID , orgId string , testId uuid.UUID , documentPath string , responseCode int ) {
73
+ t .Helper ()
74
+ response := v20241221 .NewTestResponse ()
75
+ state := v20241221 .NewTestCompleteState ()
76
+ state .Documents .EnrichedSarif = documentPath
77
+ state .Results .Webui .Link = & expectedWebuilink
78
+ state .Results .Webui .ProjectId = & projectId
79
+ stateBytes , err := json .Marshal (state )
80
+ assert .NoError (t , err )
81
+ response .Data .Attributes .UnmarshalJSON (stateBytes )
82
+ responseBodyBytes , err := json .Marshal (response )
83
+ assert .NoError (t , err )
84
+ expectedRetrieveTestUrl := fmt .Sprintf ("http://localhost/hidden/orgs/%s/tests/%s?version=%s" , orgId , testId , v20241221 .ApiVersion )
85
+ mockHTTPClient .EXPECT ().Do (mock .MatchedBy (func (i interface {}) bool {
86
+ req := i .(* http.Request )
87
+ return req .URL .String () == expectedRetrieveTestUrl
88
+ })).Times (1 ).Return (& http.Response {
89
+ StatusCode : responseCode ,
90
+ Header : http.Header {
91
+ "Content-Type" : []string {"application/json" },
92
+ },
93
+ Body : io .NopCloser (bytes .NewReader (responseBodyBytes )),
94
+ }, mockDeriveErrorFromStatusCode (responseCode ))
95
+ }
96
+
97
+ func mockTestCreatedResponse (t * testing.T , mockHTTPClient * httpmocks.MockHTTPClient , testId uuid.UUID , orgId string , responseCode int ) {
98
+ t .Helper ()
99
+ response := v20241221 .NewTestResponse ()
100
+ response .Data .Id = testId
101
+ responseBodyBytes , err := json .Marshal (response )
102
+ assert .NoError (t , err )
103
+ expectedTestCreatedUrl := fmt .Sprintf ("http://localhost/hidden/orgs/%s/tests?version=%s" , orgId , v20241221 .ApiVersion )
104
+ mockHTTPClient .EXPECT ().Do (mock .MatchedBy (func (i interface {}) bool {
105
+ req := i .(* http.Request )
106
+ return req .URL .String () == expectedTestCreatedUrl &&
107
+ req .Method == http .MethodPost
108
+ })).Times (1 ).Return (& http.Response {
109
+ StatusCode : responseCode ,
110
+ Header : http.Header {
111
+ "Content-Type" : []string {"application/json" },
112
+ },
113
+ Body : io .NopCloser (bytes .NewReader (responseBodyBytes )),
114
+ }, mockDeriveErrorFromStatusCode (responseCode ))
115
+ }
116
+
42
117
func setup (t * testing.T , timeout * time.Duration ) (* confMocks.MockConfig , * httpmocks.MockHTTPClient , * mocks.MockInstrumentor , * mocks.MockErrorReporter , * trackerMocks.MockTracker , * trackerMocks.MockTrackerFactory , zerolog.Logger ) {
43
118
t .Helper ()
44
119
ctrl := gomock .NewController (t )
@@ -68,58 +143,88 @@ func setup(t *testing.T, timeout *time.Duration) (*confMocks.MockConfig, *httpmo
68
143
return mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger
69
144
}
70
145
71
- //go:embed fake.json
72
- var fakeResponse []byte
146
+ func TestAnalysis_RunTest (t * testing.T ) {
147
+ ctrl := gomock .NewController (t )
148
+ mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger := setup (t , nil )
149
+ mockTracker .EXPECT ().Begin (gomock .Eq ("Snyk Code analysis for ../mypath/" ), gomock .Eq ("Retrieving results..." )).Return ()
150
+ mockTracker .EXPECT ().End (gomock .Eq ("Analysis completed." )).Return ()
151
+
152
+ orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
153
+ projectId := uuid .New ()
154
+ testId := uuid .New ()
155
+ report := true
156
+ inputBundle := mocks2 .NewMockBundle (ctrl )
157
+ targetId , err := scan .NewRepositoryTarget ("../mypath/" )
158
+ assert .NoError (t , err )
159
+
160
+ inputBundle .EXPECT ().GetBundleHash ().Return ("" ).AnyTimes ()
161
+ inputBundle .EXPECT ().GetLimitToFiles ().Return ([]string {}).AnyTimes ()
162
+
163
+ // Test Created Response
164
+ mockTestCreatedResponse (t , mockHTTPClient , testId , orgId , http .StatusCreated )
165
+
166
+ // Get Test Result Response
167
+ expectedWebuilink := ""
168
+ expectedDocumentPath := "/1234"
169
+ mockResultCompletedResponse (t , mockHTTPClient , expectedWebuilink , projectId , orgId , testId , expectedDocumentPath , http .StatusOK )
170
+
171
+ // get document
172
+ sarifResponse := sarif.SarifDocument {
173
+ Version : "42.0" ,
174
+ }
175
+ mockGetDocumentResponse (t , sarifResponse , expectedDocumentPath , mockHTTPClient , http .StatusOK )
176
+
177
+ analysisOrchestrator := analysis .NewAnalysisOrchestrator (
178
+ mockConfig ,
179
+ mockHTTPClient ,
180
+ analysis .WithLogger (& logger ),
181
+ analysis .WithInstrumentor (mockInstrumentor ),
182
+ analysis .WithTrackerFactory (mockTrackerFactory ),
183
+ analysis .WithErrorReporter (mockErrorReporter ),
184
+ )
185
+
186
+ // run method under test
187
+ result , resultMetadata , err := analysisOrchestrator .RunTest (
188
+ context .Background (),
189
+ orgId ,
190
+ inputBundle ,
191
+ targetId ,
192
+ analysis.AnalysisConfig {
193
+ Report : report ,
194
+ },
195
+ )
196
+
197
+ require .NoError (t , err )
198
+ assert .NotNil (t , result )
199
+ assert .NotNil (t , resultMetadata )
200
+ assert .Equal (t , expectedWebuilink , resultMetadata .WebUiUrl )
201
+ assert .Equal (t , sarifResponse .Version , result .Sarif .Version )
202
+ }
73
203
74
204
func TestAnalysis_RunTestRemote (t * testing.T ) {
75
- t .Skip ()
76
205
mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger := setup (t , nil )
77
-
78
206
mockTracker .EXPECT ().Begin (gomock .Eq ("Snyk Code analysis for remote project" ), gomock .Eq ("Retrieving results..." )).Return ()
79
- mockTracker .EXPECT ().End (gomock .Eq ("Analysis complete ." )).Return ()
207
+ mockTracker .EXPECT ().End (gomock .Eq ("Analysis completed ." )).Return ()
80
208
209
+ orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
81
210
projectId := uuid .New ()
211
+ testId := uuid .New ()
82
212
commitId := "abc123"
83
213
report := true
84
214
85
- // Mock the initial test creation request
86
- mockHTTPClient .EXPECT ().Do (mock .MatchedBy (func (i interface {}) bool {
87
- req := i .(* http.Request )
88
- return req .URL .String () == "http://localhost/hidden/orgs/4a72d1db-b465-4764-99e1-ecedad03b06a/tests?version=2024-12-21" &&
89
- req .Method == http .MethodPost
90
- })).Times (1 ).Return (& http.Response {
91
- StatusCode : http .StatusCreated ,
92
- Header : http.Header {
93
- "Content-Type" : []string {"application/json" },
94
- },
95
- Body : io .NopCloser (bytes .NewReader ([]byte (`{
96
- "data": {
97
- "id": "a6fb2742-b67f-4dc3-bb27-42b67f1dc344",
98
- "type": "test-result",
99
- "attributes": {
100
- "status": "completed",
101
- "documents": {
102
- "enriched_sarif": "/tests/123/sarif"
103
- }
104
- }
105
- },
106
- "jsonapi": {
107
- "version": "1.0"
108
- },
109
- "links": {
110
- "self": "http://localhost/hidden/orgs/4a72d1db-b465-4764-99e1-ecedad03b06a/tests/a6fb2742-b67f-4dc3-bb27-42b67f1dc344"
111
- }
112
- }` ))),
113
- }, nil )
114
- // Mock the findings retrieval request
115
- mockHTTPClient .EXPECT ().Do (mock .MatchedBy (func (i interface {}) bool {
116
- req := i .(* http.Request )
117
- return req .URL .String () == "http://localhost/tests/123/sarif" &&
118
- req .Method == http .MethodGet
119
- })).Times (1 ).Return (& http.Response {
120
- StatusCode : http .StatusOK ,
121
- Body : io .NopCloser (bytes .NewReader (fakeResponse )),
122
- }, nil )
215
+ // Test Created Response
216
+ mockTestCreatedResponse (t , mockHTTPClient , testId , orgId , http .StatusCreated )
217
+
218
+ // Get Test Result Response
219
+ expectedWebuilink := ""
220
+ expectedDocumentPath := "/1234"
221
+ mockResultCompletedResponse (t , mockHTTPClient , expectedWebuilink , projectId , orgId , testId , expectedDocumentPath , http .StatusOK )
222
+
223
+ // get document
224
+ sarifResponse := sarif.SarifDocument {
225
+ Version : "42.0" ,
226
+ }
227
+ mockGetDocumentResponse (t , sarifResponse , expectedDocumentPath , mockHTTPClient , http .StatusOK )
123
228
124
229
analysisOrchestrator := analysis .NewAnalysisOrchestrator (
125
230
mockConfig ,
@@ -130,9 +235,10 @@ func TestAnalysis_RunTestRemote(t *testing.T) {
130
235
analysis .WithErrorReporter (mockErrorReporter ),
131
236
)
132
237
133
- result , _ , err := analysisOrchestrator .RunTestRemote (
238
+ // run method under test
239
+ result , resultMetadata , err := analysisOrchestrator .RunTestRemote (
134
240
context .Background (),
135
- "4a72d1db-b465-4764-99e1-ecedad03b06a" ,
241
+ orgId ,
136
242
analysis.AnalysisConfig {
137
243
ProjectId : & projectId ,
138
244
CommitId : & commitId ,
@@ -142,6 +248,142 @@ func TestAnalysis_RunTestRemote(t *testing.T) {
142
248
143
249
require .NoError (t , err )
144
250
assert .NotNil (t , result )
251
+ assert .NotNil (t , resultMetadata )
252
+ assert .Equal (t , expectedWebuilink , resultMetadata .WebUiUrl )
253
+ assert .Equal (t , sarifResponse .Version , result .Sarif .Version )
254
+ }
255
+
256
+ func TestAnalysis_RunTestRemote_CreateTestFailed (t * testing.T ) {
257
+ mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger := setup (t , nil )
258
+ mockTracker .EXPECT ().Begin (gomock .Eq ("Snyk Code analysis for remote project" ), gomock .Eq ("Retrieving results..." )).Return ()
259
+ mockTracker .EXPECT ().End (gomock .Eq ("Analysis failed." )).Return ()
260
+
261
+ orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
262
+ projectId := uuid .New ()
263
+ testId := uuid .New ()
264
+ commitId := "abc123"
265
+ report := true
266
+
267
+ // Test Created Response
268
+ mockTestCreatedResponse (t , mockHTTPClient , testId , orgId , http .StatusInternalServerError )
269
+
270
+ analysisOrchestrator := analysis .NewAnalysisOrchestrator (
271
+ mockConfig ,
272
+ mockHTTPClient ,
273
+ analysis .WithLogger (& logger ),
274
+ analysis .WithInstrumentor (mockInstrumentor ),
275
+ analysis .WithTrackerFactory (mockTrackerFactory ),
276
+ analysis .WithErrorReporter (mockErrorReporter ),
277
+ )
278
+
279
+ // run method under test
280
+ result , resultMetadata , err := analysisOrchestrator .RunTestRemote (
281
+ context .Background (),
282
+ orgId ,
283
+ analysis.AnalysisConfig {
284
+ ProjectId : & projectId ,
285
+ CommitId : & commitId ,
286
+ Report : report ,
287
+ },
288
+ )
289
+
290
+ require .Error (t , err )
291
+ assert .Nil (t , result )
292
+ assert .Nil (t , resultMetadata )
293
+ }
294
+
295
+ func TestAnalysis_RunTestRemote_PollingFailed (t * testing.T ) {
296
+ mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger := setup (t , nil )
297
+ mockTracker .EXPECT ().Begin (gomock .Eq ("Snyk Code analysis for remote project" ), gomock .Eq ("Retrieving results..." )).Return ()
298
+ mockTracker .EXPECT ().End (gomock .Eq ("Analysis failed." )).Return ()
299
+
300
+ orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
301
+ projectId := uuid .New ()
302
+ testId := uuid .New ()
303
+ commitId := "abc123"
304
+ report := true
305
+
306
+ // Test Created Response
307
+ mockTestCreatedResponse (t , mockHTTPClient , testId , orgId , http .StatusCreated )
308
+
309
+ // Get Test Result Response
310
+ expectedWebuilink := ""
311
+ expectedDocumentPath := "/1234"
312
+ mockResultCompletedResponse (t , mockHTTPClient , expectedWebuilink , projectId , orgId , testId , expectedDocumentPath , http .StatusInternalServerError )
313
+
314
+ analysisOrchestrator := analysis .NewAnalysisOrchestrator (
315
+ mockConfig ,
316
+ mockHTTPClient ,
317
+ analysis .WithLogger (& logger ),
318
+ analysis .WithInstrumentor (mockInstrumentor ),
319
+ analysis .WithTrackerFactory (mockTrackerFactory ),
320
+ analysis .WithErrorReporter (mockErrorReporter ),
321
+ )
322
+
323
+ // run method under test
324
+ result , resultMetadata , err := analysisOrchestrator .RunTestRemote (
325
+ context .Background (),
326
+ orgId ,
327
+ analysis.AnalysisConfig {
328
+ ProjectId : & projectId ,
329
+ CommitId : & commitId ,
330
+ Report : report ,
331
+ },
332
+ )
333
+
334
+ require .Error (t , err )
335
+ assert .Nil (t , result )
336
+ assert .Nil (t , resultMetadata )
337
+ }
338
+
339
+ func TestAnalysis_RunTestRemote_GetDocumentFailed (t * testing.T ) {
340
+ mockConfig , mockHTTPClient , mockInstrumentor , mockErrorReporter , mockTracker , mockTrackerFactory , logger := setup (t , nil )
341
+ mockTracker .EXPECT ().Begin (gomock .Eq ("Snyk Code analysis for remote project" ), gomock .Eq ("Retrieving results..." )).Return ()
342
+ mockTracker .EXPECT ().End (gomock .Eq ("Analysis failed." )).Return ()
343
+
344
+ orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
345
+ projectId := uuid .New ()
346
+ testId := uuid .New ()
347
+ commitId := "abc123"
348
+ report := true
349
+
350
+ // Test Created Response
351
+ mockTestCreatedResponse (t , mockHTTPClient , testId , orgId , http .StatusCreated )
352
+
353
+ // Get Test Result Response
354
+ expectedWebuilink := ""
355
+ expectedDocumentPath := "/1234"
356
+ mockResultCompletedResponse (t , mockHTTPClient , expectedWebuilink , projectId , orgId , testId , expectedDocumentPath , http .StatusOK )
357
+
358
+ // get document
359
+ sarifResponse := sarif.SarifDocument {
360
+ Version : "42.0" ,
361
+ }
362
+ mockGetDocumentResponse (t , sarifResponse , expectedDocumentPath , mockHTTPClient , http .StatusInternalServerError )
363
+
364
+ analysisOrchestrator := analysis .NewAnalysisOrchestrator (
365
+ mockConfig ,
366
+ mockHTTPClient ,
367
+ analysis .WithLogger (& logger ),
368
+ analysis .WithInstrumentor (mockInstrumentor ),
369
+ analysis .WithTrackerFactory (mockTrackerFactory ),
370
+ analysis .WithErrorReporter (mockErrorReporter ),
371
+ )
372
+
373
+ // run method under test
374
+ result , resultMetadata , err := analysisOrchestrator .RunTestRemote (
375
+ context .Background (),
376
+ orgId ,
377
+ analysis.AnalysisConfig {
378
+ ProjectId : & projectId ,
379
+ CommitId : & commitId ,
380
+ Report : report ,
381
+ },
382
+ )
383
+
384
+ require .Error (t , err )
385
+ assert .Nil (t , result )
386
+ assert .Nil (t , resultMetadata )
145
387
}
146
388
147
389
func TestAnalysis_RunTestRemote_MissingRequiredParams (t * testing.T ) {
0 commit comments