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