コード例 #1
0
        public async Task Handle_InstancesPresentFutureExpiry_ReturnsEmptyList()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            await environment.WithFreshDataContext(async dataContext =>
                                                   await dataContext.Instances.AddAsync(new Instance()
            {
                Name         = "dummy",
                PlanId       = "dummy",
                ExpiresAtUtc = DateTime.UtcNow.AddMinutes(1),
                Cluster      = new Cluster()
            }));

            //Act
            var expiredInstances = await environment.Mediator.Send(new GetExpiredInstancesQuery());

            //Assert
            Assert.IsNotNull(expiredInstances);
            Assert.AreEqual(0, expiredInstances.Length);
        }
コード例 #2
0
        public async Task Handle_ProperArgumentsGiven_DoggerInstanceIsCreatedInDatabase()
        {
            //Arrange
            var fakeProvisioningService = Substitute.For <IProvisioningService>();

            var fakeAmazonLightsail = Substitute.For <IAmazonLightsail>();

            FakeOutBundleFetching(fakeAmazonLightsail);

            var fakeFile = Substitute.For <IFile>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeProvisioningService);
                    services.AddSingleton(fakeAmazonLightsail);
                    services.AddSingleton(fakeFile);
                }
            });

            //Act
            await environment.Mediator.Send(
                new ProvisionDogfeedInstanceCommand(
                    "some-instance-name"));

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var cluster = await dataContext
                              .Clusters
                              .Include(x => x.Instances)
                              .SingleOrDefaultAsync();
                Assert.IsNotNull(cluster);

                Assert.AreEqual(DataContext.DoggerClusterId, cluster.Id);
                Assert.AreEqual(1, cluster.Instances.Count);
                Assert.AreEqual("some-instance-name", cluster.Instances.Single().Name);
            });
        }
コード例 #3
0
        public async Task Handle_ExistingPullRequestFound_ReturnsExistingPullRequest()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var pullDogRepository = new PullDogRepository()
            {
                Handle          = "some-repository-handle",
                PullDogSettings = new PullDogSettings()
                {
                    User = new User()
                    {
                        StripeCustomerId = "dummy"
                    },
                    PlanId          = "dummy",
                    EncryptedApiKey = Array.Empty <byte>()
                }
            };
            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.PullDogPullRequests.AddAsync(new PullDogPullRequest()
                {
                    Handle            = "some-pull-request-handle",
                    PullDogRepository = pullDogRepository
                });
            });

            //Act
            var pullRequest = await environment.Mediator.Send(new EnsurePullDogPullRequestCommand(
                                                                  pullDogRepository,
                                                                  "some-pull-request-handle"));

            //Assert
            Assert.IsNotNull(pullRequest);

            await environment.WithFreshDataContext(async dataContext =>
            {
                Assert.AreEqual(1, await dataContext.PullDogPullRequests.CountAsync());
            });
        }
コード例 #4
0
        public async Task Handle_ProperArgumentsGiven_UnprovisionedInstanceIsAddedToDatabase()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(Substitute.For <IProvisioningService>());
                }
            });

            var user = await environment.Mediator.Send(
                new CreateUserForIdentityCommand(
                    TestClaimsPrincipalFactory.CreateWithIdentityName("some-identity-name")));

            //Act
            await environment.Mediator.Send(
                new ProvisionInstanceForUserCommand(
                    new Plan(
                        "dummy",
                        1337,
                        new Bundle()
            {
                Price = 1337,
                BundleId = "dummy"
            },
                        Array.Empty <PullDogPlan>()),
                    user));

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var addedInstance = await dataContext
                                    .Instances
                                    .SingleAsync();

                Assert.IsNotNull(addedInstance);
                Assert.IsFalse(addedInstance.IsProvisioned);
            });
        }
コード例 #5
0
        public async Task Handle_MultipleClusters_ReturnsDemoCluster()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Clusters.AddAsync(new Cluster());

                await dataContext.Clusters.AddAsync(new Cluster()
                {
                    Id = DataContext.DemoClusterId
                });
            });

            //Act
            var cluster = await environment.Mediator.Send(new EnsureClusterWithIdCommand(DataContext.DemoClusterId));

            //Assert
            Assert.IsNotNull(cluster);
            Assert.AreEqual(DataContext.DemoClusterId, cluster.Id);
        }
コード例 #6
0
        public async Task Handle_NoPullDogSettingsFoundOnUser_ThrowsException()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var userId = Guid.NewGuid();
            await environment.WithFreshDataContext(async dataContext =>
                                                   await dataContext.Users.AddAsync(new User()
            {
                Id = userId,
                StripeCustomerId = "dummy",
                PullDogSettings  = null
            }));

            //Act
            var exception = await Assert.ThrowsExceptionAsync <InvalidOperationException>(async() =>
                                                                                          await environment.Mediator.Send(
                                                                                              new GetRepositoriesForUserQuery(userId)));

            //Assert
            Assert.IsNotNull(exception);
        }
        public async Task Handle_InstancesWithoutPullRequestPresent_NothingIsDeleted()
        {
            //Arrange
            var fakeMediator = Substitute.For <IMediator>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(
                            new EnvironmentSetupOptions ()
            {
                IocConfiguration = services => services.AddSingleton(fakeMediator)
            });

            var user = new User()
            {
                StripeCustomerId = "dummy"
            };
            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Users.AddAsync(user);

                await dataContext.Instances.AddAsync(new Instance()
                {
                    Cluster = new Cluster()
                    {
                        User = user
                    },
                    Name               = "dummy",
                    PlanId             = "dummy",
                    PullDogPullRequest = null
                });
            });

            //Act
            await environment.Mediator.Send(new DeleteAllPullDogInstancesForUserCommand(user.Id));

            //Assert
            await fakeMediator
            .DidNotReceive()
            .Send(Arg.Any <DeleteInstanceByNameCommand>());
        }
