code.gitea.io/gitea@v1.21.7/tests/integration/api_issue_test.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	auth_model "code.gitea.io/gitea/models/auth"
    16  	"code.gitea.io/gitea/models/db"
    17  	issues_model "code.gitea.io/gitea/models/issues"
    18  	repo_model "code.gitea.io/gitea/models/repo"
    19  	"code.gitea.io/gitea/models/unittest"
    20  	user_model "code.gitea.io/gitea/models/user"
    21  	"code.gitea.io/gitea/modules/setting"
    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 TestAPIListIssues(t *testing.T) {
    29  	defer tests.PrepareTestEnv(t)()
    30  
    31  	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
    32  	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
    33  
    34  	session := loginUser(t, owner.Name)
    35  	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
    36  	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name))
    37  
    38  	link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode()
    39  	resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
    40  	var apiIssues []*api.Issue
    41  	DecodeJSON(t, resp, &apiIssues)
    42  	assert.Len(t, apiIssues, unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}))
    43  	for _, apiIssue := range apiIssues {
    44  		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: apiIssue.ID, RepoID: repo.ID})
    45  	}
    46  
    47  	// test milestone filter
    48  	link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode()
    49  	resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
    50  	DecodeJSON(t, resp, &apiIssues)
    51  	if assert.Len(t, apiIssues, 2) {
    52  		assert.EqualValues(t, 3, apiIssues[0].Milestone.ID)
    53  		assert.EqualValues(t, 1, apiIssues[1].Milestone.ID)
    54  	}
    55  
    56  	link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode()
    57  	resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
    58  	DecodeJSON(t, resp, &apiIssues)
    59  	if assert.Len(t, apiIssues, 1) {
    60  		assert.EqualValues(t, 5, apiIssues[0].ID)
    61  	}
    62  
    63  	link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode()
    64  	resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
    65  	DecodeJSON(t, resp, &apiIssues)
    66  	if assert.Len(t, apiIssues, 1) {
    67  		assert.EqualValues(t, 1, apiIssues[0].ID)
    68  	}
    69  
    70  	link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode()
    71  	resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
    72  	DecodeJSON(t, resp, &apiIssues)
    73  	if assert.Len(t, apiIssues, 1) {
    74  		assert.EqualValues(t, 1, apiIssues[0].ID)
    75  	}
    76  }
    77  
    78  func TestAPICreateIssue(t *testing.T) {
    79  	defer tests.PrepareTestEnv(t)()
    80  	const body, title = "apiTestBody", "apiTestTitle"
    81  
    82  	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
    83  	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
    84  
    85  	session := loginUser(t, owner.Name)
    86  	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
    87  	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token)
    88  	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
    89  		Body:     body,
    90  		Title:    title,
    91  		Assignee: owner.Name,
    92  	})
    93  	resp := MakeRequest(t, req, http.StatusCreated)
    94  	var apiIssue api.Issue
    95  	DecodeJSON(t, resp, &apiIssue)
    96  	assert.Equal(t, body, apiIssue.Body)
    97  	assert.Equal(t, title, apiIssue.Title)
    98  
    99  	unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
   100  		RepoID:     repoBefore.ID,
   101  		AssigneeID: owner.ID,
   102  		Content:    body,
   103  		Title:      title,
   104  	})
   105  
   106  	repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
   107  	assert.Equal(t, repoBefore.NumIssues+1, repoAfter.NumIssues)
   108  	assert.Equal(t, repoBefore.NumClosedIssues, repoAfter.NumClosedIssues)
   109  }
   110  
   111  func TestAPICreateIssueParallel(t *testing.T) {
   112  	defer tests.PrepareTestEnv(t)()
   113  	const body, title = "apiTestBody", "apiTestTitle"
   114  
   115  	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
   116  	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
   117  
   118  	session := loginUser(t, owner.Name)
   119  	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
   120  	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token)
   121  
   122  	var wg sync.WaitGroup
   123  	for i := 0; i < 10; i++ {
   124  		wg.Add(1)
   125  		go func(parentT *testing.T, i int) {
   126  			parentT.Run(fmt.Sprintf("ParallelCreateIssue_%d", i), func(t *testing.T) {
   127  				newTitle := title + strconv.Itoa(i)
   128  				newBody := body + strconv.Itoa(i)
   129  				req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
   130  					Body:     newBody,
   131  					Title:    newTitle,
   132  					Assignee: owner.Name,
   133  				})
   134  				resp := MakeRequest(t, req, http.StatusCreated)
   135  				var apiIssue api.Issue
   136  				DecodeJSON(t, resp, &apiIssue)
   137  				assert.Equal(t, newBody, apiIssue.Body)
   138  				assert.Equal(t, newTitle, apiIssue.Title)
   139  
   140  				unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
   141  					RepoID:     repoBefore.ID,
   142  					AssigneeID: owner.ID,
   143  					Content:    newBody,
   144  					Title:      newTitle,
   145  				})
   146  
   147  				wg.Done()
   148  			})
   149  		}(t, i)
   150  	}
   151  	wg.Wait()
   152  }
   153  
   154  func TestAPIEditIssue(t *testing.T) {
   155  	defer tests.PrepareTestEnv(t)()
   156  
   157  	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
   158  	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
   159  	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
   160  	assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
   161  	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
   162  	assert.Equal(t, api.StateOpen, issueBefore.State())
   163  
   164  	session := loginUser(t, owner.Name)
   165  	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
   166  
   167  	// update values of issue
   168  	issueState := "closed"
   169  	removeDeadline := true
   170  	milestone := int64(4)
   171  	body := "new content!"
   172  	title := "new title from api set"
   173  
   174  	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
   175  	req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
   176  		State:          &issueState,
   177  		RemoveDeadline: &removeDeadline,
   178  		Milestone:      &milestone,
   179  		Body:           &body,
   180  		Title:          title,
   181  
   182  		// ToDo change more
   183  	})
   184  	resp := MakeRequest(t, req, http.StatusCreated)
   185  	var apiIssue api.Issue
   186  	DecodeJSON(t, resp, &apiIssue)
   187  
   188  	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
   189  	repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
   190  
   191  	// check deleted user
   192  	assert.Equal(t, int64(500), issueAfter.PosterID)
   193  	assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
   194  	assert.Equal(t, int64(-1), issueAfter.PosterID)
   195  	assert.Equal(t, int64(-1), issueBefore.PosterID)
   196  	assert.Equal(t, int64(-1), apiIssue.Poster.ID)
   197  
   198  	// check repo change
   199  	assert.Equal(t, repoBefore.NumClosedIssues+1, repoAfter.NumClosedIssues)
   200  
   201  	// API response
   202  	assert.Equal(t, api.StateClosed, apiIssue.State)
   203  	assert.Equal(t, milestone, apiIssue.Milestone.ID)
   204  	assert.Equal(t, body, apiIssue.Body)
   205  	assert.True(t, apiIssue.Deadline == nil)
   206  	assert.Equal(t, title, apiIssue.Title)
   207  
   208  	// in database
   209  	assert.Equal(t, api.StateClosed, issueAfter.State())
   210  	assert.Equal(t, milestone, issueAfter.MilestoneID)
   211  	assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
   212  	assert.Equal(t, body, issueAfter.Content)
   213  	assert.Equal(t, title, issueAfter.Title)
   214  }
   215  
   216  func TestAPISearchIssues(t *testing.T) {
   217  	defer tests.PrepareTestEnv(t)()
   218  
   219  	token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadIssue)
   220  
   221  	// as this API was used in the frontend, it uses UI page size
   222  	expectedIssueCount := 18 // from the fixtures
   223  	if expectedIssueCount > setting.UI.IssuePagingNum {
   224  		expectedIssueCount = setting.UI.IssuePagingNum
   225  	}
   226  
   227  	link, _ := url.Parse("/api/v1/repos/issues/search")
   228  	query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}}
   229  	var apiIssues []*api.Issue
   230  
   231  	link.RawQuery = query.Encode()
   232  	req := NewRequest(t, "GET", link.String())
   233  	resp := MakeRequest(t, req, http.StatusOK)
   234  	DecodeJSON(t, resp, &apiIssues)
   235  	assert.Len(t, apiIssues, expectedIssueCount)
   236  
   237  	since := "2000-01-01T00:50:01+00:00" // 946687801
   238  	before := time.Unix(999307200, 0).Format(time.RFC3339)
   239  	query.Add("since", since)
   240  	query.Add("before", before)
   241  	query.Add("token", token)
   242  	link.RawQuery = query.Encode()
   243  	req = NewRequest(t, "GET", link.String())
   244  	resp = MakeRequest(t, req, http.StatusOK)
   245  	DecodeJSON(t, resp, &apiIssues)
   246  	assert.Len(t, apiIssues, 11)
   247  	query.Del("since")
   248  	query.Del("before")
   249  
   250  	query.Add("state", "closed")
   251  	link.RawQuery = query.Encode()
   252  	req = NewRequest(t, "GET", link.String())
   253  	resp = MakeRequest(t, req, http.StatusOK)
   254  	DecodeJSON(t, resp, &apiIssues)
   255  	assert.Len(t, apiIssues, 2)
   256  
   257  	query.Set("state", "all")
   258  	link.RawQuery = query.Encode()
   259  	req = NewRequest(t, "GET", link.String())
   260  	resp = MakeRequest(t, req, http.StatusOK)
   261  	DecodeJSON(t, resp, &apiIssues)
   262  	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
   263  	assert.Len(t, apiIssues, 20)
   264  
   265  	query.Add("limit", "10")
   266  	link.RawQuery = query.Encode()
   267  	req = NewRequest(t, "GET", link.String())
   268  	resp = MakeRequest(t, req, http.StatusOK)
   269  	DecodeJSON(t, resp, &apiIssues)
   270  	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
   271  	assert.Len(t, apiIssues, 10)
   272  
   273  	query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}}
   274  	link.RawQuery = query.Encode()
   275  	req = NewRequest(t, "GET", link.String())
   276  	resp = MakeRequest(t, req, http.StatusOK)
   277  	DecodeJSON(t, resp, &apiIssues)
   278  	assert.Len(t, apiIssues, 2)
   279  
   280  	query = url.Values{"milestones": {"milestone1"}, "state": {"all"}, "token": {token}}
   281  	link.RawQuery = query.Encode()
   282  	req = NewRequest(t, "GET", link.String())
   283  	resp = MakeRequest(t, req, http.StatusOK)
   284  	DecodeJSON(t, resp, &apiIssues)
   285  	assert.Len(t, apiIssues, 1)
   286  
   287  	query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}, "token": {token}}
   288  	link.RawQuery = query.Encode()
   289  	req = NewRequest(t, "GET", link.String())
   290  	resp = MakeRequest(t, req, http.StatusOK)
   291  	DecodeJSON(t, resp, &apiIssues)
   292  	assert.Len(t, apiIssues, 2)
   293  
   294  	query = url.Values{"owner": {"user2"}, "token": {token}} // user
   295  	link.RawQuery = query.Encode()
   296  	req = NewRequest(t, "GET", link.String())
   297  	resp = MakeRequest(t, req, http.StatusOK)
   298  	DecodeJSON(t, resp, &apiIssues)
   299  	assert.Len(t, apiIssues, 8)
   300  
   301  	query = url.Values{"owner": {"org3"}, "token": {token}} // organization
   302  	link.RawQuery = query.Encode()
   303  	req = NewRequest(t, "GET", link.String())
   304  	resp = MakeRequest(t, req, http.StatusOK)
   305  	DecodeJSON(t, resp, &apiIssues)
   306  	assert.Len(t, apiIssues, 5)
   307  
   308  	query = url.Values{"owner": {"org3"}, "team": {"team1"}, "token": {token}} // organization + team
   309  	link.RawQuery = query.Encode()
   310  	req = NewRequest(t, "GET", link.String())
   311  	resp = MakeRequest(t, req, http.StatusOK)
   312  	DecodeJSON(t, resp, &apiIssues)
   313  	assert.Len(t, apiIssues, 2)
   314  }
   315  
   316  func TestAPISearchIssuesWithLabels(t *testing.T) {
   317  	defer tests.PrepareTestEnv(t)()
   318  
   319  	// as this API was used in the frontend, it uses UI page size
   320  	expectedIssueCount := 18 // from the fixtures
   321  	if expectedIssueCount > setting.UI.IssuePagingNum {
   322  		expectedIssueCount = setting.UI.IssuePagingNum
   323  	}
   324  
   325  	link, _ := url.Parse("/api/v1/repos/issues/search")
   326  	query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}}
   327  	var apiIssues []*api.Issue
   328  
   329  	link.RawQuery = query.Encode()
   330  	req := NewRequest(t, "GET", link.String())
   331  	resp := MakeRequest(t, req, http.StatusOK)
   332  	DecodeJSON(t, resp, &apiIssues)
   333  	assert.Len(t, apiIssues, expectedIssueCount)
   334  
   335  	query.Add("labels", "label1")
   336  	link.RawQuery = query.Encode()
   337  	req = NewRequest(t, "GET", link.String())
   338  	resp = MakeRequest(t, req, http.StatusOK)
   339  	DecodeJSON(t, resp, &apiIssues)
   340  	assert.Len(t, apiIssues, 2)
   341  
   342  	// multiple labels
   343  	query.Set("labels", "label1,label2")
   344  	link.RawQuery = query.Encode()
   345  	req = NewRequest(t, "GET", link.String())
   346  	resp = MakeRequest(t, req, http.StatusOK)
   347  	DecodeJSON(t, resp, &apiIssues)
   348  	assert.Len(t, apiIssues, 2)
   349  
   350  	// an org label
   351  	query.Set("labels", "orglabel4")
   352  	link.RawQuery = query.Encode()
   353  	req = NewRequest(t, "GET", link.String())
   354  	resp = MakeRequest(t, req, http.StatusOK)
   355  	DecodeJSON(t, resp, &apiIssues)
   356  	assert.Len(t, apiIssues, 1)
   357  
   358  	// org and repo label
   359  	query.Set("labels", "label2,orglabel4")
   360  	query.Add("state", "all")
   361  	link.RawQuery = query.Encode()
   362  	req = NewRequest(t, "GET", link.String())
   363  	resp = MakeRequest(t, req, http.StatusOK)
   364  	DecodeJSON(t, resp, &apiIssues)
   365  	assert.Len(t, apiIssues, 2)
   366  
   367  	// org and repo label which share the same issue
   368  	query.Set("labels", "label1,orglabel4")
   369  	link.RawQuery = query.Encode()
   370  	req = NewRequest(t, "GET", link.String())
   371  	resp = MakeRequest(t, req, http.StatusOK)
   372  	DecodeJSON(t, resp, &apiIssues)
   373  	assert.Len(t, apiIssues, 2)
   374  }