diff --git a/integrations/api_admin_org_test.go b/integrations/api_admin_org_test.go index 8f36850a1a144..7293e73d3e905 100644 --- a/integrations/api_admin_org_test.go +++ b/integrations/api_admin_org_test.go @@ -17,10 +17,13 @@ import ( "github.com/stretchr/testify/assert" ) +func getUserToken(t testing.TB, userName string) string { + return getTokenForLoggedInUser(t, loginUser(t, userName)) +} + func TestAPIAdminOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user1") org := api.CreateOrgOption{ UserName: "user2_org", @@ -31,7 +34,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { Visibility: "private", } req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var apiOrg api.Organization DecodeJSON(t, resp, &apiOrg) @@ -53,8 +56,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user1") org := api.CreateOrgOption{ UserName: "user2_org", @@ -65,15 +67,14 @@ func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { Visibility: "notvalid", } req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) }) } func TestAPIAdminOrgCreateNotAdmin(t *testing.T) { defer prepareTestEnv(t)() - nonAdminUsername := "user2" - session := loginUser(t, nonAdminUsername) - token := getTokenForLoggedInUser(t, session) + + token := getUserToken(t, "user2") org := api.CreateOrgOption{ UserName: "user2_org", FullName: "User2's organization", @@ -83,5 +84,5 @@ func TestAPIAdminOrgCreateNotAdmin(t *testing.T) { Visibility: "public", } req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go index b935d3eac5948..ab4aa06508129 100644 --- a/integrations/api_admin_test.go +++ b/integrations/api_admin_test.go @@ -20,17 +20,16 @@ import ( func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { defer prepareTestEnv(t)() + // user1 is an admin user - session := loginUser(t, "user1") + token := getUserToken(t, "user1") keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}).(*user_model.User) - - token := getTokenForLoggedInUser(t, session) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", "title": "test-key", }) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var newPublicKey api.PublicKey DecodeJSON(t, resp, &newPublicKey) @@ -44,53 +43,48 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s", keyOwner.Name, newPublicKey.ID, token) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) unittest.AssertNotExistsBean(t, &asymkey_model.PublicKey{ID: newPublicKey.ID}) } func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { defer prepareTestEnv(t)() // user1 is an admin user - session := loginUser(t, "user1") - - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user1") req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) - session.MakeRequest(t, req, http.StatusNotFound) + MakeRequest(t, req, http.StatusNotFound) } func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - session := loginUser(t, adminUsername) + token := getUserToken(t, adminUsername) - token := getTokenForLoggedInUser(t, session) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", "title": "test-key", }) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var newPublicKey api.PublicKey DecodeJSON(t, resp, &newPublicKey) - session = loginUser(t, normalUsername) - token = getTokenForLoggedInUser(t, session) + token = getUserToken(t, normalUsername) req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s", adminUsername, newPublicKey.ID, token) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } func TestAPISudoUser(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) req := NewRequest(t, "GET", urlStr) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var user api.User DecodeJSON(t, resp, &user) @@ -102,23 +96,21 @@ func TestAPISudoUserForbidden(t *testing.T) { adminUsername := "user1" normalUsername := "user2" - session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, normalUsername) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) req := NewRequest(t, "GET", urlStr) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } func TestAPIListUsers(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequest(t, "GET", urlStr) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var users []api.User DecodeJSON(t, resp, &users) @@ -142,17 +134,15 @@ func TestAPIListUsersNotLoggedIn(t *testing.T) { func TestAPIListUsersNonAdmin(t *testing.T) { defer prepareTestEnv(t)() nonAdminUsername := "user2" - session := loginUser(t, nonAdminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, nonAdminUsername) req := NewRequestf(t, "GET", "/api/v1/admin/users?token=%s", token) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } func TestAPICreateUserInvalidEmail(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "email": "invalid_email@domain.com\r\n", @@ -164,14 +154,13 @@ func TestAPICreateUserInvalidEmail(t *testing.T) { "source_id": "0", "username": "invalidUser", }) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) } func TestAPIEditUser(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -181,7 +170,7 @@ func TestAPIEditUser(t *testing.T) { // to change "full_name": "Full Name User 2", }) - session.MakeRequest(t, req, http.StatusOK) + MakeRequest(t, req, http.StatusOK) empty := "" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ @@ -189,7 +178,7 @@ func TestAPIEditUser(t *testing.T) { SourceID: 0, Email: &empty, }) - resp := session.MakeRequest(t, req, http.StatusUnprocessableEntity) + resp := MakeRequest(t, WithToken(req, token), http.StatusUnprocessableEntity) errMap := make(map[string]interface{}) json.Unmarshal(resp.Body.Bytes(), &errMap) @@ -205,7 +194,7 @@ func TestAPIEditUser(t *testing.T) { // to change Restricted: &bTrue, }) - session.MakeRequest(t, req, http.StatusOK) + MakeRequest(t, WithToken(req, token), http.StatusOK) user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}).(*user_model.User) assert.True(t, user2.IsRestricted) } diff --git a/integrations/api_branch_test.go b/integrations/api_branch_test.go index e137331343f34..86b134dd9f31b 100644 --- a/integrations/api_branch_test.go +++ b/integrations/api_branch_test.go @@ -15,10 +15,9 @@ import ( ) func testAPIGetBranch(t *testing.T, branchName string, exists bool) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) - resp := session.MakeRequest(t, req, NoExpectedStatus) + resp := MakeRequest(t, req, NoExpectedStatus) if !exists { assert.EqualValues(t, http.StatusNotFound, resp.Code) return @@ -32,10 +31,9 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { } func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) - resp := session.MakeRequest(t, req, expectedHTTPStatus) + resp := MakeRequest(t, req, expectedHTTPStatus) if resp.Code == http.StatusOK { var branchProtection api.BranchProtection @@ -45,12 +43,11 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta } func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ BranchName: branchName, }) - resp := session.MakeRequest(t, req, expectedHTTPStatus) + resp := MakeRequest(t, req, expectedHTTPStatus) if resp.Code == http.StatusCreated { var branchProtection api.BranchProtection @@ -60,10 +57,9 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP } func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) - resp := session.MakeRequest(t, req, expectedHTTPStatus) + resp := MakeRequest(t, req, expectedHTTPStatus) if resp.Code == http.StatusOK { var branchProtection api.BranchProtection @@ -73,17 +69,15 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran } func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) - session.MakeRequest(t, req, expectedHTTPStatus) + MakeRequest(t, req, expectedHTTPStatus) } func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) - session.MakeRequest(t, req, expectedHTTPStatus) + MakeRequest(t, req, expectedHTTPStatus) } func TestAPIGetBranch(t *testing.T) { @@ -149,18 +143,17 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { } for _, test := range tests { defer resetFixtures(t) - session := ctx.Session - testAPICreateBranch(t, session, "user2", "my-noo-repo", test.OldBranch, test.NewBranch, test.ExpectedHTTPStatus) + testAPICreateBranch(t, username, "my-noo-repo", test.OldBranch, test.NewBranch, test.ExpectedHTTPStatus) } } -func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { - token := getTokenForLoggedInUser(t, session) +func testAPICreateBranch(t testing.TB, user, repo, oldBranch, newBranch string, status int) bool { + token := getUserToken(t, user) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ BranchName: newBranch, OldBranchName: oldBranch, }) - resp := session.MakeRequest(t, req, status) + resp := MakeRequest(t, req, status) var branch api.Branch DecodeJSON(t, resp, &branch) diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go index 5da72b7fb15a6..b503f50df38fb 100644 --- a/integrations/api_helper_for_declarative_test.go +++ b/integrations/api_helper_for_declarative_test.go @@ -26,18 +26,14 @@ import ( type APITestContext struct { Reponame string - Session *TestSession Token string Username string ExpectedCode int } func NewAPITestContext(t *testing.T, username, reponame string) APITestContext { - session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) return APITestContext{ - Session: session, - Token: token, + Token: getUserToken(t, username), Username: username, Reponame: reponame, } @@ -47,6 +43,10 @@ func (ctx APITestContext) GitPath() string { return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame) } +func (ctx APITestContext) MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder { + return MakeRequest(t, WithToken(req, ctx.Token), expectedStatus) +} + func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) { return func(t *testing.T) { createRepoOption := &api.CreateRepoOption{ @@ -61,10 +61,10 @@ func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*tes } req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+ctx.Token, createRepoOption) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var repository api.Repository DecodeJSON(t, resp, &repository) @@ -76,12 +76,12 @@ func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*tes func doAPIEditRepository(ctx APITestContext, editRepoOption *api.EditRepoOption, callback ...func(*testing.T, api.Repository)) func(*testing.T) { return func(t *testing.T) { - req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), ctx.Token), editRepoOption) + req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), editRepoOption) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + ctx.MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + resp := ctx.MakeRequest(t, req, http.StatusOK) var repository api.Repository DecodeJSON(t, resp, &repository) @@ -103,24 +103,24 @@ func doAPIAddCollaborator(ctx APITestContext, username string, mode perm.AccessM addCollaboratorOption := &api.AddCollaboratorOption{ Permission: &permission, } - req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s?token=%s", ctx.Username, ctx.Reponame, username, ctx.Token), addCollaboratorOption) + req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s", ctx.Username, ctx.Reponame, username), addCollaboratorOption) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + ctx.MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusNoContent) + ctx.MakeRequest(t, req, http.StatusNoContent) } } func doAPIForkRepository(ctx APITestContext, username string, callback ...func(*testing.T, api.Repository)) func(*testing.T) { return func(t *testing.T) { createForkOption := &api.CreateForkOption{} - req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks?token=%s", username, ctx.Reponame, ctx.Token), createForkOption) + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", username, ctx.Reponame), createForkOption) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + ctx.MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusAccepted) + resp := ctx.MakeRequest(t, req, http.StatusAccepted) var repository api.Repository DecodeJSON(t, resp, &repository) if len(callback) > 0 { @@ -135,10 +135,10 @@ func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Rep req := NewRequest(t, "GET", urlStr) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var repository api.Repository DecodeJSON(t, resp, &repository) @@ -154,10 +154,10 @@ func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) { req := NewRequest(t, "DELETE", urlStr) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) } } @@ -172,10 +172,10 @@ func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback .. Key: string(dataPubKey), }) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var publicKey api.PublicKey DecodeJSON(t, resp, &publicKey) if len(callback) > 0 { @@ -190,10 +190,10 @@ func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) { req := NewRequest(t, "DELETE", urlStr) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) } } @@ -210,10 +210,10 @@ func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly }) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusCreated) + MakeRequest(t, req, http.StatusCreated) } } @@ -231,7 +231,7 @@ func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBra if ctx.ExpectedCode != 0 { expected = ctx.ExpectedCode } - resp := ctx.Session.MakeRequest(t, req, expected) + resp := ctx.MakeRequest(t, req, expected) decoder := json.NewDecoder(resp.Body) pr := api.PullRequest{} @@ -242,15 +242,14 @@ func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBra func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) { return func(t *testing.T) (api.PullRequest, error) { - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d?token=%s", - owner, repo, index, ctx.Token) + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index) req := NewRequest(t, http.MethodGet, urlStr) - expected := http.StatusOK + expected := 200 if ctx.ExpectedCode != 0 { expected = ctx.ExpectedCode } - resp := ctx.Session.MakeRequest(t, req, expected) + resp := ctx.MakeRequest(t, req, expected) decoder := json.NewDecoder(resp.Body) pr := api.PullRequest{} @@ -263,7 +262,6 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) return func(t *testing.T) { urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s", owner, repo, index, ctx.Token) - var req *http.Request var resp *httptest.ResponseRecorder @@ -273,7 +271,7 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) Do: string(repo_model.MergeStyleMerge), }) - resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus) + resp = MakeRequest(t, req, NoExpectedStatus) if resp.Code != http.StatusMethodNotAllowed { break @@ -299,18 +297,18 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func doAPIManuallyMergePullRequest(ctx APITestContext, owner, repo, commitID string, index int64) func(*testing.T) { return func(t *testing.T) { - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s", - owner, repo, index, ctx.Token) + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", + owner, repo, index) req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{ Do: string(repo_model.MergeStyleManuallyMerged), MergeCommitID: commitID, }) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + ctx.MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusOK) + ctx.MakeRequest(t, req, http.StatusOK) } } @@ -318,10 +316,10 @@ func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing return func(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s?token=%s", ctx.Username, ctx.Reponame, branch, ctx.Token) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var branch api.Branch DecodeJSON(t, resp, &branch) @@ -336,10 +334,10 @@ func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateFil url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", ctx.Username, ctx.Reponame, treepath, ctx.Token) req := NewRequestWithJSON(t, "POST", url, &options) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var contents api.FileResponse DecodeJSON(t, resp, &contents) @@ -355,10 +353,10 @@ func doAPICreateOrganization(ctx APITestContext, options *api.CreateOrgOption, c req := NewRequestWithJSON(t, "POST", url, &options) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var contents api.Organization DecodeJSON(t, resp, &contents) @@ -374,10 +372,10 @@ func doAPICreateOrganizationRepository(ctx APITestContext, orgName string, optio req := NewRequestWithJSON(t, "POST", url, &options) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var contents api.Repository DecodeJSON(t, resp, &contents) @@ -393,10 +391,10 @@ func doAPICreateOrganizationTeam(ctx APITestContext, orgName string, options *ap req := NewRequestWithJSON(t, "POST", url, &options) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var contents api.Team DecodeJSON(t, resp, &contents) @@ -412,10 +410,10 @@ func doAPIAddUserToOrganizationTeam(ctx APITestContext, teamID int64, username s req := NewRequest(t, "PUT", url) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) } } @@ -425,9 +423,9 @@ func doAPIAddRepoToOrganizationTeam(ctx APITestContext, teamID int64, orgName, r req := NewRequest(t, "PUT", url) if ctx.ExpectedCode != 0 { - ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) + MakeRequest(t, req, ctx.ExpectedCode) return } - ctx.Session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) } } diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go index 3957c102339d3..ced1e875f1d1c 100644 --- a/integrations/api_issue_test.go +++ b/integrations/api_issue_test.go @@ -26,12 +26,11 @@ func TestAPIListIssues(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, owner.Name) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() - resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, unittest.GetCount(t, &models.Issue{RepoID: repo.ID})) @@ -41,7 +40,7 @@ func TestAPIListIssues(t *testing.T) { // test milestone filter link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode() - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiIssues) if assert.Len(t, apiIssues, 2) { assert.EqualValues(t, 3, apiIssues[0].Milestone.ID) @@ -49,21 +48,21 @@ func TestAPIListIssues(t *testing.T) { } link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode() - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiIssues) if assert.Len(t, apiIssues, 1) { assert.EqualValues(t, 5, apiIssues[0].ID) } link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode() - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiIssues) if assert.Len(t, apiIssues, 1) { assert.EqualValues(t, 1, apiIssues[0].ID) } link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode() - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiIssues) if assert.Len(t, apiIssues, 1) { assert.EqualValues(t, 1, apiIssues[0].ID) @@ -77,15 +76,14 @@ func TestAPICreateIssue(t *testing.T) { repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, owner.Name) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ Body: body, Title: title, Assignee: owner.Name, }) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var apiIssue api.Issue DecodeJSON(t, resp, &apiIssue) assert.Equal(t, body, apiIssue.Body) @@ -113,8 +111,7 @@ func TestAPIEditIssue(t *testing.T) { assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) assert.Equal(t, api.StateOpen, issueBefore.State()) - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, owner.Name) // update values of issue issueState := "closed" @@ -133,7 +130,7 @@ func TestAPIEditIssue(t *testing.T) { // ToDo change more }) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var apiIssue api.Issue DecodeJSON(t, resp, &apiIssue) @@ -168,12 +165,11 @@ func TestAPIEditIssue(t *testing.T) { func TestAPISearchIssues(t *testing.T) { defer prepareTestEnv(t)() - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") link, _ := url.Parse("/api/v1/repos/issues/search") req := NewRequest(t, "GET", link.String()) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, WithToken(req, token), http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) @@ -181,7 +177,7 @@ func TestAPISearchIssues(t *testing.T) { query := url.Values{"token": {token}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) @@ -191,7 +187,7 @@ func TestAPISearchIssues(t *testing.T) { query.Add("before", before) link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 8) query.Del("since") @@ -200,14 +196,14 @@ func TestAPISearchIssues(t *testing.T) { query.Add("state", "closed") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) query.Set("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit @@ -215,49 +211,112 @@ func TestAPISearchIssues(t *testing.T) { query.Add("limit", "20") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 15) query = url.Values{"assigned": {"true"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) query = url.Values{"owner": {"user2"}} // user link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 6) query = url.Values{"owner": {"user3"}} // organization link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 3) query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) +} + +func TestUISearchIssuesWithLabels(t *testing.T) { + defer prepareTestEnv(t)() + + token := getUserToken(t, "user1") + + link, _ := url.Parse("/api/v1/repos/issues/search?token=" + token) + req := NewRequest(t, "GET", link.String()) + resp := MakeRequest(t, req, http.StatusOK) + var apiIssues []*api.Issue + DecodeJSON(t, resp, &apiIssues) + + assert.Len(t, apiIssues, 10) + + query := url.Values{ + "token": []string{token}, + } + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 10) + + query.Add("labels", "label1") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) + + // multiple labels + query.Set("labels", "label1,label2") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) + + // an org label + query.Set("labels", "orglabel4") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 1) + + // org and repo label + query.Set("labels", "label2,orglabel4") + query.Add("state", "all") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) + + // org and repo label which share the same issue + query.Set("labels", "label1,orglabel4") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } @@ -265,12 +324,11 @@ func TestAPISearchIssues(t *testing.T) { func TestAPISearchIssuesWithLabels(t *testing.T) { defer prepareTestEnv(t)() - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user1") link, _ := url.Parse("/api/v1/repos/issues/search") req := NewRequest(t, "GET", link.String()) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, WithToken(req, token), http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) @@ -280,14 +338,14 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Add("token", token) link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) query.Add("labels", "label1") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -295,7 +353,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,label2") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -303,7 +361,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) @@ -312,7 +370,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Add("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -320,7 +378,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } diff --git a/integrations/api_notification_test.go b/integrations/api_notification_test.go index 4bf18632ca07f..f0dc2ff75e8c6 100644 --- a/integrations/api_notification_test.go +++ b/integrations/api_notification_test.go @@ -25,14 +25,13 @@ func TestAPINotification(t *testing.T) { repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification) assert.NoError(t, thread5.LoadAttributes()) - session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, user2.Name) // -- GET /notifications -- // test filter since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?since=%s&token=%s", since, token)) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var apiNL []api.NotificationThread DecodeJSON(t, resp, &apiNL) @@ -43,7 +42,7 @@ func TestAPINotification(t *testing.T) { before := "2000-01-01T01%3A06%3A59%2B00%3A00" // 946688819 req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=%s&before=%s&token=%s", "true", before, token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiNL) assert.Len(t, apiNL, 3) @@ -59,7 +58,7 @@ func TestAPINotification(t *testing.T) { // -- GET /repos/{owner}/{repo}/notifications -- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&token=%s", user2.Name, repo1.Name, token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiNL) assert.Len(t, apiNL, 1) @@ -67,7 +66,7 @@ func TestAPINotification(t *testing.T) { // -- GET /repos/{owner}/{repo}/notifications -- multiple status-types req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&status-types=pinned&token=%s", user2.Name, repo1.Name, token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiNL) assert.Len(t, apiNL, 2) @@ -81,11 +80,11 @@ func TestAPINotification(t *testing.T) { // -- GET /notifications/threads/{id} -- // get forbidden req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", 1, token)) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) // get own req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) var apiN api.NotificationThread DecodeJSON(t, resp, &apiN) @@ -103,28 +102,28 @@ func TestAPINotification(t *testing.T) { // -- check notifications -- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/new?token=%s", token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &new) assert.True(t, new.New > 0) // -- mark notifications as read -- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiNL) assert.Len(t, apiNL, 2) lastReadAt := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 <- only Notification 4 is in this filter ... req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token)) - session.MakeRequest(t, req, http.StatusResetContent) + MakeRequest(t, req, http.StatusResetContent) req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiNL) assert.Len(t, apiNL, 1) // -- PATCH /notifications/threads/{id} -- req = NewRequest(t, "PATCH", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token)) - session.MakeRequest(t, req, http.StatusResetContent) + MakeRequest(t, req, http.StatusResetContent) assert.Equal(t, models.NotificationStatusUnread, thread5.Status) thread5 = unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification) @@ -132,7 +131,7 @@ func TestAPINotification(t *testing.T) { // -- check notifications -- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/new?token=%s", token)) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &new) assert.True(t, new.New == 0) } diff --git a/integrations/api_org_test.go b/integrations/api_org_test.go index e33c010e88c2a..22550dc96c0fc 100644 --- a/integrations/api_org_test.go +++ b/integrations/api_org_test.go @@ -32,7 +32,7 @@ func TestAPIOrgCreate(t *testing.T) { Visibility: "limited", } req := NewRequestWithJSON(t, "POST", "/api/v1/orgs?token="+token, &org) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var apiOrg api.Organization DecodeJSON(t, resp, &apiOrg) @@ -51,12 +51,12 @@ func TestAPIOrgCreate(t *testing.T) { }) req = NewRequestf(t, "GET", "/api/v1/orgs/%s", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &apiOrg) assert.EqualValues(t, org.UserName, apiOrg.UserName) req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) var repos []*api.Repository DecodeJSON(t, resp, &repos) @@ -65,7 +65,7 @@ func TestAPIOrgCreate(t *testing.T) { } req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, WithToken(req, token), http.StatusOK) // user1 on this org is public var users []*api.User @@ -88,7 +88,7 @@ func TestAPIOrgEdit(t *testing.T) { Visibility: "private", } req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/user3?token="+token, &org) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var apiOrg api.Organization DecodeJSON(t, resp, &apiOrg) diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 815b749110cee..470e1239b8f8e 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -25,12 +25,11 @@ func TestAPIListReleases(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) - session := loginUser(t, user2.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, user2.LowerName) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() - resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) var apiReleases []*api.Release DecodeJSON(t, resp, &apiReleases) if assert.Len(t, apiReleases, 3) { @@ -53,15 +52,20 @@ func TestAPIListReleases(t *testing.T) { // test filter testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { - link.RawQuery = query.Encode() - if auth { - query.Set("token", token) - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) - } else { - resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + comment := link.String() + if len(msgAndArgs) > 0 { + comment = msgAndArgs[0] } - DecodeJSON(t, resp, &apiReleases) - assert.Len(t, apiReleases, expectedLength, msgAndArgs) + t.Run(comment, func(t *testing.T) { + if auth { + query.Set("token", token) + } + link.RawQuery = query.Encode() + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + + DecodeJSON(t, resp, &apiReleases) + assert.Len(t, apiReleases, expectedLength, msgAndArgs) + }) } testFilterByLen(false, url.Values{"draft": {"true"}}, 0, "anon should not see drafts") diff --git a/integrations/api_repo_teams_test.go b/integrations/api_repo_teams_test.go index a3baeba63c804..34059877eba85 100644 --- a/integrations/api_repo_teams_test.go +++ b/integrations/api_repo_teams_test.go @@ -26,13 +26,12 @@ func TestAPIRepoTeams(t *testing.T) { publicOrgRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}).(*repo_model.Repository) // user4 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, user.Name) // ListTeams url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) req := NewRequest(t, "GET", url) - res := session.MakeRequest(t, req, http.StatusOK) + res := MakeRequest(t, req, http.StatusOK) var teams []*api.Team DecodeJSON(t, res, &teams) if assert.Len(t, teams, 2) { @@ -50,32 +49,31 @@ func TestAPIRepoTeams(t *testing.T) { // IsTeam url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "Test_Team", token) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) var team *api.Team DecodeJSON(t, res, &team) assert.EqualValues(t, teams[1], team) url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "NonExistingTeam", token) req = NewRequest(t, "GET", url) - session.MakeRequest(t, req, http.StatusNotFound) + MakeRequest(t, req, http.StatusNotFound) // AddTeam with user4 url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) req = NewRequest(t, "PUT", url) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) // AddTeam with user2 user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) - session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getUserToken(t, user.Name) url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) req = NewRequest(t, "PUT", url) - session.MakeRequest(t, req, http.StatusNoContent) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) // test duplicate request + MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusUnprocessableEntity) // test duplicate request // DeleteTeam url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) req = NewRequest(t, "DELETE", url) - session.MakeRequest(t, req, http.StatusNoContent) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) // test duplicate request + MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusUnprocessableEntity) // test duplicate request } diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index ce1ecb1d43d8a..9fd2d33f1e18f 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -398,7 +398,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { RepoOwnerID: userID, RepoName: httpContext.Reponame, }) - resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) + resp := MakeRequest(t, req, http.StatusConflict) respJSON := map[string]string{} DecodeJSON(t, resp, &respJSON) assert.Equal(t, "The repository with the same name already exists.", respJSON["message"]) @@ -453,7 +453,7 @@ func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { &api.CreateRepoOption{ Name: httpContext.Reponame, }) - resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) + resp := MakeRequest(t, req, http.StatusConflict) respJSON := map[string]string{} DecodeJSON(t, resp, &respJSON) assert.Equal(t, respJSON["message"], "The repository with the same name already exists.") diff --git a/integrations/api_repo_topic_test.go b/integrations/api_repo_topic_test.go index b7f9a5a5a6898..f0a0c4e73518c 100644 --- a/integrations/api_repo_topic_test.go +++ b/integrations/api_repo_topic_test.go @@ -59,36 +59,33 @@ func TestAPIRepoTopic(t *testing.T) { repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository) // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getUserToken(t, user2.Name) // Test read topics using login url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) req := NewRequest(t, "GET", url) - res := session.MakeRequest(t, req, http.StatusOK) + res := MakeRequest(t, WithToken(req, token2), http.StatusOK) var topics *api.TopicName DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"topicname1", "topicname2"}, topics.TopicNames) - // Log out user2 - session = emptyTestSession(t) url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user2.Name, repo2.Name, token2) // Test delete a topic req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test add an existing topic req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Golang", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test add a topic req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "topicName3", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test read topics using token req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"topicname2", "golang", "topicname3"}, topics.TopicNames) @@ -97,9 +94,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) @@ -108,9 +105,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) @@ -119,9 +116,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 25) @@ -130,29 +127,27 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) // Test add a topic when there is already maximum req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "t26", token2) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) // Test delete a topic that repo doesn't have req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) - session.MakeRequest(t, req, http.StatusNotFound) + MakeRequest(t, req, http.StatusNotFound) // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) + token4 := getUserToken(t, user4.Name) // Test read topics with write access url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.Empty(t, topics.TopicNames) // Test add a topic to repo with write access (requires repo admin access) req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user3.Name, repo3.Name, "topicName", token4) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go index a622c63145f70..8c30759159b2b 100644 --- a/integrations/api_team_test.go +++ b/integrations/api_team_test.go @@ -224,11 +224,9 @@ func TestAPITeamSearch(t *testing.T) { var results TeamSearchResults - session := loginUser(t, user.Name) - csrf := GetCSRF(t, session, "/"+org.Name) + token := getUserToken(t, user.Name) req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "_team") - req.Header.Add("X-Csrf-Token", csrf) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, WithToken(req, token), http.StatusOK) DecodeJSON(t, resp, &results) assert.NotEmpty(t, results.Data) assert.Len(t, results.Data, 1) @@ -236,9 +234,7 @@ func TestAPITeamSearch(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) - session = loginUser(t, user5.Name) - csrf = GetCSRF(t, session, "/"+org.Name) + token = getUserToken(t, user5.Name) req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "team") - req.Header.Add("X-Csrf-Token", csrf) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, WithToken(req, token), http.StatusForbidden) } diff --git a/integrations/api_ui_helper_for_declarative_test.go b/integrations/api_ui_helper_for_declarative_test.go new file mode 100644 index 0000000000000..c8d65eb70d739 --- /dev/null +++ b/integrations/api_ui_helper_for_declarative_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/queue" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/forms" + + "github.com/stretchr/testify/assert" +) + +type TestContext struct { + Reponame string + Session *TestSession + Username string + ExpectedCode int +} + +func NewTestContext(t *testing.T, username, reponame string) TestContext { + return TestContext{ + Session: loginUser(t, username), + Username: username, + Reponame: reponame, + } +} + +func (ctx TestContext) GitPath() string { + return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame) +} + +func (ctx TestContext) CreateAPITestContext(t *testing.T) APITestContext { + return NewAPITestContext(t, ctx.Username, ctx.Reponame) +} + +func doMergePullRequest(ctx TestContext, owner, repo string, index int64) func(*testing.T) { + return func(t *testing.T) { + urlStr := fmt.Sprintf("/api/ui/repos/%s/%s/pulls/%d/merge", + owner, repo, index) + req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{ + MergeMessageField: "doMergePullRequest Merge", + Do: string(repo_model.MergeStyleMerge), + }) + + resp := ctx.Session.MakeRequest(t, req, NoExpectedStatus) + + if resp.Code == http.StatusMethodNotAllowed { + err := api.APIError{} + DecodeJSON(t, resp, &err) + assert.EqualValues(t, "Please try again later", err.Message) + queue.GetManager().FlushAll(context.Background(), 5*time.Second) + req = NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{ + MergeMessageField: "doMergePullRequest Merge", + Do: string(repo_model.MergeStyleMerge), + }) + resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus) + } + + expected := ctx.ExpectedCode + if expected == 0 { + expected = 200 + } + + if !assert.EqualValues(t, expected, resp.Code, + "Request: %s %s", req.Method, req.URL.String()) { + logUnexpectedResponse(t, resp) + } + } +} diff --git a/integrations/api_ui_issue_stopwatch_test.go b/integrations/api_ui_issue_stopwatch_test.go new file mode 100644 index 0000000000000..7093e8cc3e310 --- /dev/null +++ b/integrations/api_ui_issue_stopwatch_test.go @@ -0,0 +1,41 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestUIAPIListStopWatches(t *testing.T) { + defer prepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) + + session := loginUser(t, owner.Name) + req := NewRequestf(t, "GET", "/api/ui/user/stopwatches") + resp := session.MakeRequest(t, req, http.StatusOK) + var apiWatches []*api.StopWatch + DecodeJSON(t, resp, &apiWatches) + stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch) + issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue) + if assert.Len(t, apiWatches, 1) { + assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) + assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) + assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle) + assert.EqualValues(t, repo.Name, apiWatches[0].RepoName) + assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName) + assert.Greater(t, int64(apiWatches[0].Seconds), int64(0)) + } +} diff --git a/integrations/api_ui_issue_test.go b/integrations/api_ui_issue_test.go new file mode 100644 index 0000000000000..9e99e64c88f48 --- /dev/null +++ b/integrations/api_ui_issue_test.go @@ -0,0 +1,112 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "net/url" + "testing" + "time" + + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestAPIUISearchIssues(t *testing.T) { + defer prepareTestEnv(t)() + + session := loginUser(t, "user2") + + link, _ := url.Parse("/api/ui/repos/issues/search") + req := NewRequest(t, "GET", link.String()) + resp := session.MakeRequest(t, req, http.StatusOK) + var apiIssues []*api.Issue + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 10) + + query := url.Values{} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 10) + + since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 + before := time.Unix(999307200, 0).Format(time.RFC3339) + query.Add("since", since) + query.Add("before", before) + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 8) + query.Del("since") + query.Del("before") + + query.Add("state", "closed") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) + + query.Set("state", "all") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) + assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit + + query.Add("limit", "20") + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 15) + + query = url.Values{"assigned": {"true"}, "state": {"all"}} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 1) + + query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 1) + + query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) + + query = url.Values{"owner": {"user2"}} // user + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 6) + + query = url.Values{"owner": {"user3"}} // organization + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 3) + + query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) +} diff --git a/integrations/api_ui_notification_test.go b/integrations/api_ui_notification_test.go new file mode 100644 index 0000000000000..56d12809f3b4b --- /dev/null +++ b/integrations/api_ui_notification_test.go @@ -0,0 +1,126 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "fmt" + "net/http" + "testing" + + "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestNotification(t *testing.T) { + defer prepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) + thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification) + assert.NoError(t, thread5.LoadAttributes()) + + token := getUserToken(t, user2.Name) + + // -- GET /notifications -- + // test filter + since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?since=%s&token=%s", since, token)) + resp := MakeRequest(t, req, http.StatusOK) + var apiNL []api.NotificationThread + DecodeJSON(t, resp, &apiNL) + + assert.Len(t, apiNL, 1) + assert.EqualValues(t, 5, apiNL[0].ID) + + // test filter + before := "2000-01-01T01%3A06%3A59%2B00%3A00" // 946688819 + + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=%s&before=%s&token=%s", "true", before, token)) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiNL) + + assert.Len(t, apiNL, 3) + assert.EqualValues(t, 4, apiNL[0].ID) + assert.True(t, apiNL[0].Unread) + assert.False(t, apiNL[0].Pinned) + assert.EqualValues(t, 3, apiNL[1].ID) + assert.False(t, apiNL[1].Unread) + assert.True(t, apiNL[1].Pinned) + assert.EqualValues(t, 2, apiNL[2].ID) + assert.False(t, apiNL[2].Unread) + assert.False(t, apiNL[2].Pinned) + + // -- GET /repos/{owner}/{repo}/notifications -- + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&token=%s", user2.Name, repo1.Name, token)) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiNL) + + assert.Len(t, apiNL, 1) + assert.EqualValues(t, 4, apiNL[0].ID) + + // -- GET /notifications/threads/{id} -- + // get forbidden + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", 1, token)) + MakeRequest(t, req, http.StatusForbidden) + + // get own + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token)) + resp = MakeRequest(t, req, http.StatusOK) + var apiN api.NotificationThread + DecodeJSON(t, resp, &apiN) + + assert.EqualValues(t, 5, apiN.ID) + assert.False(t, apiN.Pinned) + assert.True(t, apiN.Unread) + assert.EqualValues(t, "issue4", apiN.Subject.Title) + assert.EqualValues(t, "Issue", apiN.Subject.Type) + assert.EqualValues(t, thread5.Issue.APIURL(), apiN.Subject.URL) + assert.EqualValues(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL) + + new := struct { + New int64 `json:"new"` + }{} + + session := loginUser(t, user2.Name) + // -- check notifications -- + req = NewRequest(t, "GET", "/api/ui/notifications/new") + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &new) + assert.True(t, new.New > 0) + + // -- mark notifications as read -- + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token)) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiNL) + assert.Len(t, apiNL, 2) + + lastReadAt := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 <- only Notification 4 is in this filter ... + req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token)) + MakeRequest(t, req, http.StatusResetContent) + + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token)) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiNL) + assert.Len(t, apiNL, 1) + + // -- PATCH /notifications/threads/{id} -- + req = NewRequest(t, "PATCH", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token)) + MakeRequest(t, req, http.StatusResetContent) + + assert.Equal(t, models.NotificationStatusUnread, thread5.Status) + thread5 = unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification) + assert.Equal(t, models.NotificationStatusRead, thread5.Status) + + // -- check notifications -- + req = NewRequest(t, "GET", "/api/ui/notifications/new") + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &new) + assert.True(t, new.New == 0) +} diff --git a/integrations/api_ui_repo_test.go b/integrations/api_ui_repo_test.go new file mode 100644 index 0000000000000..f3cbb45e6ea4f --- /dev/null +++ b/integrations/api_ui_repo_test.go @@ -0,0 +1,207 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "fmt" + "net/http" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestAPIUISearchRepo(t *testing.T) { + defer prepareTestEnv(t)() + const keyword = "test" + + req := NewRequestf(t, "GET", "/api/ui/repos/search?q=%s", keyword) + resp := MakeRequest(t, req, http.StatusOK) + + var body api.SearchResults + DecodeJSON(t, resp, &body) + assert.NotEmpty(t, body.Data) + for _, repo := range body.Data { + assert.Contains(t, repo.Name, keyword) + assert.False(t, repo.Private) + } + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}).(*user_model.User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18}).(*user_model.User) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}).(*user_model.User) + orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}).(*user_model.User) + + oldAPIDefaultNum := setting.API.DefaultPagingNum + defer func() { + setting.API.DefaultPagingNum = oldAPIDefaultNum + }() + setting.API.DefaultPagingNum = 10 + + // Map of expected results, where key is user for login + type expectedResults map[*user_model.User]struct { + count int + repoOwnerID int64 + repoName string + includesPrivate bool + } + + testCases := []struct { + name, requestURL string + expectedResults + }{ + { + name: "RepositoriesMax50", requestURL: "/api/ui/repos/search?limit=50&private=false", expectedResults: expectedResults{ + nil: {count: 30}, + user: {count: 30}, + user2: {count: 30}, + }, + }, + { + name: "RepositoriesMax10", requestURL: "/api/ui/repos/search?limit=10&private=false", expectedResults: expectedResults{ + nil: {count: 10}, + user: {count: 10}, + user2: {count: 10}, + }, + }, + { + name: "RepositoriesDefault", requestURL: "/api/ui/repos/search?default&private=false", expectedResults: expectedResults{ + nil: {count: 10}, + user: {count: 10}, + user2: {count: 10}, + }, + }, + { + name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/ui/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{ + nil: {count: 7, repoName: "big_test_"}, + user: {count: 7, repoName: "big_test_"}, + user2: {count: 7, repoName: "big_test_"}, + }, + }, + { + name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d", user.ID), expectedResults: expectedResults{ + nil: {count: 5}, + user: {count: 9, includesPrivate: true}, + user2: {count: 6, includesPrivate: true}, + }, + }, + { + name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{ + nil: {count: 1}, + user: {count: 2, includesPrivate: true}, + user2: {count: 2, includesPrivate: true}, + user4: {count: 1}, + }, + }, + { + name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{ + nil: {count: 1}, + user: {count: 4, includesPrivate: true}, + user2: {count: 3, includesPrivate: true}, + user3: {count: 4, includesPrivate: true}, + }, + }, + { + name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{ + nil: {count: 1, repoOwnerID: orgUser.ID}, + user: {count: 2, repoOwnerID: orgUser.ID, includesPrivate: true}, + user2: {count: 1, repoOwnerID: orgUser.ID}, + }, + }, + {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{ + nil: {count: 3}, + user: {count: 4, includesPrivate: true}, + user4: {count: 7, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{ + nil: {count: 0}, + user: {count: 1, includesPrivate: true}, + user4: {count: 1, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{ + nil: {count: 1}, + user: {count: 1}, + user4: {count: 2, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "fork"), expectedResults: expectedResults{ + nil: {count: 1}, + user: {count: 1}, + user4: {count: 2, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s", user4.ID, "mirror"), expectedResults: expectedResults{ + nil: {count: 2}, + user: {count: 2}, + user4: {count: 4, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "mirror"), expectedResults: expectedResults{ + nil: {count: 1}, + user: {count: 1}, + user4: {count: 2, includesPrivate: true}, + }}, + {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/ui/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{ + nil: {count: 0}, + user: {count: 1, includesPrivate: true}, + user4: {count: 1, includesPrivate: true}, + }}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + for userToLogin, expected := range testCase.expectedResults { + var session *TestSession + var testName string + var userID int64 + if userToLogin != nil && userToLogin.ID > 0 { + testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) + session = loginUser(t, userToLogin.Name) + userID = userToLogin.ID + } else { + testName = "AnonymousUser" + session = emptyTestSession(t) + } + + t.Run(testName, func(t *testing.T) { + request := NewRequest(t, "GET", testCase.requestURL) + response := session.MakeRequest(t, request, http.StatusOK) + + var body api.SearchResults + DecodeJSON(t, response, &body) + + repoNames := make([]string, 0, len(body.Data)) + for _, repo := range body.Data { + repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private)) + } + assert.Len(t, repoNames, expected.count) + for _, repo := range body.Data { + r := getRepo(t, repo.ID) + hasAccess, err := models.HasAccess(userID, r) + assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err) + assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName) + + assert.NotEmpty(t, repo.Name) + assert.Equal(t, repo.Name, r.Name) + + if len(expected.repoName) > 0 { + assert.Contains(t, repo.Name, expected.repoName) + } + + if expected.repoOwnerID > 0 { + assert.Equal(t, expected.repoOwnerID, repo.Owner.ID) + } + + if !expected.includesPrivate { + assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName) + } + } + }) + } + }) + } +} diff --git a/integrations/api_ui_repo_topic_test.go b/integrations/api_ui_repo_topic_test.go new file mode 100644 index 0000000000000..1190596bb639f --- /dev/null +++ b/integrations/api_ui_repo_topic_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "net/url" + "testing" + + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestAPIUITopicSearch(t *testing.T) { + defer prepareTestEnv(t)() + searchURL, _ := url.Parse("/api/ui/topics/search") + var topics struct { + TopicNames []*api.TopicResponse `json:"topics"` + } + + query := url.Values{"page": []string{"1"}, "limit": []string{"4"}} + + searchURL.RawQuery = query.Encode() + res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) + DecodeJSON(t, res, &topics) + assert.Len(t, topics.TopicNames, 4) + assert.EqualValues(t, "6", res.Header().Get("x-total-count")) + + query.Add("q", "topic") + searchURL.RawQuery = query.Encode() + res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) + DecodeJSON(t, res, &topics) + assert.Len(t, topics.TopicNames, 2) + + query.Set("q", "database") + searchURL.RawQuery = query.Encode() + res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) + DecodeJSON(t, res, &topics) + if assert.Len(t, topics.TopicNames, 1) { + assert.EqualValues(t, 2, topics.TopicNames[0].ID) + assert.EqualValues(t, "database", topics.TopicNames[0].Name) + assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount) + } +} diff --git a/integrations/api_ui_user_search_test.go b/integrations/api_ui_user_search_test.go new file mode 100644 index 0000000000000..511f3bfb7c448 --- /dev/null +++ b/integrations/api_ui_user_search_test.go @@ -0,0 +1,87 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file.package models + +package integrations + +import ( + "fmt" + "net/http" + "testing" + + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" +) + +func TestAPIUIUserSearchLoggedIn(t *testing.T) { + defer prepareTestEnv(t)() + adminUsername := "user1" + session := loginUser(t, adminUsername) + token := getTokenForLoggedInUser(t, session) + query := "user2" + req := NewRequestf(t, "GET", "/api/ui/users/search?token=%s&q=%s", token, query) + resp := session.MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.NotEmpty(t, results.Data) + for _, user := range results.Data { + assert.Contains(t, user.UserName, query) + assert.NotEmpty(t, user.Email) + } +} + +func TestAPIUIUserSearchNotLoggedIn(t *testing.T) { + defer prepareTestEnv(t)() + query := "user2" + req := NewRequestf(t, "GET", "/api/ui/users/search?q=%s", query) + resp := MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.NotEmpty(t, results.Data) + var modelUser *user_model.User + for _, user := range results.Data { + assert.Contains(t, user.UserName, query) + modelUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID}).(*user_model.User) + if modelUser.KeepEmailPrivate { + assert.EqualValues(t, fmt.Sprintf("%s@%s", modelUser.LowerName, setting.Service.NoReplyAddress), user.Email) + } else { + assert.EqualValues(t, modelUser.Email, user.Email) + } + } +} + +func TestAPIUIUserSearchAdminLoggedInUserHidden(t *testing.T) { + defer prepareTestEnv(t)() + adminUsername := "user1" + session := loginUser(t, adminUsername) + token := getTokenForLoggedInUser(t, session) + query := "user31" + req := NewRequestf(t, "GET", "/api/ui/users/search?token=%s&q=%s", token, query) + req.SetBasicAuth(token, "x-oauth-basic") + resp := session.MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.NotEmpty(t, results.Data) + for _, user := range results.Data { + assert.Contains(t, user.UserName, query) + assert.NotEmpty(t, user.Email) + assert.EqualValues(t, "private", user.Visibility) + } +} + +func TestAPIUIUserSearchNotLoggedInUserHidden(t *testing.T) { + defer prepareTestEnv(t)() + query := "user31" + req := NewRequestf(t, "GET", "/api/ui/users/search?q=%s", query) + resp := MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.Empty(t, results.Data) +} diff --git a/integrations/api_user_heatmap_test.go b/integrations/api_user_heatmap_test.go index 69f4ff2249bff..42560f5de6375 100644 --- a/integrations/api_user_heatmap_test.go +++ b/integrations/api_user_heatmap_test.go @@ -20,7 +20,7 @@ func TestUserHeatmap(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - session := loginUser(t, adminUsername) + token := getUserToken(t, adminUsername) fakeNow := time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local) timeutil.Set(fakeNow) @@ -28,7 +28,7 @@ func TestUserHeatmap(t *testing.T) { urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername) req := NewRequest(t, "GET", urlStr) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, WithToken(req, token), http.StatusOK) var heatmap []*models.UserHeatmapData DecodeJSON(t, resp, &heatmap) var dummyheatmap []*models.UserHeatmapData diff --git a/integrations/api_user_search_test.go b/integrations/api_user_search_test.go index 41f14cf944f33..959c26358c477 100644 --- a/integrations/api_user_search_test.go +++ b/integrations/api_user_search_test.go @@ -25,11 +25,10 @@ type SearchResults struct { func TestAPIUserSearchLoggedIn(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) query := "user2" req := NewRequestf(t, "GET", "/api/v1/users/search?token=%s&q=%s", token, query) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var results SearchResults DecodeJSON(t, resp, &results) @@ -64,12 +63,11 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) { func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" - session := loginUser(t, adminUsername) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, adminUsername) query := "user31" req := NewRequestf(t, "GET", "/api/v1/users/search?token=%s&q=%s", token, query) req.SetBasicAuth(token, "x-oauth-basic") - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var results SearchResults DecodeJSON(t, resp, &results) diff --git a/integrations/benchmarks_test.go b/integrations/benchmarks_test.go index ffae471307d21..a03b09878d2d3 100644 --- a/integrations/benchmarks_test.go +++ b/integrations/benchmarks_test.go @@ -45,7 +45,7 @@ func BenchmarkRepoBranchCommit(b *testing.B) { for i := 0; i < b.N; i++ { b.Run("new_"+branchName, func(b *testing.B) { b.Skip("benchmark broken") // TODO fix - testAPICreateBranch(b, session, repo.OwnerName, repo.Name, repo.DefaultBranch, "new_"+branchName, http.StatusCreated) + testAPICreateBranch(b, repo.OwnerName, repo.Name, repo.DefaultBranch, "new_"+branchName, http.StatusCreated) }) } }) diff --git a/integrations/git_test.go b/integrations/git_test.go index 675b1879fafc9..42fab9e2fb129 100644 --- a/integrations/git_test.go +++ b/integrations/git_test.go @@ -42,16 +42,16 @@ func TestGit(t *testing.T) { func testGit(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseTestContext := NewTestContext(t, username, "repo1") - u.Path = baseAPITestContext.GitPath() + u.Path = baseTestContext.GitPath() - forkedUserCtx := NewAPITestContext(t, "user4", "repo1") + forkedUserCtx := NewTestContext(t, "user4", "repo1") t.Run("HTTP", func(t *testing.T) { defer PrintCurrentTest(t)() ensureAnonymousClone(t, u) - httpContext := baseAPITestContext + httpContext := baseTestContext httpContext.Reponame = "repo-tmp-17" forkedUserCtx.Reponame = httpContext.Reponame @@ -59,10 +59,12 @@ func testGit(t *testing.T, u *url.URL) { assert.NoError(t, err) defer util.RemoveAll(dstPath) - t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false)) - t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, perm.AccessModeRead)) + apiForkedUserCtx := forkedUserCtx.CreateAPITestContext(t) - t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username)) + t.Run("CreateRepoInDifferentUser", doAPICreateRepository(apiForkedUserCtx, false)) + t.Run("AddUserAsCollaborator", doAPIAddCollaborator(apiForkedUserCtx, httpContext.Username, perm.AccessModeRead)) + + t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext.CreateAPITestContext(t), forkedUserCtx.Username)) u.Path = httpContext.GitPath() u.User = url.UserPassword(username, userPassword) @@ -94,17 +96,19 @@ func testGit(t *testing.T, u *url.URL) { }) t.Run("SSH", func(t *testing.T) { defer PrintCurrentTest(t)() - sshContext := baseAPITestContext + sshContext := baseTestContext sshContext.Reponame = "repo-tmp-18" keyname := "my-testing-key" forkedUserCtx.Reponame = sshContext.Reponame - t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false)) - t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, perm.AccessModeRead)) - t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username)) + + apiForkedUserCtx := forkedUserCtx.CreateAPITestContext(t) + t.Run("CreateRepoInDifferentUser", doAPICreateRepository(apiForkedUserCtx, false)) + t.Run("AddUserAsCollaborator", doAPIAddCollaborator(apiForkedUserCtx, sshContext.Username, perm.AccessModeRead)) + t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext.CreateAPITestContext(t), forkedUserCtx.Username)) // Setup key the user ssh key withKeyFile(t, keyname, func(keyFile string) { - t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile)) + t.Run("CreateUserKey", doAPICreateUserKey(sshContext.CreateAPITestContext(t), "test-key", keyFile)) // Setup remote link // TODO: get url from api @@ -211,7 +215,7 @@ func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string return } -func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { +func rawTest(t *testing.T, ctx *TestContext, little, big, littleLFS, bigLFS string) { t.Run("Raw", func(t *testing.T) { defer PrintCurrentTest(t)() username := ctx.Username @@ -252,7 +256,7 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s }) } -func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { +func mediaTest(t *testing.T, ctx *TestContext, little, big, littleLFS, bigLFS string) { t.Run("Media", func(t *testing.T) { defer PrintCurrentTest(t)() @@ -367,13 +371,14 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin return filepath.Base(tmpFile.Name()), err } -func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { +func doBranchProtectPRMerge(baseCtx *TestContext, dstPath string) func(t *testing.T) { return func(t *testing.T) { defer PrintCurrentTest(t)() t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) - ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) + ctx := NewTestContext(t, baseCtx.Username, baseCtx.Reponame) + apiCtx := ctx.CreateAPITestContext(t) t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) t.Run("GenerateCommit", func(t *testing.T) { _, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") @@ -384,7 +389,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes var pr api.PullRequest var err error t.Run("CreatePullRequest", func(t *testing.T) { - pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t) + pr, err = doAPICreatePullRequest(apiCtx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t) assert.NoError(t, err) }) t.Run("GenerateCommit", func(t *testing.T) { @@ -394,11 +399,11 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected-2")) var pr2 api.PullRequest t.Run("CreatePullRequest", func(t *testing.T) { - pr2, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "unprotected", "unprotected-2")(t) + pr2, err = doAPICreatePullRequest(apiCtx, baseCtx.Username, baseCtx.Reponame, "unprotected", "unprotected-2")(t) assert.NoError(t, err) }) - t.Run("MergePR2", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr2.Index)) - t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)) + t.Run("MergePR2", doMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr2.Index)) + t.Run("MergePR", doMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)) t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) t.Run("ProtectProtectedBranchUnprotectedFilePaths", doProtectBranch(ctx, "protected", "", "unprotected-file-*")) @@ -423,7 +428,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes } } -func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFilePatterns string) func(t *testing.T) { +func doProtectBranch(ctx TestContext, branch, userToWhitelist, unprotectedFilePatterns string) func(t *testing.T) { // We are going to just use the owner to set the protection. return func(t *testing.T) { csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) @@ -457,15 +462,17 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil } } -func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) func(t *testing.T) { +func doMergeFork(ctx, baseCtx TestContext, baseBranch, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer PrintCurrentTest(t)() var pr api.PullRequest var err error + apiCtx := ctx.CreateAPITestContext(t) + // Create a test pullrequest t.Run("CreatePullRequest", func(t *testing.T) { - pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) + pr, err = doAPICreatePullRequest(apiCtx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) assert.NoError(t, err) }) @@ -483,11 +490,11 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun }) // Now: Merge the PR & make sure that doesn't break the PR page or change its diff - t.Run("MergePR", doAPIMergePullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index)) + t.Run("MergePR", doMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)) t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr)) t.Run("CheckPR", func(t *testing.T) { oldMergeBase := pr.MergeBase - pr2, err := doAPIGetPullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) + pr2, err := doAPIGetPullRequest(baseCtx.CreateAPITestContext(t), baseCtx.Username, baseCtx.Reponame, pr.Index)(t) assert.NoError(t, err) assert.Equal(t, oldMergeBase, pr2.MergeBase) }) @@ -499,13 +506,13 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength)) // Delete the head repository & make sure that doesn't break the PR page or change its diff - t.Run("DeleteHeadRepository", doAPIDeleteRepository(ctx)) + t.Run("DeleteHeadRepository", doAPIDeleteRepository(ctx.CreateAPITestContext(t))) t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr)) t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength)) } } -func doCreatePRAndSetManuallyMerged(ctx, baseCtx APITestContext, dstPath, baseBranch, headBranch string) func(t *testing.T) { +func doCreatePRAndSetManuallyMerged(ctx, baseCtx TestContext, dstPath, baseBranch, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer PrintCurrentTest(t)() var ( @@ -517,7 +524,7 @@ func doCreatePRAndSetManuallyMerged(ctx, baseCtx APITestContext, dstPath, baseBr trueBool := true falseBool := false - t.Run("AllowSetManuallyMergedAndSwitchOffAutodetectManualMerge", doAPIEditRepository(baseCtx, &api.EditRepoOption{ + t.Run("AllowSetManuallyMergedAndSwitchOffAutodetectManualMerge", doAPIEditRepository(baseCtx.CreateAPITestContext(t), &api.EditRepoOption{ HasPullRequests: &trueBool, AllowManualMerge: &trueBool, AutodetectManualMerge: &falseBool, @@ -526,15 +533,16 @@ func doCreatePRAndSetManuallyMerged(ctx, baseCtx APITestContext, dstPath, baseBr t.Run("CreateHeadBranch", doGitCreateBranch(dstPath, headBranch)) t.Run("PushToHeadBranch", doGitPushTestRepository(dstPath, "origin", headBranch)) t.Run("CreateEmptyPullRequest", func(t *testing.T) { - pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) + apiCtx := ctx.CreateAPITestContext(t) + pr, err = doAPICreatePullRequest(apiCtx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) assert.NoError(t, err) }) lastCommitID = pr.Base.Sha - t.Run("ManuallyMergePR", doAPIManuallyMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, lastCommitID, pr.Index)) + t.Run("ManuallyMergePR", doAPIManuallyMergePullRequest(ctx.CreateAPITestContext(t), baseCtx.Username, baseCtx.Reponame, lastCommitID, pr.Index)) } } -func doEnsureCanSeePull(ctx APITestContext, pr api.PullRequest) func(t *testing.T) { +func doEnsureCanSeePull(ctx TestContext, pr api.PullRequest) func(t *testing.T) { return func(t *testing.T) { req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), pr.Index)) ctx.Session.MakeRequest(t, req, http.StatusOK) @@ -545,7 +553,7 @@ func doEnsureCanSeePull(ctx APITestContext, pr api.PullRequest) func(t *testing. } } -func doEnsureDiffNoChange(ctx APITestContext, pr api.PullRequest, diffHash string, diffLength int) func(t *testing.T) { +func doEnsureDiffNoChange(ctx TestContext, pr api.PullRequest, diffHash string, diffLength int) func(t *testing.T) { return func(t *testing.T) { req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d.diff", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), pr.Index)) resp := ctx.Session.MakeRequestNilResponseHashSumRecorder(t, req, http.StatusOK) @@ -557,7 +565,7 @@ func doEnsureDiffNoChange(ctx APITestContext, pr api.PullRequest, diffHash strin } } -func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) { +func doPushCreate(ctx TestContext, u *url.URL) func(t *testing.T) { return func(t *testing.T) { defer PrintCurrentTest(t)() @@ -604,7 +612,7 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) { } } -func doBranchDelete(ctx APITestContext, owner, repo, branch string) func(*testing.T) { +func doBranchDelete(ctx TestContext, owner, repo, branch string) func(*testing.T) { return func(t *testing.T) { csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/branches", url.PathEscape(owner), url.PathEscape(repo))) @@ -615,7 +623,7 @@ func doBranchDelete(ctx APITestContext, owner, repo, branch string) func(*testin } } -func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headBranch string) func(t *testing.T) { +func doCreateAgitFlowPull(dstPath string, ctx *TestContext, baseBranch, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer PrintCurrentTest(t)() @@ -683,7 +691,8 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB if !assert.NotEmpty(t, pr1) { return } - prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) + apiCtx := ctx.CreateAPITestContext(t) + prMsg, err := doAPIGetPullRequest(apiCtx, ctx.Username, ctx.Reponame, pr1.Index)(t) if !assert.NoError(t, err) { return } @@ -705,7 +714,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB if !assert.NotEmpty(t, pr2) { return } - prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) + prMsg, err = doAPIGetPullRequest(apiCtx, ctx.Username, ctx.Reponame, pr2.Index)(t) if !assert.NoError(t, err) { return } @@ -750,7 +759,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB return } unittest.AssertCount(t, &models.PullRequest{}, pullNum+2) - prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) + prMsg, err := doAPIGetPullRequest(ctx.CreateAPITestContext(t), ctx.Username, ctx.Reponame, pr1.Index)(t) if !assert.NoError(t, err) { return } @@ -762,14 +771,14 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB return } unittest.AssertCount(t, &models.PullRequest{}, pullNum+2) - prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) + prMsg, err = doAPIGetPullRequest(ctx.CreateAPITestContext(t), ctx.Username, ctx.Reponame, pr2.Index)(t) if !assert.NoError(t, err) { return } assert.Equal(t, false, prMsg.HasMerged) assert.Equal(t, commit, prMsg.Head.Sha) }) - t.Run("Merge", doAPIMergePullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)) + t.Run("Merge", doMergePullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)) t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master")) } } diff --git a/integrations/integration_test.go b/integrations/integration_test.go index c778fb8013459..f59885b5ff4e2 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -458,6 +458,11 @@ func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *ht return request } +func WithToken(req *http.Request, token string) *http.Request { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) + return req +} + func AddBasicAuthHeader(request *http.Request, username string) *http.Request { request.SetBasicAuth(username, userPassword) return request diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go index c5f45643ec290..fd75612db4ff2 100644 --- a/integrations/mirror_push_test.go +++ b/integrations/mirror_push_test.go @@ -43,7 +43,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { }) assert.NoError(t, err) - ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name) + ctx := NewTestContext(t, user.LowerName, srcRepo.Name) doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword)(t) @@ -77,7 +77,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { assert.Len(t, mirrors, 0) } -func doCreatePushMirror(ctx APITestContext, address, username, password string) func(t *testing.T) { +func doCreatePushMirror(ctx TestContext, address, username, password string) func(t *testing.T) { return func(t *testing.T) { csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) @@ -97,7 +97,7 @@ func doCreatePushMirror(ctx APITestContext, address, username, password string) } } -func doRemovePushMirror(ctx APITestContext, address, username, password string, pushMirrorID int) func(t *testing.T) { +func doRemovePushMirror(ctx TestContext, address, username, password string, pushMirrorID int) func(t *testing.T) { return func(t *testing.T) { csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) diff --git a/modules/context/api.go b/modules/context/api.go index e847ca35fac9b..13756001bf43e 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -8,7 +8,6 @@ package context import ( "context" "fmt" - "html" "net/http" "net/url" "strings" @@ -20,8 +19,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" auth_service "code.gitea.io/gitea/services/auth" - - "gitea.com/go-chi/session" ) // APIContext is a specific context for API service @@ -269,17 +266,13 @@ func APIAuth(authMethod auth_service.Method) func(*APIContext) { // APIContexter returns apicontext as middleware func APIContexter() func(http.Handler) http.Handler { - csrfOpts := getCsrfOpts() - return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - locale := middleware.Locale(w, req) ctx := APIContext{ Context: &Context{ - Resp: NewResponse(w), - Data: map[string]interface{}{}, - Locale: locale, - Session: session.GetSession(req), + Resp: NewResponse(w), + Data: map[string]interface{}{}, + Locale: middleware.Locale(w, req), Repo: &Repository{ PullRequest: &PullRequest{}, }, @@ -289,7 +282,6 @@ func APIContexter() func(http.Handler) http.Handler { } ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) - ctx.csrf = Csrfer(csrfOpts, ctx.Context) // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { @@ -301,9 +293,6 @@ func APIContexter() func(http.Handler) http.Handler { ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) - ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) - ctx.Data["Context"] = &ctx - next.ServeHTTP(ctx.Resp, ctx.Req) // Handle adding signedUserName to the context for the AccessLogger diff --git a/modules/context/api_ui.go b/modules/context/api_ui.go new file mode 100644 index 0000000000000..ee0251ebf0ede --- /dev/null +++ b/modules/context/api_ui.go @@ -0,0 +1,57 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "html" + "net/http" + "strings" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web/middleware" + + "gitea.com/go-chi/session" +) + +// APIUIContexter returns apicontext as middleware +func APIUIContexter() func(http.Handler) http.Handler { + csrfOpts := getCsrfOpts() + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + locale := middleware.Locale(w, req) + ctx := APIContext{ + Context: &Context{ + Resp: NewResponse(w), + Data: map[string]interface{}{}, + Locale: locale, + Session: session.GetSession(req), + Repo: &Repository{ + PullRequest: &PullRequest{}, + }, + Org: &Organization{}, + }, + Org: &APIOrganization{}, + } + + ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) + ctx.csrf = Csrfer(csrfOpts, ctx.Context) + + // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. + if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { + if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size + ctx.InternalServerError(err) + return + } + } + + ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) + + next.ServeHTTP(ctx.Resp, ctx.Req) + }) + } +} diff --git a/routers/api/ui/api.go b/routers/api/ui/api.go new file mode 100644 index 0000000000000..bf02be40af947 --- /dev/null +++ b/routers/api/ui/api.go @@ -0,0 +1,356 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package ui + +import ( + "net/http" + "reflect" + "strings" + + "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/notify" + "code.gitea.io/gitea/routers/api/v1/org" + "code.gitea.io/gitea/routers/api/v1/repo" + "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/forms" + + "gitea.com/go-chi/binding" + "github.com/go-chi/cors" +) + +func repoAssignment() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + userName := ctx.Params("username") + repoName := ctx.Params("reponame") + + var ( + owner *user_model.User + err error + ) + + // Check if the user is the same as the repository owner. + if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) { + owner = ctx.Doer + } else { + owner, err = user_model.GetUserByName(userName) + if err != nil { + if user_model.IsErrUserNotExist(err) { + if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil { + context.RedirectToUser(ctx.Context, userName, redirectUserID) + } else if user_model.IsErrUserRedirectNotExist(err) { + ctx.NotFound("GetUserByName", err) + } else { + ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) + } + } else { + ctx.Error(http.StatusInternalServerError, "GetUserByName", err) + } + return + } + } + ctx.Repo.Owner = owner + + // Get repository. + repo, err := repo_model.GetRepositoryByName(owner.ID, repoName) + if err != nil { + if repo_model.IsErrRepoNotExist(err) { + redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName) + if err == nil { + context.RedirectToRepo(ctx.Context, redirectRepoID) + } else if repo_model.IsErrRedirectNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err) + } + } else { + ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err) + } + return + } + + repo.Owner = owner + ctx.Repo.Repository = repo + + ctx.Repo.Permission, err = models.GetUserRepoPermission(repo, ctx.Doer) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) + return + } + + if !ctx.Repo.HasAccess() { + ctx.NotFound() + return + } + } +} + +// Contexter middleware already checks token for user sign in process. +func reqToken() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if ctx.Context.IsBasicAuth { + ctx.CheckForOTP() + return + } + if ctx.IsSigned { + ctx.RequireCSRF() + return + } + ctx.Error(http.StatusUnauthorized, "reqToken", "token is required") + } +} + +func reqExploreSignIn() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { + ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") + } + } +} + +// reqOrgOwnership user should be an organization owner, or a site admin +func reqOrgOwnership() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if ctx.Context.IsUserSiteAdmin() { + return + } + + var orgID int64 + if ctx.Org.Organization != nil { + orgID = ctx.Org.Organization.ID + } else if ctx.Org.Team != nil { + orgID = ctx.Org.Team.OrgID + } else { + ctx.Error(http.StatusInternalServerError, "", "reqOrgOwnership: unprepared context") + return + } + + isOwner, err := models.IsOrganizationOwner(orgID, ctx.Doer.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err) + return + } else if !isOwner { + if ctx.Org.Organization != nil { + ctx.Error(http.StatusForbidden, "", "Must be an organization owner") + } else { + ctx.NotFound() + } + return + } + } +} + +// reqOrgMembership user should be an organization member, or a site admin +func reqOrgMembership() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if ctx.Context.IsUserSiteAdmin() { + return + } + + var orgID int64 + if ctx.Org.Organization != nil { + orgID = ctx.Org.Organization.ID + } else if ctx.Org.Team != nil { + orgID = ctx.Org.Team.OrgID + } else { + ctx.Error(http.StatusInternalServerError, "", "reqOrgMembership: unprepared context") + return + } + + if isMember, err := models.IsOrganizationMember(orgID, ctx.Doer.ID); err != nil { + ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err) + return + } else if !isMember { + if ctx.Org.Organization != nil { + ctx.Error(http.StatusForbidden, "", "Must be an organization member") + } else { + ctx.NotFound() + } + return + } + } +} + +func orgAssignment(args ...bool) func(ctx *context.APIContext) { + var ( + assignOrg bool + assignTeam bool + ) + if len(args) > 0 { + assignOrg = args[0] + } + if len(args) > 1 { + assignTeam = args[1] + } + return func(ctx *context.APIContext) { + ctx.Org = new(context.APIOrganization) + + var err error + if assignOrg { + ctx.Org.Organization, err = models.GetOrgByName(ctx.Params(":org")) + if err != nil { + if models.IsErrOrgNotExist(err) { + redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org")) + if err == nil { + context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID) + } else if user_model.IsErrUserRedirectNotExist(err) { + ctx.NotFound("GetOrgByName", err) + } else { + ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) + } + } else { + ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) + } + return + } + } + + if assignTeam { + ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid")) + if err != nil { + if models.IsErrTeamNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetTeamById", err) + } + return + } + } + } +} + +func mustEnableIssuesOrPulls(ctx *context.APIContext) { + if !ctx.Repo.CanRead(unit.TypeIssues) && + !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) { + if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { + if ctx.IsSigned { + log.Trace("Permission Denied: User %-v cannot read %-v and %-v in Repo %-v\n"+ + "User in Repo has Permissions: %-+v", + ctx.Doer, + unit.TypeIssues, + unit.TypePullRequests, + ctx.Repo.Repository, + ctx.Repo.Permission) + } else { + log.Trace("Permission Denied: Anonymous user cannot read %-v and %-v in Repo %-v\n"+ + "Anonymous user in Repo has Permissions: %-+v", + unit.TypeIssues, + unit.TypePullRequests, + ctx.Repo.Repository, + ctx.Repo.Permission) + } + } + ctx.NotFound() + return + } +} + +func mustNotBeArchived(ctx *context.APIContext) { + if ctx.Repo.Repository.IsArchived { + ctx.NotFound() + return + } +} + +// bind binding an obj to a func(ctx *context.APIContext) +func bind(obj interface{}) http.HandlerFunc { + tp := reflect.TypeOf(obj) + for tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return web.Wrap(func(ctx *context.APIContext) { + theObj := reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + errs := binding.Bind(ctx.Req, theObj) + if len(errs) > 0 { + ctx.Error(http.StatusUnprocessableEntity, "validationError", errs[0].Error()) + return + } + web.SetForm(ctx, theObj) + }) +} + +// Routes registers all v1 APIs routes to web application. +func Routes(sess func(next http.Handler) http.Handler) *web.Route { + m := web.NewRoute() + + m.Use(sess) + m.Use(common.SecurityHeaders()) + if setting.CORSConfig.Enabled { + m.Use(cors.Handler(cors.Options{ + // Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option + AllowedOrigins: setting.CORSConfig.AllowDomain, + // setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option + AllowedMethods: setting.CORSConfig.Methods, + AllowCredentials: setting.CORSConfig.AllowCredentials, + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + })) + } + m.Use(context.APIUIContexter()) + m.Use(context.APIAuth(auth.NewGroup(auth.Methods()...))) + + m.Use(context.ToggleAPI(&context.ToggleOptions{ + SignInRequired: setting.Service.RequireSignInView, + })) + + m.Group("", func() { + // Notifications + m.Group("/notifications", func() { + m.Get("/new", notify.NewAvailable) + }) + + // Users + m.Group("/users", func() { + m.Get("/search", reqExploreSignIn(), user.Search) + }) + + m.Group("/user", func() { + m.Get("/stopwatches", repo.GetStopwatches) + }) + + // Repositories + m.Group("/repos", func() { + m.Get("/search", repo.Search) + + m.Get("/issues/search", repo.SearchIssues) + + m.Group("/{username}/{reponame}", func() { + m.Group("/issues", func() { + m.Combo("").Get(repo.ListIssues). + Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) + m.Group("/{index}", func() { + m.Combo("").Get(repo.GetIssue). + Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue) + }) + }, mustEnableIssuesOrPulls) + + m.Post("/pulls/{index}/merge", reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest) + }, repoAssignment()) + }) + + // Organizations + m.Group("/orgs/{org}", func() { + m.Group("/teams", func() { + m.Combo("", reqToken()).Get(org.ListTeams). + Post(reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) + m.Get("/search", org.SearchTeam) + }, reqOrgMembership()) + }, orgAssignment(true)) + + m.Group("/topics", func() { + m.Get("/search", repo.TopicSearch) + }) + }) + + return m +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 4b30164026745..a8a61e160b49c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -86,6 +86,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/forms" @@ -561,12 +562,9 @@ func bind(obj interface{}) http.HandlerFunc { } // Routes registers all v1 APIs routes to web application. -func Routes(sessioner func(http.Handler) http.Handler) *web.Route { +func Routes() *web.Route { m := web.NewRoute() - - m.Use(sessioner) - - m.Use(securityHeaders()) + m.Use(common.SecurityHeaders()) if setting.CORSConfig.Enabled { m.Use(cors.Handler(cors.Options{ // Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option @@ -581,7 +579,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { m.Use(context.APIContexter()) // Get user from session if logged in. - m.Use(context.APIAuth(auth.NewGroup(auth.Methods()...))) + m.Use(context.APIAuth(auth.NewGroup(auth.APIMethods()...))) m.Use(context.ToggleAPI(&context.ToggleOptions{ SignInRequired: setting.Service.RequireSignInView, @@ -1099,14 +1097,3 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { return m } - -func securityHeaders() func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers - // http://stackoverflow.com/a/3146618/244009 - resp.Header().Set("x-content-type-options", "nosniff") - next.ServeHTTP(resp, req) - }) - } -} diff --git a/routers/common/security.go b/routers/common/security.go new file mode 100644 index 0000000000000..7f5da4fce3c32 --- /dev/null +++ b/routers/common/security.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package common + +import "net/http" + +// SecurityHeaders returns a chi middleware to add nosniff header +func SecurityHeaders() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers + // http://stackoverflow.com/a/3146618/244009 + resp.Header().Set("x-content-type-options", "nosniff") + next.ServeHTTP(resp, req) + }) + } +} diff --git a/routers/init.go b/routers/init.go index 804dfd65335b6..58f2425de4e9c 100644 --- a/routers/init.go +++ b/routers/init.go @@ -32,6 +32,7 @@ import ( "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/web" + apiui "code.gitea.io/gitea/routers/api/ui" apiv1 "code.gitea.io/gitea/routers/api/v1" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/routers/private" @@ -186,7 +187,8 @@ func NormalRoutes() *web.Route { }) r.Mount("/", web_routers.Routes(sessioner)) - r.Mount("/api/v1", apiv1.Routes(sessioner)) + r.Mount("/api/v1", apiv1.Routes()) + r.Mount("/api/ui", apiui.Routes(sessioner)) r.Mount("/api/internal", private.Routes()) return r } diff --git a/services/auth/auth.go b/services/auth/auth.go index bdff777f506e4..6d37619ab06c6 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -36,13 +36,24 @@ var authMethods = []Method{ &Session{}, } +var authAPIMethods = []Method{ + &OAuth2{}, + &Basic{}, + &ReverseProxy{}, +} + // The purpose of the following three function variables is to let the linter know that // those functions are not dead code and are actually being used var ( _ = handleSignIn ) -// Methods returns the instances of all registered methods +// APIMethods returns the instances of all SSO methods API needed +func APIMethods() []Method { + return authAPIMethods +} + +// Methods returns the instances of all registered SSO methods func Methods() []Method { return authMethods } diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index aed155fdbfb43..e0880f1952f66 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -429,7 +429,7 @@ {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}