public void ShouldFailIfStackExists()
        {
            var logger            = new TestLogger(this.output);
            var mockClientFactory = TestHelpers.GetClientFactoryMock();
            var mockContext       = TestHelpers.GetContextMock(logger);

            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.Setup(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default)).ReturnsAsync(
                new DescribeStacksResponse {
                Stacks = new List <Stack> {
                    new Stack {
                        StackName = StackName
                    }
                }
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath)
                         .Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.CreateStackAsync();

            action.Should().Throw <StackOperationException>().And.OperationalState.Should().Be(StackOperationalState.Exists);
        }
        public async void ShouldCreateStackIfStackDoesNotExist()
        {
            var logger            = new TestLogger(this.output);
            var mockClientFactory = TestHelpers.GetClientFactoryMock();
            var mockContext       = TestHelpers.GetContextMock(logger);

            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .Throws(new AmazonCloudFormationException($"Stack with id {StackName} does not exist")).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackId     = StackId,
                        StackStatus = StackStatus.CREATE_COMPLETE
                    }
                }
            });

            mockCloudFormation.Setup(cf => cf.CreateStackAsync(It.IsAny <CreateStackRequest>(), default))
            .ReturnsAsync(new CreateStackResponse {
                StackId = StackId
            });

            mockCloudFormation.Setup(cf => cf.DescribeStackEventsAsync(It.IsAny <DescribeStackEventsRequest>(), default))
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent
                    {
                        StackName      = StackName,
                        StackId        = StackId,
                        ResourceStatus = ResourceStatus.CREATE_COMPLETE,
                        Timestamp      = DateTime.Now.AddSeconds(1)
                    }
                }
            });

            mockCloudFormation.Setup(cf => cf.DescribeStackResourcesAsync(It.IsAny <DescribeStackResourcesRequest>(), default))
            .ReturnsAsync(new DescribeStackResourcesResponse {
                StackResources = new List <StackResource>()
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithFollowOperation()
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath)
                         .Build();

            (await runner.CreateStackAsync()).StackOperationResult.Should().Be(StackOperationResult.StackCreated);
            logger.StackEvents.Count.Should().BeGreaterThan(0);
        }
Exemple #3
0
        public async Task ShouldReturnStackUpdateInProgressIfWaitForInProgressUpdateNotSpecified()
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete)
            .ReturnsAsync(ResponseStackCreateComplete);

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_COMPLETE, Changes = StockChange
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeStackEventsAsync(It.IsAny <DescribeStackEventsRequest>(), default))
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent
                    {
                        StackName      = StackName,
                        StackId        = StackId,
                        ResourceStatus = ResourceStatus.CREATE_COMPLETE,
                        Timestamp      = DateTime.Now.AddDays(-1)
                    }
                }
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).Build();

            (await runner.UpdateStackAsync(_ => true)).StackOperationResult.Should()
            .Be(StackOperationResult.StackUpdateInProgress);
            logger.ChangeSets.Count.Should().BeGreaterThan(0);
            logger.InfoMessages.Any(i => i.Contains($"Updating stack '{StackName}'")).Should().BeTrue();
        }
Exemple #4
0
        public void ShouldThrowIfAnotherUpdateBeganWhileUserWasReviewingChangeset()
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackStatus = StackStatus.UPDATE_IN_PROGRESS,
                        Parameters  = new List <Parameter>()
                    }
                }
            });

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_COMPLETE, Changes = StockChange
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.UpdateStackAsync(_ => true);

            action.Should().Throw <StackOperationException>().WithMessage("Stack is being modified by another process.");
            logger.ChangeSets.Count.Should().BeGreaterThan(0);
        }
Exemple #5
0
        public async Task ShouldUploadToS3BeforeCreateChangesetIfForceS3IsSet()
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockS3Util         = TestHelpers.GetS3UtilMock();
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockContext.Setup(ctx => ctx.S3Util).Returns(mockS3Util.Object);

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete);

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_COMPLETE, Changes = StockChange
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).WithChangesetOnly().WithForceS3()
                         .Build();

            (await runner.UpdateStackAsync(null)).StackOperationResult.Should().Be(StackOperationResult.NoChange);
            mockS3Util.Verify(
                s3 => s3.UploadOversizeArtifactToS3(
                    It.IsAny <string>(),
                    It.IsAny <string>(),
                    It.IsAny <string>(),
                    It.IsAny <UploadFileType>()),
                Times.Exactly(1));

            logger.InfoMessages.Last().Should().Be("Not updating stack since CreateChangesetOnly = true");
            logger.ChangeSets.Count.Should().BeGreaterThan(0);
        }
