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
118 changes: 118 additions & 0 deletions artifactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5766,3 +5766,121 @@ func deleteReceivedReleaseBundle(t *testing.T, url, bundleName, bundleVersion st
_, _, err = client.SendDelete(*tests.JfrogUrl+deleteApi, []byte{}, artHttpDetails, "Deleting release bundle")
assert.NoError(t, err)
}

var buildAqlSearchQueryDataProvider = []struct {
pattern string
recursive bool
expectedAql string
}{
{"**/a-.*.tar.gz", false, `{"$or":[{"$and":[{"path":{"$match":"**"},"name":{"$match":"a-.*.tar.gz"}}]},{"$and":[{"path":".","name":{"$match":"*a-.*.tar.gz"}}]}]}`},
}

// TestArtifactorySearchByPattern is an integration test that validates search pattern functionality
// by creating test repositories, uploading files, and verifying that searches return correct results.
// This test validates the unit test functionality of CreateAqlBodyForSpecWithPattern.
func TestArtifactorySearchByPattern(t *testing.T) {
initArtifactoryCli()
initArtifactoryTest(t, "")
defer cleanArtifactoryTest()
testRepos := []string{"repo-local", "repo-wildcard", "repo-local2", "repo-local3"}
for _, repoName := range testRepos {
if !isRepoExist(repoName) {
repoConfig := fmt.Sprintf(`{"key":"%s","rclass":"local","packageType":"generic"}`, repoName)
execCreateRepoRestFromString(repoConfig, repoName)
} else {
runRt(t, "delete", repoName+"/*", "--quiet")
}
}
setupTestFilesForSearchPatterns(t)
defer cleanupTestFilesForSearchPatterns(t)
for _, searchSample := range buildAqlSearchQueryDataProvider {
testName := fmt.Sprintf("Pattern_%s_Recursive_%s",
strings.ReplaceAll(strings.ReplaceAll(searchSample.pattern, "/", "_"), "*", "star"),
strconv.FormatBool(searchSample.recursive))
t.Run(testName, func(t *testing.T) {
args := []string{"search", searchSample.pattern}
if searchSample.recursive {
args = append(args, "--recursive")
}
err := artifactoryCli.Exec(args...)
assert.NoError(t, err, "Search command should execute successfully for pattern: %s", searchSample.pattern)
validateSearchPatternWithAPI(t, searchSample.pattern, searchSample.recursive)
})
}
}

// setupTestFilesForSearchPatterns uploads test files to match the search patterns
func setupTestFilesForSearchPatterns(t *testing.T) {
tmpFile, err := os.CreateTemp("", "test-file-*.txt")
assert.NoError(t, err)
defer func(name string) {
_ = os.Remove(name)
}(tmpFile.Name())
_, err = tmpFile.WriteString("test content")
if err != nil {
return
}
err = tmpFile.Close()
if err != nil {
return
}
runRt(t, "upload", tmpFile.Name(), "repo-local/test-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local/subdir/nested-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-wildcard/test-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local2/a1b2c3/dd/test-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local2/a1b2c3/dd/subdir/nested-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local2/other-path/test-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local3/a1b2c3/dd/test-file.txt")
runRt(t, "upload", tmpFile.Name(), "repo-local/path/to/a-1.2.3.tar.gz")
runRt(t, "upload", tmpFile.Name(), "repo-local/a-2.3.4.tar.gz")
}

// cleanupTestFilesForSearchPatterns removes test files uploaded during the test
func cleanupTestFilesForSearchPatterns(t *testing.T) {
testRepos := []string{"repo-local", "repo-wildcard", "repo-local2", "repo-local3"}
for _, repoName := range testRepos {
if isRepoExist(repoName) {
runRt(t, "delete", repoName+"/*", "--quiet")
}
}
}

// execCreateRepoRestFromString creates a repository from a JSON string
func execCreateRepoRestFromString(repoConfig, repoName string) {
rtutils.AddHeader("Content-Type", "application/json", &artHttpDetails.Headers)
client, err := httpclient.ClientBuilder().Build()
if err != nil {
log.Error(err)
os.Exit(1)
}
resp, body, err := client.SendPut(serverDetails.ArtifactoryUrl+"api/repositories/"+repoName, []byte(repoConfig), artHttpDetails, "")
if err != nil {
log.Error(err)
os.Exit(1)
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusCreated); err != nil {
log.Error(err)
os.Exit(1)
}
log.Info("Repository", repoName, "created.")
}

