A row in the new `user_settings` table does not always exist for a user, even if they have been around for a while. Since `user_settings` is now what defines the state of a user's onboarding flow, prior to this fix, even old users would receive the onboarding flow again. This change appropriately updates `user_settings` for users who already have projects, and thus have already gone through the onboarding flow. A brand new user will still be navigated to the beginning of onboarding. Change-Id: Ie745d280f6b8094ec60c200c2dca8d018d51f7d1
1026 lines
30 KiB
Go
1026 lines
30 KiB
Go
// Copyright (C) 2021 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package consoleweb_test
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/cookiejar"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"storj.io/common/testcontext"
|
|
"storj.io/common/uuid"
|
|
"storj.io/storj/private/testplanet"
|
|
"storj.io/storj/satellite/payments/storjscan/blockchaintest"
|
|
)
|
|
|
|
func TestAuth(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
|
|
{ // Register User
|
|
_ = test.registerUser("user@mail.test", "#$Rnkl12i3nkljfds")
|
|
}
|
|
|
|
{ // Login_GetToken_Fail
|
|
resp, body := test.request(
|
|
http.MethodPost, "/auth/token",
|
|
strings.NewReader(`{"email":"wrong@invalid.test","password":"wrong"}`))
|
|
require.Nil(t, findCookie(resp, "_tokenKey"))
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
|
_ = body
|
|
// TODO: require.Contains(t, body, "unauthorized")
|
|
}
|
|
|
|
{ // Login_GetToken_Pass
|
|
test.login(user.email, user.password)
|
|
}
|
|
|
|
{ // Login_ChangePassword_IncorrectCurrentPassword
|
|
resp, body := test.request(
|
|
http.MethodPost, "/auth/account/change-password",
|
|
test.toJSON(map[string]string{
|
|
"email": user.email,
|
|
"password": user.password + "1",
|
|
"newPassword": user.password + "2",
|
|
}))
|
|
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
_ = body
|
|
//TODO: require.Contains(t, body, "password was incorrect")
|
|
}
|
|
|
|
{ // Login_ChangePassword
|
|
resp, _ := test.request(
|
|
http.MethodPost, "/auth/account/change-password`",
|
|
test.toJSON(map[string]string{
|
|
"email": user.email,
|
|
"password": user.password,
|
|
"newPassword": user.password,
|
|
}))
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
var oldCookies []*http.Cookie
|
|
|
|
{ // Get_AccountInfo
|
|
resp, body := test.request(http.MethodGet, "/auth/account", nil)
|
|
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
|
require.Contains(test.t, body, "fullName")
|
|
oldCookies = resp.Cookies()
|
|
|
|
var userIdentifier struct{ ID string }
|
|
require.NoError(test.t, json.Unmarshal([]byte(body), &userIdentifier))
|
|
require.NotEmpty(test.t, userIdentifier.ID)
|
|
}
|
|
|
|
{ // Get_FreezeStatus
|
|
resp, body := test.request(http.MethodGet, "/auth/account/freezestatus", nil)
|
|
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
|
require.Contains(test.t, body, "frozen")
|
|
|
|
var freezestatus struct{ Frozen bool }
|
|
require.NoError(test.t, json.Unmarshal([]byte(body), &freezestatus))
|
|
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
|
require.False(test.t, freezestatus.Frozen)
|
|
}
|
|
|
|
{ // Test_UserSettings
|
|
testGetSettings := func(expected struct {
|
|
SessionDuration *string
|
|
OnboardingStart bool
|
|
OnboardingEnd bool
|
|
OnboardingStep *string
|
|
}) {
|
|
resp, body := test.request(http.MethodGet, "/auth/account/settings", nil)
|
|
|
|
var settings struct {
|
|
SessionDuration *string
|
|
OnboardingStart bool
|
|
OnboardingEnd bool
|
|
OnboardingStep *string
|
|
}
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
require.NoError(test.t, json.Unmarshal([]byte(body), &settings))
|
|
require.Equal(test.t, expected.OnboardingStart, settings.OnboardingStart)
|
|
require.Equal(test.t, expected.OnboardingEnd, settings.OnboardingEnd)
|
|
require.Equal(test.t, expected.OnboardingStep, settings.OnboardingStep)
|
|
require.Equal(test.t, expected.SessionDuration, settings.SessionDuration)
|
|
}
|
|
|
|
testGetSettings(struct {
|
|
SessionDuration *string
|
|
OnboardingStart bool
|
|
OnboardingEnd bool
|
|
OnboardingStep *string
|
|
}{
|
|
SessionDuration: nil,
|
|
OnboardingStart: true,
|
|
OnboardingEnd: true,
|
|
OnboardingStep: nil,
|
|
})
|
|
|
|
resp, _ := test.request(http.MethodPatch, "/auth/account/onboarding",
|
|
test.toJSON(map[string]interface{}{
|
|
"onboardingStart": true,
|
|
"onboardingEnd": false,
|
|
"onboardingStep": "cli",
|
|
}))
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
step := "cli"
|
|
testGetSettings(struct {
|
|
SessionDuration *string
|
|
OnboardingStart bool
|
|
OnboardingEnd bool
|
|
OnboardingStep *string
|
|
}{
|
|
SessionDuration: nil,
|
|
OnboardingStart: true,
|
|
OnboardingEnd: false,
|
|
OnboardingStep: &step,
|
|
})
|
|
|
|
resp, _ = test.request(http.MethodPatch, "/auth/account/onboarding",
|
|
test.toJSON(map[string]interface{}{
|
|
"onboardingStart": nil,
|
|
"onboardingEnd": nil,
|
|
"onboardingStep": nil,
|
|
}))
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
// having passed nil to /auth/account/onboarding shouldn't have changed existing values.
|
|
testGetSettings(struct {
|
|
SessionDuration *string
|
|
OnboardingStart bool
|
|
OnboardingEnd bool
|
|
OnboardingStep *string
|
|
}{
|
|
SessionDuration: nil,
|
|
OnboardingStart: true,
|
|
OnboardingEnd: false,
|
|
OnboardingStep: &step,
|
|
})
|
|
}
|
|
|
|
{ // Logout
|
|
resp, _ := test.request(http.MethodPost, "/auth/logout", nil)
|
|
cookie := findCookie(resp, "_tokenKey")
|
|
require.NotNil(t, cookie)
|
|
require.Equal(t, "", cookie.Value)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_AccountInfo shouldn't succeed after logging out
|
|
resp, body := test.request(http.MethodGet, "/auth/account", nil)
|
|
// TODO: wrong error text
|
|
// require.Contains(test.t, body, "unauthorized")
|
|
require.Contains(test.t, body, "error")
|
|
require.Equal(test.t, http.StatusUnauthorized, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_AccountInfo shouldn't succeed with reused session cookie
|
|
satURL, err := url.Parse(test.url(""))
|
|
require.NoError(t, err)
|
|
test.client.Jar.SetCookies(satURL, oldCookies)
|
|
|
|
resp, body := test.request(http.MethodGet, "/auth/account", nil)
|
|
require.Contains(test.t, body, "error")
|
|
require.Equal(test.t, http.StatusUnauthorized, resp.StatusCode)
|
|
}
|
|
|
|
{ // repeated login attempts should end in too many requests
|
|
hitRateLimiter := false
|
|
for i := 0; i < 30; i++ {
|
|
resp, _ := test.request(
|
|
http.MethodPost, "/auth/token",
|
|
strings.NewReader(`{"email":"wrong@invalid.test","password":"wrong"}`))
|
|
require.Nil(t, findCookie(resp, "_tokenKey"))
|
|
if resp.StatusCode != http.StatusUnauthorized {
|
|
require.Equal(t, http.StatusTooManyRequests, resp.StatusCode)
|
|
hitRateLimiter = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, hitRateLimiter, "did not hit rate limiter")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPayments(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
|
|
{ // Unauthorized
|
|
for _, path := range []string{
|
|
"/payments/cards",
|
|
"/payments/account/balance",
|
|
"/payments/billing-history",
|
|
"/payments/account/charges?from=1619827200&to=1620844320",
|
|
} {
|
|
resp, body := test.request(http.MethodGet, path, nil)
|
|
require.Contains(t, body, "unauthorized", path)
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode, path)
|
|
}
|
|
}
|
|
|
|
test.login(user.email, user.password)
|
|
|
|
{ // Get_PaymentCards_EmptyReturn
|
|
resp, body := test.request(http.MethodGet, "/payments/cards", nil)
|
|
require.JSONEq(t, "[]", body)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_AccountBalance
|
|
resp, body := test.request(http.MethodGet, "/payments/account/balance", nil)
|
|
require.Contains(t, body, "freeCredits")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_BillingHistory
|
|
resp, body := test.request(http.MethodGet, "/payments/billing-history", nil)
|
|
require.JSONEq(t, "[]", body)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_AccountChargesByDateRange
|
|
resp, body := test.request(http.MethodGet, "/payments/account/charges?from=1619827200&to=1620844320", nil)
|
|
require.Contains(t, body, "egress")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestWalletPayments(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
sat := planet.Satellites[0]
|
|
|
|
userData := test.defaultUser()
|
|
test.login(userData.email, userData.password)
|
|
|
|
user, err := sat.DB.Console().Users().GetByEmail(ctx, userData.email)
|
|
require.NoError(t, err)
|
|
|
|
wallet := blockchaintest.NewAddress()
|
|
err = sat.DB.Wallets().Add(ctx, user.ID, wallet)
|
|
require.NoError(t, err)
|
|
|
|
resp, _ := test.request(http.MethodGet, "/payments/wallet/payments", nil)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
})
|
|
}
|
|
|
|
func TestBuckets(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
|
|
{ // Unauthorized
|
|
for _, path := range []string{
|
|
"/buckets/bucket-names?projectID=" + test.defaultProjectID(),
|
|
} {
|
|
resp, body := test.request(http.MethodGet, path, nil)
|
|
require.Contains(t, body, "unauthorized", path)
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode, path)
|
|
}
|
|
}
|
|
|
|
test.login(user.email, user.password)
|
|
|
|
{ // Get_BucketNamesByProjectId
|
|
resp, body := test.request(http.MethodGet, "/buckets/bucket-names?projectID="+test.defaultProjectID(), nil)
|
|
// TODO: this should be []
|
|
require.JSONEq(t, "null", body)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // get bucket usages
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"before": "2021-05-12T18:32:30.533Z",
|
|
"limit": 7,
|
|
"search": "",
|
|
"page": 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $before: DateTime!, $limit: Int!, $search: String!, $page: Int!) {
|
|
project(id: $projectId) {
|
|
bucketUsages(before: $before, cursor: {limit: $limit, search: $search, page: $page}) {
|
|
bucketUsages {
|
|
bucketName
|
|
storage
|
|
egress
|
|
objectCount
|
|
segmentCount
|
|
since
|
|
before
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
offset
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "bucketUsagePage")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestAPIKeys(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
test.login(user.email, user.password)
|
|
|
|
{ // Post_GenerateApiKey
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"name": user.email,
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $name: String!) {
|
|
createAPIKey(projectID: $projectId, name: $name) {
|
|
key
|
|
keyInfo {
|
|
id
|
|
name
|
|
createdAt
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "createAPIKey")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_APIKeyInfoByProjectId
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"orderDirection": 1,
|
|
"projectId": test.defaultProjectID(),
|
|
"limit": 6,
|
|
"search": ``,
|
|
"page": 1,
|
|
"order": 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $limit: Int!, $search: String!, $page: Int!, $order: Int!, $orderDirection: Int!) {
|
|
project(id: $projectId) {
|
|
apiKeys(cursor: {limit: $limit, search: $search, page: $page, order: $order, orderDirection: $orderDirection}) {
|
|
apiKeys {
|
|
id
|
|
name
|
|
createdAt
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
order
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "apiKeysPage")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestProjects(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
user2 := test.registerUser("user@mail.test", "#$Rnkl12i3nkljfds")
|
|
test.login(user.email, user.password)
|
|
|
|
{ // Get_ProjectId
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"query": `
|
|
{
|
|
myProjects {
|
|
name
|
|
id
|
|
description
|
|
createdAt
|
|
ownerId
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, test.defaultProjectID())
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_Salt
|
|
projectID := test.defaultProjectID()
|
|
id, err := uuid.FromString(projectID)
|
|
require.NoError(t, err)
|
|
|
|
// get salt from endpoint
|
|
var b64Salt string
|
|
resp, body := test.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", test.defaultProjectID()), nil)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
require.NoError(t, json.Unmarshal([]byte(body), &b64Salt))
|
|
|
|
// get salt from db and base64 encode it
|
|
salt, err := planet.Satellites[0].DB.Console().Projects().GetSalt(ctx, id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, b64Salt, base64.StdEncoding.EncodeToString(salt))
|
|
}
|
|
|
|
{ // Get_ProjectInfo
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"before": "2021-05-12T18:32:30.533Z",
|
|
"limit": 7,
|
|
"search": "",
|
|
"page": 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $before: DateTime!, $limit: Int!, $search: String!, $page: Int!) {
|
|
project(id: $projectId) {
|
|
bucketUsages(before: $before, cursor: {limit: $limit, search: $search, page: $page}) {
|
|
bucketUsages {
|
|
bucketName
|
|
storage
|
|
egress
|
|
objectCount
|
|
segmentCount
|
|
since
|
|
before
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
offset
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "bucketUsagePage")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_ProjectUsageLimitById
|
|
resp, body := test.request(http.MethodGet, `/projects/`+test.defaultProjectID()+`/usage-limits`, nil)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
require.Contains(t, body, "storageLimit")
|
|
}
|
|
|
|
{ // Get_OwnedProjects
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"limit": 7,
|
|
"page": 1,
|
|
},
|
|
"query": `
|
|
query ($limit: Int!, $page: Int!) {
|
|
ownedProjects(cursor: {limit: $limit, page: $page}) {
|
|
projects {
|
|
id
|
|
name
|
|
ownerId
|
|
description
|
|
createdAt
|
|
memberCount
|
|
__typename
|
|
}
|
|
limit
|
|
offset
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "projectsPage")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_ProjectMembersByProjectId
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
`orderDirection`: 1,
|
|
`projectId`: test.defaultProjectID(),
|
|
`limit`: 6,
|
|
`search`: ``,
|
|
`page`: 1,
|
|
`order`: 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $limit: Int!, $search: String!, $page: Int!, $order: Int!, $orderDirection: Int!) {
|
|
project(id: $projectId) {
|
|
members(cursor: {limit: $limit, search: $search, page: $page, order: $order, orderDirection: $orderDirection}) {
|
|
projectMembers {
|
|
user {
|
|
id
|
|
fullName
|
|
shortName
|
|
email
|
|
__typename
|
|
}
|
|
joinedAt
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
order
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "projectMembersPage")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_AddUserToProject
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
addProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "addProjectMembers")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_RemoveUserFromProject
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
deleteProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "deleteProjectMembers")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_AddMultipleUsersToProjectWhere1UserIsInvalid
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email, "invalid@mail.test"},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
addProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "addProjectMembers")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_AddMultipleUsersToProjectWhereUserIsAlreadyAMember
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email, user.email},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
addProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "error")
|
|
// TODO: this should return a better error
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_ProjectRenameInvalid
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
`projectId`: `e4a929a6-cc69-4920-ad06-c84f3c943928`,
|
|
`name`: `My Second Project`,
|
|
`description`: `___`,
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $name: String!, $description: String!) {
|
|
updateProject(id: $projectId, projectFields: {name: $name, description: $description}, projectLimits: {storageLimit: "1000", bandwidthLimit: "1000"}) {
|
|
name
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "error")
|
|
// TODO: this should return a better error
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_ProjectRename
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
`projectId`: test.defaultProjectID(),
|
|
`name`: `Test`,
|
|
`description`: `Misc`,
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $name: String!, $description: String!) {
|
|
updateProject(id: $projectId, projectFields: {name: $name, description: $description}, projectLimits: {storageLimit: "1000", bandwidthLimit: "1000"}) {
|
|
name
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "updateProject")
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestWrongUser(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
test := newTest(t, ctx, planet)
|
|
user := test.defaultUser()
|
|
_ = user
|
|
user2 := test.registerUser("user@mail.test", "#$Rnkl12i3nkljfds")
|
|
test.login(user2.email, user2.password)
|
|
|
|
{ // Get_ProjectInfo
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"before": "2021-05-12T18:32:30.533Z",
|
|
"limit": 7,
|
|
"search": "",
|
|
"page": 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $before: DateTime!, $limit: Int!, $search: String!, $page: Int!) {
|
|
project(id: $projectId) {
|
|
bucketUsages(before: $before, cursor: {limit: $limit, search: $search, page: $page}) {
|
|
bucketUsages {
|
|
bucketName
|
|
storage
|
|
egress
|
|
objectCount
|
|
segmentCount
|
|
since
|
|
before
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
offset
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_ProjectUsageLimitById
|
|
resp, body := test.request(http.MethodGet, `/projects/`+test.defaultProjectID()+`/usage-limits`, nil)
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Get_ProjectMembersByProjectId
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
`orderDirection`: 1,
|
|
`projectId`: test.defaultProjectID(),
|
|
`limit`: 6,
|
|
`search`: ``,
|
|
`page`: 1,
|
|
`order`: 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $limit: Int!, $search: String!, $page: Int!, $order: Int!, $orderDirection: Int!) {
|
|
project(id: $projectId) {
|
|
members(cursor: {limit: $limit, search: $search, page: $page, order: $order, orderDirection: $orderDirection}) {
|
|
projectMembers {
|
|
user {
|
|
id
|
|
fullName
|
|
shortName
|
|
email
|
|
__typename
|
|
}
|
|
joinedAt
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
order
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_AddUserToProject
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
addProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_RemoveUserFromProject
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"emails": []string{user2.email},
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $emails: [String!]!) {
|
|
deleteProjectMembers(projectID: $projectId, email: $emails) {
|
|
id
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // Post_ProjectRename
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
`projectId`: test.defaultProjectID(),
|
|
`name`: `Test`,
|
|
`description`: `Misc`,
|
|
},
|
|
"query": `
|
|
mutation ($projectId: String!, $name: String!, $description: String!) {
|
|
updateProject(id: $projectId, projectFields: {name: $name, description: $description}, projectLimits: {storageLimit: "1000", bandwidthLimit: "1000"}) {
|
|
name
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
|
|
{ // get bucket usages
|
|
resp, body := test.request(http.MethodPost, "/graphql",
|
|
test.toJSON(map[string]interface{}{
|
|
"variables": map[string]interface{}{
|
|
"projectId": test.defaultProjectID(),
|
|
"before": "2021-05-12T18:32:30.533Z",
|
|
"limit": 7,
|
|
"search": "",
|
|
"page": 1,
|
|
},
|
|
"query": `
|
|
query ($projectId: String!, $before: DateTime!, $limit: Int!, $search: String!, $page: Int!) {
|
|
project(id: $projectId) {
|
|
bucketUsages(before: $before, cursor: {limit: $limit, search: $search, page: $page}) {
|
|
bucketUsages {
|
|
bucketName
|
|
storage
|
|
egress
|
|
objectCount
|
|
segmentCount
|
|
since
|
|
before
|
|
__typename
|
|
}
|
|
search
|
|
limit
|
|
offset
|
|
pageCount
|
|
currentPage
|
|
totalCount
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}`}))
|
|
require.Contains(t, body, "not authorized")
|
|
// TODO: wrong error code
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
|
|
type test struct {
|
|
t *testing.T
|
|
ctx *testcontext.Context
|
|
planet *testplanet.Planet
|
|
client *http.Client
|
|
}
|
|
|
|
func newTest(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) test {
|
|
jar, err := cookiejar.New(nil)
|
|
require.NoError(t, err)
|
|
return test{t: t, ctx: ctx, planet: planet, client: &http.Client{Jar: jar}}
|
|
}
|
|
|
|
type registeredUser struct {
|
|
id string
|
|
email string
|
|
password string
|
|
}
|
|
|
|
func (test *test) request(method string, path string, data io.Reader) (resp Response, body string) {
|
|
req, err := http.NewRequestWithContext(test.ctx, method, test.url(path), data)
|
|
require.NoError(test.t, err)
|
|
req.Header = map[string][]string{
|
|
"sec-ch-ua": {`" Not A;Brand";v="99"`, `"Chromium";v="90"`, `"Google Chrome";v="90"`},
|
|
"sec-ch-ua-mobile": {"?0"},
|
|
"User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"},
|
|
"Content-Type": {"application/json"},
|
|
"Accept": {"*/*"},
|
|
}
|
|
return test.do(req)
|
|
}
|
|
|
|
// Response is a wrapper for http.Request to prevent false-positive with bodyclose check.
|
|
type Response struct{ *http.Response }
|
|
|
|
func (test *test) do(req *http.Request) (_ Response, body string) {
|
|
resp, err := test.client.Do(req)
|
|
require.NoError(test.t, err)
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
require.NoError(test.t, err)
|
|
require.NoError(test.t, resp.Body.Close())
|
|
|
|
return Response{resp}, string(data)
|
|
}
|
|
|
|
func (test *test) url(suffix string) string {
|
|
return test.planet.Satellites[0].ConsoleURL() + "/api/v0" + suffix
|
|
}
|
|
|
|
func (test *test) toJSON(v interface{}) io.Reader {
|
|
data, err := json.Marshal(v)
|
|
require.NoError(test.t, err)
|
|
return strings.NewReader(string(data))
|
|
}
|
|
|
|
func (test *test) defaultUser() registeredUser {
|
|
user := test.planet.Uplinks[0].User[test.planet.Satellites[0].ID()]
|
|
return registeredUser{
|
|
email: user.Email,
|
|
password: user.Password,
|
|
}
|
|
}
|
|
|
|
func (test *test) defaultProjectID() string { return test.planet.Uplinks[0].Projects[0].ID.String() }
|
|
|
|
func (test *test) login(email, password string) Response {
|
|
resp, body := test.request(
|
|
http.MethodPost, "/auth/token",
|
|
test.toJSON(map[string]string{
|
|
"email": email,
|
|
"password": password,
|
|
}))
|
|
cookie := findCookie(resp, "_tokenKey")
|
|
require.NotNil(test.t, cookie)
|
|
|
|
var tokenInfo struct {
|
|
Token string `json:"token"`
|
|
}
|
|
require.NoError(test.t, json.Unmarshal([]byte(body), &tokenInfo))
|
|
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
|
require.Equal(test.t, tokenInfo.Token, cookie.Value)
|
|
|
|
return resp
|
|
}
|
|
|
|
func (test *test) registerUser(email, password string) registeredUser {
|
|
resp, body := test.request(
|
|
http.MethodPost, "/auth/register",
|
|
test.toJSON(map[string]interface{}{
|
|
"secret": "",
|
|
"password": password,
|
|
"fullName": "Chester Cheeto",
|
|
"shortName": "",
|
|
"email": email,
|
|
"partner": "",
|
|
"partnerId": "",
|
|
"isProfessional": false,
|
|
"position": "",
|
|
"companyName": "",
|
|
"employeeCount": "",
|
|
"haveSalesContact": false,
|
|
}))
|
|
|
|
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
|
|
|
time.Sleep(time.Second) // TODO: hack-fix, register activates account asynchronously
|
|
|
|
return registeredUser{
|
|
id: body,
|
|
email: email,
|
|
password: password,
|
|
}
|
|
}
|
|
|
|
func findCookie(response Response, name string) *http.Cookie {
|
|
for _, c := range response.Cookies() {
|
|
if c.Name == name {
|
|
return c
|
|
}
|
|
}
|
|
return nil
|
|
}
|