Skip to content

Commit 428b642

Browse files
chore: add basic test for analysis (#80)
2 parents 44e6574 + bddd4ea commit 428b642

File tree

2 files changed

+324
-47
lines changed

2 files changed

+324
-47
lines changed

internal/analysis/analysis_test.go

Lines changed: 289 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ import (
1919
"bytes"
2020
"context"
2121
_ "embed"
22+
"encoding/json"
23+
"fmt"
2224
"io"
2325
"net/http"
2426
"testing"
2527
"time"
2628

27-
"github.com/google/uuid"
28-
2929
"github.com/golang/mock/gomock"
30+
"github.com/google/uuid"
3031
"github.com/rs/zerolog"
3132
"github.com/stretchr/testify/assert"
3233
"github.com/stretchr/testify/mock"
@@ -35,10 +36,84 @@ import (
3536
confMocks "github.com/snyk/code-client-go/config/mocks"
3637
httpmocks "github.com/snyk/code-client-go/http/mocks"
3738
"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"
3841
"github.com/snyk/code-client-go/observability/mocks"
42+
"github.com/snyk/code-client-go/sarif"
43+
"github.com/snyk/code-client-go/scan"
3944
trackerMocks "github.com/snyk/code-client-go/scan/mocks"
4045
)
4146

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+
42117
func setup(t *testing.T, timeout *time.Duration) (*confMocks.MockConfig, *httpmocks.MockHTTPClient, *mocks.MockInstrumentor, *mocks.MockErrorReporter, *trackerMocks.MockTracker, *trackerMocks.MockTrackerFactory, zerolog.Logger) {
43118
t.Helper()
44119
ctrl := gomock.NewController(t)
@@ -68,58 +143,88 @@ func setup(t *testing.T, timeout *time.Duration) (*confMocks.MockConfig, *httpmo
68143
return mockConfig, mockHTTPClient, mockInstrumentor, mockErrorReporter, mockTracker, mockTrackerFactory, logger
69144
}
70145

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+
}
73203

74204
func TestAnalysis_RunTestRemote(t *testing.T) {
75-
t.Skip()
76205
mockConfig, mockHTTPClient, mockInstrumentor, mockErrorReporter, mockTracker, mockTrackerFactory, logger := setup(t, nil)
77-
78206
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()
80208

209+
orgId := "4a72d1db-b465-4764-99e1-ecedad03b06a"
81210
projectId := uuid.New()
211+
testId := uuid.New()
82212
commitId := "abc123"
83213
report := true
84214

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)
123228

124229
analysisOrchestrator := analysis.NewAnalysisOrchestrator(
125230
mockConfig,
@@ -130,9 +235,10 @@ func TestAnalysis_RunTestRemote(t *testing.T) {
130235
analysis.WithErrorReporter(mockErrorReporter),
131236
)
132237

133-
result, _, err := analysisOrchestrator.RunTestRemote(
238+
// run method under test
239+
result, resultMetadata, err := analysisOrchestrator.RunTestRemote(
134240
context.Background(),
135-
"4a72d1db-b465-4764-99e1-ecedad03b06a",
241+
orgId,
136242
analysis.AnalysisConfig{
137243
ProjectId: &projectId,
138244
CommitId: &commitId,
@@ -142,6 +248,142 @@ func TestAnalysis_RunTestRemote(t *testing.T) {
142248

143249
require.NoError(t, err)
144250
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)
145387
}
146388

147389
func TestAnalysis_RunTestRemote_MissingRequiredParams(t *testing.T) {

0 commit comments

Comments
 (0)