Exemple #6
0
        public void ShouldFailIfStackBusyAndWaitIsFalse(string stackStatus)
        {
            var expectedMessage    = "Stack is being modified by another process.";
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackStatus = StackStatus.FindValue(stackStatus)
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackStatus = StackStatus.FindValue(stackStatus)
                    }
                }
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.UpdateStackAsync(null);

            action.Should().Throw <StackOperationException>().WithMessage(expectedMessage);
        }
Exemple #7
0
        public void ShouldFailIfStackDoesNotExist()
        {
            var logger            = new TestLogger(this.output);
            var mockClientFactory = TestHelpers.GetClientFactoryMock();
            var mockContext       = TestHelpers.GetContextMock(logger);

            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.Setup(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default)).Throws(
                new AmazonCloudFormationException($"Stack with id {StackName} does not exist"));

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.UpdateStackAsync(null);

            action.Should().Throw <StackOperationException>().WithMessage("Stack does not exist.");
        }
        public void ShouldFailIfStackIsBrokenOrBusy(string stackStatus, StackOperationalState expectedOutcome)
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.FindValue(stackStatus)
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.FindValue(stackStatus)
                    }
                }
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.DeleteStackAsync();

            action.Should().Throw <StackOperationException>().And.OperationalState.Should().Be(expectedOutcome);
        }
        public void ShouldFailIfStackDoesNotExist()
        {
            var logger            = new TestLogger(this.output);
            var mockClientFactory = TestHelpers.GetClientFactoryMock();
            var mockContext       = TestHelpers.GetContextMock(logger);

            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.Setup(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default)).Throws(
                new AmazonCloudFormationException($"Stack with id {StackName} does not exist"));

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithFollowOperation()
                         .Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.DeleteStackAsync();

            action.Should().Throw <StackOperationException>().And.OperationalState.Should().Be(StackOperationalState.NotFound);
        }
Exemple #10
0
        public async Task ShouldCreateChangesetAndNotUpdateStackIfChangesetOnlySpecified()
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete);

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_COMPLETE, Changes = StockChange
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).WithChangesetOnly().Build();

            (await runner.UpdateStackAsync(null)).StackOperationResult.Should().Be(StackOperationResult.NoChange);
            logger.InfoMessages.Last().Should().Be("Not updating stack since CreateChangesetOnly = true");
            logger.ChangeSets.Count.Should().BeGreaterThan(0);
        }
Exemple #11
0
        public async Task ShouldReturnNoChangeIfChangesetReportsNoChanges(string statusReason)
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete);

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.FAILED, StatusReason = statusReason
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).Build();

            (await runner.UpdateStackAsync(null)).StackOperationResult.Should().Be(StackOperationResult.NoChange);
            logger.ChangeSets.Count.Should().Be(0);
        }
        public async void ShouldDeleteStackIfStackExistsAndIsInCorrectState(string status)
        {
            var logger            = new TestLogger(this.output);
            var mockClientFactory = TestHelpers.GetClientFactoryMock();
            var mockContext       = TestHelpers.GetContextMock(logger);

            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.FindValue(status)
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.FindValue(status)
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.DELETE_IN_PROGRESS
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName = StackName, StackStatus = StackStatus.DELETE_COMPLETE
                    }
                }
            });

            mockCloudFormation.Setup(cf => cf.DeleteStackAsync(It.IsAny <DeleteStackRequest>(), default))
            .ReturnsAsync(new DeleteStackResponse());

            mockCloudFormation.SetupSequence(cf => cf.DescribeStackEventsAsync(It.IsAny <DescribeStackEventsRequest>(), default))
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent
                    {
                        StackName      = StackName,
                        StackId        = StackId,
                        ResourceStatus = status,
                        Timestamp      = DateTime.Now.AddDays(-1)
                    }
                }
            })
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent
                    {
                        StackName      = StackName,
                        StackId        = StackId,
                        ResourceStatus = ResourceStatus.DELETE_COMPLETE,
                        Timestamp      = DateTime.Now.AddSeconds(1)
                    }
                }
            })
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>()
            });

            mockCloudFormation.Setup(cf => cf.DescribeStackResourcesAsync(It.IsAny <DescribeStackResourcesRequest>(), default))
            .ReturnsAsync(new DescribeStackResourcesResponse {
                StackResources = new List <StackResource>()
            });

            mockCloudFormation.Setup(cf => cf.GetTemplateAsync(It.IsAny <GetTemplateRequest>(), default)).ReturnsAsync(
                new GetTemplateResponse
            {
                TemplateBody = this.fixture.TestStackJsonString
            });

            mockCloudFormation.Setup(cf => cf.GetTemplateSummaryAsync(It.IsAny <GetTemplateSummaryRequest>(), default))
            .ReturnsAsync(new GetTemplateSummaryResponse {
                Parameters = new List <ParameterDeclaration>()
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);
            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithFollowOperation()
                         .Build();

            (await runner.DeleteStackAsync()).StackOperationResult.Should().Be(StackOperationResult.StackDeleted);
            logger.StackEvents.Count.Should().BeGreaterThan(0);
        }
 /// <summary>
 /// Gets the builder for <see cref="CloudFormationRunner"/> and populates the fields pertinent to this level.
 /// </summary>
 /// <returns>Builder for <see cref="CloudFormationRunner"/>.</returns>
 protected virtual CloudFormationBuilder GetBuilder()
 {
     return(CloudFormationRunner.Builder(this.CreateCloudFormationContext(), this.StackName)
            .WithClientToken(this.ClientRequestToken).WithRoleArn(this.RoleARN)
            .WithFollowOperation(!this.PassThru));
 }
