code.gitea.io/gitea@v1.22.3/tests/integration/api_releases_test.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "mime/multipart" 11 "net/http" 12 "net/url" 13 "strings" 14 "testing" 15 16 auth_model "code.gitea.io/gitea/models/auth" 17 repo_model "code.gitea.io/gitea/models/repo" 18 "code.gitea.io/gitea/models/unittest" 19 user_model "code.gitea.io/gitea/models/user" 20 "code.gitea.io/gitea/modules/git" 21 "code.gitea.io/gitea/modules/gitrepo" 22 api "code.gitea.io/gitea/modules/structs" 23 "code.gitea.io/gitea/tests" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestAPIListReleases(t *testing.T) { 29 defer tests.PrepareTestEnv(t)() 30 31 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 32 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 33 token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeReadRepository) 34 35 link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) 36 resp := MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) 37 var apiReleases []*api.Release 38 DecodeJSON(t, resp, &apiReleases) 39 if assert.Len(t, apiReleases, 3) { 40 for _, release := range apiReleases { 41 switch release.ID { 42 case 1: 43 assert.False(t, release.IsDraft) 44 assert.False(t, release.IsPrerelease) 45 assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/1/assets"), release.UploadURL) 46 case 4: 47 assert.True(t, release.IsDraft) 48 assert.False(t, release.IsPrerelease) 49 assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/4/assets"), release.UploadURL) 50 case 5: 51 assert.False(t, release.IsDraft) 52 assert.True(t, release.IsPrerelease) 53 assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/5/assets"), release.UploadURL) 54 default: 55 assert.NoError(t, fmt.Errorf("unexpected release: %v", release)) 56 } 57 } 58 } 59 60 // test filter 61 testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { 62 link.RawQuery = query.Encode() 63 req := NewRequest(t, "GET", link.String()) 64 if auth { 65 req.AddTokenAuth(token) 66 } 67 resp = MakeRequest(t, req, http.StatusOK) 68 DecodeJSON(t, resp, &apiReleases) 69 assert.Len(t, apiReleases, expectedLength, msgAndArgs) 70 } 71 72 testFilterByLen(false, url.Values{"draft": {"true"}}, 0, "anon should not see drafts") 73 testFilterByLen(true, url.Values{"draft": {"true"}}, 1, "repo owner should see drafts") 74 testFilterByLen(true, url.Values{"draft": {"false"}}, 2, "exclude drafts") 75 testFilterByLen(true, url.Values{"draft": {"false"}, "pre-release": {"false"}}, 1, "exclude drafts and pre-releases") 76 testFilterByLen(true, url.Values{"pre-release": {"true"}}, 1, "only get pre-release") 77 testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft") 78 } 79 80 func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *user_model.User, repo *repo_model.Repository, name, target, title, desc string) *api.Release { 81 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner.Name, repo.Name) 82 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{ 83 TagName: name, 84 Title: title, 85 Note: desc, 86 IsDraft: false, 87 IsPrerelease: false, 88 Target: target, 89 }).AddTokenAuth(token) 90 resp := MakeRequest(t, req, http.StatusCreated) 91 92 var newRelease api.Release 93 DecodeJSON(t, resp, &newRelease) 94 rel := &repo_model.Release{ 95 ID: newRelease.ID, 96 TagName: newRelease.TagName, 97 Title: newRelease.Title, 98 } 99 unittest.AssertExistsAndLoadBean(t, rel) 100 assert.EqualValues(t, newRelease.Note, rel.Note) 101 102 return &newRelease 103 } 104 105 func TestAPICreateAndUpdateRelease(t *testing.T) { 106 defer tests.PrepareTestEnv(t)() 107 108 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 109 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 110 session := loginUser(t, owner.LowerName) 111 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 112 113 gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) 114 assert.NoError(t, err) 115 defer gitRepo.Close() 116 117 err = gitRepo.CreateTag("v0.0.1", "master") 118 assert.NoError(t, err) 119 120 target, err := gitRepo.GetTagCommitID("v0.0.1") 121 assert.NoError(t, err) 122 123 newRelease := createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", target, "v0.0.1", "test") 124 125 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d", owner.Name, repo.Name, newRelease.ID) 126 req := NewRequest(t, "GET", urlStr). 127 AddTokenAuth(token) 128 resp := MakeRequest(t, req, http.StatusOK) 129 130 var release api.Release 131 DecodeJSON(t, resp, &release) 132 133 assert.Equal(t, newRelease.TagName, release.TagName) 134 assert.Equal(t, newRelease.Title, release.Title) 135 assert.Equal(t, newRelease.Note, release.Note) 136 137 req = NewRequestWithJSON(t, "PATCH", urlStr, &api.EditReleaseOption{ 138 TagName: release.TagName, 139 Title: release.Title, 140 Note: "updated", 141 IsDraft: &release.IsDraft, 142 IsPrerelease: &release.IsPrerelease, 143 Target: release.Target, 144 }).AddTokenAuth(token) 145 resp = MakeRequest(t, req, http.StatusOK) 146 147 DecodeJSON(t, resp, &newRelease) 148 rel := &repo_model.Release{ 149 ID: newRelease.ID, 150 TagName: newRelease.TagName, 151 Title: newRelease.Title, 152 } 153 unittest.AssertExistsAndLoadBean(t, rel) 154 assert.EqualValues(t, rel.Note, newRelease.Note) 155 } 156 157 func TestAPICreateProtectedTagRelease(t *testing.T) { 158 defer tests.PrepareTestEnv(t)() 159 160 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) 161 writer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) 162 session := loginUser(t, writer.LowerName) 163 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 164 165 gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) 166 assert.NoError(t, err) 167 defer gitRepo.Close() 168 169 commit, err := gitRepo.GetBranchCommit("master") 170 assert.NoError(t, err) 171 172 req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/releases", repo.OwnerName, repo.Name), &api.CreateReleaseOption{ 173 TagName: "v0.0.1", 174 Title: "v0.0.1", 175 IsDraft: false, 176 IsPrerelease: false, 177 Target: commit.ID.String(), 178 }).AddTokenAuth(token) 179 MakeRequest(t, req, http.StatusUnprocessableEntity) 180 } 181 182 func TestAPICreateReleaseToDefaultBranch(t *testing.T) { 183 defer tests.PrepareTestEnv(t)() 184 185 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 186 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 187 session := loginUser(t, owner.LowerName) 188 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 189 190 createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") 191 } 192 193 func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { 194 defer tests.PrepareTestEnv(t)() 195 196 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 197 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 198 session := loginUser(t, owner.LowerName) 199 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 200 201 gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) 202 assert.NoError(t, err) 203 defer gitRepo.Close() 204 205 err = gitRepo.CreateTag("v0.0.1", "master") 206 assert.NoError(t, err) 207 208 createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") 209 } 210 211 func TestAPICreateReleaseGivenInvalidTarget(t *testing.T) { 212 defer tests.PrepareTestEnv(t)() 213 214 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 215 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 216 session := loginUser(t, owner.LowerName) 217 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 218 219 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner.Name, repo.Name) 220 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{ 221 TagName: "i-point-to-an-invalid-target", 222 Title: "Invalid Target", 223 Target: "invalid-target", 224 }).AddTokenAuth(token) 225 226 MakeRequest(t, req, http.StatusNotFound) 227 } 228 229 func TestAPIGetLatestRelease(t *testing.T) { 230 defer tests.PrepareTestEnv(t)() 231 232 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 233 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 234 235 req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/latest", owner.Name, repo.Name)) 236 resp := MakeRequest(t, req, http.StatusOK) 237 238 var release *api.Release 239 DecodeJSON(t, resp, &release) 240 241 assert.Equal(t, "testing-release", release.Title) 242 } 243 244 func TestAPIGetReleaseByTag(t *testing.T) { 245 defer tests.PrepareTestEnv(t)() 246 247 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 248 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 249 250 tag := "v1.1" 251 252 req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, tag)) 253 resp := MakeRequest(t, req, http.StatusOK) 254 255 var release *api.Release 256 DecodeJSON(t, resp, &release) 257 258 assert.Equal(t, "testing-release", release.Title) 259 260 nonexistingtag := "nonexistingtag" 261 262 req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, nonexistingtag)) 263 resp = MakeRequest(t, req, http.StatusNotFound) 264 265 var err *api.APIError 266 DecodeJSON(t, resp, &err) 267 assert.NotEmpty(t, err.Message) 268 } 269 270 func TestAPIDeleteReleaseByTagName(t *testing.T) { 271 defer tests.PrepareTestEnv(t)() 272 273 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 274 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 275 session := loginUser(t, owner.LowerName) 276 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 277 278 createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") 279 280 // delete release 281 req := NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag", owner.Name, repo.Name)). 282 AddTokenAuth(token) 283 _ = MakeRequest(t, req, http.StatusNoContent) 284 285 // make sure release is deleted 286 req = NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag", owner.Name, repo.Name)). 287 AddTokenAuth(token) 288 _ = MakeRequest(t, req, http.StatusNotFound) 289 290 // delete release tag too 291 req = NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag", owner.Name, repo.Name)). 292 AddTokenAuth(token) 293 _ = MakeRequest(t, req, http.StatusNoContent) 294 } 295 296 func TestAPIUploadAssetRelease(t *testing.T) { 297 defer tests.PrepareTestEnv(t)() 298 299 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 300 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 301 session := loginUser(t, owner.LowerName) 302 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 303 304 r := createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") 305 306 filename := "image.png" 307 buff := generateImg() 308 309 assetURL := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner.Name, repo.Name, r.ID) 310 311 t.Run("multipart/form-data", func(t *testing.T) { 312 defer tests.PrintCurrentTest(t)() 313 314 body := &bytes.Buffer{} 315 316 writer := multipart.NewWriter(body) 317 part, err := writer.CreateFormFile("attachment", filename) 318 assert.NoError(t, err) 319 _, err = io.Copy(part, bytes.NewReader(buff.Bytes())) 320 assert.NoError(t, err) 321 err = writer.Close() 322 assert.NoError(t, err) 323 324 req := NewRequestWithBody(t, http.MethodPost, assetURL, bytes.NewReader(body.Bytes())). 325 AddTokenAuth(token). 326 SetHeader("Content-Type", writer.FormDataContentType()) 327 resp := MakeRequest(t, req, http.StatusCreated) 328 329 var attachment *api.Attachment 330 DecodeJSON(t, resp, &attachment) 331 332 assert.EqualValues(t, filename, attachment.Name) 333 assert.EqualValues(t, 104, attachment.Size) 334 335 req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=test-asset", bytes.NewReader(body.Bytes())). 336 AddTokenAuth(token). 337 SetHeader("Content-Type", writer.FormDataContentType()) 338 resp = MakeRequest(t, req, http.StatusCreated) 339 340 var attachment2 *api.Attachment 341 DecodeJSON(t, resp, &attachment2) 342 343 assert.EqualValues(t, "test-asset", attachment2.Name) 344 assert.EqualValues(t, 104, attachment2.Size) 345 }) 346 347 t.Run("application/octet-stream", func(t *testing.T) { 348 defer tests.PrintCurrentTest(t)() 349 350 req := NewRequestWithBody(t, http.MethodPost, assetURL, bytes.NewReader(buff.Bytes())). 351 AddTokenAuth(token) 352 MakeRequest(t, req, http.StatusBadRequest) 353 354 req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=stream.bin", bytes.NewReader(buff.Bytes())). 355 AddTokenAuth(token) 356 resp := MakeRequest(t, req, http.StatusCreated) 357 358 var attachment *api.Attachment 359 DecodeJSON(t, resp, &attachment) 360 361 assert.EqualValues(t, "stream.bin", attachment.Name) 362 assert.EqualValues(t, 104, attachment.Size) 363 }) 364 }