コード例 #8
0
        public async Task Handle_NoUserFoundInDatabase_SavesCredentialsOnCreatedUser()
        {
            //Arrange
            var fakeAmazonIdentityManagementService = Substitute.For <IAmazonIdentityManagementService>();

            fakeAmazonIdentityManagementService
            .CreateAccessKeyAsync(Arg.Any <CreateAccessKeyRequest>())
            .Returns(new CreateAccessKeyResponse()
            {
                AccessKey = new AccessKey()
                {
                    SecretAccessKey = "some-secret-access-key",
                    AccessKeyId     = "some-access-key-id"
                }
            });

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services => services.AddSingleton(fakeAmazonIdentityManagementService)
            });

            var aesEncryptionHelper = environment.ServiceProvider.GetRequiredService <IAesEncryptionHelper>();

            //Act
            await environment.Mediator.Send(new EnsureAmazonUserWithNameCommand("some-name"));

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var userFromDatabase = await dataContext.AmazonUsers.SingleAsync();
                Assert.AreNotEqual(0, userFromDatabase.EncryptedSecretAccessKey);
                Assert.AreNotEqual(0, userFromDatabase.EncryptedAccessKeyId);

                var decryptedAccessKey = await aesEncryptionHelper.DecryptAsync(userFromDatabase.EncryptedSecretAccessKey);
                var decryptedKeyId     = await aesEncryptionHelper.DecryptAsync(userFromDatabase.EncryptedAccessKeyId);
                Assert.AreEqual("some-secret-access-key", decryptedAccessKey);
                Assert.AreEqual("some-access-key-id", decryptedKeyId);
            });
        }
コード例 #9
0
        public async Task Handle_NestedTransactionsOuterExceptionThrown_InnerAndOuterTransactionContentsReverted()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            //Act
            var exception = await Assert.ThrowsExceptionAsync <TestException>(async() =>
            {
                await environment.Mediator.Send(new TestCommand(async() =>
                {
                    await environment.DataContext.Clusters.AddAsync(new Cluster()
                    {
                        Name = "outer"
                    });
                    await environment.DataContext.SaveChangesAsync();

                    await environment.Mediator.Send(new TestCommand(async() =>
                    {
                        await environment.DataContext.Clusters.AddAsync(new Cluster()
                        {
                            Name = "inner"
                        });
                        await environment.DataContext.SaveChangesAsync();
                    }));

                    throw new TestException();
                }));
            });

            //Assert
            Assert.IsNotNull(exception);

            await environment.WithFreshDataContext(async (dataContext) =>
            {
                var clusterCount = await dataContext.Clusters.CountAsync();
                Assert.AreEqual(0, clusterCount);
            });
        }
コード例 #10
0
        public async Task ThrowError_AnonymousUserAndProductionEnvironment_ReturnsNoErrorDetails()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                EnvironmentName = Environments.Production
            });

            //Act
            using var httpClient = new HttpClient();
            var response = await httpClient.GetAsync("http://localhost:14568/errors/throw");

            //Assert
            Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode);

            var responseString = await response.Content.ReadAsStringAsync();

            var responseObject = JsonSerializer.Deserialize <ProblemDetails>(responseString);

            Assert.AreEqual("An error occured while processing your request.", responseObject.Title);
            Assert.IsNull(responseObject.Detail);
            Assert.AreEqual(500, responseObject.Status);
        }
コード例 #11
0
        public async Task Handle_SinglePaymentMethodPresentOnStripeCustomer_SinglePaymentMethodReturned()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var createdUser = await environment.Mediator.Send(
                new CreateUserForIdentityCommand(
                    TestClaimsPrincipalFactory.CreateWithIdentityName("some-identity-name")));

            var createdPaymentMethod = await environment
                                       .ServiceProvider
                                       .GetRequiredService <PaymentMethodService>()
                                       .CreateAsync(new PaymentMethodCreateOptions()
            {
                Card = new PaymentMethodCardCreateOptions()
                {
                    Number   = "4242424242424242",
                    Cvc      = "123",
                    ExpMonth = 10,
                    ExpYear  = 30
                },
                Type = "card"
            });

            await environment.Mediator.Send(
                new SetActivePaymentMethodForUserCommand(
                    createdUser,
                    createdPaymentMethod.Id));

            //Act
            var activePaymentMethod = await environment.Mediator.Send(
                new GetActivePaymentMethodForUserQuery(
                    createdUser));

            //Assert
            Assert.IsNotNull(activePaymentMethod);
        }
コード例 #12
0
        public async Task Handle_ClusterIdGivenAndOneMatchingClusterOnUser_ClusterReturned()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var fakeUserId    = Guid.NewGuid();
            var fakeClusterId = Guid.NewGuid();

            var user = new User()
            {
                Id = fakeUserId,
                StripeCustomerId = "dummy"
            };

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Clusters.AddAsync(new Cluster()
                {
                    User = user
                });
                await dataContext.Clusters.AddAsync(new Cluster()
                {
                    Id   = fakeClusterId,
                    User = user
                });
            });

            //Act
            var cluster = await environment.Mediator.Send(new GetClusterForUserQuery(fakeUserId)
            {
                ClusterId = fakeClusterId
            });

            //Assert
            Assert.IsNotNull(cluster);
            Assert.AreEqual(fakeClusterId, cluster.Id);
        }
コード例 #13
0
        public async Task Handle_NoClustersOnUserByName_CreatesClusterByName()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var user = new User()
            {
                StripeCustomerId = "dummy",
                Clusters         = new List <Cluster>()
                {
                    new Cluster()
                    {
                        Name = "some-non-matching-cluster-1"
                    },
                    new Cluster()
                    {
                        Name = "some-non-matching-cluster-2"
                    }
                }
            };
            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Users.AddAsync(user);
            });

            //Act
            var cluster = await environment.Mediator.Send(new EnsureClusterForUserCommand(user.Id)
            {
                ClusterName = "some-cluster-name"
            });

            //Assert
            Assert.IsNotNull(cluster);
            Assert.AreEqual(cluster.UserId, user.Id);
            Assert.AreEqual("some-cluster-name", cluster.Name);
        }
