You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api_packages_nuget_test.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package integrations
  5. import (
  6. "archive/zip"
  7. "bytes"
  8. "encoding/base64"
  9. "fmt"
  10. "io"
  11. "net/http"
  12. "testing"
  13. "code.gitea.io/gitea/models/db"
  14. "code.gitea.io/gitea/models/packages"
  15. "code.gitea.io/gitea/models/unittest"
  16. user_model "code.gitea.io/gitea/models/user"
  17. nuget_module "code.gitea.io/gitea/modules/packages/nuget"
  18. "code.gitea.io/gitea/modules/setting"
  19. "code.gitea.io/gitea/routers/api/packages/nuget"
  20. "github.com/stretchr/testify/assert"
  21. )
  22. func TestPackageNuGet(t *testing.T) {
  23. defer prepareTestEnv(t)()
  24. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
  25. packageName := "test.package"
  26. packageVersion := "1.0.3"
  27. packageAuthors := "KN4CK3R"
  28. packageDescription := "Gitea Test Package"
  29. symbolFilename := "test.pdb"
  30. symbolID := "d910bb6948bd4c6cb40155bcf52c3c94"
  31. var buf bytes.Buffer
  32. archive := zip.NewWriter(&buf)
  33. w, _ := archive.Create("package.nuspec")
  34. w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?>
  35. <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  36. <metadata>
  37. <id>` + packageName + `</id>
  38. <version>` + packageVersion + `</version>
  39. <authors>` + packageAuthors + `</authors>
  40. <description>` + packageDescription + `</description>
  41. <group targetFramework=".NETStandard2.0">
  42. <dependency id="Microsoft.CSharp" version="4.5.0" />
  43. </group>
  44. </metadata>
  45. </package>`))
  46. archive.Close()
  47. content := buf.Bytes()
  48. url := fmt.Sprintf("/api/packages/%s/nuget", user.Name)
  49. t.Run("ServiceIndex", func(t *testing.T) {
  50. defer PrintCurrentTest(t)()
  51. req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
  52. req = AddBasicAuthHeader(req, user.Name)
  53. resp := MakeRequest(t, req, http.StatusOK)
  54. var result nuget.ServiceIndexResponse
  55. DecodeJSON(t, resp, &result)
  56. assert.Equal(t, "3.0.0", result.Version)
  57. assert.NotEmpty(t, result.Resources)
  58. root := setting.AppURL + url[1:]
  59. for _, r := range result.Resources {
  60. switch r.Type {
  61. case "SearchQueryService":
  62. fallthrough
  63. case "SearchQueryService/3.0.0-beta":
  64. fallthrough
  65. case "SearchQueryService/3.0.0-rc":
  66. assert.Equal(t, root+"/query", r.ID)
  67. case "RegistrationsBaseUrl":
  68. fallthrough
  69. case "RegistrationsBaseUrl/3.0.0-beta":
  70. fallthrough
  71. case "RegistrationsBaseUrl/3.0.0-rc":
  72. assert.Equal(t, root+"/registration", r.ID)
  73. case "PackageBaseAddress/3.0.0":
  74. assert.Equal(t, root+"/package", r.ID)
  75. case "PackagePublish/2.0.0":
  76. assert.Equal(t, root, r.ID)
  77. }
  78. }
  79. })
  80. t.Run("Upload", func(t *testing.T) {
  81. t.Run("DependencyPackage", func(t *testing.T) {
  82. defer PrintCurrentTest(t)()
  83. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
  84. req = AddBasicAuthHeader(req, user.Name)
  85. MakeRequest(t, req, http.StatusCreated)
  86. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
  87. assert.NoError(t, err)
  88. assert.Len(t, pvs, 1)
  89. pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
  90. assert.NoError(t, err)
  91. assert.NotNil(t, pd.SemVer)
  92. assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata)
  93. assert.Equal(t, packageName, pd.Package.Name)
  94. assert.Equal(t, packageVersion, pd.Version.Version)
  95. pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
  96. assert.NoError(t, err)
  97. assert.Len(t, pfs, 1)
  98. assert.Equal(t, fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion), pfs[0].Name)
  99. assert.True(t, pfs[0].IsLead)
  100. pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
  101. assert.NoError(t, err)
  102. assert.Equal(t, int64(len(content)), pb.Size)
  103. req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
  104. req = AddBasicAuthHeader(req, user.Name)
  105. MakeRequest(t, req, http.StatusBadRequest)
  106. })
  107. t.Run("SymbolPackage", func(t *testing.T) {
  108. defer PrintCurrentTest(t)()
  109. createPackage := func(id, packageType string) io.Reader {
  110. var buf bytes.Buffer
  111. archive := zip.NewWriter(&buf)
  112. w, _ := archive.Create("package.nuspec")
  113. w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?>
  114. <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  115. <metadata>
  116. <id>` + id + `</id>
  117. <version>` + packageVersion + `</version>
  118. <authors>` + packageAuthors + `</authors>
  119. <description>` + packageDescription + `</description>
  120. <packageTypes><packageType name="` + packageType + `" /></packageTypes>
  121. </metadata>
  122. </package>`))
  123. w, _ = archive.Create(symbolFilename)
  124. b, _ := base64.StdEncoding.DecodeString(`QlNKQgEAAQAAAAAADAAAAFBEQiB2MS4wAAAAAAAABgB8AAAAWAAAACNQZGIAAAAA1AAAAAgBAAAj
  125. fgAA3AEAAAQAAAAjU3RyaW5ncwAAAADgAQAABAAAACNVUwDkAQAAMAAAACNHVUlEAAAAFAIAACgB
  126. AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
  127. w.Write(b)
  128. archive.Close()
  129. return &buf
  130. }
  131. req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage("unknown-package", "SymbolsPackage"))
  132. req = AddBasicAuthHeader(req, user.Name)
  133. MakeRequest(t, req, http.StatusNotFound)
  134. req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "DummyPackage"))
  135. req = AddBasicAuthHeader(req, user.Name)
  136. MakeRequest(t, req, http.StatusBadRequest)
  137. req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
  138. req = AddBasicAuthHeader(req, user.Name)
  139. MakeRequest(t, req, http.StatusCreated)
  140. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
  141. assert.NoError(t, err)
  142. assert.Len(t, pvs, 1)
  143. pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
  144. assert.NoError(t, err)
  145. assert.NotNil(t, pd.SemVer)
  146. assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata)
  147. assert.Equal(t, packageName, pd.Package.Name)
  148. assert.Equal(t, packageVersion, pd.Version.Version)
  149. pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
  150. assert.NoError(t, err)
  151. assert.Len(t, pfs, 3)
  152. for _, pf := range pfs {
  153. switch pf.Name {
  154. case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion):
  155. case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion):
  156. assert.False(t, pf.IsLead)
  157. pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
  158. assert.NoError(t, err)
  159. assert.Equal(t, int64(616), pb.Size)
  160. case symbolFilename:
  161. assert.False(t, pf.IsLead)
  162. pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
  163. assert.NoError(t, err)
  164. assert.Equal(t, int64(160), pb.Size)
  165. pps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID)
  166. assert.NoError(t, err)
  167. assert.Len(t, pps, 1)
  168. assert.Equal(t, nuget_module.PropertySymbolID, pps[0].Name)
  169. assert.Equal(t, symbolID, pps[0].Value)
  170. default:
  171. assert.Fail(t, "unexpected file: %v", pf.Name)
  172. }
  173. }
  174. req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
  175. req = AddBasicAuthHeader(req, user.Name)
  176. MakeRequest(t, req, http.StatusBadRequest)
  177. })
  178. })
  179. t.Run("Download", func(t *testing.T) {
  180. defer PrintCurrentTest(t)()
  181. checkDownloadCount := func(count int64) {
  182. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
  183. assert.NoError(t, err)
  184. assert.Len(t, pvs, 1)
  185. assert.Equal(t, count, pvs[0].DownloadCount)
  186. }
  187. checkDownloadCount(0)
  188. req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion))
  189. req = AddBasicAuthHeader(req, user.Name)
  190. resp := MakeRequest(t, req, http.StatusOK)
  191. assert.Equal(t, content, resp.Body.Bytes())
  192. checkDownloadCount(1)
  193. req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion))
  194. req = AddBasicAuthHeader(req, user.Name)
  195. MakeRequest(t, req, http.StatusOK)
  196. checkDownloadCount(1)
  197. t.Run("Symbol", func(t *testing.T) {
  198. defer PrintCurrentTest(t)()
  199. req := NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/gitea.pdb", url, symbolFilename, symbolID))
  200. MakeRequest(t, req, http.StatusBadRequest)
  201. req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, "00000000000000000000000000000000", symbolFilename))
  202. req = AddBasicAuthHeader(req, user.Name)
  203. MakeRequest(t, req, http.StatusNotFound)
  204. req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, symbolID, symbolFilename))
  205. req = AddBasicAuthHeader(req, user.Name)
  206. MakeRequest(t, req, http.StatusOK)
  207. checkDownloadCount(1)
  208. })
  209. })
  210. t.Run("SearchService", func(t *testing.T) {
  211. defer PrintCurrentTest(t)()
  212. cases := []struct {
  213. Query string
  214. Skip int
  215. Take int
  216. ExpectedTotal int64
  217. ExpectedResults int
  218. }{
  219. {"", 0, 0, 1, 1},
  220. {"", 0, 10, 1, 1},
  221. {"gitea", 0, 10, 0, 0},
  222. {"test", 0, 10, 1, 1},
  223. {"test", 1, 10, 1, 0},
  224. }
  225. for i, c := range cases {
  226. req := NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s&skip=%d&take=%d", url, c.Query, c.Skip, c.Take))
  227. req = AddBasicAuthHeader(req, user.Name)
  228. resp := MakeRequest(t, req, http.StatusOK)
  229. var result nuget.SearchResultResponse
  230. DecodeJSON(t, resp, &result)
  231. assert.Equal(t, c.ExpectedTotal, result.TotalHits, "case %d: unexpected total hits", i)
  232. assert.Len(t, result.Data, c.ExpectedResults, "case %d: unexpected result count", i)
  233. }
  234. })
  235. t.Run("RegistrationService", func(t *testing.T) {
  236. indexURL := fmt.Sprintf("%s%s/registration/%s/index.json", setting.AppURL, url[1:], packageName)
  237. leafURL := fmt.Sprintf("%s%s/registration/%s/%s.json", setting.AppURL, url[1:], packageName, packageVersion)
  238. contentURL := fmt.Sprintf("%s%s/package/%s/%s/%s.%s.nupkg", setting.AppURL, url[1:], packageName, packageVersion, packageName, packageVersion)
  239. t.Run("RegistrationIndex", func(t *testing.T) {
  240. defer PrintCurrentTest(t)()
  241. req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/index.json", url, packageName))
  242. req = AddBasicAuthHeader(req, user.Name)
  243. resp := MakeRequest(t, req, http.StatusOK)
  244. var result nuget.RegistrationIndexResponse
  245. DecodeJSON(t, resp, &result)
  246. assert.Equal(t, indexURL, result.RegistrationIndexURL)
  247. assert.Equal(t, 1, result.Count)
  248. assert.Len(t, result.Pages, 1)
  249. assert.Equal(t, indexURL, result.Pages[0].RegistrationPageURL)
  250. assert.Equal(t, packageVersion, result.Pages[0].Lower)
  251. assert.Equal(t, packageVersion, result.Pages[0].Upper)
  252. assert.Equal(t, 1, result.Pages[0].Count)
  253. assert.Len(t, result.Pages[0].Items, 1)
  254. assert.Equal(t, packageName, result.Pages[0].Items[0].CatalogEntry.ID)
  255. assert.Equal(t, packageVersion, result.Pages[0].Items[0].CatalogEntry.Version)
  256. assert.Equal(t, packageAuthors, result.Pages[0].Items[0].CatalogEntry.Authors)
  257. assert.Equal(t, packageDescription, result.Pages[0].Items[0].CatalogEntry.Description)
  258. assert.Equal(t, leafURL, result.Pages[0].Items[0].CatalogEntry.CatalogLeafURL)
  259. assert.Equal(t, contentURL, result.Pages[0].Items[0].CatalogEntry.PackageContentURL)
  260. })
  261. t.Run("RegistrationLeaf", func(t *testing.T) {
  262. defer PrintCurrentTest(t)()
  263. req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/%s.json", url, packageName, packageVersion))
  264. req = AddBasicAuthHeader(req, user.Name)
  265. resp := MakeRequest(t, req, http.StatusOK)
  266. var result nuget.RegistrationLeafResponse
  267. DecodeJSON(t, resp, &result)
  268. assert.Equal(t, leafURL, result.RegistrationLeafURL)
  269. assert.Equal(t, contentURL, result.PackageContentURL)
  270. assert.Equal(t, indexURL, result.RegistrationIndexURL)
  271. })
  272. })
  273. t.Run("PackageService", func(t *testing.T) {
  274. defer PrintCurrentTest(t)()
  275. req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/index.json", url, packageName))
  276. req = AddBasicAuthHeader(req, user.Name)
  277. resp := MakeRequest(t, req, http.StatusOK)
  278. var result nuget.PackageVersionsResponse
  279. DecodeJSON(t, resp, &result)
  280. assert.Len(t, result.Versions, 1)
  281. assert.Equal(t, packageVersion, result.Versions[0])
  282. })
  283. t.Run("Delete", func(t *testing.T) {
  284. defer PrintCurrentTest(t)()
  285. req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, packageVersion))
  286. req = AddBasicAuthHeader(req, user.Name)
  287. MakeRequest(t, req, http.StatusOK)
  288. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
  289. assert.NoError(t, err)
  290. assert.Empty(t, pvs)
  291. })
  292. t.Run("DownloadNotExists", func(t *testing.T) {
  293. defer PrintCurrentTest(t)()
  294. req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion))
  295. req = AddBasicAuthHeader(req, user.Name)
  296. MakeRequest(t, req, http.StatusNotFound)
  297. req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion))
  298. req = AddBasicAuthHeader(req, user.Name)
  299. MakeRequest(t, req, http.StatusNotFound)
  300. })
  301. t.Run("DeleteNotExists", func(t *testing.T) {
  302. defer PrintCurrentTest(t)()
  303. req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s", url, packageName, packageVersion))
  304. req = AddBasicAuthHeader(req, user.Name)
  305. MakeRequest(t, req, http.StatusNotFound)
  306. })
  307. }