0a48071854
Add ProjectsCursor type for pagination Add PageCount, CurrentPage, and TotalCount ProjectsPage This allows us to mimic the logic of GetBucketTotals and the implementation of BucketUsages in graphql for the new ProjectsByOwnerID functionality. Change-Id: I4e1613859085db65971b44fcacd9813d9ddad8eb
429 lines
12 KiB
Go
429 lines
12 KiB
Go
// Copyright (C) 2019 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package console_test
|
|
|
|
import (
|
|
"math/rand"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"storj.io/common/testcontext"
|
|
"storj.io/common/testrand"
|
|
"storj.io/common/uuid"
|
|
"storj.io/storj/satellite"
|
|
"storj.io/storj/satellite/console"
|
|
"storj.io/storj/satellite/satellitedb/satellitedbtest"
|
|
)
|
|
|
|
func TestProjectsRepository(t *testing.T) {
|
|
const (
|
|
// for user
|
|
shortName = "lastName"
|
|
email = "email@mail.test"
|
|
pass = "123456"
|
|
userFullName = "name"
|
|
|
|
// for project
|
|
name = "Project"
|
|
description = "some description"
|
|
|
|
// updated project values
|
|
newName = "newProjectName"
|
|
newDescription = "some new description"
|
|
)
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) { // repositories
|
|
users := db.Console().Users()
|
|
projects := db.Console().Projects()
|
|
var project *console.Project
|
|
var owner *console.User
|
|
|
|
rateLimit := 100
|
|
t.Run("Insert project successfully", func(t *testing.T) {
|
|
var err error
|
|
owner, err = users.Insert(ctx, &console.User{
|
|
ID: testrand.UUID(),
|
|
FullName: userFullName,
|
|
ShortName: shortName,
|
|
Email: email,
|
|
PasswordHash: []byte(pass),
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, owner)
|
|
owner, err := users.Insert(ctx, &console.User{
|
|
ID: testrand.UUID(),
|
|
FullName: userFullName,
|
|
ShortName: shortName,
|
|
Email: email,
|
|
PasswordHash: []byte(pass),
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, owner)
|
|
|
|
t.Run("Insert project successfully", func(t *testing.T) {
|
|
project = &console.Project{
|
|
Name: name,
|
|
Description: description,
|
|
OwnerID: owner.ID,
|
|
RateLimit: &rateLimit,
|
|
}
|
|
|
|
project, err = projects.Insert(ctx, project)
|
|
assert.NotNil(t, project)
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Get project success", func(t *testing.T) {
|
|
projectByID, err := projects.Get(ctx, project.ID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, projectByID.ID, project.ID)
|
|
assert.Equal(t, projectByID.Name, name)
|
|
assert.Equal(t, projectByID.OwnerID, owner.ID)
|
|
assert.Equal(t, projectByID.Description, description)
|
|
require.NotNil(t, project)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Get by projectID success", func(t *testing.T) {
|
|
projectByID, err := projects.Get(ctx, project.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, project.ID, projectByID.ID)
|
|
require.Equal(t, name, projectByID.Name)
|
|
require.Equal(t, owner.ID, projectByID.OwnerID)
|
|
require.Equal(t, description, projectByID.Description)
|
|
require.Equal(t, rateLimit, *projectByID.RateLimit)
|
|
})
|
|
|
|
t.Run("Update project success", func(t *testing.T) {
|
|
oldProject, err := projects.Get(ctx, project.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, oldProject)
|
|
|
|
newRateLimit := 1000
|
|
|
|
// creating new project with updated values.
|
|
newProject := &console.Project{
|
|
ID: oldProject.ID,
|
|
Name: newName,
|
|
Description: newDescription,
|
|
RateLimit: &newRateLimit,
|
|
}
|
|
|
|
err = projects.Update(ctx, newProject)
|
|
require.NoError(t, err)
|
|
|
|
// fetching updated project from db
|
|
newProject, err = projects.Get(ctx, oldProject.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, oldProject.ID, newProject.ID)
|
|
require.Equal(t, newName, newProject.Name)
|
|
require.Equal(t, newDescription, newProject.Description)
|
|
require.Equal(t, newRateLimit, *newProject.RateLimit)
|
|
})
|
|
|
|
t.Run("Delete project success", func(t *testing.T) {
|
|
oldProject, err := projects.Get(ctx, project.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, oldProject)
|
|
|
|
err = projects.Delete(ctx, oldProject.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = projects.Get(ctx, oldProject.ID)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("GetAll success", func(t *testing.T) {
|
|
allProjects, err := projects.GetAll(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(allProjects))
|
|
|
|
newProject := &console.Project{
|
|
Description: description,
|
|
Name: name,
|
|
}
|
|
|
|
_, err = projects.Insert(ctx, newProject)
|
|
require.NoError(t, err)
|
|
|
|
allProjects, err = projects.GetAll(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(allProjects))
|
|
|
|
newProject2 := &console.Project{
|
|
Description: description,
|
|
Name: name + "2",
|
|
}
|
|
|
|
_, err = projects.Insert(ctx, newProject2)
|
|
require.NoError(t, err)
|
|
|
|
allProjects, err = projects.GetAll(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(allProjects))
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestProjectsList(t *testing.T) {
|
|
const (
|
|
limit = 5
|
|
length = limit * 4
|
|
)
|
|
|
|
rateLimit := 100
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) { // repositories
|
|
// create owner
|
|
owner, err := db.Console().Users().Insert(ctx,
|
|
&console.User{
|
|
ID: testrand.UUID(),
|
|
FullName: "Billy H",
|
|
Email: "billyh@example.com",
|
|
PasswordHash: []byte("example_password"),
|
|
Status: 1,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
projectsDB := db.Console().Projects()
|
|
|
|
// Create projects
|
|
var projects []console.Project
|
|
for i := 0; i < length; i++ {
|
|
proj, err := projectsDB.Insert(ctx,
|
|
&console.Project{
|
|
Name: "example",
|
|
Description: "example",
|
|
OwnerID: owner.ID,
|
|
RateLimit: &rateLimit,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
projects = append(projects, *proj)
|
|
}
|
|
|
|
now := time.Now().Add(time.Second)
|
|
|
|
projsPage, err := projectsDB.List(ctx, 0, limit, now)
|
|
require.NoError(t, err)
|
|
|
|
projectsList := projsPage.Projects
|
|
|
|
for projsPage.Next {
|
|
projsPage, err = projectsDB.List(ctx, projsPage.NextOffset, limit, now)
|
|
require.NoError(t, err)
|
|
|
|
projectsList = append(projectsList, projsPage.Projects...)
|
|
}
|
|
|
|
require.False(t, projsPage.Next)
|
|
require.EqualValues(t, 0, projsPage.NextOffset)
|
|
require.Equal(t, length, len(projectsList))
|
|
require.Empty(t, cmp.Diff(projects[0], projectsList[0],
|
|
cmp.Transformer("Sort", func(xs []console.Project) []console.Project {
|
|
rs := append([]console.Project{}, xs...)
|
|
sort.Slice(rs, func(i, k int) bool {
|
|
return rs[i].ID.String() < rs[k].ID.String()
|
|
})
|
|
return rs
|
|
})))
|
|
})
|
|
}
|
|
|
|
func TestProjectsListByOwner(t *testing.T) {
|
|
const (
|
|
limit = 5
|
|
length = limit*4 - 1 // make length offset from page size so we can test incomplete page at end
|
|
totalPages = 4
|
|
)
|
|
|
|
rateLimit := 100
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
owner1, err := db.Console().Users().Insert(ctx,
|
|
&console.User{
|
|
ID: testrand.UUID(),
|
|
FullName: "Billy H",
|
|
Email: "billyh@example.com",
|
|
PasswordHash: []byte("example_password"),
|
|
Status: 1,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
owner2, err := db.Console().Users().Insert(ctx,
|
|
&console.User{
|
|
ID: testrand.UUID(),
|
|
FullName: "James H",
|
|
Email: "james@example.com",
|
|
PasswordHash: []byte("example_password_2"),
|
|
Status: 1,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
projectsDB := db.Console().Projects()
|
|
projectMembersDB := db.Console().ProjectMembers()
|
|
|
|
// Create projects
|
|
var owner1Projects []console.Project
|
|
var owner2Projects []console.Project
|
|
for i := 0; i < length; i++ {
|
|
proj1, err := projectsDB.Insert(ctx,
|
|
&console.Project{
|
|
Name: "owner1example" + strconv.Itoa(i),
|
|
Description: "example",
|
|
OwnerID: owner1.ID,
|
|
RateLimit: &rateLimit,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
proj2, err := projectsDB.Insert(ctx,
|
|
&console.Project{
|
|
Name: "owner2example" + strconv.Itoa(i),
|
|
Description: "example",
|
|
OwnerID: owner2.ID,
|
|
RateLimit: &rateLimit,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// insert 0, 1, or 2 project members
|
|
numMembers := i % 3
|
|
switch numMembers {
|
|
case 1:
|
|
_, err = projectMembersDB.Insert(ctx, owner1.ID, proj1.ID)
|
|
require.NoError(t, err)
|
|
_, err = projectMembersDB.Insert(ctx, owner2.ID, proj2.ID)
|
|
require.NoError(t, err)
|
|
case 2:
|
|
_, err = projectMembersDB.Insert(ctx, owner1.ID, proj1.ID)
|
|
require.NoError(t, err)
|
|
_, err = projectMembersDB.Insert(ctx, owner2.ID, proj1.ID)
|
|
require.NoError(t, err)
|
|
_, err = projectMembersDB.Insert(ctx, owner1.ID, proj2.ID)
|
|
require.NoError(t, err)
|
|
_, err = projectMembersDB.Insert(ctx, owner2.ID, proj2.ID)
|
|
require.NoError(t, err)
|
|
}
|
|
proj1.MemberCount = numMembers
|
|
proj2.MemberCount = numMembers
|
|
|
|
owner1Projects = append(owner1Projects, *proj1)
|
|
owner2Projects = append(owner2Projects, *proj2)
|
|
}
|
|
|
|
// test listing for each
|
|
var testCases = []struct {
|
|
id uuid.UUID
|
|
originalProjects []console.Project
|
|
}{
|
|
{id: owner1.ID, originalProjects: owner1Projects},
|
|
{id: owner2.ID, originalProjects: owner2Projects},
|
|
}
|
|
for _, tt := range testCases {
|
|
cursor := &console.ProjectsCursor{
|
|
Limit: limit,
|
|
Page: 1,
|
|
}
|
|
projsPage, err := projectsDB.ListByOwnerID(ctx, tt.id, *cursor)
|
|
require.NoError(t, err)
|
|
require.Len(t, projsPage.Projects, limit)
|
|
require.EqualValues(t, 1, projsPage.CurrentPage)
|
|
require.EqualValues(t, totalPages, projsPage.PageCount)
|
|
require.EqualValues(t, length, projsPage.TotalCount)
|
|
|
|
ownerProjectsDB := projsPage.Projects
|
|
|
|
for projsPage.Next {
|
|
cursor.Page++
|
|
projsPage, err = projectsDB.ListByOwnerID(ctx, tt.id, *cursor)
|
|
require.NoError(t, err)
|
|
// number of projects should not exceed page limit
|
|
require.True(t, len(projsPage.Projects) > 0 && len(projsPage.Projects) <= limit)
|
|
require.EqualValues(t, cursor.Page, projsPage.CurrentPage)
|
|
require.EqualValues(t, totalPages, projsPage.PageCount)
|
|
require.EqualValues(t, length, projsPage.TotalCount)
|
|
|
|
ownerProjectsDB = append(ownerProjectsDB, projsPage.Projects...)
|
|
}
|
|
|
|
require.False(t, projsPage.Next)
|
|
require.EqualValues(t, 0, projsPage.NextOffset)
|
|
require.Equal(t, length, len(ownerProjectsDB))
|
|
// sort originalProjects by Name in alphabetical order
|
|
originalProjects := tt.originalProjects
|
|
sort.SliceStable(originalProjects, func(i, j int) bool {
|
|
return strings.Compare(originalProjects[i].Name, originalProjects[j].Name) < 0
|
|
})
|
|
for i, p := range ownerProjectsDB {
|
|
// expect response projects to be in alphabetical order
|
|
require.Equal(t, originalProjects[i].Name, p.Name)
|
|
require.Equal(t, originalProjects[i].MemberCount, p.MemberCount)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetMaxBuckets(t *testing.T) {
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
maxCount := 100
|
|
consoleDB := db.Console()
|
|
project, err := consoleDB.Projects().Insert(ctx, &console.Project{Name: "testproject1", MaxBuckets: &maxCount})
|
|
require.NoError(t, err)
|
|
projectsDB := db.Console().Projects()
|
|
max, err := projectsDB.GetMaxBuckets(ctx, project.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, maxCount, *max)
|
|
})
|
|
}
|
|
|
|
func TestValidateNameAndDescription(t *testing.T) {
|
|
t.Run("Project name and description validation test", func(t *testing.T) {
|
|
validDescription := randString(100)
|
|
|
|
// update project with empty name.
|
|
err := console.ValidateNameAndDescription("", validDescription)
|
|
require.Error(t, err)
|
|
|
|
notValidName := randString(21)
|
|
|
|
// update project with too long name.
|
|
err = console.ValidateNameAndDescription(notValidName, validDescription)
|
|
require.Error(t, err)
|
|
|
|
validName := randString(15)
|
|
notValidDescription := randString(101)
|
|
|
|
// update project with too long description.
|
|
err = console.ValidateNameAndDescription(validName, notValidDescription)
|
|
require.Error(t, err)
|
|
|
|
// update project with valid name and description.
|
|
err = console.ValidateNameAndDescription(validName, validDescription)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func randString(n int) string {
|
|
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
|
|
b := make([]rune, n)
|
|
for i := range b {
|
|
b[i] = letters[rand.Intn(len(letters))]
|
|
}
|
|
return string(b)
|
|
}
|