コード例 #14
0
        public async Task Handle_NoMatchingInstanceFound_DoesNothing()
        {
            //Arrange
            var fakeMediator = Substitute.For <IMediator>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services => services.AddSingleton(fakeMediator)
            });

            //Act
            await environment.Mediator.Send(new DeleteInstanceByPullRequestCommand(
                                                "dummy",
                                                "dummy"));

            //Assert
            await fakeMediator
            .DidNotReceive()
            .Send(Arg.Any <DeleteInstanceByNameCommand>(), default);

            await fakeMediator
            .DidNotReceive()
            .Send(Arg.Any <UpsertPullRequestCommentCommand>(), default);
        }
コード例 #15
0
        public async Task Handle_ExistingPaymentMethodsPresent_DefaultPaymentMethodChangedToAddedMethod()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var user = await environment.Mediator.Send(
                new CreateUserForIdentityCommand(
                    TestClaimsPrincipalFactory.CreateWithIdentityName("some-identity-name")));

            var paymentMethodService = environment
                                       .ServiceProvider
                                       .GetRequiredService <PaymentMethodService>();

            var existingPaymentMethod = await CreatePaymentMethodAsync(paymentMethodService);

            await paymentMethodService.AttachAsync(existingPaymentMethod.Id, new PaymentMethodAttachOptions()
            {
                Customer = user.StripeCustomerId
            });

            var newPaymentMethod = await CreatePaymentMethodAsync(paymentMethodService);

            //Act
            await environment.Mediator.Send(
                new SetActivePaymentMethodForUserCommand(
                    user,
                    newPaymentMethod.Id));

            //Assert
            var stripeCustomer = await environment
                                 .ServiceProvider
                                 .GetRequiredService <CustomerService>()
                                 .GetAsync(user.StripeCustomerId);

            Assert.AreEqual(stripeCustomer.InvoiceSettings.DefaultPaymentMethodId, newPaymentMethod.Id);
        }
        public async Task Handle_MultipleProvisionedInstancesButNoProvisionedPresent_NoLightsailInstancesReturned()
        {
            //Arrange
            var fakeGetInstanceByNameQueryHandler = Substitute.For <IRequestHandler <GetLightsailInstanceByNameQuery, global::Amazon.Lightsail.Model.Instance> >();

            fakeGetInstanceByNameQueryHandler
            .Handle(
                Arg.Is <GetLightsailInstanceByNameQuery>(
                    arg => arg.Name == "some-instance-1"),
                default)
            .Returns(new global::Amazon.Lightsail.Model.Instance()
            {
                Name = "some-instance-1"
            });

            fakeGetInstanceByNameQueryHandler
            .Handle(
                Arg.Is <GetLightsailInstanceByNameQuery>(
                    arg => arg.Name == "some-instance-2"),
                default)
            .Returns(new global::Amazon.Lightsail.Model.Instance()
            {
                Name = "some-instance-2"
            });

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeGetInstanceByNameQueryHandler);
                }
            });

            var userId = Guid.NewGuid();

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Users.AddAsync(new User()
                {
                    Id = userId,
                    StripeCustomerId = "dummy",
                    Clusters         = new List <Cluster>()
                    {
                        new Cluster()
                        {
                            Instances = new List <Instance>()
                            {
                                new Instance()
                                {
                                    Name          = "some-instance-1",
                                    PlanId        = "dummy",
                                    IsProvisioned = false
                                },
                                new Instance()
                                {
                                    Name          = "some-instance-2",
                                    PlanId        = "dummy",
                                    IsProvisioned = false
                                }
                            }
                        }
                    }
                });
            });

            //Act
            var clusters = await environment.Mediator.Send(
                new GetProvisionedClustersWithInstancesForUserQuery(userId));

            //Assert
            Assert.IsNotNull(clusters);
            Assert.AreEqual(0, clusters.Count);

            await fakeGetInstanceByNameQueryHandler
            .DidNotReceive()
            .Handle(
                Arg.Is <GetLightsailInstanceByNameQuery>(
                    arg => arg.Name == "some-instance-1"),
                default);

            await fakeGetInstanceByNameQueryHandler
            .DidNotReceive()
            .Handle(
                Arg.Is <GetLightsailInstanceByNameQuery>(
                    arg => arg.Name == "some-instance-2"),
                default);
        }
コード例 #17
0
        public async Task Handle_ProperArgumentsGivenAtEndOfMonth_FullServerProvisioningFlowIsRunAndProperInstanceIsCreated()
        {
            //Arrange
            var lastDayOfLastMonth =
                new DateTime(
                    DateTime.Now.Year,
                    DateTime.Now.Month,
                    1)
                .AddDays(-1);

            var fakeTimeProvider = Substitute.For <ITimeProvider>();

            fakeTimeProvider
            .UtcNow
            .Returns(lastDayOfLastMonth);

            await using var environment = await IntegrationTestEnvironment.CreateAsync(
                            new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    FakeOutMinimalLightsailFeaturesForFullProvisioning(services);

                    services.AddSingleton(fakeTimeProvider);
                }
            });

            var provisioningService = environment
                                      .ServiceProvider
                                      .GetRequiredService <IProvisioningService>();

            var paymentMethodService = environment
                                       .ServiceProvider
                                       .GetRequiredService <PaymentMethodService>();

            var user = await environment.Mediator.Send(
                new CreateUserForIdentityCommand(
                    TestClaimsPrincipalFactory.CreateWithIdentityName("some-identity-name")));

            var paymentMethod = await CreatePaymentMethodAsync(paymentMethodService);

            await environment.Mediator.Send(
                new SetActivePaymentMethodForUserCommand(
                    user,
                    paymentMethod.Id));

            //Act
            await environment.Mediator.Send(
                new ProvisionInstanceForUserCommand(
                    new Plan(
                        "nano_2_0",
                        1337,
                        new Bundle()
            {
                Price = 1337,
                BundleId = "nano_2_0"
            },
                        Array.Empty <PullDogPlan>()),
                    user));

            await provisioningService.ProcessPendingJobsAsync();

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var provisionedInstance = await dataContext
                                          .Instances
                                          .Include(x => x.Cluster)
                                          .ThenInclude(x => x.User)
                                          .SingleAsync();

                Assert.IsTrue(provisionedInstance.IsProvisioned);
                Assert.AreEqual(provisionedInstance.Cluster.UserId, user.Id);
            });
        }
