1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package consoleql_test 5 6import ( 7 "context" 8 "fmt" 9 "testing" 10 "time" 11 12 "github.com/graphql-go/graphql" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 "go.uber.org/zap/zaptest" 16 17 "storj.io/common/testcontext" 18 "storj.io/common/testrand" 19 "storj.io/common/uuid" 20 "storj.io/storj/private/post" 21 "storj.io/storj/private/testplanet" 22 "storj.io/storj/private/testredis" 23 "storj.io/storj/satellite/accounting" 24 "storj.io/storj/satellite/accounting/live" 25 "storj.io/storj/satellite/analytics" 26 "storj.io/storj/satellite/console" 27 "storj.io/storj/satellite/console/consoleauth" 28 "storj.io/storj/satellite/console/consoleweb/consoleql" 29 "storj.io/storj/satellite/mailservice" 30 "storj.io/storj/satellite/payments/paymentsconfig" 31 "storj.io/storj/satellite/payments/stripecoinpayments" 32 "storj.io/storj/satellite/rewards" 33) 34 35// discardSender discard sending of an actual email. 36type discardSender struct{} 37 38// SendEmail immediately returns with nil error. 39func (*discardSender) SendEmail(ctx context.Context, msg *post.Message) error { 40 return nil 41} 42 43// FromAddress returns empty post.Address. 44func (*discardSender) FromAddress() post.Address { 45 return post.Address{} 46} 47 48func TestGraphqlMutation(t *testing.T) { 49 testplanet.Run(t, testplanet.Config{SatelliteCount: 1}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) { 50 sat := planet.Satellites[0] 51 db := sat.DB 52 log := zaptest.NewLogger(t) 53 54 partnersService := rewards.NewPartnersService( 55 log.Named("partners"), 56 rewards.DefaultPartnersDB, 57 ) 58 59 analyticsService := analytics.NewService(log, analytics.Config{}, "test-satellite") 60 61 redis, err := testredis.Mini(ctx) 62 require.NoError(t, err) 63 defer ctx.Check(redis.Close) 64 65 cache, err := live.OpenCache(ctx, log.Named("cache"), live.Config{StorageBackend: "redis://" + redis.Addr() + "?db=0"}) 66 require.NoError(t, err) 67 68 projectLimitCache := accounting.NewProjectLimitCache(db.ProjectAccounting(), 0, 0, 0, accounting.ProjectLimitConfig{CacheCapacity: 100}) 69 70 projectUsage := accounting.NewService(db.ProjectAccounting(), cache, projectLimitCache, 5*time.Minute, -10*time.Second) 71 72 // TODO maybe switch this test to testplanet to avoid defining config and Stripe service 73 pc := paymentsconfig.Config{ 74 StorageTBPrice: "10", 75 EgressTBPrice: "45", 76 SegmentPrice: "0.0000022", 77 } 78 79 paymentsService, err := stripecoinpayments.NewService( 80 log.Named("payments.stripe:service"), 81 stripecoinpayments.NewStripeMock( 82 testrand.NodeID(), 83 db.StripeCoinPayments().Customers(), 84 db.Console().Users(), 85 ), 86 pc.StripeCoinPayments, 87 db.StripeCoinPayments(), 88 db.Console().Projects(), 89 db.ProjectAccounting(), 90 pc.StorageTBPrice, 91 pc.EgressTBPrice, 92 pc.SegmentPrice, 93 pc.BonusRate) 94 require.NoError(t, err) 95 96 service, err := console.NewService( 97 log.Named("console"), 98 &consoleauth.Hmac{Secret: []byte("my-suppa-secret-key")}, 99 db.Console(), 100 db.ProjectAccounting(), 101 projectUsage, 102 sat.API.Buckets.Service, 103 partnersService, 104 paymentsService.Accounts(), 105 analyticsService, 106 console.Config{PasswordCost: console.TestPasswordCost, DefaultProjectLimit: 5}, 107 ) 108 require.NoError(t, err) 109 110 mailService, err := mailservice.New(log, &discardSender{}, "testdata") 111 require.NoError(t, err) 112 defer ctx.Check(mailService.Close) 113 114 rootObject := make(map[string]interface{}) 115 rootObject["origin"] = "http://doesntmatter.com/" 116 rootObject[consoleql.ActivationPath] = "?activationToken=" 117 rootObject[consoleql.SignInPath] = "login" 118 rootObject[consoleql.LetUsKnowURL] = "letUsKnowURL" 119 rootObject[consoleql.ContactInfoURL] = "contactInfoURL" 120 rootObject[consoleql.TermsAndConditionsURL] = "termsAndConditionsURL" 121 122 schema, err := consoleql.CreateSchema(log, service, mailService) 123 require.NoError(t, err) 124 125 createUser := console.CreateUser{ 126 FullName: "John Roll", 127 ShortName: "Roll", 128 Email: "test@mail.test", 129 UserAgent: []byte("120bf202-8252-437e-ac12-0e364bee852e"), 130 Password: "123a123", 131 } 132 133 regToken, err := service.CreateRegToken(ctx, 1) 134 require.NoError(t, err) 135 136 rootUser, err := service.CreateUser(ctx, createUser, regToken.Secret) 137 require.NoError(t, err) 138 require.Equal(t, createUser.UserAgent, rootUser.UserAgent) 139 140 err = paymentsService.Accounts().Setup(ctx, rootUser.ID, rootUser.Email) 141 require.NoError(t, err) 142 143 activationToken, err := service.GenerateActivationToken(ctx, rootUser.ID, rootUser.Email) 144 require.NoError(t, err) 145 146 _, err = service.ActivateAccount(ctx, activationToken) 147 require.NoError(t, err) 148 149 token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password}) 150 require.NoError(t, err) 151 152 sauth, err := service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) 153 require.NoError(t, err) 154 155 authCtx := console.WithAuth(ctx, sauth) 156 157 testQuery := func(t *testing.T, query string) (interface{}, error) { 158 result := graphql.Do(graphql.Params{ 159 Schema: schema, 160 Context: authCtx, 161 RequestString: query, 162 RootObject: rootObject, 163 }) 164 165 for _, err := range result.Errors { 166 if err.OriginalError() != nil { 167 return nil, err 168 } 169 } 170 require.False(t, result.HasErrors()) 171 172 return result.Data, nil 173 } 174 175 token, err = service.Token(ctx, console.AuthUser{Email: rootUser.Email, Password: createUser.Password}) 176 require.NoError(t, err) 177 178 sauth, err = service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) 179 require.NoError(t, err) 180 181 authCtx = console.WithAuth(ctx, sauth) 182 183 var projectIDField string 184 t.Run("Create project mutation", func(t *testing.T) { 185 projectInfo := console.ProjectInfo{ 186 Name: "Project name", 187 Description: "desc", 188 } 189 190 query := fmt.Sprintf( 191 "mutation {createProject(input:{name:\"%s\",description:\"%s\"}){name,description,id,createdAt}}", 192 projectInfo.Name, 193 projectInfo.Description, 194 ) 195 196 result, err := testQuery(t, query) 197 require.NoError(t, err) 198 199 data := result.(map[string]interface{}) 200 project := data[consoleql.CreateProjectMutation].(map[string]interface{}) 201 202 assert.Equal(t, projectInfo.Name, project[consoleql.FieldName]) 203 assert.Equal(t, projectInfo.Description, project[consoleql.FieldDescription]) 204 205 projectIDField = project[consoleql.FieldID].(string) 206 }) 207 208 projectID, err := uuid.FromString(projectIDField) 209 require.NoError(t, err) 210 211 project, err := service.GetProject(authCtx, projectID) 212 require.NoError(t, err) 213 require.Equal(t, rootUser.PartnerID, project.PartnerID) 214 215 regTokenUser1, err := service.CreateRegToken(ctx, 1) 216 require.NoError(t, err) 217 218 user1, err := service.CreateUser(authCtx, console.CreateUser{ 219 FullName: "User1", 220 Email: "u1@mail.test", 221 Password: "123a123", 222 }, regTokenUser1.Secret) 223 require.NoError(t, err) 224 225 t.Run("Activation", func(t *testing.T) { 226 activationToken1, err := service.GenerateActivationToken( 227 ctx, 228 user1.ID, 229 "u1@mail.test", 230 ) 231 require.NoError(t, err) 232 233 _, err = service.ActivateAccount(ctx, activationToken1) 234 require.NoError(t, err) 235 236 user1.Email = "u1@mail.test" 237 }) 238 239 regTokenUser2, err := service.CreateRegToken(ctx, 1) 240 require.NoError(t, err) 241 242 user2, err := service.CreateUser(authCtx, console.CreateUser{ 243 FullName: "User1", 244 Email: "u2@mail.test", 245 Password: "123a123", 246 }, regTokenUser2.Secret) 247 require.NoError(t, err) 248 249 t.Run("Activation", func(t *testing.T) { 250 activationToken2, err := service.GenerateActivationToken( 251 ctx, 252 user2.ID, 253 "u2@mail.test", 254 ) 255 require.NoError(t, err) 256 257 _, err = service.ActivateAccount(ctx, activationToken2) 258 require.NoError(t, err) 259 260 user2.Email = "u2@mail.test" 261 }) 262 263 t.Run("Add project members mutation", func(t *testing.T) { 264 query := fmt.Sprintf( 265 "mutation {addProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(cursor: { limit: 50, search: \"\", page: 1, order: 1, orderDirection: 2 }){projectMembers{joinedAt}}}}", 266 project.ID.String(), 267 user1.Email, 268 user2.Email, 269 ) 270 271 result, err := testQuery(t, query) 272 require.NoError(t, err) 273 274 data := result.(map[string]interface{}) 275 proj := data[consoleql.AddProjectMembersMutation].(map[string]interface{}) 276 277 members := proj[consoleql.FieldMembers].(map[string]interface{}) 278 projectMembers := members[consoleql.FieldProjectMembers].([]interface{}) 279 280 assert.Equal(t, project.ID.String(), proj[consoleql.FieldID]) 281 assert.Equal(t, project.Name, proj[consoleql.FieldName]) 282 assert.Equal(t, 3, len(projectMembers)) 283 }) 284 285 t.Run("Delete project members mutation", func(t *testing.T) { 286 query := fmt.Sprintf( 287 "mutation {deleteProjectMembers(projectID:\"%s\",email:[\"%s\",\"%s\"]){id,name,members(cursor: { limit: 50, search: \"\", page: 1, order: 1, orderDirection: 2 }){projectMembers{user{id}}}}}", 288 project.ID.String(), 289 user1.Email, 290 user2.Email, 291 ) 292 293 result, err := testQuery(t, query) 294 require.NoError(t, err) 295 296 data := result.(map[string]interface{}) 297 proj := data[consoleql.DeleteProjectMembersMutation].(map[string]interface{}) 298 299 members := proj[consoleql.FieldMembers].(map[string]interface{}) 300 projectMembers := members[consoleql.FieldProjectMembers].([]interface{}) 301 rootMember := projectMembers[0].(map[string]interface{})[consoleql.UserType].(map[string]interface{}) 302 303 assert.Equal(t, project.ID.String(), proj[consoleql.FieldID]) 304 assert.Equal(t, project.Name, proj[consoleql.FieldName]) 305 assert.Equal(t, 1, len(members)) 306 307 assert.Equal(t, rootUser.ID.String(), rootMember[consoleql.FieldID]) 308 }) 309 310 var keyID string 311 t.Run("Create api key mutation", func(t *testing.T) { 312 keyName := "key1" 313 query := fmt.Sprintf( 314 "mutation {createAPIKey(projectID:\"%s\",name:\"%s\"){key,keyInfo{id,name,projectID,partnerId}}}", 315 project.ID.String(), 316 keyName, 317 ) 318 319 result, err := testQuery(t, query) 320 require.NoError(t, err) 321 322 data := result.(map[string]interface{}) 323 createAPIKey := data[consoleql.CreateAPIKeyMutation].(map[string]interface{}) 324 325 key := createAPIKey[consoleql.FieldKey].(string) 326 keyInfo := createAPIKey[consoleql.APIKeyInfoType].(map[string]interface{}) 327 328 assert.NotEqual(t, "", key) 329 330 assert.Equal(t, keyName, keyInfo[consoleql.FieldName]) 331 assert.Equal(t, project.ID.String(), keyInfo[consoleql.FieldProjectID]) 332 assert.Equal(t, rootUser.PartnerID.String(), keyInfo[consoleql.FieldPartnerID]) 333 334 keyID = keyInfo[consoleql.FieldID].(string) 335 }) 336 337 t.Run("Delete api key mutation", func(t *testing.T) { 338 id, err := uuid.FromString(keyID) 339 require.NoError(t, err) 340 341 info, err := service.GetAPIKeyInfo(authCtx, id) 342 require.NoError(t, err) 343 344 query := fmt.Sprintf( 345 "mutation {deleteAPIKeys(id:[\"%s\"]){name,projectID}}", 346 keyID, 347 ) 348 349 result, err := testQuery(t, query) 350 require.NoError(t, err) 351 352 data := result.(map[string]interface{}) 353 keyInfoList := data[consoleql.DeleteAPIKeysMutation].([]interface{}) 354 355 for _, k := range keyInfoList { 356 keyInfo := k.(map[string]interface{}) 357 358 assert.Equal(t, info.Name, keyInfo[consoleql.FieldName]) 359 assert.Equal(t, project.ID.String(), keyInfo[consoleql.FieldProjectID]) 360 } 361 }) 362 363 const testName = "testName" 364 const testDescription = "test description" 365 const StorageLimit = "100" 366 const BandwidthLimit = "100" 367 368 t.Run("Update project mutation", func(t *testing.T) { 369 query := fmt.Sprintf( 370 "mutation {updateProject(id:\"%s\",projectFields:{name:\"%s\",description:\"%s\"},projectLimits:{storageLimit:\"%s\",bandwidthLimit:\"%s\"}){id,name,description}}", 371 project.ID.String(), 372 testName, 373 testDescription, 374 StorageLimit, 375 BandwidthLimit, 376 ) 377 378 result, err := testQuery(t, query) 379 require.NoError(t, err) 380 381 data := result.(map[string]interface{}) 382 proj := data[consoleql.UpdateProjectMutation].(map[string]interface{}) 383 384 assert.Equal(t, project.ID.String(), proj[consoleql.FieldID]) 385 assert.Equal(t, testName, proj[consoleql.FieldName]) 386 assert.Equal(t, testDescription, proj[consoleql.FieldDescription]) 387 }) 388 389 t.Run("Delete project mutation", func(t *testing.T) { 390 query := fmt.Sprintf( 391 "mutation {deleteProject(id:\"%s\"){id,name}}", 392 projectID, 393 ) 394 395 result, err := testQuery(t, query) 396 require.Error(t, err) 397 require.Nil(t, result) 398 require.Equal(t, console.ErrUnauthorized.New("not implemented").Error(), err.Error()) 399 }) 400 }) 401} 402