code.gitea.io/gitea@v1.22.3/tests/integration/api_packages_nuget_test.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "archive/zip" 8 "bytes" 9 "encoding/base64" 10 "encoding/xml" 11 "fmt" 12 "io" 13 "net/http" 14 "net/http/httptest" 15 neturl "net/url" 16 "strconv" 17 "testing" 18 "time" 19 20 auth_model "code.gitea.io/gitea/models/auth" 21 "code.gitea.io/gitea/models/db" 22 "code.gitea.io/gitea/models/packages" 23 "code.gitea.io/gitea/models/unittest" 24 user_model "code.gitea.io/gitea/models/user" 25 nuget_module "code.gitea.io/gitea/modules/packages/nuget" 26 "code.gitea.io/gitea/modules/setting" 27 "code.gitea.io/gitea/modules/structs" 28 "code.gitea.io/gitea/routers/api/packages/nuget" 29 packageService "code.gitea.io/gitea/services/packages" 30 "code.gitea.io/gitea/tests" 31 32 "github.com/stretchr/testify/assert" 33 ) 34 35 func addNuGetAPIKeyHeader(req *RequestWrapper, token string) { 36 req.SetHeader("X-NuGet-ApiKey", token) 37 } 38 39 func decodeXML(t testing.TB, resp *httptest.ResponseRecorder, v any) { 40 t.Helper() 41 42 assert.NoError(t, xml.NewDecoder(resp.Body).Decode(v)) 43 } 44 45 func TestPackageNuGet(t *testing.T) { 46 defer tests.PrepareTestEnv(t)() 47 48 type FeedEntryProperties struct { 49 Version string `xml:"Version"` 50 NormalizedVersion string `xml:"NormalizedVersion"` 51 Authors string `xml:"Authors"` 52 Dependencies string `xml:"Dependencies"` 53 Description string `xml:"Description"` 54 VersionDownloadCount nuget.TypedValue[int64] `xml:"VersionDownloadCount"` 55 DownloadCount nuget.TypedValue[int64] `xml:"DownloadCount"` 56 PackageSize nuget.TypedValue[int64] `xml:"PackageSize"` 57 Created nuget.TypedValue[time.Time] `xml:"Created"` 58 LastUpdated nuget.TypedValue[time.Time] `xml:"LastUpdated"` 59 Published nuget.TypedValue[time.Time] `xml:"Published"` 60 ProjectURL string `xml:"ProjectUrl,omitempty"` 61 ReleaseNotes string `xml:"ReleaseNotes,omitempty"` 62 RequireLicenseAcceptance nuget.TypedValue[bool] `xml:"RequireLicenseAcceptance"` 63 Title string `xml:"Title"` 64 } 65 66 type FeedEntry struct { 67 XMLName xml.Name `xml:"entry"` 68 Properties *FeedEntryProperties `xml:"properties"` 69 Content string `xml:",innerxml"` 70 } 71 72 type FeedEntryLink struct { 73 Rel string `xml:"rel,attr"` 74 Href string `xml:"href,attr"` 75 } 76 77 type FeedResponse struct { 78 XMLName xml.Name `xml:"feed"` 79 Links []FeedEntryLink `xml:"link"` 80 Entries []*FeedEntry `xml:"entry"` 81 Count int64 `xml:"count"` 82 } 83 84 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 85 writeToken := getUserToken(t, user.Name, auth_model.AccessTokenScopeWritePackage) 86 readToken := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadPackage) 87 badToken := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadNotification) 88 89 packageName := "test.package" 90 packageVersion := "1.0.3" 91 packageAuthors := "KN4CK3R" 92 packageDescription := "Gitea Test Package" 93 symbolFilename := "test.pdb" 94 symbolID := "d910bb6948bd4c6cb40155bcf52c3c94" 95 96 createNuspec := func(id, version string) string { 97 return `<?xml version="1.0" encoding="utf-8"?> 98 <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> 99 <metadata> 100 <id>` + id + `</id> 101 <version>` + version + `</version> 102 <authors>` + packageAuthors + `</authors> 103 <description>` + packageDescription + `</description> 104 <dependencies> 105 <group targetFramework=".NETStandard2.0"> 106 <dependency id="Microsoft.CSharp" version="4.5.0" /> 107 </group> 108 </dependencies> 109 </metadata> 110 </package>` 111 } 112 113 createPackage := func(id, version string) *bytes.Buffer { 114 var buf bytes.Buffer 115 archive := zip.NewWriter(&buf) 116 w, _ := archive.Create("package.nuspec") 117 w.Write([]byte(createNuspec(id, version))) 118 archive.Close() 119 return &buf 120 } 121 122 content := createPackage(packageName, packageVersion).Bytes() 123 124 url := fmt.Sprintf("/api/packages/%s/nuget", user.Name) 125 126 t.Run("ServiceIndex", func(t *testing.T) { 127 t.Run("v2", func(t *testing.T) { 128 defer tests.PrintCurrentTest(t)() 129 130 privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}) 131 132 cases := []struct { 133 Owner string 134 UseBasicAuth bool 135 token string 136 expectedStatus int 137 }{ 138 {privateUser.Name, false, "", http.StatusOK}, 139 {privateUser.Name, true, "", http.StatusOK}, 140 {privateUser.Name, false, writeToken, http.StatusOK}, 141 {privateUser.Name, false, readToken, http.StatusOK}, 142 {privateUser.Name, false, badToken, http.StatusOK}, 143 {user.Name, false, "", http.StatusOK}, 144 {user.Name, true, "", http.StatusOK}, 145 {user.Name, false, writeToken, http.StatusOK}, 146 {user.Name, false, readToken, http.StatusOK}, 147 {user.Name, false, badToken, http.StatusOK}, 148 } 149 150 for _, c := range cases { 151 t.Run(c.Owner, func(t *testing.T) { 152 url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner) 153 154 req := NewRequest(t, "GET", url) 155 if c.UseBasicAuth { 156 req.AddBasicAuth(user.Name) 157 } else if c.token != "" { 158 addNuGetAPIKeyHeader(req, c.token) 159 } 160 resp := MakeRequest(t, req, c.expectedStatus) 161 if c.expectedStatus != http.StatusOK { 162 return 163 } 164 165 var result nuget.ServiceIndexResponseV2 166 decodeXML(t, resp, &result) 167 168 assert.Equal(t, setting.AppURL+url[1:], result.Base) 169 assert.Equal(t, "Packages", result.Workspace.Collection.Href) 170 }) 171 } 172 }) 173 174 t.Run("v3", func(t *testing.T) { 175 defer tests.PrintCurrentTest(t)() 176 177 privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}) 178 179 cases := []struct { 180 Owner string 181 UseBasicAuth bool 182 token string 183 expectedStatus int 184 }{ 185 {privateUser.Name, false, "", http.StatusOK}, 186 {privateUser.Name, true, "", http.StatusOK}, 187 {privateUser.Name, false, writeToken, http.StatusOK}, 188 {privateUser.Name, false, readToken, http.StatusOK}, 189 {privateUser.Name, false, badToken, http.StatusOK}, 190 {user.Name, false, "", http.StatusOK}, 191 {user.Name, true, "", http.StatusOK}, 192 {user.Name, false, writeToken, http.StatusOK}, 193 {user.Name, false, readToken, http.StatusOK}, 194 {user.Name, false, badToken, http.StatusOK}, 195 } 196 197 for _, c := range cases { 198 t.Run(c.Owner, func(t *testing.T) { 199 url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner) 200 201 req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) 202 if c.UseBasicAuth { 203 req.AddBasicAuth(user.Name) 204 } else if c.token != "" { 205 addNuGetAPIKeyHeader(req, c.token) 206 } 207 resp := MakeRequest(t, req, c.expectedStatus) 208 209 if c.expectedStatus != http.StatusOK { 210 return 211 } 212 213 var result nuget.ServiceIndexResponseV3 214 DecodeJSON(t, resp, &result) 215 216 assert.Equal(t, "3.0.0", result.Version) 217 assert.NotEmpty(t, result.Resources) 218 219 root := setting.AppURL + url[1:] 220 for _, r := range result.Resources { 221 switch r.Type { 222 case "SearchQueryService": 223 fallthrough 224 case "SearchQueryService/3.0.0-beta": 225 fallthrough 226 case "SearchQueryService/3.0.0-rc": 227 assert.Equal(t, root+"/query", r.ID) 228 case "RegistrationsBaseUrl": 229 fallthrough 230 case "RegistrationsBaseUrl/3.0.0-beta": 231 fallthrough 232 case "RegistrationsBaseUrl/3.0.0-rc": 233 assert.Equal(t, root+"/registration", r.ID) 234 case "PackageBaseAddress/3.0.0": 235 assert.Equal(t, root+"/package", r.ID) 236 case "PackagePublish/2.0.0": 237 assert.Equal(t, root, r.ID) 238 } 239 } 240 }) 241 } 242 }) 243 }) 244 245 t.Run("Upload", func(t *testing.T) { 246 t.Run("DependencyPackage", func(t *testing.T) { 247 defer tests.PrintCurrentTest(t)() 248 249 // create with username/password 250 req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). 251 AddBasicAuth(user.Name) 252 MakeRequest(t, req, http.StatusCreated) 253 254 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) 255 assert.NoError(t, err) 256 assert.Len(t, pvs, 1, "Should have one version") 257 258 pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) 259 assert.NoError(t, err) 260 assert.NotNil(t, pd.SemVer) 261 assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata) 262 assert.Equal(t, packageName, pd.Package.Name) 263 assert.Equal(t, packageVersion, pd.Version.Version) 264 265 pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) 266 assert.NoError(t, err) 267 assert.Len(t, pfs, 2, "Should have 2 files: nuget and nuspec") 268 for _, pf := range pfs { 269 switch pf.Name { 270 case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion): 271 assert.True(t, pf.IsLead) 272 273 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 274 assert.NoError(t, err) 275 assert.Equal(t, int64(len(content)), pb.Size) 276 case fmt.Sprintf("%s.nuspec", packageName): 277 assert.False(t, pf.IsLead) 278 default: 279 assert.Fail(t, "unexpected filename: %v", pf.Name) 280 } 281 } 282 283 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). 284 AddBasicAuth(user.Name) 285 MakeRequest(t, req, http.StatusConflict) 286 287 // delete the package 288 assert.NoError(t, packageService.DeletePackageVersionAndReferences(db.DefaultContext, pvs[0])) 289 290 // create failure with token without write access 291 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). 292 AddTokenAuth(readToken) 293 MakeRequest(t, req, http.StatusUnauthorized) 294 295 // create with token 296 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). 297 AddTokenAuth(writeToken) 298 MakeRequest(t, req, http.StatusCreated) 299 300 pvs, err = packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) 301 assert.NoError(t, err) 302 assert.Len(t, pvs, 1, "Should have one version") 303 304 pd, err = packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) 305 assert.NoError(t, err) 306 assert.NotNil(t, pd.SemVer) 307 assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata) 308 assert.Equal(t, packageName, pd.Package.Name) 309 assert.Equal(t, packageVersion, pd.Version.Version) 310 311 pfs, err = packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) 312 assert.NoError(t, err) 313 assert.Len(t, pfs, 2, "Should have 2 files: nuget and nuspec") 314 for _, pf := range pfs { 315 switch pf.Name { 316 case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion): 317 assert.True(t, pf.IsLead) 318 319 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 320 assert.NoError(t, err) 321 assert.Equal(t, int64(len(content)), pb.Size) 322 case fmt.Sprintf("%s.nuspec", packageName): 323 assert.False(t, pf.IsLead) 324 default: 325 assert.Fail(t, "unexpected filename: %v", pf.Name) 326 } 327 } 328 329 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). 330 AddBasicAuth(user.Name) 331 MakeRequest(t, req, http.StatusConflict) 332 }) 333 334 t.Run("SymbolPackage", func(t *testing.T) { 335 defer tests.PrintCurrentTest(t)() 336 337 createSymbolPackage := func(id, packageType string) io.Reader { 338 var buf bytes.Buffer 339 archive := zip.NewWriter(&buf) 340 341 w, _ := archive.Create("package.nuspec") 342 w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?> 343 <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> 344 <metadata> 345 <id>` + id + `</id> 346 <version>` + packageVersion + `</version> 347 <authors>` + packageAuthors + `</authors> 348 <description>` + packageDescription + `</description> 349 <packageTypes><packageType name="` + packageType + `" /></packageTypes> 350 </metadata> 351 </package>`)) 352 353 w, _ = archive.Create(symbolFilename) 354 b, _ := base64.StdEncoding.DecodeString(`QlNKQgEAAQAAAAAADAAAAFBEQiB2MS4wAAAAAAAABgB8AAAAWAAAACNQZGIAAAAA1AAAAAgBAAAj 355 fgAA3AEAAAQAAAAjU3RyaW5ncwAAAADgAQAABAAAACNVUwDkAQAAMAAAACNHVUlEAAAAFAIAACgB 356 AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) 357 w.Write(b) 358 359 archive.Close() 360 return &buf 361 } 362 363 req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage("unknown-package", "SymbolsPackage")). 364 AddBasicAuth(user.Name) 365 MakeRequest(t, req, http.StatusNotFound) 366 367 req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "DummyPackage")). 368 AddBasicAuth(user.Name) 369 MakeRequest(t, req, http.StatusBadRequest) 370 371 req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")). 372 AddBasicAuth(user.Name) 373 MakeRequest(t, req, http.StatusCreated) 374 375 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) 376 assert.NoError(t, err) 377 assert.Len(t, pvs, 1) 378 379 pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) 380 assert.NoError(t, err) 381 assert.NotNil(t, pd.SemVer) 382 assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata) 383 assert.Equal(t, packageName, pd.Package.Name) 384 assert.Equal(t, packageVersion, pd.Version.Version) 385 386 pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) 387 assert.NoError(t, err) 388 assert.Len(t, pfs, 4, "Should have 4 files: nupkg, snupkg, nuspec and pdb") 389 for _, pf := range pfs { 390 switch pf.Name { 391 case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion): 392 assert.True(t, pf.IsLead) 393 394 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 395 assert.NoError(t, err) 396 assert.Equal(t, int64(412), pb.Size) 397 case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion): 398 assert.False(t, pf.IsLead) 399 400 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 401 assert.NoError(t, err) 402 assert.Equal(t, int64(616), pb.Size) 403 case fmt.Sprintf("%s.nuspec", packageName): 404 assert.False(t, pf.IsLead) 405 406 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 407 assert.NoError(t, err) 408 assert.Equal(t, int64(427), pb.Size) 409 case symbolFilename: 410 assert.False(t, pf.IsLead) 411 412 pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) 413 assert.NoError(t, err) 414 assert.Equal(t, int64(160), pb.Size) 415 416 pps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID) 417 assert.NoError(t, err) 418 assert.Len(t, pps, 1) 419 assert.Equal(t, nuget_module.PropertySymbolID, pps[0].Name) 420 assert.Equal(t, symbolID, pps[0].Value) 421 default: 422 assert.FailNow(t, "unexpected file: %v", pf.Name) 423 } 424 } 425 426 req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")). 427 AddBasicAuth(user.Name) 428 MakeRequest(t, req, http.StatusConflict) 429 }) 430 }) 431 432 t.Run("Download", func(t *testing.T) { 433 defer tests.PrintCurrentTest(t)() 434 435 checkDownloadCount := func(count int64) { 436 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) 437 assert.NoError(t, err) 438 assert.Len(t, pvs, 1) 439 assert.Equal(t, count, pvs[0].DownloadCount) 440 } 441 442 checkDownloadCount(0) 443 444 req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion)). 445 AddBasicAuth(user.Name) 446 resp := MakeRequest(t, req, http.StatusOK) 447 448 assert.Equal(t, content, resp.Body.Bytes()) 449 450 req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.nuspec", url, packageName, packageVersion, packageName)). 451 AddBasicAuth(user.Name) 452 resp = MakeRequest(t, req, http.StatusOK) 453 454 assert.Equal(t, createNuspec(packageName, packageVersion), resp.Body.String()) 455 456 checkDownloadCount(1) 457 458 req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion)). 459 AddBasicAuth(user.Name) 460 MakeRequest(t, req, http.StatusOK) 461 462 checkDownloadCount(1) 463 464 t.Run("Symbol", func(t *testing.T) { 465 defer tests.PrintCurrentTest(t)() 466 467 req := NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/gitea.pdb", url, symbolFilename, symbolID)) 468 MakeRequest(t, req, http.StatusBadRequest) 469 470 req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, "00000000000000000000000000000000", symbolFilename)). 471 AddBasicAuth(user.Name) 472 MakeRequest(t, req, http.StatusNotFound) 473 474 req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFffff/%s", url, symbolFilename, symbolID, symbolFilename)). 475 AddBasicAuth(user.Name) 476 MakeRequest(t, req, http.StatusOK) 477 478 checkDownloadCount(1) 479 }) 480 }) 481 482 containsOneNextLink := func(t *testing.T, links []FeedEntryLink) func() bool { 483 return func() bool { 484 found := 0 485 for _, l := range links { 486 if l.Rel == "next" { 487 found++ 488 u, err := neturl.Parse(l.Href) 489 assert.NoError(t, err) 490 q := u.Query() 491 assert.Contains(t, q, "$skip") 492 assert.Contains(t, q, "$top") 493 assert.Equal(t, "1", q.Get("$skip")) 494 assert.Equal(t, "1", q.Get("$top")) 495 } 496 } 497 return found == 1 498 } 499 } 500 501 t.Run("SearchService", func(t *testing.T) { 502 cases := []struct { 503 Query string 504 Skip int 505 Take int 506 ExpectedTotal int64 507 ExpectedResults int 508 ExpectedExactMatch bool 509 }{ 510 {"", 0, 0, 4, 4, false}, 511 {"", 0, 10, 4, 4, false}, 512 {"gitea", 0, 10, 0, 0, false}, 513 {"test", 0, 10, 1, 1, false}, 514 {"test", 1, 10, 1, 0, false}, 515 {"almost.similar", 0, 0, 3, 3, true}, 516 } 517 518 fakePackages := []string{ 519 packageName, 520 "almost.similar.dependency", 521 "almost.similar", 522 "almost.similar.dependant", 523 } 524 525 for _, fakePackageName := range fakePackages { 526 req := NewRequestWithBody(t, "PUT", url, createPackage(fakePackageName, "1.0.99")). 527 AddBasicAuth(user.Name) 528 MakeRequest(t, req, http.StatusCreated) 529 } 530 531 t.Run("v2", func(t *testing.T) { 532 t.Run("Search()", func(t *testing.T) { 533 defer tests.PrintCurrentTest(t)() 534 535 for i, c := range cases { 536 req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='%s'&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 537 AddBasicAuth(user.Name) 538 resp := MakeRequest(t, req, http.StatusOK) 539 540 var result FeedResponse 541 decodeXML(t, resp, &result) 542 543 assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) 544 assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) 545 546 req = NewRequest(t, "GET", fmt.Sprintf("%s/Search()/$count?searchTerm='%s'&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 547 AddBasicAuth(user.Name) 548 resp = MakeRequest(t, req, http.StatusOK) 549 550 assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) 551 } 552 }) 553 554 t.Run("Packages()", func(t *testing.T) { 555 defer tests.PrintCurrentTest(t)() 556 557 for i, c := range cases { 558 req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 559 AddBasicAuth(user.Name) 560 resp := MakeRequest(t, req, http.StatusOK) 561 562 var result FeedResponse 563 decodeXML(t, resp, &result) 564 565 assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) 566 assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) 567 568 req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 569 AddBasicAuth(user.Name) 570 resp = MakeRequest(t, req, http.StatusOK) 571 572 assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) 573 } 574 }) 575 576 t.Run("Packages()", func(t *testing.T) { 577 defer tests.PrintCurrentTest(t)() 578 579 t.Run("substringof", func(t *testing.T) { 580 defer tests.PrintCurrentTest(t)() 581 582 for i, c := range cases { 583 req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 584 AddBasicAuth(user.Name) 585 resp := MakeRequest(t, req, http.StatusOK) 586 587 var result FeedResponse 588 decodeXML(t, resp, &result) 589 590 assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) 591 assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) 592 593 req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 594 AddBasicAuth(user.Name) 595 resp = MakeRequest(t, req, http.StatusOK) 596 597 assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) 598 } 599 }) 600 601 t.Run("IdEq", func(t *testing.T) { 602 defer tests.PrintCurrentTest(t)() 603 604 for i, c := range cases { 605 if c.Query == "" { 606 // Ignore the `tolower(Id) eq ''` as it's unlikely to happen 607 continue 608 } 609 req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=(tolower(Id) eq '%s')&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 610 AddBasicAuth(user.Name) 611 resp := MakeRequest(t, req, http.StatusOK) 612 613 var result FeedResponse 614 decodeXML(t, resp, &result) 615 616 expectedCount := 0 617 if c.ExpectedExactMatch { 618 expectedCount = 1 619 } 620 621 assert.Equal(t, int64(expectedCount), result.Count, "case %d: unexpected total hits", i) 622 assert.Len(t, result.Entries, expectedCount, "case %d: unexpected result count", i) 623 624 req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=(tolower(Id) eq '%s')&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 625 AddBasicAuth(user.Name) 626 resp = MakeRequest(t, req, http.StatusOK) 627 628 assert.Equal(t, strconv.FormatInt(int64(expectedCount), 10), resp.Body.String(), "case %d: unexpected total hits", i) 629 } 630 }) 631 }) 632 633 t.Run("Next", func(t *testing.T) { 634 req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='test'&$skip=0&$top=1", url)). 635 AddBasicAuth(user.Name) 636 resp := MakeRequest(t, req, http.StatusOK) 637 638 var result FeedResponse 639 decodeXML(t, resp, &result) 640 641 assert.Condition(t, containsOneNextLink(t, result.Links)) 642 }) 643 }) 644 645 t.Run("v3", func(t *testing.T) { 646 defer tests.PrintCurrentTest(t)() 647 648 for i, c := range cases { 649 req := NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s&skip=%d&take=%d", url, c.Query, c.Skip, c.Take)). 650 AddBasicAuth(user.Name) 651 resp := MakeRequest(t, req, http.StatusOK) 652 653 var result nuget.SearchResultResponse 654 DecodeJSON(t, resp, &result) 655 656 assert.Equal(t, c.ExpectedTotal, result.TotalHits, "case %d: unexpected total hits", i) 657 assert.Len(t, result.Data, c.ExpectedResults, "case %d: unexpected result count", i) 658 } 659 660 t.Run("EnforceGrouped", func(t *testing.T) { 661 defer tests.PrintCurrentTest(t)() 662 663 req := NewRequestWithBody(t, "PUT", url, createPackage(packageName+".dummy", "1.0.0")). 664 AddBasicAuth(user.Name) 665 MakeRequest(t, req, http.StatusCreated) 666 667 req = NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s", url, packageName)). 668 AddBasicAuth(user.Name) 669 resp := MakeRequest(t, req, http.StatusOK) 670 671 var result nuget.SearchResultResponse 672 DecodeJSON(t, resp, &result) 673 674 assert.EqualValues(t, 2, result.TotalHits) 675 assert.Len(t, result.Data, 2) 676 for _, sr := range result.Data { 677 if sr.ID == packageName { 678 assert.Len(t, sr.Versions, 2) 679 } else { 680 assert.Len(t, sr.Versions, 1) 681 } 682 } 683 684 req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName+".dummy", "1.0.0")). 685 AddBasicAuth(user.Name) 686 MakeRequest(t, req, http.StatusNoContent) 687 }) 688 }) 689 690 for _, fakePackageName := range fakePackages { 691 req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, fakePackageName, "1.0.99")). 692 AddBasicAuth(user.Name) 693 MakeRequest(t, req, http.StatusNoContent) 694 } 695 }) 696 697 t.Run("RegistrationService", func(t *testing.T) { 698 indexURL := fmt.Sprintf("%s%s/registration/%s/index.json", setting.AppURL, url[1:], packageName) 699 leafURL := fmt.Sprintf("%s%s/registration/%s/%s.json", setting.AppURL, url[1:], packageName, packageVersion) 700 contentURL := fmt.Sprintf("%s%s/package/%s/%s/%s.%s.nupkg", setting.AppURL, url[1:], packageName, packageVersion, packageName, packageVersion) 701 702 t.Run("RegistrationIndex", func(t *testing.T) { 703 defer tests.PrintCurrentTest(t)() 704 705 req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/index.json", url, packageName)). 706 AddBasicAuth(user.Name) 707 resp := MakeRequest(t, req, http.StatusOK) 708 709 var result nuget.RegistrationIndexResponse 710 DecodeJSON(t, resp, &result) 711 712 assert.Equal(t, indexURL, result.RegistrationIndexURL) 713 assert.Equal(t, 1, result.Count) 714 assert.Len(t, result.Pages, 1) 715 assert.Equal(t, indexURL, result.Pages[0].RegistrationPageURL) 716 assert.Equal(t, packageVersion, result.Pages[0].Lower) 717 assert.Equal(t, packageVersion, result.Pages[0].Upper) 718 assert.Equal(t, 1, result.Pages[0].Count) 719 assert.Len(t, result.Pages[0].Items, 1) 720 assert.Equal(t, packageName, result.Pages[0].Items[0].CatalogEntry.ID) 721 assert.Equal(t, packageVersion, result.Pages[0].Items[0].CatalogEntry.Version) 722 assert.Equal(t, packageAuthors, result.Pages[0].Items[0].CatalogEntry.Authors) 723 assert.Equal(t, packageDescription, result.Pages[0].Items[0].CatalogEntry.Description) 724 assert.Equal(t, leafURL, result.Pages[0].Items[0].CatalogEntry.CatalogLeafURL) 725 assert.Equal(t, contentURL, result.Pages[0].Items[0].CatalogEntry.PackageContentURL) 726 }) 727 728 t.Run("RegistrationLeaf", func(t *testing.T) { 729 t.Run("v2", func(t *testing.T) { 730 defer tests.PrintCurrentTest(t)() 731 732 req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages(Id='%s',Version='%s')", url, packageName, packageVersion)). 733 AddBasicAuth(user.Name) 734 resp := MakeRequest(t, req, http.StatusOK) 735 736 var result FeedEntry 737 decodeXML(t, resp, &result) 738 739 assert.Equal(t, packageName, result.Properties.Title) 740 assert.Equal(t, packageVersion, result.Properties.Version) 741 assert.Equal(t, packageAuthors, result.Properties.Authors) 742 assert.Equal(t, packageDescription, result.Properties.Description) 743 assert.Equal(t, "Microsoft.CSharp:4.5.0:.NETStandard2.0", result.Properties.Dependencies) 744 }) 745 746 t.Run("v3", func(t *testing.T) { 747 defer tests.PrintCurrentTest(t)() 748 749 req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/%s.json", url, packageName, packageVersion)). 750 AddBasicAuth(user.Name) 751 resp := MakeRequest(t, req, http.StatusOK) 752 753 var result nuget.RegistrationLeafResponse 754 DecodeJSON(t, resp, &result) 755 756 assert.Equal(t, leafURL, result.RegistrationLeafURL) 757 assert.Equal(t, contentURL, result.PackageContentURL) 758 assert.Equal(t, indexURL, result.RegistrationIndexURL) 759 }) 760 }) 761 }) 762 763 t.Run("PackageService", func(t *testing.T) { 764 t.Run("v2", func(t *testing.T) { 765 defer tests.PrintCurrentTest(t)() 766 767 req := NewRequest(t, "GET", fmt.Sprintf("%s/FindPackagesById()?id='%s'&$top=1", url, packageName)). 768 AddBasicAuth(user.Name) 769 resp := MakeRequest(t, req, http.StatusOK) 770 771 var result FeedResponse 772 decodeXML(t, resp, &result) 773 774 assert.Len(t, result.Entries, 1) 775 assert.Equal(t, packageVersion, result.Entries[0].Properties.Version) 776 assert.Condition(t, containsOneNextLink(t, result.Links)) 777 778 req = NewRequest(t, "GET", fmt.Sprintf("%s/FindPackagesById()/$count?id='%s'", url, packageName)). 779 AddBasicAuth(user.Name) 780 resp = MakeRequest(t, req, http.StatusOK) 781 782 assert.Equal(t, "1", resp.Body.String()) 783 }) 784 785 t.Run("v3", func(t *testing.T) { 786 defer tests.PrintCurrentTest(t)() 787 788 req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/index.json", url, packageName)). 789 AddBasicAuth(user.Name) 790 resp := MakeRequest(t, req, http.StatusOK) 791 792 var result nuget.PackageVersionsResponse 793 DecodeJSON(t, resp, &result) 794 795 assert.Len(t, result.Versions, 1) 796 assert.Equal(t, packageVersion, result.Versions[0]) 797 }) 798 }) 799 800 t.Run("Delete", func(t *testing.T) { 801 defer tests.PrintCurrentTest(t)() 802 803 req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, packageVersion)). 804 AddBasicAuth(user.Name) 805 MakeRequest(t, req, http.StatusNoContent) 806 807 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) 808 assert.NoError(t, err) 809 assert.Empty(t, pvs) 810 }) 811 812 t.Run("DownloadNotExists", func(t *testing.T) { 813 defer tests.PrintCurrentTest(t)() 814 815 req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion)). 816 AddBasicAuth(user.Name) 817 MakeRequest(t, req, http.StatusNotFound) 818 819 req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion)). 820 AddBasicAuth(user.Name) 821 MakeRequest(t, req, http.StatusNotFound) 822 }) 823 824 t.Run("DeleteNotExists", func(t *testing.T) { 825 defer tests.PrintCurrentTest(t)() 826 827 req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s", url, packageName, packageVersion)). 828 AddBasicAuth(user.Name) 829 MakeRequest(t, req, http.StatusNotFound) 830 }) 831 }