コード例 #18
0
        public async Task Handle_DatabaseRepositoriesAndGitHubRepositoriesFoundWithNoOverlap_IncludesAllRepositories()
        {
            //Arrange
            var fakeGitHubClientFactory = Substitute.For <IGitHubClientFactory>();

            var fakeGitHubClient = await fakeGitHubClientFactory.CreateInstallationClientAsync(1337);

            fakeGitHubClient
            .GitHubApps
            .Installation
            .GetAllRepositoriesForCurrent()
            .Returns(new RepositoriesResponse(
                         2,
                         new[]
            {
                new Repository(1),
                new Repository(2)
            }));

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services => services.AddSingleton(fakeGitHubClientFactory)
            });

            var userId = Guid.NewGuid();
            await environment.WithFreshDataContext(async dataContext =>
                                                   await dataContext.Users.AddAsync(new User()
            {
                Id = userId,
                StripeCustomerId = "dummy",
                PullDogSettings  = new PullDogSettings()
                {
                    PlanId          = "dummy",
                    EncryptedApiKey = Array.Empty <byte>(),
                    Repositories    = new List <PullDogRepository>()
                    {
                        new PullDogRepository()
                        {
                            Handle = "3",
                            GitHubInstallationId = 1337
                        },
                        new PullDogRepository()
                        {
                            Handle = "4",
                            GitHubInstallationId = 1337
                        }
                    }
                }
            }));

            //Act
            var repositories = await environment.Mediator.Send(
                new GetRepositoriesForUserQuery(userId),
                default);

            //Assert
            Assert.IsNotNull(repositories);
            Assert.AreEqual(4, repositories.Count);

            Assert.IsTrue(repositories.Any(x => x.Handle == "1" && x.PullDogId == null));
            Assert.IsTrue(repositories.Any(x => x.Handle == "2" && x.PullDogId == null));
            Assert.IsTrue(repositories.Any(x => x.Handle == "3" && x.PullDogId != null));
            Assert.IsTrue(repositories.Any(x => x.Handle == "4" && x.PullDogId != null));
        }
        public async Task Handle_SeveralSettings_ReturnsMatchingSettings()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.PullDogSettings.AddAsync(new PullDogSettings()
                {
                    PlanId = "dummy",
                    User   = new User()
                    {
                        StripeCustomerId = "dummy"
                    },
                    EncryptedApiKey = Array.Empty <byte>(),
                    Repositories    = new List <PullDogRepository>()
                    {
                        new PullDogRepository()
                        {
                            Handle = "dummy",
                            GitHubInstallationId = 1336
                        }
                    }
                });
                await dataContext.PullDogSettings.AddAsync(new PullDogSettings()
                {
                    PlanId = "dummy",
                    User   = new User()
                    {
                        StripeCustomerId = "dummy"
                    },
                    EncryptedApiKey = Array.Empty <byte>(),
                    Repositories    = new List <PullDogRepository>()
                    {
                        new PullDogRepository()
                        {
                            Handle = "dummy",
                            GitHubInstallationId = 1337
                        }
                    }
                });
                await dataContext.PullDogSettings.AddAsync(new PullDogSettings()
                {
                    PlanId = "dummy",
                    User   = new User()
                    {
                        StripeCustomerId = "dummy"
                    },
                    EncryptedApiKey = Array.Empty <byte>(),
                    Repositories    = new List <PullDogRepository>()
                    {
                        new PullDogRepository()
                        {
                            Handle = "dummy",
                            GitHubInstallationId = 1338
                        }
                    }
                });
            });

            //Act
            var settings = await environment.Mediator.Send(new GetPullDogSettingsByGitHubInstallationIdQuery(1337));

            //Assert
            Assert.IsNotNull(settings);
            Assert.AreEqual(1337, settings.Repositories.Single().GitHubInstallationId);
        }
コード例 #20
0
        public async Task PullDogWebhook_PullRequestNumberGivenViaPullRequest_FetchesPullRequestFromPullRequestNumber()
        {
            //Arrange
            var context = new WebhookPayloadContext(
                new WebhookPayload()
            {
                Repository = new RepositoryPayload()
                {
                    Id = 1337
                },
                PullRequest = new PullRequestPayload()
                {
                    Number = 1339
                },
                Installation = new InstallationPayload()
                {
                    Id = 1338
                }
            },
                null !,
                null !,
                null !);

            var fakeMediator = Substitute.For <IMediator>();

            fakeMediator
            .Send(Arg.Is <EnsurePullDogPullRequestCommand>(args =>
                                                           args.PullRequestHandle == "1339"))
            .Returns(new PullDogPullRequest());

            fakeMediator
            .Send(Arg.Is <GetRepositoryByHandleQuery>(args => args.RepositoryHandle == "1337"))
            .Returns(new PullDogRepository()
            {
                GitHubInstallationId = 1338,
                PullDogSettings      = new PullDogSettings()
            });

            var fakeWebhookPayloadHandler = Substitute.For <IWebhookPayloadHandler>();

            fakeWebhookPayloadHandler
            .CanHandle(Arg.Any <WebhookPayload>())
            .Returns(true);

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeMediator);
                    services.AddSingleton(fakeWebhookPayloadHandler);

                    services.AddSingleton(Substitute.For <IConfigurationPayloadHandler>());
                    services.AddSingleton(GenerateTestGitHubOptions());
                }
            });

            var webhooksController = environment.ServiceProvider.GetRequiredService <WebhooksController>();

            FakeOutAuthenticResponse(webhooksController);

            //Act
            await webhooksController.PullDogWebhook(context.Payload, default);

            //Assert
            fakeWebhookPayloadHandler
            .Received(1)
            .CanHandle(Arg.Any <WebhookPayload>());

            await fakeWebhookPayloadHandler
            .Received(1)
            .HandleAsync(Arg.Is <WebhookPayloadContext>(args =>
                                                        args.PullRequest != null));
        }
