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  }