Exemple #14
0
        public void ShouldThrowIfUpdateFails(string finalState)
        {
            var logger             = new TestLogger(this.output);
            var mockClientFactory  = TestHelpers.GetClientFactoryMock();
            var mockContext        = TestHelpers.GetContextMock(logger);
            var mockCloudFormation = new Mock <IAmazonCloudFormation>();

            mockCloudFormation.SetupSequence(cf => cf.DescribeStacksAsync(It.IsAny <DescribeStacksRequest>(), default))
            .ReturnsAsync(ResponseStackCreateComplete).ReturnsAsync(ResponseStackCreateComplete)
            .ReturnsAsync(ResponseStackCreateComplete)
            .ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackStatus = StackStatus.UPDATE_IN_PROGRESS,
                        Parameters  = new List <Parameter>()
                    }
                }
            }).ReturnsAsync(
                new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack()
                    {
                        StackName   = StackName,
                        StackStatus = StackStatus.FindValue(finalState),
                        Parameters  = new List <Parameter>()
                    }
                }
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeStackEventsAsync(It.IsAny <DescribeStackEventsRequest>(), default))
            .ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent
                    {
                        StackName      = StackName,
                        StackId        = StackId,
                        ResourceStatus = ResourceStatus.CREATE_COMPLETE,
                        Timestamp      = DateTime.Now.AddDays(-1)
                    }
                }
            }).ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent {
                        Timestamp = DateTime.Now.AddSeconds(1)
                    }
                }
            }).ReturnsAsync(
                new DescribeStackEventsResponse
            {
                StackEvents = new List <StackEvent>
                {
                    new StackEvent {
                        Timestamp = DateTime.Now.AddSeconds(2)
                    }
                }
            }).ReturnsAsync(new DescribeStackEventsResponse {
                StackEvents = new List <StackEvent>()
            });

            mockCloudFormation.Setup(cf => cf.CreateChangeSetAsync(It.IsAny <CreateChangeSetRequest>(), default))
            .ReturnsAsync(
                new CreateChangeSetResponse
            {
                Id = "arn:aws:cloudformation:eu-west-1:123456789012:changeset/1234"
            });

            mockCloudFormation
            .SetupSequence(cf => cf.DescribeChangeSetAsync(It.IsAny <DescribeChangeSetRequest>(), default))
            .ReturnsAsync(new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_IN_PROGRESS
            })
            .ReturnsAsync(
                new DescribeChangeSetResponse {
                Status = ChangeSetStatus.CREATE_COMPLETE, Changes = StockChange
            });

            mockCloudFormation
            .Setup(cf => cf.DescribeStackResourcesAsync(It.IsAny <DescribeStackResourcesRequest>(), default))
            .ReturnsAsync(new DescribeStackResourcesResponse {
                StackResources = new List <StackResource>()
            });

            mockClientFactory.Setup(f => f.CreateCloudFormationClient()).Returns(mockCloudFormation.Object);

            var runner = CloudFormationRunner.Builder(mockContext.Object, StackName)
                         .WithClientFactory(mockClientFactory.Object)
                         .WithTemplateLocation(this.fixture.TestStackJsonTemplate.FullPath).WithFollowOperation().Build();

            Func <Task <CloudFormationResult> > action = async() => await runner.UpdateStackAsync(_ => true);

            action.Should().Throw <StackOperationException>()
            .WithMessage($"Stack '{StackName}': Operation failed. Status is {finalState}");
            logger.ChangeSets.Count.Should().BeGreaterThan(0);
            logger.InfoMessages.Any(i => i.Contains($"Updating stack '{StackName}'")).Should().BeTrue();
            logger.StackEvents.Count.Should().BeGreaterThan(0);
        }