コード例 #21
0
        public async Task Handle_DatabaseInstancePresentAndAmazonExceptionThrown_NothingIsRemovedFromDatabase()
        {
            //Arrange
            var fakeAmazonLightsailClient = Substitute.For <IAmazonLightsail>();

            fakeAmazonLightsailClient
            .DeleteInstanceAsync(
                Arg.Any <DeleteInstanceRequest>(),
                default)
            .Throws <TestException>();

            var fakeLightsailOperationService = Substitute.For <ILightsailOperationService>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeAmazonLightsailClient);
                    services.AddSingleton(fakeLightsailOperationService);
                }
            });

            var clusterId = Guid.NewGuid();
            var cluster   = new Cluster()
            {
                Id        = clusterId,
                Instances = new List <Instance>()
                {
                    new Instance()
                    {
                        Name   = "not-matching",
                        PlanId = "dummy"
                    },
                    new Instance()
                    {
                        Name   = "some-instance-name",
                        PlanId = "dummy"
                    }
                }
            };

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Clusters.AddAsync(cluster);
            });

            //Act
            var exception = await Assert.ThrowsExceptionAsync <TestException>(async() =>
                                                                              await environment.Mediator.Send(
                                                                                  new DeleteInstanceByNameCommand("some-instance-name"),
                                                                                  default));

            //Assert
            Assert.IsNotNull(exception);

            await environment.WithFreshDataContext(async dataContext =>
            {
                var refreshedCluster = await dataContext
                                       .Clusters
                                       .Include(x => x.Instances)
                                       .FirstOrDefaultAsync(x => x.Id == clusterId);
                var refreshedInstance = await dataContext.Instances.FirstOrDefaultAsync(x => x.Name == "some-instance-name");

                Assert.IsNotNull(refreshedInstance);
                Assert.IsNotNull(refreshedCluster);

                Assert.AreEqual(2, refreshedCluster.Instances.Count);
            });
        }
コード例 #22
0
        public async Task Dogfeed_ExistingDetachedInstances_DestroysDetachedInstances()
        {
            //Arrange
            var fakeMediator = Substitute.For <IMediator>();

            FakeOutValidSupportedPlans(fakeMediator);
            FakeOutNewlyCreatedInstance(fakeMediator);

            fakeMediator
            .Send(Arg.Any <GetAllInstancesQuery>())
            .Returns(
                new[]
            {
                new Instance()
                {
                    Name = "some-random-instance"
                },
                new Instance()
                {
                    Name = "main-attached-instance-from-load-balancer"
                },
                new Instance()
                {
                    Name = "main-detached-instance-from-load-balancer"
                }
            });

            fakeMediator
            .Send(Arg.Any <GetLoadBalancerByNameQuery>())
            .Returns(
                new LoadBalancer()
            {
                InstanceHealthSummary = new List <InstanceHealthSummary>()
                {
                    new InstanceHealthSummary()
                    {
                        InstanceName   = "main-attached-instance-from-load-balancer",
                        InstanceHealth = InstanceHealthState.Healthy
                    },
                    new InstanceHealthSummary()
                    {
                        InstanceName   = "new-instance",
                        InstanceHealth = InstanceHealthState.Healthy
                    }
                }
            });

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    IocRegistry.ConfigureDogfeeding(services);
                    FakeOutMinimalServices(services);

                    services.AddSingleton(fakeMediator);
                }
            });

            var dogfeedService = environment.ServiceProvider.GetRequiredService <IDogfeedService>();

            //Act
            await dogfeedService.DogfeedAsync();

            //Assert
            await fakeMediator
            .Received()
            .Send(Arg.Is <DeleteInstanceByNameCommand>(
                      arg => arg.Name == "main-detached-instance-from-load-balancer"));
        }
コード例 #23
0
        public async Task Dogfeed_NewInstanceHealthTimeoutExceeded_ThrowsException()
        {
            //Arrange
            var fakeMediator = Substitute.For <IMediator>();

            FakeOutValidSupportedPlans(fakeMediator);
            FakeOutNewlyCreatedInstance(fakeMediator);

            fakeMediator
            .Send(Arg.Any <GetLoadBalancerByNameQuery>())
            .Returns(
                new LoadBalancer()
            {
                InstanceHealthSummary = new List <InstanceHealthSummary>()
                {
                    new InstanceHealthSummary()
                    {
                        InstanceName   = "new-instance",
                        InstanceHealth = InstanceHealthState.Healthy
                    }
                }
            });

            var fakeStopwatchWithHighElapsedTime = Substitute.For <IStopwatch>();

            fakeStopwatchWithHighElapsedTime
            .Elapsed
            .Returns(TimeSpan.FromMinutes(31));

            var fakeTime = Substitute.For <ITime>();

            fakeTime
            .StartStopwatch()
            .Returns(fakeStopwatchWithHighElapsedTime);

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    IocRegistry.ConfigureDogfeeding(services);
                    FakeOutMinimalServices(services);

                    services.AddSingleton(fakeMediator);
                    services.AddSingleton(fakeTime);
                }
            });

            var dogfeedService = environment.ServiceProvider.GetRequiredService <IDogfeedService>();

            //Act
            var exception = await Assert.ThrowsExceptionAsync <NewInstanceHealthTimeoutException>(async() =>
                                                                                                  await dogfeedService.DogfeedAsync());

            //Assert
            Assert.IsNotNull(exception);

            await fakeMediator
            .Received()
            .Send(Arg.Is <DeleteInstanceByNameCommand>(
                      arg => arg.Name == "new-instance"));
        }
