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