Skip to content

Commit d15a625

Browse files
committed
feat: uv mock integration
1 parent c58a4a2 commit d15a625

File tree

6 files changed

+221
-4
lines changed

6 files changed

+221
-4
lines changed

internal/commands/ostest/routing.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ func RouteToFlow(ctx context.Context, orgUUID uuid.UUID, sc settings.Client) (Fl
117117
sbomReachabilityTest := reachability && sbom != ""
118118
reachabilityFilter := cfg.GetString(flags.FlagReachabilityFilter)
119119

120+
uvTestFlowEnabled := cfg.GetBool(EnableExperimentalUvSupportEnvVar)
120121
forceLegacyTest := cfg.GetBool(ForceLegacyCLIEnvVar)
121122
requiresLegacy := cfg.GetBool(flags.FlagPrintGraph) ||
122123
cfg.GetBool(flags.FlagPrintDeps) ||
@@ -147,12 +148,13 @@ func RouteToFlow(ctx context.Context, orgUUID uuid.UUID, sc settings.Client) (Fl
147148
}
148149

149150
switch {
150-
case forceLegacyTest || requiresLegacy || (!riskScoreTest && !reachability && sbom == ""):
151+
case forceLegacyTest || requiresLegacy || (!riskScoreTest && !reachability && sbom == "" && !uvTestFlowEnabled):
151152
logger.Debug().Msgf(
152-
"Using legacy flow. Legacy CLI Env var: %t. SBOM Reachability Test: %t. Risk Score Test: %t.",
153+
"Using legacy flow. Legacy CLI Env var: %t. SBOM Reachability Test: %t. Risk Score Test: %t. UV Test Flow: %t.",
153154
forceLegacyTest,
154155
sbomReachabilityTest,
155156
riskScoreTest,
157+
uvTestFlowEnabled,
156158
)
157159
return LegacyFlow, nil
158160
case sbomReachabilityTest:
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package ostest_test
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"os"
7+
"testing"
8+
9+
"github.com/golang/mock/gomock"
10+
"github.com/rs/zerolog"
11+
gafclientmocks "github.com/snyk/go-application-framework/pkg/apiclients/mocks"
12+
"github.com/snyk/go-application-framework/pkg/apiclients/testapi"
13+
"github.com/snyk/go-application-framework/pkg/configuration"
14+
gafmocks "github.com/snyk/go-application-framework/pkg/mocks"
15+
"github.com/snyk/go-application-framework/pkg/runtimeinfo"
16+
"github.com/snyk/go-application-framework/pkg/workflow"
17+
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
19+
20+
"github.com/snyk/cli-extension-os-flows/internal/commands/cmdctx"
21+
"github.com/snyk/cli-extension-os-flows/internal/commands/ostest"
22+
common "github.com/snyk/cli-extension-os-flows/internal/common"
23+
"github.com/snyk/cli-extension-os-flows/internal/errors"
24+
"github.com/snyk/cli-extension-os-flows/internal/flags"
25+
)
26+
27+
// TestSBOMResolveIntegration_DepGraphsPassedToUnifiedTestAPI verifies that when the UV test flow
28+
// is enabled, dep-graphs from SBOM resolution are properly passed to the Unified Test API.
29+
func TestSBOMResolveIntegration_DepGraphsPassedToUnifiedTestAPI(t *testing.T) {
30+
ctrl := gomock.NewController(t)
31+
defer ctrl.Finish()
32+
33+
ctx := context.Background()
34+
mockIctx, mockTestClient, mockDepGraph, orgID, cfg, testLogger := setupSBOMResolveIntegrationTest(t, ctrl)
35+
36+
// Set up context with dependencies
37+
testErrFactory := errors.NewErrorFactory(testLogger)
38+
nopProgressBar := &NopProgressBar{}
39+
ctx = cmdctx.WithIctx(ctx, mockIctx)
40+
ctx = cmdctx.WithConfig(ctx, cfg)
41+
ctx = cmdctx.WithLogger(ctx, testLogger)
42+
ctx = cmdctx.WithErrorFactory(ctx, testErrFactory)
43+
ctx = cmdctx.WithProgressBar(ctx, nopProgressBar)
44+
45+
_, _, err := ostest.RunUnifiedTestFlow(
46+
ctx,
47+
".",
48+
mockTestClient,
49+
orgID,
50+
nil,
51+
nil,
52+
)
53+
54+
require.NoError(t, err)
55+
require.NotNil(t, mockDepGraph)
56+
}
57+
58+
//nolint:gocritic // Test helper needs to return multiple values for test setup
59+
func setupSBOMResolveIntegrationTest(
60+
t *testing.T,
61+
ctrl *gomock.Controller,
62+
) (
63+
workflow.InvocationContext,
64+
testapi.TestClient,
65+
*testapi.IoSnykApiV1testdepgraphRequestDepGraph,
66+
string,
67+
configuration.Configuration,
68+
*zerolog.Logger,
69+
) {
70+
t.Helper()
71+
72+
mockEngine := gafmocks.NewMockEngine(ctrl)
73+
mockIctx := gafmocks.NewMockInvocationContext(ctrl)
74+
mockTestClient := gafclientmocks.NewMockTestClient(ctrl)
75+
76+
cfg := configuration.New()
77+
cfg.Set(ostest.EnableExperimentalUvSupportEnvVar, true)
78+
cfg.Set(configuration.ORGANIZATION, "test-org-id")
79+
cfg.Set(flags.FlagFile, "uv.lock")
80+
81+
nopLogger := zerolog.Nop()
82+
83+
mockIctx.EXPECT().GetConfiguration().Return(cfg).AnyTimes()
84+
mockIctx.EXPECT().GetEnhancedLogger().Return(&nopLogger).AnyTimes()
85+
mockIctx.EXPECT().GetRuntimeInfo().Return(runtimeinfo.New()).AnyTimes()
86+
mockIctx.EXPECT().GetEngine().Return(mockEngine).AnyTimes()
87+
mockIctx.EXPECT().GetWorkflowIdentifier().Return(workflow.NewWorkflowIdentifier("test")).AnyTimes()
88+
89+
depGraphBytes, err := os.ReadFile("testdata/uv_depgraph.json")
90+
require.NoError(t, err)
91+
92+
var mockDepGraph testapi.IoSnykApiV1testdepgraphRequestDepGraph
93+
require.NoError(t, json.Unmarshal(depGraphBytes, &mockDepGraph))
94+
95+
depGraphData := workflow.NewData(
96+
workflow.NewTypeIdentifier(common.DepGraphWorkflowID, "depgraph"),
97+
"application/json",
98+
depGraphBytes,
99+
)
100+
depGraphData.SetMetaData("Content-Location", "uv.lock")
101+
102+
mockEngine.EXPECT().
103+
InvokeWithConfig(common.DepGraphWorkflowID, gomock.Any()).
104+
DoAndReturn(func(_ workflow.Identifier, cfg configuration.Configuration) ([]workflow.Data, error) {
105+
// Verify that use-sbom-resolve flag is set
106+
assert.True(t, cfg.GetBool("use-sbom-resolve"), "use-sbom-resolve flag should be set when UV support is enabled")
107+
return []workflow.Data{depGraphData}, nil
108+
}).
109+
Times(1)
110+
111+
mockTestClient.EXPECT().
112+
StartTest(gomock.Any(), gomock.Any()).
113+
DoAndReturn(func(_ context.Context, params testapi.StartTestParams) (testapi.TestHandle, error) {
114+
require.NotNil(t, params.Subject)
115+
116+
depGraphSubject, subjectErr := params.Subject.AsDepGraphSubjectCreate()
117+
require.NoError(t, subjectErr)
118+
119+
assert.Equal(t, mockDepGraph, depGraphSubject.DepGraph)
120+
121+
handle := gafclientmocks.NewMockTestHandle(ctrl)
122+
handle.EXPECT().Wait(gomock.Any()).Return(nil).Times(1)
123+
124+
result := gafclientmocks.NewMockTestResult(ctrl)
125+
result.EXPECT().GetExecutionState().Return(testapi.TestExecutionStatesFinished).AnyTimes()
126+
result.EXPECT().Findings(gomock.Any()).Return([]testapi.FindingData{}, true, nil).AnyTimes()
127+
handle.EXPECT().Result().Return(result).Times(1)
128+
129+
return handle, nil
130+
}).
131+
Times(1)
132+
133+
return mockIctx, mockTestClient, &mockDepGraph, "test-org-id", cfg, &nopLogger
134+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"schemaVersion": "1.2.0",
3+
"pkgManager": {
4+
"name": "pip"
5+
},
6+
"pkgs": [
7+
{
8+
"id": "test-uv-project@1.0.0",
9+
"info": {
10+
"name": "test-uv-project",
11+
"version": "1.0.0"
12+
}
13+
},
14+
{
15+
"id": "flask@2.3.0",
16+
"info": {
17+
"name": "flask",
18+
"version": "2.3.0"
19+
}
20+
}
21+
],
22+
"graph": {
23+
"rootNodeId": "root-node",
24+
"nodes": [
25+
{
26+
"nodeId": "root-node",
27+
"pkgId": "test-uv-project@1.0.0",
28+
"deps": [
29+
{
30+
"nodeId": "flask@2.3.0"
31+
}
32+
]
33+
},
34+
{
35+
"nodeId": "flask@2.3.0",
36+
"pkgId": "flask@2.3.0",
37+
"deps": []
38+
}
39+
]
40+
}
41+
}

internal/commands/ostest/workflow.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ const FeatureFlagRiskScoreInCLI = "feature_flag_experimental_risk_score_in_cli"
5757
// ForceLegacyCLIEnvVar is an internal environment variable to force the legacy CLI flow.
5858
const ForceLegacyCLIEnvVar = "SNYK_FORCE_LEGACY_CLI"
5959

60+
// EnableExperimentalUvSupportEnvVar is an internal environment variable to enable support for testing UV projects.
61+
const EnableExperimentalUvSupportEnvVar = "SNYK_ENABLE_EXPERIMENTAL_UV_SUPPORT"
62+
6063
// PollInterval is the polling interval for the test API. It is exported to be configurable in tests.
6164
var PollInterval = 2 * time.Second
6265

internal/commands/ostest/workflow_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,36 @@ func TestOSWorkflow_FlagCombinations(t *testing.T) {
564564
},
565565
expectedError: "The feature you are trying to use is not available for your organization",
566566
},
567+
{
568+
name: "UV test flow enabled, expects depgraph workflow with use-sbom-resolve flag",
569+
setup: func(config configuration.Configuration, mockEngine *mocks.MockEngine) {
570+
config.Set(ostest.EnableExperimentalUvSupportEnvVar, true)
571+
mockEngine.EXPECT().
572+
InvokeWithConfig(common.DepGraphWorkflowID, gomock.Any()).
573+
DoAndReturn(func(_ workflow.Identifier, cfg configuration.Configuration) ([]workflow.Data, error) {
574+
// Verify that use-sbom-resolve flag is set
575+
if !cfg.GetBool("use-sbom-resolve") {
576+
return nil, fmt.Errorf("expected use-sbom-resolve flag to be set")
577+
}
578+
return nil, assert.AnError
579+
}).
580+
Times(1)
581+
},
582+
expectedError: "failed to get dependency graph",
583+
},
584+
{
585+
name: "UV test flow disabled, depgraph calls legacy internally",
586+
setup: func(config configuration.Configuration, mockEngine *mocks.MockEngine) {
587+
config.Set(ostest.EnableExperimentalUvSupportEnvVar, false)
588+
// When UV is disabled, depgraph workflow is called without use-sbom-resolve flag
589+
// Then depgraph internally calls legacycli
590+
mockEngine.EXPECT().
591+
InvokeWithConfig(gomock.Any(), gomock.Any()).
592+
Return([]workflow.Data{}, nil).
593+
Times(1)
594+
},
595+
expectedError: "", // No error, should succeed via legacy path
596+
},
567597
}
568598

569599
for _, test := range tests {

internal/common/depgraph.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@ func GetDepGraph(ictx workflow.InvocationContext, inputDir string) (*DepGraphRes
2929
logger := ictx.GetEnhancedLogger()
3030
errFactory := errors.NewErrorFactory(logger)
3131

32-
logger.Println("Invoking depgraph workflow")
33-
3432
depGraphConfig := config.Clone()
33+
uvTestFlowEnabled := config.GetBool("SNYK_ENABLE_EXPERIMENTAL_UV_SUPPORT")
34+
35+
if uvTestFlowEnabled {
36+
logger.Info().Msg("UV test flow enabled, using SBOM resolution in depgraph workflow")
37+
depGraphConfig.Set("use-sbom-resolve", true)
38+
} else {
39+
logger.Println("Invoking depgraph workflow")
40+
}
41+
3542
// Overriding the INPUT_DIRECTORY flag which the depgraph workflow will use to extract the depgraphs.
3643
depGraphConfig.Set(configuration.INPUT_DIRECTORY, inputDir)
3744
depGraphs, err := engine.InvokeWithConfig(DepGraphWorkflowID, depGraphConfig)

0 commit comments

Comments
 (0)