コード例 #24
0
        public async Task Handle_ExistingClusterInstanceFoundAndDemoUserWithNoExpiry_ReducesExpiryToAnHour()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var pullDogPullRequest = new PullDogPullRequest()
            {
                Handle            = "dummy",
                PullDogRepository = new PullDogRepository()
                {
                    Handle          = "dummy",
                    PullDogSettings = new PullDogSettings()
                    {
                        User = new User()
                        {
                            StripeCustomerId = "dummy"
                        },
                        PoolSize        = 0,
                        PlanId          = "dummy",
                        EncryptedApiKey = Array.Empty <byte>()
                    }
                }
            };

            var oldInstance = new Instance()
            {
                Name               = "existing-instance",
                PlanId             = "dummy",
                PullDogPullRequest = pullDogPullRequest,
                Cluster            = new Cluster()
                {
                    Id = DataContext.PullDogDemoClusterId
                }
            };

            await environment.DataContext.Instances.AddAsync(oldInstance);

            await environment.DataContext.SaveChangesAsync();

            //Act
            var instance = await environment.Mediator.Send(new EnsurePullDogDatabaseInstanceCommand(
                                                               pullDogPullRequest,
                                                               new ConfigurationFile(new List <string>())));

            //Assert
            Assert.AreSame(instance, oldInstance);
            Assert.AreEqual(instance.Name, oldInstance.Name);
            Assert.AreEqual(instance.Id, oldInstance.Id);

            await environment.WithFreshDataContext(async dataContext =>
            {
                var refreshedOldInstance = await dataContext
                                           .Instances
                                           .AsNoTracking()
                                           .SingleAsync();

                Assert.AreEqual(oldInstance.Name, refreshedOldInstance.Name);
                Assert.AreEqual(oldInstance.Id, refreshedOldInstance.Id);
                Assert.IsTrue(refreshedOldInstance.ExpiresAtUtc < DateTime.UtcNow.AddHours(1));
                Assert.IsTrue(refreshedOldInstance.ExpiresAtUtc > DateTime.UtcNow.AddMinutes(50));
            });
        }
コード例 #25
0
        public async Task Handle_MatchingAndUnmatchingRepositoriesPresent_DeletesMatchingRepository()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.PullDogRepositories.AddAsync(new PullDogRepository()
                {
                    Handle          = "dummy-1",
                    PullDogSettings = new PullDogSettings()
                    {
                        EncryptedApiKey = Array.Empty <byte>(),
                        PlanId          = "dummy",
                        User            = new User()
                        {
                            StripeCustomerId = "dummy"
                        }
                    },
                });

                await dataContext.PullDogRepositories.AddAsync(new PullDogRepository()
                {
                    Handle          = "some-handle",
                    PullDogSettings = new PullDogSettings()
                    {
                        EncryptedApiKey = Array.Empty <byte>(),
                        PlanId          = "dummy",
                        User            = new User()
                        {
                            StripeCustomerId = "dummy"
                        }
                    },
                });

                await dataContext.PullDogRepositories.AddAsync(new PullDogRepository()
                {
                    Handle          = "dummy-2",
                    PullDogSettings = new PullDogSettings()
                    {
                        EncryptedApiKey = Array.Empty <byte>(),
                        PlanId          = "dummy",
                        User            = new User()
                        {
                            StripeCustomerId = "dummy"
                        }
                    },
                });
            });

            Assert.AreEqual(3, await environment.DataContext.PullDogRepositories.CountAsync());

            //Act
            await environment.Mediator.Send(new DeletePullDogRepositoryCommand(
                                                "some-handle"));

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var repositories = await dataContext
                                   .PullDogRepositories
                                   .ToListAsync();
                Assert.AreEqual(2, repositories.Count);

                Assert.IsFalse(repositories.Any(r => r.Handle == "some-handle"));
            });
        }
コード例 #26
0
        public async Task Handle_DatabaseInstancesPresentWithPullRequests_InstanceIsRemovedFromClusterAndDatabase()
        {
            //Arrange
            var fakeAmazonLightsailClient = Substitute.For <IAmazonLightsail>();

            fakeAmazonLightsailClient
            .DeleteInstanceAsync(
                Arg.Any <DeleteInstanceRequest>(),
                default)
            .Returns(new DeleteInstanceResponse()
            {
                Operations = new List <Operation>()
            });

            var fakeLightsailOperationService = Substitute.For <ILightsailOperationService>();

            var fakeGitHubClient = Substitute.For <IGitHubClient>();

            fakeGitHubClient
            .GitHubApps
            .CreateInstallationToken(Arg.Any <long>())
            .Returns(new AccessToken("dummy", DateTimeOffset.MaxValue));

            var fakeGitHubClientFactory = Substitute.For <IGitHubClientFactory>();

            var fakeGitHubInstallationClient = await fakeGitHubClientFactory.CreateInstallationClientAsync(1337);

            fakeGitHubInstallationClient
            .PullRequest
            .Get(
                1338,
                1339)
            .Returns(
                CreatePullRequestDto(
                    CreateGitReferenceDto(
                        CreateRepositoryDto(
                            1338)),
                    CreateGitReferenceDto(
                        CreateRepositoryDto(
                            1338))));

            var fakePullDogFileCollectorFactory = Substitute.For <IPullDogFileCollectorFactory>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeAmazonLightsailClient);
                    services.AddSingleton(fakeLightsailOperationService);
                    services.AddSingleton(fakeGitHubClient);
                    services.AddSingleton(fakeGitHubClientFactory);
                    services.AddSingleton(fakePullDogFileCollectorFactory);
                }
            });

            var clusterId = Guid.NewGuid();
            var cluster   = new Cluster()
            {
                Id        = clusterId,
                Instances = new List <Instance>()
                {
                    new Instance()
                    {
                        Name               = "not-matching",
                        PlanId             = "dummy",
                        PullDogPullRequest = new PullDogPullRequest()
                        {
                            Handle            = "dummy",
                            PullDogRepository = new PullDogRepository()
                            {
                                Handle          = "dummy",
                                PullDogSettings = new PullDogSettings()
                                {
                                    PlanId          = "dummy",
                                    EncryptedApiKey = Array.Empty <byte>(),
                                    User            = new User()
                                    {
                                        StripeCustomerId = "dummy"
                                    }
                                }
                            }
                        }
                    },
                    new Instance()
                    {
                        Name               = "some-instance-name",
                        PlanId             = "dummy",
                        PullDogPullRequest = new PullDogPullRequest()
                        {
                            Handle            = "1339",
                            PullDogRepository = new PullDogRepository()
                            {
                                Handle = "1338",
                                GitHubInstallationId = 1337,
                                PullDogSettings      = new PullDogSettings()
                                {
                                    PlanId          = "dummy",
                                    EncryptedApiKey = Array.Empty <byte>(),
                                    User            = new User()
                                    {
                                        StripeCustomerId = "dummy"
                                    }
                                }
                            }
                        }
                    }
                }
            };

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Clusters.AddAsync(cluster);
            });

            //Act
            await environment.Mediator.Send(
                new DeleteInstanceByNameCommand("some-instance-name"),
                default);

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var refreshedCluster = await dataContext
                                       .Clusters
                                       .Include(x => x.Instances)
                                       .FirstOrDefaultAsync(x => x.Id == clusterId);
                var refreshedInstance = await dataContext.Instances.FirstOrDefaultAsync(x => x.Name == "some-instance-name");

                Assert.IsNull(refreshedInstance);
                Assert.IsNotNull(refreshedCluster);

                Assert.AreEqual(1, refreshedCluster.Instances.Count);
                Assert.AreNotEqual("some-instance-name", refreshedCluster.Instances.Single().Name);
            });
        }
