2017-11-15 07:41:26 +01:00
|
|
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2018-01-05 08:27:43 +01:00
|
|
|
"io/ioutil"
|
2017-11-15 07:41:26 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2018-08-23 09:02:20 +02:00
|
|
|
"github.com/goharbor/harbor/src/common"
|
2018-02-06 03:59:49 +01:00
|
|
|
|
2017-11-15 07:41:26 +01:00
|
|
|
"github.com/astaxie/beego"
|
|
|
|
"github.com/dghubble/sling"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2018-08-23 09:02:20 +02:00
|
|
|
"github.com/goharbor/harbor/src/common/dao"
|
|
|
|
"github.com/goharbor/harbor/src/common/dao/project"
|
|
|
|
common_http "github.com/goharbor/harbor/src/common/http"
|
|
|
|
"github.com/goharbor/harbor/src/common/models"
|
2017-11-15 07:41:26 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-04-11 11:12:33 +02:00
|
|
|
nonSysAdminID, projAdminID, projDeveloperID, projGuestID int64
|
|
|
|
projAdminPMID, projDeveloperPMID, projGuestPMID int
|
2018-01-05 08:27:43 +01:00
|
|
|
// The following users/credentials are registered and assigned roles at the beginning of
|
|
|
|
// running testing and cleaned up at the end.
|
|
|
|
// Do not try to change the system and project roles that the users have during
|
|
|
|
// the testing. Creating a new one in your own case if needed.
|
|
|
|
// The project roles that the users have are for project library.
|
|
|
|
sysAdmin = &usrInfo{
|
2017-11-15 07:41:26 +01:00
|
|
|
Name: "admin",
|
|
|
|
Passwd: "Harbor12345",
|
|
|
|
}
|
|
|
|
nonSysAdmin = &usrInfo{
|
|
|
|
Name: "non_admin",
|
|
|
|
Passwd: "Harbor12345",
|
|
|
|
}
|
2018-01-05 08:27:43 +01:00
|
|
|
projAdmin = &usrInfo{
|
|
|
|
Name: "proj_admin",
|
|
|
|
Passwd: "Harbor12345",
|
|
|
|
}
|
|
|
|
projDeveloper = &usrInfo{
|
|
|
|
Name: "proj_developer",
|
|
|
|
Passwd: "Harbor12345",
|
|
|
|
}
|
|
|
|
projGuest = &usrInfo{
|
|
|
|
Name: "proj_guest",
|
|
|
|
Passwd: "Harbor12345",
|
|
|
|
}
|
2017-11-15 07:41:26 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type testingRequest struct {
|
|
|
|
method string
|
|
|
|
url string
|
|
|
|
header http.Header
|
|
|
|
queryStruct interface{}
|
|
|
|
bodyJSON interface{}
|
|
|
|
credential *usrInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
type codeCheckingCase struct {
|
|
|
|
request *testingRequest
|
|
|
|
code int
|
|
|
|
postFunc func(*httptest.ResponseRecorder) error
|
|
|
|
}
|
|
|
|
|
|
|
|
func newRequest(r *testingRequest) (*http.Request, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
reqBuilder := sling.New()
|
|
|
|
switch strings.ToUpper(r.method) {
|
|
|
|
case "", http.MethodGet:
|
|
|
|
reqBuilder = reqBuilder.Get(r.url)
|
|
|
|
case http.MethodPost:
|
|
|
|
reqBuilder = reqBuilder.Post(r.url)
|
|
|
|
case http.MethodPut:
|
|
|
|
reqBuilder = reqBuilder.Put(r.url)
|
|
|
|
case http.MethodDelete:
|
|
|
|
reqBuilder = reqBuilder.Delete(r.url)
|
|
|
|
case http.MethodHead:
|
|
|
|
reqBuilder = reqBuilder.Head(r.url)
|
|
|
|
case http.MethodPatch:
|
|
|
|
reqBuilder = reqBuilder.Patch(r.url)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unsupported method %s", r.method)
|
|
|
|
}
|
|
|
|
|
|
|
|
for key, values := range r.header {
|
|
|
|
for _, value := range values {
|
|
|
|
reqBuilder = reqBuilder.Add(key, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.queryStruct != nil {
|
|
|
|
reqBuilder = reqBuilder.QueryStruct(r.queryStruct)
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.bodyJSON != nil {
|
|
|
|
reqBuilder = reqBuilder.BodyJSON(r.bodyJSON)
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.credential != nil {
|
|
|
|
reqBuilder = reqBuilder.SetBasicAuth(r.credential.Name, r.credential.Passwd)
|
|
|
|
}
|
|
|
|
|
|
|
|
return reqBuilder.Request()
|
|
|
|
}
|
|
|
|
|
|
|
|
func handle(r *testingRequest) (*httptest.ResponseRecorder, error) {
|
|
|
|
req, err := newRequest(r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
beego.BeeApp.Handlers.ServeHTTP(resp, req)
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2018-01-05 08:27:43 +01:00
|
|
|
func handleAndParse(r *testingRequest, v interface{}) error {
|
|
|
|
resp, err := handle(r)
|
2017-11-15 07:41:26 +01:00
|
|
|
if err != nil {
|
2018-01-05 08:27:43 +01:00
|
|
|
return err
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|
|
|
|
|
2018-01-05 08:27:43 +01:00
|
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-15 07:41:26 +01:00
|
|
|
|
|
|
|
if resp.Code >= 200 && resp.Code <= 299 {
|
2018-01-05 08:27:43 +01:00
|
|
|
return json.Unmarshal(data, v)
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|
|
|
|
|
2018-01-05 08:27:43 +01:00
|
|
|
return &common_http.Error{
|
|
|
|
Code: resp.Code,
|
|
|
|
Message: string(data),
|
|
|
|
}
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func runCodeCheckingCases(t *testing.T, cases ...*codeCheckingCase) {
|
2018-04-03 17:48:55 +02:00
|
|
|
for i, c := range cases {
|
|
|
|
t.Logf("running case %d ...", i)
|
2017-11-15 07:41:26 +01:00
|
|
|
resp, err := handle(c.request)
|
|
|
|
require.Nil(t, err)
|
|
|
|
equal := assert.Equal(t, c.code, resp.Code)
|
|
|
|
if !equal {
|
|
|
|
if resp.Body.Len() > 0 {
|
|
|
|
t.Log(resp.Body.String())
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.postFunc != nil {
|
|
|
|
if err := c.postFunc(resp); err != nil {
|
|
|
|
t.Logf("error in running post function: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseResourceID(resp *httptest.ResponseRecorder) (int64, error) {
|
|
|
|
location := resp.Header().Get(http.CanonicalHeaderKey("location"))
|
|
|
|
if len(location) == 0 {
|
|
|
|
return 0, fmt.Errorf("empty location header")
|
|
|
|
}
|
|
|
|
index := strings.LastIndex(location, "/")
|
|
|
|
if index == -1 {
|
|
|
|
return 0, fmt.Errorf("location header %s contains no /", location)
|
|
|
|
}
|
|
|
|
|
|
|
|
id := strings.TrimPrefix(location, location[:index+1])
|
|
|
|
if len(id) == 0 {
|
|
|
|
return 0, fmt.Errorf("location header %s contains no resource ID", location)
|
|
|
|
}
|
|
|
|
|
|
|
|
return strconv.ParseInt(id, 10, 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
if err := prepare(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2018-07-30 07:25:11 +02:00
|
|
|
ret := m.Run()
|
|
|
|
clean()
|
|
|
|
os.Exit(ret)
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func prepare() error {
|
2018-01-05 08:27:43 +01:00
|
|
|
// register nonSysAdmin
|
2018-04-11 11:12:33 +02:00
|
|
|
var err error
|
|
|
|
nonSysAdminID, err = dao.Register(models.User{
|
2017-11-15 07:41:26 +01:00
|
|
|
Username: nonSysAdmin.Name,
|
|
|
|
Password: nonSysAdmin.Passwd,
|
2018-01-05 08:27:43 +01:00
|
|
|
Email: nonSysAdmin.Name + "@test.com",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// register projAdmin and assign project admin role
|
2018-04-11 11:12:33 +02:00
|
|
|
projAdminID, err = dao.Register(models.User{
|
2018-01-05 08:27:43 +01:00
|
|
|
Username: projAdmin.Name,
|
|
|
|
Password: projAdmin.Passwd,
|
|
|
|
Email: projAdmin.Name + "@test.com",
|
2017-11-15 07:41:26 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-05 08:27:43 +01:00
|
|
|
|
2018-04-11 11:12:33 +02:00
|
|
|
if projAdminPMID, err = project.AddProjectMember(models.Member{
|
|
|
|
ProjectID: 1,
|
|
|
|
Role: models.PROJECTADMIN,
|
|
|
|
EntityID: int(projAdminID),
|
|
|
|
EntityType: common.UserMember,
|
|
|
|
}); err != nil {
|
2018-01-05 08:27:43 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// register projDeveloper and assign project developer role
|
2018-04-11 11:12:33 +02:00
|
|
|
projDeveloperID, err = dao.Register(models.User{
|
2018-01-05 08:27:43 +01:00
|
|
|
Username: projDeveloper.Name,
|
|
|
|
Password: projDeveloper.Passwd,
|
|
|
|
Email: projDeveloper.Name + "@test.com",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-11 11:12:33 +02:00
|
|
|
if projDeveloperPMID, err = project.AddProjectMember(models.Member{
|
|
|
|
ProjectID: 1,
|
|
|
|
Role: models.DEVELOPER,
|
|
|
|
EntityID: int(projDeveloperID),
|
|
|
|
EntityType: common.UserMember,
|
|
|
|
}); err != nil {
|
2018-01-05 08:27:43 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// register projGuest and assign project guest role
|
2018-04-11 11:12:33 +02:00
|
|
|
projGuestID, err = dao.Register(models.User{
|
2018-01-05 08:27:43 +01:00
|
|
|
Username: projGuest.Name,
|
|
|
|
Password: projGuest.Passwd,
|
|
|
|
Email: projGuest.Name + "@test.com",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-11 11:12:33 +02:00
|
|
|
if projGuestPMID, err = project.AddProjectMember(models.Member{
|
|
|
|
ProjectID: 1,
|
|
|
|
Role: models.GUEST,
|
|
|
|
EntityID: int(projGuestID),
|
|
|
|
EntityType: common.UserMember,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-02-06 03:59:49 +01:00
|
|
|
return err
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|
|
|
|
|
2018-01-05 08:27:43 +01:00
|
|
|
func clean() {
|
2018-04-11 11:12:33 +02:00
|
|
|
pmids := []int{projAdminPMID, projDeveloperPMID, projGuestPMID}
|
|
|
|
|
|
|
|
for _, id := range pmids {
|
|
|
|
if err := project.DeleteProjectMemberByID(id); err != nil {
|
2018-01-05 08:27:43 +01:00
|
|
|
fmt.Printf("failed to clean up member %d from project library: %v", id, err)
|
|
|
|
}
|
|
|
|
}
|
2018-04-11 11:12:33 +02:00
|
|
|
userids := []int64{nonSysAdminID, projAdminID, projDeveloperID, projGuestID}
|
|
|
|
for _, id := range userids {
|
|
|
|
if err := dao.DeleteUser(int(id)); err != nil {
|
2018-01-05 08:27:43 +01:00
|
|
|
fmt.Printf("failed to clean up user %d: %v \n", id, err)
|
|
|
|
}
|
|
|
|
}
|
2017-11-15 07:41:26 +01:00
|
|
|
}
|