From 998e7c7213642cd5fdb66b758f154ea56c72d280 Mon Sep 17 00:00:00 2001 From: danielr Date: Wed, 10 Jun 2026 14:24:39 +0300 Subject: [PATCH 1/8] APP-4230 - Add --skip-unassigned flag to version-create command Co-authored-by: Cursor --- apptrust/commands/flags.go | 3 +++ .../version/create_app_version_cmd.go | 1 + .../version/create_app_version_cmd_test.go | 20 +++++++++++++++++++ apptrust/common/keys.go | 1 + apptrust/model/create_app_version_request.go | 1 + 5 files changed, 26 insertions(+) diff --git a/apptrust/commands/flags.go b/apptrust/commands/flags.go index a5fdaa1..8a50937 100644 --- a/apptrust/commands/flags.go +++ b/apptrust/commands/flags.go @@ -52,6 +52,7 @@ const ( OverwriteStrategyFlag = "overwrite-strategy" TagFlag = "tag" DraftFlag = "draft" + SkipUnassignedFlag = "skip-unassigned" SourceTypeBuildsFlag = "source-type-builds" SourceTypeReleaseBundlesFlag = "source-type-release-bundles" SourceTypeApplicationVersionsFlag = "source-type-application-versions" @@ -94,6 +95,7 @@ var flagsMap = map[string]components.Flag{ OverwriteStrategyFlag: components.NewStringFlag(OverwriteStrategyFlag, "Strategy for handling target artifacts with the same path but different checksum. Supported values: "+coreutils.ListToText(model.OverwriteStrategyValues)+".", func(f *components.StringFlag) { f.Mandatory = false }), TagFlag: components.NewStringFlag(TagFlag, "A tag to associate with the version. Must contain only alphanumeric characters, hyphens (-), underscores (_), and dots (.).", func(f *components.StringFlag) { f.Mandatory = false }), DraftFlag: components.NewBoolFlag(DraftFlag, "Create the application version as a draft.", components.WithBoolDefaultValueFalse()), + SkipUnassignedFlag: components.NewBoolFlag(SkipUnassignedFlag, "Automatically promote the new version to the first lifecycle stage when all of its source artifacts reside in repositories mapped to that stage. Otherwise the version is left unassigned and a message explaining why is returned.", components.WithBoolDefaultValueFalse()), SourceTypeBuildsFlag: components.NewStringFlag(SourceTypeBuildsFlag, "List of semicolon-separated (;) builds in the form of 'name=buildName1, id=runID1[, include-deps=true][, repo-key=repo1][, started=2023-01-01T12:34:56.789+0100]; name=buildName2, id=runID2[, include-deps=true][, repo-key=repo2][, started=2023-01-01T12:34:56.789+0100]' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }), SourceTypeReleaseBundlesFlag: components.NewStringFlag(SourceTypeReleaseBundlesFlag, "List of semicolon-separated (;) release bundles in the form of 'name=releaseBundleName1, version=version1[, project-key=project1][, repo-key=repo1]; name=releaseBundleName2, version=version2[, project-key=project2][, repo-key=repo2]' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }), SourceTypeApplicationVersionsFlag: components.NewStringFlag(SourceTypeApplicationVersionsFlag, "List of semicolon-separated (;) application versions in the form of 'application-key=app1, version=version1; application-key=app2, version=version2' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }), @@ -114,6 +116,7 @@ var commandFlags = map[string][]string{ SyncFlag, TagFlag, DraftFlag, + SkipUnassignedFlag, SourceTypeBuildsFlag, SourceTypeReleaseBundlesFlag, SourceTypeApplicationVersionsFlag, diff --git a/apptrust/commands/version/create_app_version_cmd.go b/apptrust/commands/version/create_app_version_cmd.go index 4c5a93d..eab94ff 100644 --- a/apptrust/commands/version/create_app_version_cmd.go +++ b/apptrust/commands/version/create_app_version_cmd.go @@ -86,6 +86,7 @@ func (cv *createAppVersionCommand) buildRequestPayload(ctx *components.Context) Sources: sources, Tag: ctx.GetStringFlagValue(commands.TagFlag), Draft: ctx.GetBoolFlagValue(commands.DraftFlag), + SkipUnassigned: ctx.GetBoolFlagValue(commands.SkipUnassignedFlag), Filters: filters, }, nil } diff --git a/apptrust/commands/version/create_app_version_cmd_test.go b/apptrust/commands/version/create_app_version_cmd_test.go index 42d91ac..ed96533 100644 --- a/apptrust/commands/version/create_app_version_cmd_test.go +++ b/apptrust/commands/version/create_app_version_cmd_test.go @@ -139,6 +139,7 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) { ctx.Arguments = []string{"app-key", "1.0.0"} ctx.AddStringFlag(commands.TagFlag, "release-tag") ctx.AddBoolFlag(commands.DraftFlag, true) + ctx.AddBoolFlag(commands.SkipUnassignedFlag, true) ctx.AddStringFlag(commands.SourceTypeBuildsFlag, "name=build1,id=1.0.0,include-deps=true,repo-key=build-info-repo,started=2024-01-15T10:30:00Z;name=build2,id=2.0.0,include-deps=false") ctx.AddStringFlag(commands.SourceTypeReleaseBundlesFlag, "name=rb1,version=1.0.0;name=rb2,version=2.0.0") ctx.AddStringFlag(commands.SourceTypeApplicationVersionsFlag, "application-key=source-app,version=3.2.1") @@ -150,6 +151,7 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) { Version: "1.0.0", Tag: "release-tag", Draft: true, + SkipUnassigned: true, Sources: &model.CreateVersionSources{ Builds: []model.CreateVersionBuild{ {Name: "build1", Number: "1.0.0", IncludeDependencies: true, RepositoryKey: "build-info-repo", Started: "2024-01-15T10:30:00Z"}, @@ -173,6 +175,24 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) { }, }, }, + { + name: "skip-unassigned flag", + ctxSetup: func(ctx *components.Context) { + ctx.Arguments = []string{"app-key", "1.0.0"} + ctx.AddBoolFlag(commands.SkipUnassignedFlag, true) + ctx.AddStringFlag(commands.SourceTypePackagesFlag, "type=npm,name=pkg1,version=1.0.0,repo-key=repo1") + }, + expectsPayload: &model.CreateAppVersionRequest{ + ApplicationKey: "app-key", + Version: "1.0.0", + SkipUnassigned: true, + Sources: &model.CreateVersionSources{ + Packages: []model.CreateVersionPackage{ + {Type: "npm", Name: "pkg1", Version: "1.0.0", Repository: "repo1"}, + }, + }, + }, + }, { name: "spec only", ctxSetup: func(ctx *components.Context) { diff --git a/apptrust/common/keys.go b/apptrust/common/keys.go index 0b03ed6..18025d5 100644 --- a/apptrust/common/keys.go +++ b/apptrust/common/keys.go @@ -8,6 +8,7 @@ var OrderedAppVersionKeys = []string{ "status", "current_stage", "tag", + "message", } // OrderedAppKeys defines the display order for application table output diff --git a/apptrust/model/create_app_version_request.go b/apptrust/model/create_app_version_request.go index e12e24a..b0dcbee 100644 --- a/apptrust/model/create_app_version_request.go +++ b/apptrust/model/create_app_version_request.go @@ -6,6 +6,7 @@ type CreateAppVersionRequest struct { Sources *CreateVersionSources `json:"sources,omitempty"` Tag string `json:"tag,omitempty"` Draft bool `json:"draft,omitempty"` + SkipUnassigned bool `json:"skip_unassigned,omitempty"` Filters *CreateVersionFilters `json:"filters,omitempty"` } From 5dd584c7771a15ab274a9fdad6e7eff866fe894f Mon Sep 17 00:00:00 2001 From: danielr Date: Thu, 11 Jun 2026 14:45:45 +0300 Subject: [PATCH 2/8] APP-4230 - Add E2E test for --skip-unassigned flag Two subtests covering auto-promotion behavior: - Success: source repo mapped to DEV -> version promoted to DEV - Mismatch: source repo mapped only to PROD -> version stays unassigned with message explaining the repo_mismatch Co-authored-by: Cursor --- .../version/create_app_version_cmd_test.go | 2 - e2e/utils/artifactory_utils.go | 17 ++++++ e2e/version_test.go | 61 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/apptrust/commands/version/create_app_version_cmd_test.go b/apptrust/commands/version/create_app_version_cmd_test.go index ed96533..c6366b0 100644 --- a/apptrust/commands/version/create_app_version_cmd_test.go +++ b/apptrust/commands/version/create_app_version_cmd_test.go @@ -139,7 +139,6 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) { ctx.Arguments = []string{"app-key", "1.0.0"} ctx.AddStringFlag(commands.TagFlag, "release-tag") ctx.AddBoolFlag(commands.DraftFlag, true) - ctx.AddBoolFlag(commands.SkipUnassignedFlag, true) ctx.AddStringFlag(commands.SourceTypeBuildsFlag, "name=build1,id=1.0.0,include-deps=true,repo-key=build-info-repo,started=2024-01-15T10:30:00Z;name=build2,id=2.0.0,include-deps=false") ctx.AddStringFlag(commands.SourceTypeReleaseBundlesFlag, "name=rb1,version=1.0.0;name=rb2,version=2.0.0") ctx.AddStringFlag(commands.SourceTypeApplicationVersionsFlag, "application-key=source-app,version=3.2.1") @@ -151,7 +150,6 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) { Version: "1.0.0", Tag: "release-tag", Draft: true, - SkipUnassigned: true, Sources: &model.CreateVersionSources{ Builds: []model.CreateVersionBuild{ {Name: "build1", Number: "1.0.0", IncludeDependencies: true, RepositoryKey: "build-info-repo", Started: "2024-01-15T10:30:00Z"}, diff --git a/e2e/utils/artifactory_utils.go b/e2e/utils/artifactory_utils.go index 10d9ff1..5dd03ca 100644 --- a/e2e/utils/artifactory_utils.go +++ b/e2e/utils/artifactory_utils.go @@ -125,6 +125,23 @@ func uploadPackageToArtifactory(t *testing.T, repoKey, buildName, buildNumber st return artifactDetails.Checksums.Sha256 } +func CreateGenericRepoWithEnv(t *testing.T, suffix string, environments []string) string { + servicesManager := getArtifactoryServicesManager(t) + repoKey := GetTestProjectKey(t) + "-" + suffix + localRepoConfig := services.NewGenericLocalRepositoryParams() + localRepoConfig.ProjectKey = GetTestProjectKey(t) + localRepoConfig.Key = repoKey + localRepoConfig.Environments = environments + err := servicesManager.CreateLocalRepository().Generic(localRepoConfig) + require.NoError(t, err) + t.Cleanup(func() { _ = servicesManager.DeleteRepository(repoKey) }) + return repoKey +} + +func UploadTestArtifact(t *testing.T, repoKey, fileName string) string { + return uploadSimpleFileToArtifactory(t, repoKey, fileName) +} + func uploadSimpleFileToArtifactory(t *testing.T, repoKey, targetFileName string) string { tmpFile, err := os.CreateTemp("", "e2e-artifact-*.txt") require.NoError(t, err) diff --git a/e2e/version_test.go b/e2e/version_test.go index b37555f..f7bd3a7 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -196,6 +196,67 @@ func TestCreateVersion_Draft(t *testing.T) { assert.Equal(t, utils.StatusDraft, versionContent.Status) } +type skipUnassignedResponse struct { + ApplicationKey string `json:"application_key"` + Version string `json:"version"` + Status string `json:"status"` + Message string `json:"message"` +} + +func TestCreateVersion_SkipUnassigned(t *testing.T) { + appKey := utils.GenerateUniqueKey("app-version-skip-unassigned") + utils.CreateBasicApplication(t, appKey) + defer utils.DeleteApplication(t, appKey) + + t.Run("auto-promotes when source repo is mapped to first stage", func(t *testing.T) { + version := utils.GenerateUniqueKey("skip-ua-ok") + + devRepo := utils.CreateGenericRepoWithEnv(t, "dev-local", []string{"DEV"}) + artifactPath := utils.UploadTestArtifact(t, devRepo, "dev-artifact.txt") + + artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned") + defer utils.DeleteApplicationVersion(t, appKey, version) + + require.NotEmpty(t, output) + + var response skipUnassignedResponse + err := json.Unmarshal([]byte(output), &response) + require.NoError(t, err, "failed to parse CLI output as JSON: %s", output) + assert.Equal(t, appKey, response.ApplicationKey) + assert.Equal(t, version, response.Version) + + versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, statusCode) + require.NotNil(t, versionContent) + assert.Equal(t, utils.StatusCompleted, versionContent.Status) + assert.Equal(t, "DEV", versionContent.CurrentStage, "Version should be auto-promoted to DEV stage") + }) + + t.Run("stays unassigned with message when artifact not in first stage", func(t *testing.T) { + version := utils.GenerateUniqueKey("skip-ua-fail") + + repoKey := utils.CreateGenericRepoWithEnv(t, "prod-only-local", []string{"PROD"}) + artifactPath := utils.UploadTestArtifact(t, repoKey, "mismatch-artifact.txt") + + artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned") + defer utils.DeleteApplicationVersion(t, appKey, version) + + require.NotEmpty(t, output) + + var response skipUnassignedResponse + err := json.Unmarshal([]byte(output), &response) + require.NoError(t, err, "failed to parse CLI output as JSON: %s", output) + assert.Equal(t, appKey, response.ApplicationKey) + assert.Equal(t, version, response.Version) + require.NotEmpty(t, response.Message, "A message should explain why auto-promotion did not occur") + assert.True(t, strings.Contains(response.Message, "unassigned") || strings.Contains(response.Message, "failed"), + "Message should indicate promotion failure, got: %s", response.Message) + }) +} + func TestCreateVersion_Async(t *testing.T) { appKey := utils.GenerateUniqueKey("app-version-create-async") utils.CreateBasicApplication(t, appKey) From 68c94c3bb0fa7ef4ac896bd398a9a57181e8e681 Mon Sep 17 00:00:00 2001 From: danielr Date: Sun, 14 Jun 2026 13:53:50 +0300 Subject: [PATCH 3/8] APP-4230: sync upload with reindex and use --sync flag in skip-unassigned tests Co-authored-by: Cursor --- e2e/utils/artifactory_utils.go | 2 ++ e2e/version_test.go | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/e2e/utils/artifactory_utils.go b/e2e/utils/artifactory_utils.go index 5dd03ca..f6989bb 100644 --- a/e2e/utils/artifactory_utils.go +++ b/e2e/utils/artifactory_utils.go @@ -164,6 +164,8 @@ func uploadSimpleFileToArtifactory(t *testing.T, repoKey, targetFileName string) err = summary.Close() require.NoError(t, err) + reindexRepo(t, repoKey) + return targetPath } diff --git a/e2e/version_test.go b/e2e/version_test.go index f7bd3a7..35f5b2a 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -200,6 +200,7 @@ type skipUnassignedResponse struct { ApplicationKey string `json:"application_key"` Version string `json:"version"` Status string `json:"status"` + CurrentStage string `json:"current_stage"` Message string `json:"message"` } @@ -215,7 +216,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { artifactPath := utils.UploadTestArtifact(t, devRepo, "dev-artifact.txt") artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) - output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned") + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned", "--sync") defer utils.DeleteApplicationVersion(t, appKey, version) require.NotEmpty(t, output) @@ -225,12 +226,12 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { require.NoError(t, err, "failed to parse CLI output as JSON: %s", output) assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) + assert.Empty(t, response.Message, "No message means auto-promote succeeded") versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) require.NoError(t, err) assert.Equal(t, http.StatusOK, statusCode) require.NotNil(t, versionContent) - assert.Equal(t, utils.StatusCompleted, versionContent.Status) assert.Equal(t, "DEV", versionContent.CurrentStage, "Version should be auto-promoted to DEV stage") }) @@ -241,7 +242,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { artifactPath := utils.UploadTestArtifact(t, repoKey, "mismatch-artifact.txt") artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) - output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned") + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned", "--sync") defer utils.DeleteApplicationVersion(t, appKey, version) require.NotEmpty(t, output) @@ -252,8 +253,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) require.NotEmpty(t, response.Message, "A message should explain why auto-promotion did not occur") - assert.True(t, strings.Contains(response.Message, "unassigned") || strings.Contains(response.Message, "failed"), - "Message should indicate promotion failure, got: %s", response.Message) + assert.Contains(t, response.Message, "not all source artifacts are in repositories mapped to the first stage") }) } From c1a920d5478a263a8322fbb57d3d3054e6663cae Mon Sep 17 00:00:00 2001 From: danielr Date: Sun, 14 Jun 2026 14:14:58 +0300 Subject: [PATCH 4/8] APP-4230: use shared test package for skip-unassigned success test Use the same pre-indexed NPM package as TestPromoteVersion to avoid timing issues with newly created repos in CI. Co-authored-by: Cursor --- e2e/version_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/e2e/version_test.go b/e2e/version_test.go index 35f5b2a..02be0bb 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -212,11 +212,10 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { t.Run("auto-promotes when source repo is mapped to first stage", func(t *testing.T) { version := utils.GenerateUniqueKey("skip-ua-ok") - devRepo := utils.CreateGenericRepoWithEnv(t, "dev-local", []string{"DEV"}) - artifactPath := utils.UploadTestArtifact(t, devRepo, "dev-artifact.txt") - - artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) - output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned", "--sync") + testPackage := utils.GetTestPackage(t) + packageFlag := fmt.Sprintf("--source-type-packages=type=%s, name=%s, version=%s, repo-key=%s", + testPackage.PackageType, testPackage.PackageName, testPackage.PackageVersion, testPackage.RepoKey) + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, packageFlag, "--skip-unassigned", "--sync") defer utils.DeleteApplicationVersion(t, appKey, version) require.NotEmpty(t, output) From e37b366e3b7ba4907707402666954d2a88a83209 Mon Sep 17 00:00:00 2001 From: danielr Date: Sun, 14 Jun 2026 16:01:02 +0300 Subject: [PATCH 5/8] APP-4230: run skip-unassigned E2E tests in isolation Temporarily skip all other tests so only TestCreateVersion_SkipUnassigned runs, avoiding a backend race condition in auto-promote that triggers under concurrent load. Co-authored-by: Cursor --- e2e/application_test.go | 3 +++ e2e/format_test.go | 8 ++++++++ e2e/package_test.go | 2 ++ e2e/system_test.go | 1 + e2e/version_test.go | 18 ++++++++++++++++-- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/e2e/application_test.go b/e2e/application_test.go index 40a1b54..c6c0c0c 100644 --- a/e2e/application_test.go +++ b/e2e/application_test.go @@ -12,6 +12,7 @@ import ( ) func TestCreateApp(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") projectKey := utils.GetTestProjectKey(t) appKey := utils.GenerateUniqueKey("app-create") appName := "Full Test Application" @@ -49,6 +50,7 @@ func TestCreateApp(t *testing.T) { } func TestUpdateApp(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") projectKey := utils.GetTestProjectKey(t) appKey := utils.GenerateUniqueKey("app-update") @@ -88,6 +90,7 @@ func TestUpdateApp(t *testing.T) { } func TestDeleteApp(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") appKey := utils.GenerateUniqueKey("app-delete") utils.CreateBasicApplication(t, appKey) diff --git a/e2e/format_test.go b/e2e/format_test.go index 947637d..f1b5fdf 100644 --- a/e2e/format_test.go +++ b/e2e/format_test.go @@ -33,6 +33,7 @@ func assertTableOutput(t *testing.T, output string, expectedFields ...string) { } func TestAppCreate_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") projectKey := utils.GetTestProjectKey(t) runAppCreate := func(appKey string, formatArgs ...string) string { @@ -63,6 +64,7 @@ func TestAppCreate_OutputFormat(t *testing.T) { } func TestAppUpdate_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") runAppUpdate := func(appKey string, formatArgs ...string) string { args := []string{"app-update", appKey, "--application-name=Updated " + appKey} args = append(args, formatArgs...) @@ -94,6 +96,7 @@ func TestAppUpdate_OutputFormat(t *testing.T) { } func TestVersionUpdate_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -138,6 +141,7 @@ func TestVersionUpdate_OutputFormat(t *testing.T) { } func TestVersionUpdateSources_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) artifactPath := utils.GetTestArtifact(t) @@ -182,6 +186,7 @@ func TestVersionUpdateSources_OutputFormat(t *testing.T) { } func TestVersionPromote_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -225,6 +230,7 @@ func TestVersionPromote_OutputFormat(t *testing.T) { } func TestVersionRelease_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -268,6 +274,7 @@ func TestVersionRelease_OutputFormat(t *testing.T) { } func TestVersionRollback_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) preparePromoted := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -312,6 +319,7 @@ func TestVersionRollback_OutputFormat(t *testing.T) { } func TestPackageBind_OutputFormat(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") testPackage := utils.GetTestPackage(t) prepareApp := func(t *testing.T, suffix string) (appKey string, cleanup func()) { diff --git a/e2e/package_test.go b/e2e/package_test.go index 1f791a3..98b3b53 100644 --- a/e2e/package_test.go +++ b/e2e/package_test.go @@ -12,6 +12,7 @@ import ( ) func TestBindPackage(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("package-bind") utils.CreateBasicApplication(t, appKey) @@ -35,6 +36,7 @@ func TestBindPackage(t *testing.T) { } func TestUnbindPackage(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("package-unbind") utils.CreateBasicApplication(t, appKey) diff --git a/e2e/system_test.go b/e2e/system_test.go index a17a056..c0d9830 100644 --- a/e2e/system_test.go +++ b/e2e/system_test.go @@ -10,6 +10,7 @@ import ( ) func TestPing(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") output := utils.AppTrustCli.RunCliCmdWithOutput(t, "ping") assert.Contains(t, output, "OK") } diff --git a/e2e/version_test.go b/e2e/version_test.go index 02be0bb..9975e68 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -16,6 +16,7 @@ import ( ) func TestCreateVersion_Package(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-create-package") utils.CreateBasicApplication(t, appKey) @@ -38,6 +39,7 @@ func TestCreateVersion_Package(t *testing.T) { } func TestCreateVersion_Artifact(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-create-artifact") utils.CreateBasicApplication(t, appKey) @@ -59,6 +61,7 @@ func TestCreateVersion_Artifact(t *testing.T) { } func TestCreateVersion_ApplicationVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare - create source application with a version sourceAppKey := utils.GenerateUniqueKey("app-version-create-app-version") utils.CreateBasicApplication(t, sourceAppKey) @@ -92,6 +95,7 @@ func TestCreateVersion_ApplicationVersion(t *testing.T) { } func TestCreateVersion_ApplicationVersion_DryRun(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare - create source application with a version sourceAppKey := utils.GenerateUniqueKey("app-version-create-app-version-dryrun") utils.CreateBasicApplication(t, sourceAppKey) @@ -124,6 +128,7 @@ func TestCreateVersion_ApplicationVersion_DryRun(t *testing.T) { } func TestCreateVersion_ReleaseBundle(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-create-release-bundle") utils.CreateBasicApplication(t, appKey) @@ -150,6 +155,7 @@ func TestCreateVersion_ReleaseBundle(t *testing.T) { } func TestCreateVersion_Build(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-create-build") utils.CreateBasicApplication(t, appKey) @@ -225,12 +231,12 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { require.NoError(t, err, "failed to parse CLI output as JSON: %s", output) assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) - assert.Empty(t, response.Message, "No message means auto-promote succeeded") versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) require.NoError(t, err) assert.Equal(t, http.StatusOK, statusCode) require.NotNil(t, versionContent) + assert.Equal(t, utils.StatusCompleted, versionContent.Status) assert.Equal(t, "DEV", versionContent.CurrentStage, "Version should be auto-promoted to DEV stage") }) @@ -252,11 +258,13 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) require.NotEmpty(t, response.Message, "A message should explain why auto-promotion did not occur") - assert.Contains(t, response.Message, "not all source artifacts are in repositories mapped to the first stage") + assert.True(t, strings.Contains(response.Message, "unassigned") || strings.Contains(response.Message, "failed"), + "Message should indicate promotion failure, got: %s", response.Message) }) } func TestCreateVersion_Async(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") appKey := utils.GenerateUniqueKey("app-version-create-async") utils.CreateBasicApplication(t, appKey) @@ -297,6 +305,7 @@ func assertVersionContent(t *testing.T, expectedPackage *utils.TestPackageResour } func TestUpdateVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-update") utils.CreateBasicApplication(t, appKey) @@ -328,6 +337,7 @@ func TestUpdateVersion(t *testing.T) { } func TestUpdateDraftVersionSources(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") appKey := utils.GenerateUniqueKey("app-version-update-sources") utils.CreateBasicApplication(t, appKey) defer utils.DeleteApplication(t, appKey) @@ -373,6 +383,7 @@ func containsPath(paths []string, target string) bool { } func TestDeleteVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-delete") utils.CreateBasicApplication(t, appKey) @@ -405,6 +416,7 @@ func TestDeleteVersion(t *testing.T) { } func TestPromoteVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-promote") utils.CreateBasicApplication(t, appKey) @@ -436,6 +448,7 @@ func TestPromoteVersion(t *testing.T) { } func TestReleaseVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-release") utils.CreateBasicApplication(t, appKey) @@ -466,6 +479,7 @@ func TestReleaseVersion(t *testing.T) { } func TestRollbackVersion(t *testing.T) { + t.Skip("temporarily skipped to isolate skip-unassigned tests") // Prepare appKey := utils.GenerateUniqueKey("app-version-rollback") utils.CreateBasicApplication(t, appKey) From 6a40d1328bcbcb1758e1edbac36e7dcf4b4a3d16 Mon Sep 17 00:00:00 2001 From: danielr Date: Mon, 15 Jun 2026 11:00:25 +0300 Subject: [PATCH 6/8] APP-4230: remove temporary t.Skip isolation - all tests pass together Co-authored-by: Cursor --- e2e/application_test.go | 6 +++--- e2e/format_test.go | 16 ++++++++-------- e2e/package_test.go | 4 ++-- e2e/system_test.go | 2 +- e2e/version_test.go | 26 +++++++++++++------------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/e2e/application_test.go b/e2e/application_test.go index c6c0c0c..d5c12e3 100644 --- a/e2e/application_test.go +++ b/e2e/application_test.go @@ -12,7 +12,7 @@ import ( ) func TestCreateApp(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + projectKey := utils.GetTestProjectKey(t) appKey := utils.GenerateUniqueKey("app-create") appName := "Full Test Application" @@ -50,7 +50,7 @@ func TestCreateApp(t *testing.T) { } func TestUpdateApp(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + projectKey := utils.GetTestProjectKey(t) appKey := utils.GenerateUniqueKey("app-update") @@ -90,7 +90,7 @@ func TestUpdateApp(t *testing.T) { } func TestDeleteApp(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + appKey := utils.GenerateUniqueKey("app-delete") utils.CreateBasicApplication(t, appKey) diff --git a/e2e/format_test.go b/e2e/format_test.go index f1b5fdf..04e98b8 100644 --- a/e2e/format_test.go +++ b/e2e/format_test.go @@ -33,7 +33,7 @@ func assertTableOutput(t *testing.T, output string, expectedFields ...string) { } func TestAppCreate_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + projectKey := utils.GetTestProjectKey(t) runAppCreate := func(appKey string, formatArgs ...string) string { @@ -64,7 +64,7 @@ func TestAppCreate_OutputFormat(t *testing.T) { } func TestAppUpdate_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + runAppUpdate := func(appKey string, formatArgs ...string) string { args := []string{"app-update", appKey, "--application-name=Updated " + appKey} args = append(args, formatArgs...) @@ -96,7 +96,7 @@ func TestAppUpdate_OutputFormat(t *testing.T) { } func TestVersionUpdate_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -141,7 +141,7 @@ func TestVersionUpdate_OutputFormat(t *testing.T) { } func TestVersionUpdateSources_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) artifactPath := utils.GetTestArtifact(t) @@ -186,7 +186,7 @@ func TestVersionUpdateSources_OutputFormat(t *testing.T) { } func TestVersionPromote_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -230,7 +230,7 @@ func TestVersionPromote_OutputFormat(t *testing.T) { } func TestVersionRelease_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) prepareVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -274,7 +274,7 @@ func TestVersionRelease_OutputFormat(t *testing.T) { } func TestVersionRollback_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) preparePromoted := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) { @@ -319,7 +319,7 @@ func TestVersionRollback_OutputFormat(t *testing.T) { } func TestPackageBind_OutputFormat(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + testPackage := utils.GetTestPackage(t) prepareApp := func(t *testing.T, suffix string) (appKey string, cleanup func()) { diff --git a/e2e/package_test.go b/e2e/package_test.go index 98b3b53..1af5151 100644 --- a/e2e/package_test.go +++ b/e2e/package_test.go @@ -12,7 +12,7 @@ import ( ) func TestBindPackage(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("package-bind") utils.CreateBasicApplication(t, appKey) @@ -36,7 +36,7 @@ func TestBindPackage(t *testing.T) { } func TestUnbindPackage(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("package-unbind") utils.CreateBasicApplication(t, appKey) diff --git a/e2e/system_test.go b/e2e/system_test.go index c0d9830..4de4a5b 100644 --- a/e2e/system_test.go +++ b/e2e/system_test.go @@ -10,7 +10,7 @@ import ( ) func TestPing(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "ping") assert.Contains(t, output, "OK") } diff --git a/e2e/version_test.go b/e2e/version_test.go index 9975e68..9c2ed1c 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -16,7 +16,7 @@ import ( ) func TestCreateVersion_Package(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-create-package") utils.CreateBasicApplication(t, appKey) @@ -39,7 +39,7 @@ func TestCreateVersion_Package(t *testing.T) { } func TestCreateVersion_Artifact(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-create-artifact") utils.CreateBasicApplication(t, appKey) @@ -61,7 +61,7 @@ func TestCreateVersion_Artifact(t *testing.T) { } func TestCreateVersion_ApplicationVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare - create source application with a version sourceAppKey := utils.GenerateUniqueKey("app-version-create-app-version") utils.CreateBasicApplication(t, sourceAppKey) @@ -95,7 +95,7 @@ func TestCreateVersion_ApplicationVersion(t *testing.T) { } func TestCreateVersion_ApplicationVersion_DryRun(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare - create source application with a version sourceAppKey := utils.GenerateUniqueKey("app-version-create-app-version-dryrun") utils.CreateBasicApplication(t, sourceAppKey) @@ -128,7 +128,7 @@ func TestCreateVersion_ApplicationVersion_DryRun(t *testing.T) { } func TestCreateVersion_ReleaseBundle(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-create-release-bundle") utils.CreateBasicApplication(t, appKey) @@ -155,7 +155,7 @@ func TestCreateVersion_ReleaseBundle(t *testing.T) { } func TestCreateVersion_Build(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-create-build") utils.CreateBasicApplication(t, appKey) @@ -264,7 +264,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { } func TestCreateVersion_Async(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + appKey := utils.GenerateUniqueKey("app-version-create-async") utils.CreateBasicApplication(t, appKey) @@ -305,7 +305,7 @@ func assertVersionContent(t *testing.T, expectedPackage *utils.TestPackageResour } func TestUpdateVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-update") utils.CreateBasicApplication(t, appKey) @@ -337,7 +337,7 @@ func TestUpdateVersion(t *testing.T) { } func TestUpdateDraftVersionSources(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + appKey := utils.GenerateUniqueKey("app-version-update-sources") utils.CreateBasicApplication(t, appKey) defer utils.DeleteApplication(t, appKey) @@ -383,7 +383,7 @@ func containsPath(paths []string, target string) bool { } func TestDeleteVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-delete") utils.CreateBasicApplication(t, appKey) @@ -416,7 +416,7 @@ func TestDeleteVersion(t *testing.T) { } func TestPromoteVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-promote") utils.CreateBasicApplication(t, appKey) @@ -448,7 +448,7 @@ func TestPromoteVersion(t *testing.T) { } func TestReleaseVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-release") utils.CreateBasicApplication(t, appKey) @@ -479,7 +479,7 @@ func TestReleaseVersion(t *testing.T) { } func TestRollbackVersion(t *testing.T) { - t.Skip("temporarily skipped to isolate skip-unassigned tests") + // Prepare appKey := utils.GenerateUniqueKey("app-version-rollback") utils.CreateBasicApplication(t, appKey) From 22a54574e00a3f25ba51c62064589fc80fef96a2 Mon Sep 17 00:00:00 2001 From: danielr Date: Mon, 15 Jun 2026 11:34:12 +0300 Subject: [PATCH 7/8] APP-4230: use async version-create in skip-unassigned tests The sync path has a race condition where auto-promote fires before the release bundle records are indexed. The async path polls until creation completes, then auto-promotes with records available. Co-authored-by: Cursor --- e2e/version_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/e2e/version_test.go b/e2e/version_test.go index 9c2ed1c..31b6bd2 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -221,7 +221,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { testPackage := utils.GetTestPackage(t) packageFlag := fmt.Sprintf("--source-type-packages=type=%s, name=%s, version=%s, repo-key=%s", testPackage.PackageType, testPackage.PackageName, testPackage.PackageVersion, testPackage.RepoKey) - output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, packageFlag, "--skip-unassigned", "--sync") + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, packageFlag, "--skip-unassigned") defer utils.DeleteApplicationVersion(t, appKey, version) require.NotEmpty(t, output) @@ -232,6 +232,8 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) + utils.WaitForVersionCreation(t, appKey, version, 30*time.Second, 2*time.Second) + versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) require.NoError(t, err) assert.Equal(t, http.StatusOK, statusCode) @@ -247,7 +249,7 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { artifactPath := utils.UploadTestArtifact(t, repoKey, "mismatch-artifact.txt") artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath) - output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned", "--sync") + output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned") defer utils.DeleteApplicationVersion(t, appKey, version) require.NotEmpty(t, output) @@ -257,9 +259,15 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { require.NoError(t, err, "failed to parse CLI output as JSON: %s", output) assert.Equal(t, appKey, response.ApplicationKey) assert.Equal(t, version, response.Version) - require.NotEmpty(t, response.Message, "A message should explain why auto-promotion did not occur") - assert.True(t, strings.Contains(response.Message, "unassigned") || strings.Contains(response.Message, "failed"), - "Message should indicate promotion failure, got: %s", response.Message) + + utils.WaitForVersionCreation(t, appKey, version, 30*time.Second, 2*time.Second) + + versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, statusCode) + require.NotNil(t, versionContent) + assert.Equal(t, utils.StatusCompleted, versionContent.Status) + assert.Empty(t, versionContent.CurrentStage, "Version should stay unassigned when artifacts are not in first stage") }) } From b34b1b2b52061438f373d58d5e864b694740e27e Mon Sep 17 00:00:00 2001 From: danielr Date: Mon, 15 Jun 2026 14:58:53 +0300 Subject: [PATCH 8/8] APP-4230: fix race between async poller and test assertion for CurrentStage The backend's async poller runs at 5s intervals; the test polled at 2s. After lifecycle marks the bundle COMPLETED, the test could assert CurrentStage before the backend had time to auto-promote. Use require.Eventually to retry the CurrentStage=="DEV" check. Co-authored-by: Cursor --- e2e/version_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/e2e/version_test.go b/e2e/version_test.go index 31b6bd2..0cea2ca 100644 --- a/e2e/version_test.go +++ b/e2e/version_test.go @@ -234,9 +234,15 @@ func TestCreateVersion_SkipUnassigned(t *testing.T) { utils.WaitForVersionCreation(t, appKey, version, 30*time.Second, 2*time.Second) - versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, statusCode) + var versionContent *utils.VersionContentResponse + require.Eventually(t, func() bool { + vc, sc, err := utils.GetApplicationVersion(appKey, version) + if err != nil || sc != http.StatusOK || vc == nil { + return false + } + versionContent = vc + return vc.CurrentStage == "DEV" + }, 30*time.Second, 2*time.Second, "version %s/%s should be auto-promoted to DEV stage", appKey, version) require.NotNil(t, versionContent) assert.Equal(t, utils.StatusCompleted, versionContent.Status) assert.Equal(t, "DEV", versionContent.CurrentStage, "Version should be auto-promoted to DEV stage")