コード例 #27
0
        public async Task Handle_ExistingAuth0UserByGitHubUserIdPresent_DoesNothing()
        {
            //Arrange
            var fakeMediator            = Substitute.For <IMediator>();
            var fakeGitHubClientFactory = Substitute.For <IGitHubClientFactory>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeGitHubClientFactory);
                    services.AddSingleton(fakeMediator);
                }
            });

            var userInDatabase = new Dogger.Domain.Models.User()
            {
                StripeCustomerId = "dummy",
                PullDogSettings  = new PullDogSettings()
                {
                    PlanId          = "some-plan-id",
                    EncryptedApiKey = Array.Empty <byte>(),
                    Repositories    = new List <PullDogRepository>()
                    {
                        new PullDogRepository()
                        {
                            GitHubInstallationId = 1337,
                            Handle = "dummy"
                        }
                    }
                }
            };
            await environment.DataContext.Users.AddAsync(userInDatabase);

            await environment.DataContext.SaveChangesAsync();

            fakeMediator
            .Send(Arg.Any <GetDemoPlanQuery>())
            .Returns(
                new Dogger.Domain.Queries.Plans.GetSupportedPlans.Plan(
                    "demo-plan-id",
                    1337,
                    new Bundle()
            {
                Price    = 1337,
                BundleId = "demo-plan-id"
            },
                    Array.Empty <PullDogPlan>()));

            fakeMediator
            .Send(Arg.Any <GetAuth0UserFromGitHubUserIdQuery>())
            .Returns(new global::Auth0.ManagementApi.Models.User()
            {
                UserId = "auth0-user-id"
            });

            fakeMediator
            .Send(Arg.Is <EnsureUserForIdentityCommand>(args =>
                                                        args.Email == "*****@*****.**" &&
                                                        args.IdentityName == "auth0-user-id"))
            .Returns(userInDatabase);

            var fakeGitHubInstallationClient = await fakeGitHubClientFactory.CreateInstallationClientAsync(1337);

            fakeGitHubInstallationClient
            .GitHubApps
            .Installation
            .GetAllRepositoriesForCurrent()
            .Returns(new RepositoriesResponse(0, Array.Empty <Repository>()));

            var fakeGitHubClient = await fakeGitHubClientFactory.CreateInstallationInitiatorClientAsync("some-code");

            fakeGitHubClient
            .User
            .Current()
            .Returns(new User());

            fakeGitHubClient
            .User
            .Email
            .GetAll()
            .Returns(new[]
            {
                new EmailAddress("*****@*****.**", true, false, EmailVisibility.Public),
                new EmailAddress("*****@*****.**", true, true, EmailVisibility.Public),
                new EmailAddress("*****@*****.**", true, false, EmailVisibility.Public)
            });

            //Act
            await environment.Mediator.Send(new InstallPullDogFromGitHubCommand(
                                                "some-code",
                                                1337));

            //Assert
            await environment.WithFreshDataContext(async dataContext =>
            {
                var user = await dataContext
                           .Users
                           .Include(x => x.PullDogSettings)
                           .ThenInclude(x => x.Repositories)
                           .SingleAsync();
                var settings = user.PullDogSettings;

                Assert.AreEqual(settings.Repositories.Single().GitHubInstallationId, 1337);
                Assert.AreEqual(settings.PlanId, "some-plan-id");
                Assert.AreEqual(settings.PoolSize, 0);
            });
        }
コード例 #28
0
        public async Task Handle_ExistingClusterInstanceFoundAndPaidUser_ReusesExistingInstanceAndUpdatesExpiry()
        {
            //Arrange
            await using var environment = await IntegrationTestEnvironment.CreateAsync();

            var user = new User()
            {
                StripeCustomerId = "dummy"
            };

            var pullDogPullRequest = new PullDogPullRequest()
            {
                Handle            = "dummy",
                PullDogRepository = new PullDogRepository()
                {
                    Handle          = "dummy",
                    PullDogSettings = new PullDogSettings()
                    {
                        User            = user,
                        PoolSize        = 1,
                        PlanId          = "dummy",
                        EncryptedApiKey = Array.Empty <byte>()
                    }
                }
            };

            var oldInstance = new Instance()
            {
                Name               = "existing-instance",
                PlanId             = "dummy",
                PullDogPullRequest = pullDogPullRequest,
                Cluster            = new Cluster()
                {
                    Name = "pull-dog",
                    User = user
                }
            };

            await environment.DataContext.Instances.AddAsync(oldInstance);

            await environment.DataContext.SaveChangesAsync();

            //Act
            var instance = await environment.Mediator.Send(new EnsurePullDogDatabaseInstanceCommand(
                                                               pullDogPullRequest,
                                                               new ConfigurationFile(new List <string>())
            {
                Expiry = TimeSpan.FromDays(30)
            }));

            //Assert
            Assert.AreSame(instance, oldInstance);
            Assert.AreEqual(instance.Name, oldInstance.Name);
            Assert.AreEqual(instance.Id, oldInstance.Id);

            await environment.WithFreshDataContext(async dataContext =>
            {
                var refreshedOldInstance = await dataContext
                                           .Instances
                                           .AsNoTracking()
                                           .SingleOrDefaultAsync(x =>
                                                                 x.Name == oldInstance.Name &&
                                                                 x.Id == oldInstance.Id &&
                                                                 x.ExpiresAtUtc > DateTime.UtcNow.AddDays(7));
                Assert.IsNotNull(refreshedOldInstance);
            });
        }
