storj/satellite/console/projects_test.go
Moby von Briesen 0a48071854 satellite/console: Add pagination fields for ListProjectsByOwnerID
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
2021-01-20 16:15:29 +00:00

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)
}