// validateSearchPatternWithAPI validates the search pattern using the search API directly
func validateSearchPatternWithAPI(t *testing.T, pattern string, recursive bool) {
searchSpec := spec.NewBuilder().Pattern(pattern).Recursive(recursive).BuildSpec()
searchCmd := generic.NewSearchCommand()
searchCmd.SetServerDetails(serverDetails).SetSpec(searchSpec)
reader, err := searchCmd.Search()
assert.NoError(t, err, "Search API should work for pattern: %s", pattern)
var resultItems []utils.SearchResult
readerNoDate, err := utils.SearchResultNoDate(reader)
assert.NoError(t, err)
for resultItem := new(utils.SearchResult); readerNoDate.NextRecord(resultItem) == nil; resultItem = new(utils.SearchResult) {
resultItems = append(resultItems, *resultItem)
}
if strings.HasPrefix(pattern, "repo-local") && !strings.Contains(pattern, "*repo-local") {
assert.True(t, len(resultItems) > 0, "Pattern %s should return results", pattern)
}
readerGetErrorAndAssert(t, readerNoDate)
readerCloseAndAssert(t, readerNoDate)
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ require (
github.com/jfrog/build-info-go v1.12.2
github.com/jfrog/gofrog v1.7.6
github.com/jfrog/jfrog-cli-application v1.0.1
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251029093809-3aaa4e93a359
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251107104809-61e0eb1318a5
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251028101519-8c79bb06de65
github.com/jfrog/jfrog-cli-evidence v0.8.2
github.com/jfrog/jfrog-cli-platform-services v1.10.0
github.com/jfrog/jfrog-cli-security v1.21.10
github.com/jfrog/jfrog-client-go v1.55.1-0.20251028101538-d0d16f019d67
github.com/jfrog/jfrog-cli-security v1.22.0
github.com/jfrog/jfrog-client-go v1.55.1-0.20251106114658-e01e86b037c8
github.com/jszwec/csvutil v1.10.0
github.com/manifoldco/promptui v0.9.0
github.com/stretchr/testify v1.11.1
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1159,18 +1159,18 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-cli-application v1.0.1 h1:PiiSVKFFs8VLAwjCe2JrIKlX7LmARFt+r8BLTupWIDc=
github.com/jfrog/jfrog-cli-application v1.0.1/go.mod h1:gjV6Q2hhoMe3FF77GFiTyIRLSHokpDrkVj0GvYM+1Y4=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251029093809-3aaa4e93a359 h1:r6rrXnFJYbdpy/mFRcSXxj4pcusZlE0552hj3exv2GY=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251029093809-3aaa4e93a359/go.mod h1:4D19dyt67m1wtAojNhSYR34xpOSzTyucs2iHEJG9tF8=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251107104809-61e0eb1318a5 h1:pzx0AdO31G/9bOOukZ/kNF7Ria4x3vwo4eaie45EKqE=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251107104809-61e0eb1318a5/go.mod h1:l9orAXG293Sx8pyk8nwZyHtnMXmPoVehUKZATIAy14U=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251028101519-8c79bb06de65 h1:9HDaoQXzP2EcSL7JML3j6qQ18tDvpKRuonFe1DEKvb8=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251028101519-8c79bb06de65/go.mod h1:qGHIijD2doUdFaRFRhudtCmlt0ENDaBvM3dnUClpmHc=
github.com/jfrog/jfrog-cli-evidence v0.8.2 h1:GQi2BjIruGzTWJ9AJM59frRgiPQoLLDo4IVfwiye0tc=
github.com/jfrog/jfrog-cli-evidence v0.8.2/go.mod h1:6KohlDOtLswaJ4YYi/FqflJEZTvt7yP7SA1cjGG0If4=
github.com/jfrog/jfrog-cli-platform-services v1.10.0 h1:O+N/VAF+QjFvq9xkHpmzKLcdl9aJu3IP204Su0L14rw=
github.com/jfrog/jfrog-cli-platform-services v1.10.0/go.mod h1:qbu4iqBST9x8LgD8HhzUm91iOB3vHqtoGmaxOnmw0ok=
github.com/jfrog/jfrog-cli-security v1.21.10 h1:g5wTAWXJE4V7Ly+AoxU6HzuBaxDj9U+z8aKmQasPOi0=
github.com/jfrog/jfrog-cli-security v1.21.10/go.mod h1:XoJ6YBf/TrwkqC/r9+x/WKDIwnZogTF6+2wos1PJBUs=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251028101538-d0d16f019d67 h1:SpzS1xdPY/N0a4KYlQoyA9UVC3o57vj7mbq7EJTYsD8=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251028101538-d0d16f019d67/go.mod h1:wsMEtoyAu/1bARUHxFdmgz83g96ml7ZWcFioIPiuz/U=
github.com/jfrog/jfrog-cli-security v1.22.0 h1:KNovA+BA1IpE0c0jI6jWF33fOimgylR9a7T84ZmgNJI=
github.com/jfrog/jfrog-cli-security v1.22.0/go.mod h1:uoACrGyWZViNPU0STC0fF38bVKtNXjm3hzWW/DKI0DY=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251106114658-e01e86b037c8 h1:kSbJw3FRy7/tnQ25CSGnbut4iYQ8jXibRfBwUYrl6Vg=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251106114658-e01e86b037c8/go.mod h1:wsMEtoyAu/1bARUHxFdmgz83g96ml7ZWcFioIPiuz/U=
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=
Expand Down
53 changes: 40 additions & 13 deletions lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,10 @@ func createRbIfDoesNotExists(t *testing.T, rbName, rbVersion string, lcManager *
isExist, err := lcManager.IsReleaseBundleExist(rbName, rbVersion, "")
assert.NoError(t, err)
if isExist {
return
_, statusErr := getStatus(lcManager, rbName, rbVersion, "")
if statusErr == nil {
return
}
}
createRbFromSpec(t, tests.LifecycleBuilds12, rbName, rbVersion, true, true)
}
Expand All @@ -773,30 +776,57 @@ func TestReleaseBundlesSearchGroups(t *testing.T) {
const rbNameB = rbPrefix + "-beta"
const rbNameC = rbPrefix + "-core"
const rbNameD = "another-app"

const version1 = "1.0.0"

// Create Release Bundle A
createRbIfDoesNotExists(t, rbNameA, version1, lcManager)
defer deleteReleaseBundle(t, lcManager, rbNameA, version1)
assertStatusCompleted(t, lcManager, rbNameA, version1, "")

// Create Release Bundle B
createRbIfDoesNotExists(t, rbNameB, version1, lcManager)
defer deleteReleaseBundle(t, lcManager, rbNameB, version1)
assertStatusCompleted(t, lcManager, rbNameB, version1, "")

// Create Release Bundle C
createRbIfDoesNotExists(t, rbNameC, version1, lcManager)
defer deleteReleaseBundle(t, lcManager, rbNameC, version1)
assertStatusCompleted(t, lcManager, rbNameC, version1, "")

// Create Release Bundle D (for filter/exclusion)
createRbIfDoesNotExists(t, rbNameD, version1, lcManager)
defer deleteReleaseBundle(t, lcManager, rbNameD, version1)
assertStatusCompleted(t, lcManager, rbNameD, version1, "")

time.Sleep(3 * time.Second)
const pollTimeout = 45 * time.Second
const pollInterval = 3 * time.Second

startTime := time.Now()
found := false
var lastSearchError error

for time.Since(startTime) < pollTimeout {
resp, err := lcManager.ReleaseBundlesSearchGroup(services.GetSearchOptionalQueryParams{})
if err != nil {
lastSearchError = fmt.Errorf("error while polling: %w", err)
time.Sleep(pollInterval)
continue
}
for _, rb := range resp.ReleaseBundleSearchGroup {
if rb.ReleaseBundleName == rbNameA {
found = true
break
}
}
if found {
log.Info(fmt.Sprintf("Found '%s' in search index after %s", rbNameA, time.Since(startTime)))
break
}
time.Sleep(pollInterval)
}

if !found {
if lastSearchError != nil {
t.Fatalf("Failed to find '%s' in search index after %s. Last error: %v", rbNameA, pollTimeout, lastSearchError)
}
t.Fatalf("Failed to find '%s' in search index after %s. Test cannot continue.", rbNameA, pollTimeout)
}

testCases := []struct {
name string
Expand Down Expand Up @@ -871,18 +901,15 @@ func TestReleaseBundlesSearchGroups(t *testing.T) {
return
}
assert.NoError(t, err, fmt.Sprintf("Expected no error for test case: %s", tc.name))
if tc.queryParams.FilterBy != "" {
assert.Equal(t, tc.expectedTotal, len(resp.ReleaseBundleSearchGroup), "Total count mismatch for filtered search")
} else {
assert.GreaterOrEqual(t, resp.Total, tc.expectedTotal, "Total count should be at least expected for unfiltered search")
}
var actualNames []string
for _, rb := range resp.ReleaseBundleSearchGroup {
actualNames = append(actualNames, rb.ReleaseBundleName)
}
if tc.queryParams.FilterBy != "" {
assert.Equal(t, tc.expectedRbNames, actualNames, "Release bundle names order mismatch")
assert.Equal(t, tc.expectedTotal, len(resp.ReleaseBundleSearchGroup), "Total count mismatch for filtered search")
assert.ElementsMatch(t, tc.expectedRbNames, actualNames, "Release bundle names mismatch")
} else {
assert.GreaterOrEqual(t, resp.Total, tc.expectedTotal, "Total count should be at least expected for unfiltered search")
assert.Subset(t, actualNames, tc.expectedRbNames, "Actual names should contain all expected names")
}
})
Expand Down
4 changes: 2 additions & 2 deletions testdata/mixed_repository_and_pkg_types_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"key": "${DOCKER_VIRTUAL_REPO}",
"rclass": "virtual",
"packageType": "docker",
"repositories": ["${REPO1}", "${DEBIAN_REPO}"],
"defaultDeploymentRepo": "${REPO1}",
"repositories": ["${DEBIAN_REPO}"],
"defaultDeploymentRepo": "${DEBIAN_REPO}",
"blockPushingSchema1": "false"
}
]
4 changes: 2 additions & 2 deletions testdata/mixed_repository_and_pkg_types_update_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"key": "${DOCKER_VIRTUAL_REPO}",
"rclass": "virtual",
"packageType": "docker",
"repositories": ["${REPO1}", "${DEBIAN_REPO}"],
"defaultDeploymentRepo": "${REPO1}",
"repositories": ["${DEBIAN_REPO}"],
"defaultDeploymentRepo": "${DEBIAN_REPO}",
"blockPushingSchema1": "false",
"description": "UPDATED: Virtual docker repo for batch ops"
}
Expand Down
Loading