コード例 #29
0
        public async Task Handle_DeletionOfInstancesThrowsException_NothingIsCommitted()
        {
            //Arrange
            var fakeMediator = Substitute.For <IMediator>();

            fakeMediator
            .Send(Arg.Is <GetPullDogPlanFromSettingsQuery>(args =>
                                                           args.DoggerPlanId == "some-new-plan-id"))
            .Returns(new PullDogPlan(
                         "some-plan-id",
                         1337,
                         1338));

            fakeMediator
            .Send(Arg.Any <DeleteAllPullDogInstancesForUserCommand>())
            .Throws(new TestException());

            await using var environment = await IntegrationTestEnvironment.CreateAsync(
                            new EnvironmentSetupOptions ()
            {
                IocConfiguration = services => services.AddSingleton(fakeMediator)
            });

            var databaseUser = new User()
            {
                StripeCustomerId = "dummy",
                PullDogSettings  = new PullDogSettings()
                {
                    PoolSize        = 1337,
                    EncryptedApiKey = Array.Empty <byte>(),
                    PlanId          = "some-plan-id"
                }
            };

            fakeMediator
            .Send(Arg.Is <GetUserByIdQuery>(args => args.UserId == databaseUser.Id))
            .Returns(databaseUser);

            await environment.WithFreshDataContext(async dataContext =>
                                                   await dataContext.Users.AddAsync(databaseUser));

            //Act
            var exception = await Assert.ThrowsExceptionAsync <TestException>(async() =>
                                                                              await environment.Mediator.Send(new ChangePullDogPlanCommand(
                                                                                                                  databaseUser.Id,
                                                                                                                  1338,
                                                                                                                  "some-new-plan-id")));

            //Assert
            Assert.IsNotNull(exception);

            await environment.WithFreshDataContext(async dataContext =>
            {
                var refreshedSettings = await dataContext
                                        .PullDogSettings
                                        .AsNoTracking()
                                        .SingleOrDefaultAsync();
                Assert.IsNotNull(refreshedSettings);

                Assert.AreEqual(1337, refreshedSettings.PoolSize);
                Assert.AreEqual("some-plan-id", refreshedSettings.PlanId);
            });
        }
コード例 #30
0
        public async Task Handle_ProvisionedNonFreeInstancePresent_SubscriptionIsUpdated()
        {
            //Arrange
            var fakeInstanceId = Guid.NewGuid();

            var fakeAmazonLightsailClient = Substitute.For <IAmazonLightsail>();

            fakeAmazonLightsailClient
            .DeleteInstanceAsync(
                Arg.Any <DeleteInstanceRequest>(),
                default)
            .Returns(new DeleteInstanceResponse()
            {
                Operations = new List <Operation>()
            });

            var fakeLightsailOperationService = Substitute.For <ILightsailOperationService>();

            await using var environment = await IntegrationTestEnvironment.CreateAsync(new EnvironmentSetupOptions ()
            {
                IocConfiguration = services =>
                {
                    services.AddSingleton(fakeAmazonLightsailClient);
                    services.AddSingleton(fakeLightsailOperationService);
                }
            });

            var stripeCustomerService = environment.ServiceProvider.GetRequiredService <CustomerService>();
            var customer = await stripeCustomerService.CreateAsync(new CustomerCreateOptions()
            {
                Email = "*****@*****.**"
            });

            var stripePaymentMethodService = environment.ServiceProvider.GetRequiredService <PaymentMethodService>();
            var paymentMethod = await stripePaymentMethodService.AttachAsync("pm_card_visa", new PaymentMethodAttachOptions()
            {
                Customer = customer.Id
            });

            var stripeSubscriptionService = environment.ServiceProvider.GetRequiredService <SubscriptionService>();
            var subscription = await stripeSubscriptionService.CreateAsync(new SubscriptionCreateOptions()
            {
                Customer             = customer.Id,
                DefaultPaymentMethod = paymentMethod.Id,
                Items = new List <SubscriptionItemOptions>()
                {
                    new SubscriptionItemOptions()
                    {
                        Plan = "nano_2_0"
                    }
                },
                Metadata = new Dictionary <string, string>()
                {
                    {
                        "InstanceId", fakeInstanceId.ToString()
                    }
                }
            });

            var clusterId = Guid.NewGuid();
            var user      = new User()
            {
                StripeCustomerId     = customer.Id,
                StripeSubscriptionId = subscription.Id
            };

            var cluster = new Cluster()
            {
                Id     = clusterId,
                User   = user,
                UserId = user.Id
            };

            user.Clusters.Add(cluster);

            var instance = new Instance()
            {
                Id            = fakeInstanceId,
                Name          = "some-instance-name",
                PlanId        = "dummy",
                Cluster       = cluster,
                ClusterId     = cluster.Id,
                IsProvisioned = true
            };

            cluster.Instances.Add(instance);

            await environment.WithFreshDataContext(async dataContext =>
            {
                await dataContext.Clusters.AddAsync(cluster);
                await dataContext.Instances.AddAsync(instance);
                await dataContext.Users.AddAsync(user);
            });

            //Act
            await environment.Mediator.Send(
                new DeleteInstanceByNameCommand("some-instance-name"),
                default);

            //Assert
            var refreshedSubscription = await stripeSubscriptionService.GetAsync(subscription.Id);

            Assert.AreNotEqual(refreshedSubscription.Status, subscription.Status);
            Assert.AreEqual("canceled", refreshedSubscription.Status);
        }