Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apptrust/commands/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ var commandFlags = map[string][]string{
IncludeFilterFlag,
ExcludeFilterFlag,
SpecVarsFlag,
DryRunFlag,
},
VersionPromote: {
url,
Expand Down
4 changes: 3 additions & 1 deletion apptrust/commands/version/create_app_version_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type createAppVersionCommand struct {
serverDetails *coreConfig.ServerDetails
requestPayload *model.CreateAppVersionRequest
sync bool
dryRun bool
}

func (cv *createAppVersionCommand) Run() error {
Expand All @@ -29,7 +30,7 @@ func (cv *createAppVersionCommand) Run() error {
return err
}

return cv.versionService.CreateAppVersion(ctx, cv.requestPayload, cv.sync)
return cv.versionService.CreateAppVersion(ctx, cv.requestPayload, cv.sync, cv.dryRun)
}

func (cv *createAppVersionCommand) ServerDetails() (*coreConfig.ServerDetails, error) {
Expand All @@ -54,6 +55,7 @@ func (cv *createAppVersionCommand) prepareAndRunCommand(ctx *components.Context)
if errorutils.CheckError(err) != nil {
return err
}
cv.dryRun = ctx.GetBoolFlagValue(commands.DryRunFlag)
return commonCLiCommands.Exec(cv)
}

Expand Down
36 changes: 28 additions & 8 deletions apptrust/commands/version/create_app_version_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestCreateAppVersionCommand(t *testing.T) {
tests := []struct {
name string
request *model.CreateAppVersionRequest
dryRun bool
shouldError bool
errorMessage string
}{
Expand All @@ -36,10 +37,28 @@ func TestCreateAppVersionCommand(t *testing.T) {
}},
},
},
dryRun: false,
},
{
name: "success with dry-run",
request: &model.CreateAppVersionRequest{
ApplicationKey: "app-key",
Version: "1.0.0",
Sources: &model.CreateVersionSources{
Packages: []model.CreateVersionPackage{{
Type: "type",
Name: "name",
Version: "1.0.0",
Repository: "repo",
}},
},
},
dryRun: true,
},
{
name: "context error",
request: &model.CreateAppVersionRequest{ApplicationKey: "app-key", Version: "1.0.0", Draft: false, Sources: &model.CreateVersionSources{Packages: []model.CreateVersionPackage{{Type: "type", Name: "name", Version: "1.0.0", Repository: "repo"}}}},
dryRun: false,
shouldError: true,
errorMessage: "context error",
},
Expand All @@ -57,10 +76,10 @@ func TestCreateAppVersionCommand(t *testing.T) {

mockVersionService := mockversions.NewMockVersionService(ctrl)
if tt.shouldError {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), tt.request, true).
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), tt.request, true, tt.dryRun).
Return(errors.New(tt.errorMessage)).Times(1)
} else {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), tt.request, true).
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), tt.request, true, tt.dryRun).
Return(nil).Times(1)
}

Expand All @@ -69,6 +88,7 @@ func TestCreateAppVersionCommand(t *testing.T) {
serverDetails: &config.ServerDetails{Url: "https://example.com"},
requestPayload: tt.request,
sync: true,
dryRun: tt.dryRun,
}

