gitea源码

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