code.gitea.io/gitea@v1.21.7/tests/integration/api_packages_conan_test.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	stdurl "net/url"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/models/db"
    15  	"code.gitea.io/gitea/models/packages"
    16  	conan_model "code.gitea.io/gitea/models/packages/conan"
    17  	"code.gitea.io/gitea/models/unittest"
    18  	user_model "code.gitea.io/gitea/models/user"
    19  	conan_module "code.gitea.io/gitea/modules/packages/conan"
    20  	"code.gitea.io/gitea/modules/setting"
    21  	conan_router "code.gitea.io/gitea/routers/api/packages/conan"
    22  	"code.gitea.io/gitea/tests"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  const (
    28  	conanfileName = "conanfile.py"
    29  	conaninfoName = "conaninfo.txt"
    30  
    31  	conanLicense     = "MIT"
    32  	conanAuthor      = "Gitea <info@gitea.io>"
    33  	conanHomepage    = "https://gitea.io/"
    34  	conanURL         = "https://gitea.com/"
    35  	conanDescription = "Description of ConanPackage"
    36  	conanTopic       = "gitea"
    37  
    38  	conanPackageReference = "dummyreference"
    39  
    40  	contentConaninfo = `[settings]
    41      arch=x84_64
    42  
    43  [requires]
    44      fmt/7.1.3
    45  
    46  [options]
    47      shared=False
    48  
    49  [full_settings]
    50      arch=x84_64
    51  
    52  [full_requires]
    53      fmt/7.1.3
    54  
    55  [full_options]
    56      shared=False
    57  
    58  [recipe_hash]
    59      74714915a51073acb548ca1ce29afbac
    60  
    61  [env]
    62  CC=gcc-10`
    63  )
    64  
    65  func addTokenAuthHeader(request *http.Request, token string) *http.Request {
    66  	request.Header.Set("Authorization", token)
    67  	return request
    68  }
    69  
    70  func buildConanfileContent(name, version string) string {
    71  	return `from conans import ConanFile, CMake, tools
    72  
    73  class ConanPackageConan(ConanFile):
    74  	name = "` + name + `"
    75  	version = "` + version + `"
    76  	license = "` + conanLicense + `"
    77  	author = "` + conanAuthor + `"
    78  	homepage = "` + conanHomepage + `"
    79  	url = "` + conanURL + `"
    80  	description = "` + conanDescription + `"
    81  	topics = ("` + conanTopic + `")
    82  	settings = "os", "compiler", "build_type", "arch"
    83  	options = {"shared": [True, False], "fPIC": [True, False]}
    84  	default_options = {"shared": False, "fPIC": True}
    85  	generators = "cmake"`
    86  }
    87  
    88  func uploadConanPackageV1(t *testing.T, baseURL, token, name, version, user, channel string) {
    89  	contentConanfile := buildConanfileContent(name, version)
    90  
    91  	recipeURL := fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s", baseURL, name, version, user, channel)
    92  
    93  	req := NewRequest(t, "GET", recipeURL)
    94  	req = addTokenAuthHeader(req, token)
    95  	MakeRequest(t, req, http.StatusNotFound)
    96  
    97  	req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", recipeURL))
    98  	req = addTokenAuthHeader(req, token)
    99  	MakeRequest(t, req, http.StatusNotFound)
   100  
   101  	req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", recipeURL))
   102  	req = addTokenAuthHeader(req, token)
   103  	MakeRequest(t, req, http.StatusNotFound)
   104  
   105  	req = NewRequest(t, "POST", fmt.Sprintf("%s/upload_urls", recipeURL))
   106  	MakeRequest(t, req, http.StatusUnauthorized)
   107  
   108  	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/upload_urls", recipeURL), map[string]int64{
   109  		conanfileName: int64(len(contentConanfile)),
   110  		"removed.txt": 0,
   111  	})
   112  	req = addTokenAuthHeader(req, token)
   113  	resp := MakeRequest(t, req, http.StatusOK)
   114  
   115  	uploadURLs := make(map[string]string)
   116  	DecodeJSON(t, resp, &uploadURLs)
   117  
   118  	assert.Contains(t, uploadURLs, conanfileName)
   119  	assert.NotContains(t, uploadURLs, "removed.txt")
   120  
   121  	uploadURL := uploadURLs[conanfileName]
   122  	assert.NotEmpty(t, uploadURL)
   123  
   124  	req = NewRequestWithBody(t, "PUT", uploadURL, strings.NewReader(contentConanfile))
   125  	req = addTokenAuthHeader(req, token)
   126  	MakeRequest(t, req, http.StatusCreated)
   127  
   128  	packageURL := fmt.Sprintf("%s/packages/%s", recipeURL, conanPackageReference)
   129  
   130  	req = NewRequest(t, "GET", packageURL)
   131  	req = addTokenAuthHeader(req, token)
   132  	MakeRequest(t, req, http.StatusNotFound)
   133  
   134  	req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", packageURL))
   135  	req = addTokenAuthHeader(req, token)
   136  	MakeRequest(t, req, http.StatusNotFound)
   137  
   138  	req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", packageURL))
   139  	req = addTokenAuthHeader(req, token)
   140  	MakeRequest(t, req, http.StatusNotFound)
   141  
   142  	req = NewRequest(t, "POST", fmt.Sprintf("%s/upload_urls", packageURL))
   143  	MakeRequest(t, req, http.StatusUnauthorized)
   144  
   145  	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/upload_urls", packageURL), map[string]int64{
   146  		conaninfoName: int64(len(contentConaninfo)),
   147  		"removed.txt": 0,
   148  	})
   149  	req = addTokenAuthHeader(req, token)
   150  	resp = MakeRequest(t, req, http.StatusOK)
   151  
   152  	uploadURLs = make(map[string]string)
   153  	DecodeJSON(t, resp, &uploadURLs)
   154  
   155  	assert.Contains(t, uploadURLs, conaninfoName)
   156  	assert.NotContains(t, uploadURLs, "removed.txt")
   157  
   158  	uploadURL = uploadURLs[conaninfoName]
   159  	assert.NotEmpty(t, uploadURL)
   160  
   161  	req = NewRequestWithBody(t, "PUT", uploadURL, strings.NewReader(contentConaninfo))
   162  	req = addTokenAuthHeader(req, token)
   163  	MakeRequest(t, req, http.StatusCreated)
   164  }
   165  
   166  func uploadConanPackageV2(t *testing.T, baseURL, token, name, version, user, channel, recipeRevision, packageRevision string) {
   167  	contentConanfile := buildConanfileContent(name, version)
   168  
   169  	recipeURL := fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s", baseURL, name, version, user, channel, recipeRevision)
   170  
   171  	req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/files/%s", recipeURL, conanfileName), strings.NewReader(contentConanfile))
   172  	req = addTokenAuthHeader(req, token)
   173  	MakeRequest(t, req, http.StatusCreated)
   174  
   175  	req = NewRequest(t, "GET", fmt.Sprintf("%s/files", recipeURL))
   176  	req = addTokenAuthHeader(req, token)
   177  	resp := MakeRequest(t, req, http.StatusOK)
   178  
   179  	var list *struct {
   180  		Files map[string]any `json:"files"`
   181  	}
   182  	DecodeJSON(t, resp, &list)
   183  	assert.Len(t, list.Files, 1)
   184  	assert.Contains(t, list.Files, conanfileName)
   185  
   186  	packageURL := fmt.Sprintf("%s/packages/%s/revisions/%s", recipeURL, conanPackageReference, packageRevision)
   187  
   188  	req = NewRequest(t, "GET", fmt.Sprintf("%s/files", packageURL))
   189  	req = addTokenAuthHeader(req, token)
   190  	MakeRequest(t, req, http.StatusNotFound)
   191  
   192  	req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/files/%s", packageURL, conaninfoName), strings.NewReader(contentConaninfo))
   193  	req = addTokenAuthHeader(req, token)
   194  	MakeRequest(t, req, http.StatusCreated)
   195  
   196  	req = NewRequest(t, "GET", fmt.Sprintf("%s/files", packageURL))
   197  	req = addTokenAuthHeader(req, token)
   198  	resp = MakeRequest(t, req, http.StatusOK)
   199  
   200  	list = nil
   201  	DecodeJSON(t, resp, &list)
   202  	assert.Len(t, list.Files, 1)
   203  	assert.Contains(t, list.Files, conaninfoName)
   204  }
   205  
   206  func TestPackageConan(t *testing.T) {
   207  	defer tests.PrepareTestEnv(t)()
   208  
   209  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   210  
   211  	name := "ConanPackage"
   212  	version1 := "1.2"
   213  	version2 := "1.3"
   214  	user1 := "dummy"
   215  	user2 := "gitea"
   216  	channel1 := "test"
   217  	channel2 := "final"
   218  	revision1 := "rev1"
   219  	revision2 := "rev2"
   220  
   221  	url := fmt.Sprintf("%sapi/packages/%s/conan", setting.AppURL, user.Name)
   222  
   223  	t.Run("v1", func(t *testing.T) {
   224  		t.Run("Ping", func(t *testing.T) {
   225  			defer tests.PrintCurrentTest(t)()
   226  
   227  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/ping", url))
   228  			resp := MakeRequest(t, req, http.StatusOK)
   229  
   230  			assert.Equal(t, "revisions", resp.Header().Get("X-Conan-Server-Capabilities"))
   231  		})
   232  
   233  		token := ""
   234  
   235  		t.Run("Authenticate", func(t *testing.T) {
   236  			defer tests.PrintCurrentTest(t)()
   237  
   238  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/users/authenticate", url))
   239  			req = AddBasicAuthHeader(req, user.Name)
   240  			resp := MakeRequest(t, req, http.StatusOK)
   241  
   242  			body := resp.Body.String()
   243  			assert.NotEmpty(t, body)
   244  
   245  			token = fmt.Sprintf("Bearer %s", body)
   246  		})
   247  
   248  		t.Run("CheckCredentials", func(t *testing.T) {
   249  			defer tests.PrintCurrentTest(t)()
   250  
   251  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/users/check_credentials", url))
   252  			req = addTokenAuthHeader(req, token)
   253  			MakeRequest(t, req, http.StatusOK)
   254  		})
   255  
   256  		t.Run("Upload", func(t *testing.T) {
   257  			defer tests.PrintCurrentTest(t)()
   258  
   259  			uploadConanPackageV1(t, url, token, name, version1, user1, channel1)
   260  
   261  			t.Run("Validate", func(t *testing.T) {
   262  				defer tests.PrintCurrentTest(t)()
   263  
   264  				pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConan)
   265  				assert.NoError(t, err)
   266  				assert.Len(t, pvs, 1)
   267  
   268  				pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
   269  				assert.NoError(t, err)
   270  				assert.Nil(t, pd.SemVer)
   271  				assert.Equal(t, name, pd.Package.Name)
   272  				assert.Equal(t, version1, pd.Version.Version)
   273  				assert.IsType(t, &conan_module.Metadata{}, pd.Metadata)
   274  				metadata := pd.Metadata.(*conan_module.Metadata)
   275  				assert.Equal(t, conanLicense, metadata.License)
   276  				assert.Equal(t, conanAuthor, metadata.Author)
   277  				assert.Equal(t, conanHomepage, metadata.ProjectURL)
   278  				assert.Equal(t, conanURL, metadata.RepositoryURL)
   279  				assert.Equal(t, conanDescription, metadata.Description)
   280  				assert.Equal(t, []string{conanTopic}, metadata.Keywords)
   281  
   282  				pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
   283  				assert.NoError(t, err)
   284  				assert.Len(t, pfs, 2)
   285  
   286  				for _, pf := range pfs {
   287  					pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
   288  					assert.NoError(t, err)
   289  
   290  					if pf.Name == conanfileName {
   291  						assert.True(t, pf.IsLead)
   292  
   293  						assert.Equal(t, int64(len(buildConanfileContent(name, version1))), pb.Size)
   294  					} else if pf.Name == conaninfoName {
   295  						assert.False(t, pf.IsLead)
   296  
   297  						assert.Equal(t, int64(len(contentConaninfo)), pb.Size)
   298  					} else {
   299  						assert.Fail(t, "unknown file: %s", pf.Name)
   300  					}
   301  				}
   302  			})
   303  		})
   304  
   305  		t.Run("Download", func(t *testing.T) {
   306  			defer tests.PrintCurrentTest(t)()
   307  
   308  			recipeURL := fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s", url, name, version1, user1, channel1)
   309  
   310  			req := NewRequest(t, "GET", recipeURL)
   311  			resp := MakeRequest(t, req, http.StatusOK)
   312  
   313  			fileHashes := make(map[string]string)
   314  			DecodeJSON(t, resp, &fileHashes)
   315  			assert.Len(t, fileHashes, 1)
   316  			assert.Contains(t, fileHashes, conanfileName)
   317  			assert.Equal(t, "7abc52241c22090782c54731371847a8", fileHashes[conanfileName])
   318  
   319  			req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", recipeURL))
   320  			resp = MakeRequest(t, req, http.StatusOK)
   321  
   322  			downloadURLs := make(map[string]string)
   323  			DecodeJSON(t, resp, &downloadURLs)
   324  			assert.Contains(t, downloadURLs, conanfileName)
   325  
   326  			req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", recipeURL))
   327  			resp = MakeRequest(t, req, http.StatusOK)
   328  
   329  			DecodeJSON(t, resp, &downloadURLs)
   330  			assert.Contains(t, downloadURLs, conanfileName)
   331  
   332  			req = NewRequest(t, "GET", downloadURLs[conanfileName])
   333  			resp = MakeRequest(t, req, http.StatusOK)
   334  			assert.Equal(t, buildConanfileContent(name, version1), resp.Body.String())
   335  
   336  			packageURL := fmt.Sprintf("%s/packages/%s", recipeURL, conanPackageReference)
   337  
   338  			req = NewRequest(t, "GET", packageURL)
   339  			resp = MakeRequest(t, req, http.StatusOK)
   340  
   341  			fileHashes = make(map[string]string)
   342  			DecodeJSON(t, resp, &fileHashes)
   343  			assert.Len(t, fileHashes, 1)
   344  			assert.Contains(t, fileHashes, conaninfoName)
   345  			assert.Equal(t, "7628bfcc5b17f1470c468621a78df394", fileHashes[conaninfoName])
   346  
   347  			req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", packageURL))
   348  			resp = MakeRequest(t, req, http.StatusOK)
   349  
   350  			downloadURLs = make(map[string]string)
   351  			DecodeJSON(t, resp, &downloadURLs)
   352  			assert.Contains(t, downloadURLs, conaninfoName)
   353  
   354  			req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", packageURL))
   355  			resp = MakeRequest(t, req, http.StatusOK)
   356  
   357  			DecodeJSON(t, resp, &downloadURLs)
   358  			assert.Contains(t, downloadURLs, conaninfoName)
   359  
   360  			req = NewRequest(t, "GET", downloadURLs[conaninfoName])
   361  			resp = MakeRequest(t, req, http.StatusOK)
   362  			assert.Equal(t, contentConaninfo, resp.Body.String())
   363  		})
   364  
   365  		t.Run("Search", func(t *testing.T) {
   366  			uploadConanPackageV1(t, url, token, name, version2, user1, channel1)
   367  			uploadConanPackageV1(t, url, token, name, version1, user1, channel2)
   368  			uploadConanPackageV1(t, url, token, name, version1, user2, channel1)
   369  			uploadConanPackageV1(t, url, token, name, version1, user2, channel2)
   370  
   371  			t.Run("Recipe", func(t *testing.T) {
   372  				defer tests.PrintCurrentTest(t)()
   373  
   374  				cases := []struct {
   375  					Query    string
   376  					Expected []string
   377  				}{
   378  					{"ConanPackage", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   379  					{"ConanPackage/1.2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   380  					{"ConanPackage/1.1", []string{}},
   381  					{"Conan*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   382  					{"ConanPackage/", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   383  					{"ConanPackage/*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   384  					{"ConanPackage/1*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   385  					{"ConanPackage/*2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   386  					{"ConanPackage/1*2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   387  					{"ConanPackage/1.2@", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   388  					{"ConanPackage/1.2@du*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final"}},
   389  					{"ConanPackage/1.2@du*/", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@dummy/final"}},
   390  					{"ConanPackage/1.2@du*/*test", []string{"ConanPackage/1.2@dummy/test"}},
   391  					{"ConanPackage/1.2@du*/*st", []string{"ConanPackage/1.2@dummy/test"}},
   392  					{"ConanPackage/1.2@gitea/*", []string{"ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   393  					{"*/*@dummy", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@dummy/final"}},
   394  					{"*/*@*/final", []string{"ConanPackage/1.2@dummy/final", "ConanPackage/1.2@gitea/final"}},
   395  				}
   396  
   397  				for i, c := range cases {
   398  					req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/conans/search?q=%s", url, stdurl.QueryEscape(c.Query)))
   399  					resp := MakeRequest(t, req, http.StatusOK)
   400  
   401  					var result *conan_router.SearchResult
   402  					DecodeJSON(t, resp, &result)
   403  
   404  					assert.ElementsMatch(t, c.Expected, result.Results, "case %d: unexpected result", i)
   405  				}
   406  			})
   407  
   408  			t.Run("Package", func(t *testing.T) {
   409  				defer tests.PrintCurrentTest(t)()
   410  
   411  				req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s/search", url, name, version1, user1, channel2))
   412  				resp := MakeRequest(t, req, http.StatusOK)
   413  
   414  				var result map[string]*conan_module.Conaninfo
   415  				DecodeJSON(t, resp, &result)
   416  
   417  				assert.Contains(t, result, conanPackageReference)
   418  				info := result[conanPackageReference]
   419  				assert.NotEmpty(t, info.Settings)
   420  			})
   421  		})
   422  
   423  		t.Run("Delete", func(t *testing.T) {
   424  			t.Run("Package", func(t *testing.T) {
   425  				defer tests.PrintCurrentTest(t)()
   426  
   427  				cases := []struct {
   428  					Channel    string
   429  					References []string
   430  				}{
   431  					{channel1, []string{conanPackageReference}},
   432  					{channel2, []string{}},
   433  				}
   434  
   435  				for i, c := range cases {
   436  					rref, _ := conan_module.NewRecipeReference(name, version1, user1, c.Channel, conan_module.DefaultRevision)
   437  					references, err := conan_model.GetPackageReferences(db.DefaultContext, user.ID, rref)
   438  					assert.NoError(t, err)
   439  					assert.NotEmpty(t, references)
   440  
   441  					req := NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s/packages/delete", url, name, version1, user1, c.Channel), map[string][]string{
   442  						"package_ids": c.References,
   443  					})
   444  					req = addTokenAuthHeader(req, token)
   445  					MakeRequest(t, req, http.StatusOK)
   446  
   447  					references, err = conan_model.GetPackageReferences(db.DefaultContext, user.ID, rref)
   448  					assert.NoError(t, err)
   449  					assert.Empty(t, references, "case %d: should be empty", i)
   450  				}
   451  			})
   452  
   453  			t.Run("Recipe", func(t *testing.T) {
   454  				defer tests.PrintCurrentTest(t)()
   455  
   456  				cases := []struct {
   457  					Channel string
   458  				}{
   459  					{channel1},
   460  					{channel2},
   461  				}
   462  
   463  				for i, c := range cases {
   464  					rref, _ := conan_module.NewRecipeReference(name, version1, user1, c.Channel, conan_module.DefaultRevision)
   465  					revisions, err := conan_model.GetRecipeRevisions(db.DefaultContext, user.ID, rref)
   466  					assert.NoError(t, err)
   467  					assert.NotEmpty(t, revisions)
   468  
   469  					req := NewRequest(t, "DELETE", fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s", url, name, version1, user1, c.Channel))
   470  					req = addTokenAuthHeader(req, token)
   471  					MakeRequest(t, req, http.StatusOK)
   472  
   473  					revisions, err = conan_model.GetRecipeRevisions(db.DefaultContext, user.ID, rref)
   474  					assert.NoError(t, err)
   475  					assert.Empty(t, revisions, "case %d: should be empty", i)
   476  				}
   477  			})
   478  		})
   479  	})
   480  
   481  	t.Run("v2", func(t *testing.T) {
   482  		t.Run("Ping", func(t *testing.T) {
   483  			defer tests.PrintCurrentTest(t)()
   484  
   485  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/ping", url))
   486  			resp := MakeRequest(t, req, http.StatusOK)
   487  
   488  			assert.Equal(t, "revisions", resp.Header().Get("X-Conan-Server-Capabilities"))
   489  		})
   490  
   491  		token := ""
   492  
   493  		t.Run("Authenticate", func(t *testing.T) {
   494  			defer tests.PrintCurrentTest(t)()
   495  
   496  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/users/authenticate", url))
   497  			req = AddBasicAuthHeader(req, user.Name)
   498  			resp := MakeRequest(t, req, http.StatusOK)
   499  
   500  			body := resp.Body.String()
   501  			assert.NotEmpty(t, body)
   502  
   503  			token = fmt.Sprintf("Bearer %s", body)
   504  		})
   505  
   506  		t.Run("CheckCredentials", func(t *testing.T) {
   507  			defer tests.PrintCurrentTest(t)()
   508  
   509  			req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/users/check_credentials", url))
   510  			req = addTokenAuthHeader(req, token)
   511  			MakeRequest(t, req, http.StatusOK)
   512  		})
   513  
   514  		t.Run("Upload", func(t *testing.T) {
   515  			defer tests.PrintCurrentTest(t)()
   516  
   517  			uploadConanPackageV2(t, url, token, name, version1, user1, channel1, revision1, revision1)
   518  
   519  			t.Run("Validate", func(t *testing.T) {
   520  				defer tests.PrintCurrentTest(t)()
   521  
   522  				pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConan)
   523  				assert.NoError(t, err)
   524  				assert.Len(t, pvs, 2)
   525  			})
   526  		})
   527  
   528  		t.Run("Latest", func(t *testing.T) {
   529  			defer tests.PrintCurrentTest(t)()
   530  
   531  			recipeURL := fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s", url, name, version1, user1, channel1)
   532  
   533  			req := NewRequest(t, "GET", fmt.Sprintf("%s/latest", recipeURL))
   534  			resp := MakeRequest(t, req, http.StatusOK)
   535  
   536  			obj := make(map[string]string)
   537  			DecodeJSON(t, resp, &obj)
   538  			assert.Contains(t, obj, "revision")
   539  			assert.Equal(t, revision1, obj["revision"])
   540  
   541  			req = NewRequest(t, "GET", fmt.Sprintf("%s/revisions/%s/packages/%s/latest", recipeURL, revision1, conanPackageReference))
   542  			resp = MakeRequest(t, req, http.StatusOK)
   543  
   544  			obj = make(map[string]string)
   545  			DecodeJSON(t, resp, &obj)
   546  			assert.Contains(t, obj, "revision")
   547  			assert.Equal(t, revision1, obj["revision"])
   548  		})
   549  
   550  		t.Run("ListRevisions", func(t *testing.T) {
   551  			defer tests.PrintCurrentTest(t)()
   552  
   553  			uploadConanPackageV2(t, url, token, name, version1, user1, channel1, revision1, revision2)
   554  			uploadConanPackageV2(t, url, token, name, version1, user1, channel1, revision2, revision1)
   555  			uploadConanPackageV2(t, url, token, name, version1, user1, channel1, revision2, revision2)
   556  
   557  			recipeURL := fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions", url, name, version1, user1, channel1)
   558  
   559  			req := NewRequest(t, "GET", recipeURL)
   560  			resp := MakeRequest(t, req, http.StatusOK)
   561  
   562  			type RevisionInfo struct {
   563  				Revision string    `json:"revision"`
   564  				Time     time.Time `json:"time"`
   565  			}
   566  
   567  			type RevisionList struct {
   568  				Revisions []*RevisionInfo `json:"revisions"`
   569  			}
   570  
   571  			var list *RevisionList
   572  			DecodeJSON(t, resp, &list)
   573  			assert.Len(t, list.Revisions, 2)
   574  			revs := make([]string, 0, len(list.Revisions))
   575  			for _, rev := range list.Revisions {
   576  				revs = append(revs, rev.Revision)
   577  			}
   578  			assert.ElementsMatch(t, []string{revision1, revision2}, revs)
   579  
   580  			req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/packages/%s/revisions", recipeURL, revision1, conanPackageReference))
   581  			resp = MakeRequest(t, req, http.StatusOK)
   582  
   583  			DecodeJSON(t, resp, &list)
   584  			assert.Len(t, list.Revisions, 2)
   585  			revs = make([]string, 0, len(list.Revisions))
   586  			for _, rev := range list.Revisions {
   587  				revs = append(revs, rev.Revision)
   588  			}
   589  			assert.ElementsMatch(t, []string{revision1, revision2}, revs)
   590  		})
   591  
   592  		t.Run("Search", func(t *testing.T) {
   593  			t.Run("Recipe", func(t *testing.T) {
   594  				defer tests.PrintCurrentTest(t)()
   595  
   596  				cases := []struct {
   597  					Query    string
   598  					Expected []string
   599  				}{
   600  					{"ConanPackage", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   601  					{"ConanPackage/1.2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   602  					{"ConanPackage/1.1", []string{}},
   603  					{"Conan*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   604  					{"ConanPackage/", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   605  					{"ConanPackage/*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   606  					{"ConanPackage/1*", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   607  					{"ConanPackage/*2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   608  					{"ConanPackage/1*2", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   609  					{"ConanPackage/1.2@", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   610  					{"ConanPackage/1.2@du*", []string{"ConanPackage/1.2@dummy/test"}},
   611  					{"ConanPackage/1.2@du*/", []string{"ConanPackage/1.2@dummy/test"}},
   612  					{"ConanPackage/1.2@du*/*test", []string{"ConanPackage/1.2@dummy/test"}},
   613  					{"ConanPackage/1.2@du*/*st", []string{"ConanPackage/1.2@dummy/test"}},
   614  					{"ConanPackage/1.2@gitea/*", []string{"ConanPackage/1.2@gitea/test", "ConanPackage/1.2@gitea/final"}},
   615  					{"*/*@dummy", []string{"ConanPackage/1.2@dummy/test", "ConanPackage/1.3@dummy/test"}},
   616  					{"*/*@*/final", []string{"ConanPackage/1.2@gitea/final"}},
   617  				}
   618  
   619  				for i, c := range cases {
   620  					req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/conans/search?q=%s", url, stdurl.QueryEscape(c.Query)))
   621  					resp := MakeRequest(t, req, http.StatusOK)
   622  
   623  					var result *conan_router.SearchResult
   624  					DecodeJSON(t, resp, &result)
   625  
   626  					assert.ElementsMatch(t, c.Expected, result.Results, "case %d: unexpected result", i)
   627  				}
   628  			})
   629  
   630  			t.Run("Package", func(t *testing.T) {
   631  				defer tests.PrintCurrentTest(t)()
   632  
   633  				req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/search", url, name, version1, user1, channel1))
   634  				resp := MakeRequest(t, req, http.StatusOK)
   635  
   636  				var result map[string]*conan_module.Conaninfo
   637  				DecodeJSON(t, resp, &result)
   638  
   639  				assert.Contains(t, result, conanPackageReference)
   640  				info := result[conanPackageReference]
   641  				assert.NotEmpty(t, info.Settings)
   642  
   643  				req = NewRequest(t, "GET", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s/search", url, name, version1, user1, channel1, revision1))
   644  				resp = MakeRequest(t, req, http.StatusOK)
   645  
   646  				result = make(map[string]*conan_module.Conaninfo)
   647  				DecodeJSON(t, resp, &result)
   648  
   649  				assert.Contains(t, result, conanPackageReference)
   650  				info = result[conanPackageReference]
   651  				assert.NotEmpty(t, info.Settings)
   652  			})
   653  		})
   654  
   655  		t.Run("Delete", func(t *testing.T) {
   656  			t.Run("Package", func(t *testing.T) {
   657  				defer tests.PrintCurrentTest(t)()
   658  
   659  				rref, _ := conan_module.NewRecipeReference(name, version1, user1, channel1, revision1)
   660  				pref, _ := conan_module.NewPackageReference(rref, conanPackageReference, conan_module.DefaultRevision)
   661  
   662  				checkPackageRevisionCount := func(count int) {
   663  					revisions, err := conan_model.GetPackageRevisions(db.DefaultContext, user.ID, pref)
   664  					assert.NoError(t, err)
   665  					assert.Len(t, revisions, count)
   666  				}
   667  				checkPackageReferenceCount := func(count int) {
   668  					references, err := conan_model.GetPackageReferences(db.DefaultContext, user.ID, rref)
   669  					assert.NoError(t, err)
   670  					assert.Len(t, references, count)
   671  				}
   672  
   673  				checkPackageRevisionCount(2)
   674  
   675  				req := NewRequest(t, "DELETE", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s/packages/%s/revisions/%s", url, name, version1, user1, channel1, revision1, conanPackageReference, revision1))
   676  				req = addTokenAuthHeader(req, token)
   677  				MakeRequest(t, req, http.StatusOK)
   678  
   679  				checkPackageRevisionCount(1)
   680  
   681  				req = NewRequest(t, "DELETE", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s/packages/%s", url, name, version1, user1, channel1, revision1, conanPackageReference))
   682  				req = addTokenAuthHeader(req, token)
   683  				MakeRequest(t, req, http.StatusOK)
   684  
   685  				checkPackageRevisionCount(0)
   686  
   687  				rref = rref.WithRevision(revision2)
   688  
   689  				checkPackageReferenceCount(1)
   690  
   691  				req = NewRequest(t, "DELETE", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s/packages", url, name, version1, user1, channel1, revision2))
   692  				req = addTokenAuthHeader(req, token)
   693  				MakeRequest(t, req, http.StatusOK)
   694  
   695  				checkPackageReferenceCount(0)
   696  			})
   697  
   698  			t.Run("Recipe", func(t *testing.T) {
   699  				defer tests.PrintCurrentTest(t)()
   700  
   701  				rref, _ := conan_module.NewRecipeReference(name, version1, user1, channel1, conan_module.DefaultRevision)
   702  
   703  				checkRecipeRevisionCount := func(count int) {
   704  					revisions, err := conan_model.GetRecipeRevisions(db.DefaultContext, user.ID, rref)
   705  					assert.NoError(t, err)
   706  					assert.Len(t, revisions, count)
   707  				}
   708  
   709  				checkRecipeRevisionCount(2)
   710  
   711  				req := NewRequest(t, "DELETE", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s/revisions/%s", url, name, version1, user1, channel1, revision1))
   712  				req = addTokenAuthHeader(req, token)
   713  				MakeRequest(t, req, http.StatusOK)
   714  
   715  				checkRecipeRevisionCount(1)
   716  
   717  				req = NewRequest(t, "DELETE", fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s", url, name, version1, user1, channel1))
   718  				req = addTokenAuthHeader(req, token)
   719  				MakeRequest(t, req, http.StatusOK)
   720  
   721  				checkRecipeRevisionCount(0)
   722  			})
   723  		})
   724  	})
   725  }