err := cmd.Run()
Expand Down Expand Up @@ -196,8 +216,8 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) {
var actualPayload *model.CreateAppVersionRequest
mockVersionService := mockversions.NewMockVersionService(ctrl)
if !tt.expectsError {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, _ bool) error {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, _ bool, _ bool) error {
actualPayload = req
return nil
}).Times(1)
Expand Down Expand Up @@ -807,8 +827,8 @@ func TestCreateAppVersionCommand_SpecFileSuite(t *testing.T) {
var capturedSync bool
mockVersionService := mockversions.NewMockVersionService(ctrl)
if !tt.expectsError {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, sync bool) error {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, sync, dryRun bool) error {
actualPayload = req
capturedSync = sync
return nil
Expand Down Expand Up @@ -876,8 +896,8 @@ func TestCreateAppVersionCommand_SyncFlagSuite(t *testing.T) {

var capturedSync bool
mockVersionService := mockversions.NewMockVersionService(ctrl)
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, sync bool) error {
mockVersionService.EXPECT().CreateAppVersion(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ interface{}, req *model.CreateAppVersionRequest, sync, dryRun bool) error {
capturedSync = sync
return nil
}).Times(1)
Expand Down
5 changes: 5 additions & 0 deletions apptrust/http/http_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package http

func IsSuccessStatusCode(statusCode int) bool {
return statusCode >= 200 && statusCode < 300
}
8 changes: 4 additions & 4 deletions apptrust/service/versions/mocks/version_service_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 20 additions & 19 deletions apptrust/service/versions/version_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
"net/http"
"strconv"

apphttp "github.com/jfrog/jfrog-cli-application/apptrust/http"
"github.com/jfrog/jfrog-cli-application/apptrust/service"
"github.com/jfrog/jfrog-client-go/utils/log"

"github.com/jfrog/jfrog-cli-application/apptrust/model"
)

type VersionService interface {
CreateAppVersion(ctx service.Context, request *model.CreateAppVersionRequest, sync bool) error
CreateAppVersion(ctx service.Context, request *model.CreateAppVersionRequest, sync, dryRun bool) error
PromoteAppVersion(ctx service.Context, applicationKey string, version string, payload *model.PromoteAppVersionRequest, sync bool) error
ReleaseAppVersion(ctx service.Context, applicationKey string, version string, request *model.ReleaseAppVersionRequest, sync bool) error
RollbackAppVersion(ctx service.Context, applicationKey string, version string, request *model.RollbackAppVersionRequest, sync bool) error
Expand All @@ -29,24 +30,20 @@ func NewVersionService() VersionService {
return &versionService{}
}

func (vs *versionService) CreateAppVersion(ctx service.Context, request *model.CreateAppVersionRequest, sync bool) error {
func (vs *versionService) CreateAppVersion(ctx service.Context, request *model.CreateAppVersionRequest, sync, dryRun bool) error {
endpoint := fmt.Sprintf("/v1/applications/%s/versions/", request.ApplicationKey)
response, responseBody, err := ctx.GetHttpClient().Post(endpoint, request, map[string]string{"async": strconv.FormatBool(!sync)})
response, responseBody, err := ctx.GetHttpClient().Post(endpoint, request,
map[string]string{"async": strconv.FormatBool(!sync), "dry_run": strconv.FormatBool(dryRun)})
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add E2E test

if err != nil {
return err
}

expectedStatusCode := http.StatusCreated
if !sync {
expectedStatusCode = http.StatusAccepted
}

if response.StatusCode != expectedStatusCode {
if !apphttp.IsSuccessStatusCode(response.StatusCode) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with this approach, but it'd be better to change it for all commands (or use the previous approach 🙃)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed other instances in the same file to use the new approach, which is a bit more readable

return fmt.Errorf("failed to create app version. Status code: %d. \n%s",
response.StatusCode, responseBody)
}

log.Info("Application version created successfully.")
logSuccessMessage(sync, request, dryRun)
log.Output(string(responseBody))
return nil
}
Expand All @@ -58,7 +55,7 @@ func (vs *versionService) PromoteAppVersion(ctx service.Context, applicationKey,
return err
}

if response.StatusCode >= http.StatusBadRequest {
if !apphttp.IsSuccessStatusCode(response.StatusCode) {
return fmt.Errorf("failed to promote app version. Status code: %d. \n%s",
response.StatusCode, responseBody)
}
Expand All @@ -74,7 +71,7 @@ func (vs *versionService) ReleaseAppVersion(ctx service.Context, applicationKey,
return err
}

if response.StatusCode >= http.StatusBadRequest {
if !apphttp.IsSuccessStatusCode(response.StatusCode) {
return fmt.Errorf("failed to release app version. Status code: %d. \n%s",
response.StatusCode, responseBody)
}
Expand All @@ -90,13 +87,7 @@ func (vs *versionService) RollbackAppVersion(ctx service.Context, applicationKey
return err
}

// Validate status code based on sync mode
expectedStatusCode := http.StatusAccepted
if sync {
expectedStatusCode = http.StatusOK
}

if response.StatusCode != expectedStatusCode {
if !apphttp.IsSuccessStatusCode(response.StatusCode) {
return fmt.Errorf("failed to rollback app version. Status code: %d. \n%s",
response.StatusCode, responseBody)
}
Expand Down Expand Up @@ -165,3 +156,13 @@ func (vs *versionService) UpdateAppVersionSources(ctx service.Context, applicati
log.Output(string(responseBody))
return nil
}

func logSuccessMessage(sync bool, request *model.CreateAppVersionRequest, dryRun bool) {
if !sync {
log.Info(fmt.Sprintf("Application version creation initiated: %s:%s", request.ApplicationKey, request.Version))
} else if dryRun {
log.Info(fmt.Sprintf("Dry run successful for application version: %s:%s", request.ApplicationKey, request.Version))
} else {
log.Info(fmt.Sprintf("Application version created successfully: %s:%s", request.ApplicationKey, request.Version))
}
}
51 changes: 27 additions & 24 deletions apptrust/service/versions/version_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestCreateAppVersion(t *testing.T) {
name string
request *model.CreateAppVersionRequest
sync bool
dryRun bool
mockResponse *http.Response
mockResponseBody string
mockError error
Expand All @@ -33,15 +34,37 @@ func TestCreateAppVersion(t *testing.T) {
name: "success",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: true,
dryRun: false,
mockResponse: &http.Response{StatusCode: 201},
mockResponseBody: "{}",
mockError: nil,
expectedError: "",
},
{
name: "success with dry-run (200 OK)",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: true,
dryRun: true,
mockResponse: &http.Response{StatusCode: 200},
mockResponseBody: "{\"validation\": \"passed\"}",
mockError: nil,
expectedError: "",
},
{
name: "success with sync=false",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: false,
dryRun: false,
mockResponse: &http.Response{StatusCode: 202},
mockResponseBody: "{}",
mockError: nil,
expectedError: "",
},
{
name: "success with sync=false & dryRun=true",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: false,
dryRun: true,
mockResponse: &http.Response{StatusCode: 202},
mockResponseBody: "{}",
mockError: nil,
Expand All @@ -51,6 +74,7 @@ func TestCreateAppVersion(t *testing.T) {
name: "failure",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: true,
dryRun: false,
mockResponse: &http.Response{StatusCode: 400},
mockResponseBody: "error",
mockError: nil,
Expand All @@ -60,6 +84,7 @@ func TestCreateAppVersion(t *testing.T) {
name: "http client error",
request: &model.CreateAppVersionRequest{ApplicationKey: "test-app", Version: "1.0.0"},
sync: true,
dryRun: false,
mockResponse: nil,
mockResponseBody: "",
mockError: errors.New("http client error"),
Expand All @@ -70,13 +95,13 @@ func TestCreateAppVersion(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockHttpClient := mockhttp.NewMockApptrustHttpClient(ctrl)
mockHttpClient.EXPECT().Post("/v1/applications/test-app/versions/", tt.request, map[string]string{"async": strconv.FormatBool(!tt.sync)}).
mockHttpClient.EXPECT().Post("/v1/applications/test-app/versions/", tt.request, map[string]string{"async": strconv.FormatBool(!tt.sync), "dry_run": strconv.FormatBool(tt.dryRun)}).
Return(tt.mockResponse, []byte(tt.mockResponseBody), tt.mockError).Times(1)

mockCtx := mockservice.NewMockContext(ctrl)
mockCtx.EXPECT().GetHttpClient().Return(mockHttpClient).Times(1)

err := service.CreateAppVersion(mockCtx, tt.request, tt.sync)
err := service.CreateAppVersion(mockCtx, tt.request, tt.sync, tt.dryRun)
if tt.expectedError == "" {
assert.NoError(t, err)
} else {
Expand Down Expand Up @@ -476,28 +501,6 @@ func TestRollbackAppVersion(t *testing.T) {
expectedStatus: http.StatusBadRequest,
expectedError: true,
},
{
name: "failed rollback - sync=true but got 202",
applicationKey: "video-encoder",
version: "1.5.0",
payload: &model.RollbackAppVersionRequest{
FromStage: "qa",
},
sync: true,
expectedStatus: http.StatusAccepted,
expectedError: true,
},
{
name: "failed rollback - sync=false but got 200",
applicationKey: "video-encoder",
version: "1.5.0",
payload: &model.RollbackAppVersionRequest{
FromStage: "prod",
},
sync: false,
expectedStatus: http.StatusOK,
expectedError: true,
},
}

for _, tt := range tests {
Expand Down
32 changes: 32 additions & 0 deletions e2e/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,38 @@ func TestCreateVersion_ApplicationVersion(t *testing.T) {
assertVersionContent(t, testPackage, versionContent, statusCode, targetAppKey, targetVersion)
}

func TestCreateVersion_ApplicationVersion_DryRun(t *testing.T) {
// Prepare - create source application with a version
sourceAppKey := utils.GenerateUniqueKey("app-version-create-app-version-dryrun")
utils.CreateBasicApplication(t, sourceAppKey)
defer utils.DeleteApplication(t, sourceAppKey)

testPackage := utils.GetTestPackage(t)
sourceVersion := "1.0.2"
packageFlag := fmt.Sprintf("--source-type-packages=type=%s, name=%s, version=%s, repo-key=%s",
testPackage.PackageType, testPackage.PackageName, testPackage.PackageVersion, testPackage.RepoKey)
err := utils.AppTrustCli.Exec("version-create", sourceAppKey, sourceVersion, packageFlag)
require.NoError(t, err)
defer utils.DeleteApplicationVersion(t, sourceAppKey, sourceVersion)

// Prepare - create target application
targetAppKey := utils.GenerateUniqueKey("app-target-version-dryrun")
utils.CreateBasicApplication(t, targetAppKey)
defer utils.DeleteApplication(t, targetAppKey)

targetVersion := "1.0.3"

// Execute with dry-run flag
appVersionFlag := fmt.Sprintf("--source-type-application-versions=application-key=%s, version=%s", sourceAppKey, sourceVersion)
err = utils.AppTrustCli.Exec("version-create", targetAppKey, targetVersion, appVersionFlag, "--dry-run")
require.NoError(t, err)

// Assert - version should not exist since it was a dry run
_, statusCode, err := utils.GetApplicationVersion(targetAppKey, targetVersion)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, statusCode)
}

func TestCreateVersion_ReleaseBundle(t *testing.T) {
// Prepare
appKey := utils.GenerateUniqueKey("app-version-create-release-bundle")
Expand Down
Loading