'use strict';

const expect = require('chai').expect;
const AwsProvider = require('../../../../../../../../lib/plugins/aws/provider');
const AwsCompileStreamEvents = require('../../../../../../../../lib/plugins/aws/package/compile/events/stream');
const Serverless = require('../../../../../../../../lib/Serverless');
const runServerless = require('../../../../../../../utils/run-serverless');

describe('AwsCompileStreamEvents', () => {
  let serverless;
  let awsCompileStreamEvents;

  beforeEach(() => {
    serverless = new Serverless();
    serverless.service.provider.compiledCloudFormationTemplate = {
      Resources: {
        IamRoleLambdaExecution: {
          Properties: {
            Policies: [
              {
                PolicyDocument: {
                  Statement: [],
                },
              },
            ],
          },
        },
      },
    };
    serverless.setProvider('aws', new AwsProvider(serverless));
    awsCompileStreamEvents = new AwsCompileStreamEvents(serverless);
    awsCompileStreamEvents.serverless.service.service = 'new-service';
  });

  describe('#constructor()', () => {
    it('should set the provider variable to be an instance of AwsProvider', () =>
      expect(awsCompileStreamEvents.provider).to.be.instanceof(AwsProvider));
  });

  describe('#compileStreamEvents()', () => {
    it('should throw an error if the "consumer" property is not a string, object, or boolean', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              stream: {
                type: 'kinesis',
                arn: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream',
                consumer: 42,
              },
            },
          ],
        },
      };
    });

    it('should throw an error if stream event type is not a string or an object', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              stream: 42,
            },
          ],
        },
      };

      expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
    });

    it('should throw an error if the "arn" property is not given', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              stream: {
                arn: null,
              },
            },
          ],
        },
      };

      expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
    });

    it('should not throw error or merge role statements if default policy is not present', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role is set in function', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          role: 'arn:aws:iam::account:role/foo',
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.be.instanceof(Array);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn.length
      ).to.equal(0);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role name reference is set in function', () => {
      const roleLogicalId = 'RoleLogicalId';
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          role: roleLogicalId,
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.include(roleLogicalId);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role reference is set in function', () => {
      const roleLogicalId = 'RoleLogicalId';
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          role: { 'Fn::GetAtt': [roleLogicalId, 'Arn'] },
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.include(roleLogicalId);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role is set in provider', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      awsCompileStreamEvents.serverless.service.provider.role = 'arn:aws:iam::account:role/foo';

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.be.instanceof(Array);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn.length
      ).to.equal(0);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if IAM role is referenced from cloudformation parameters', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          role: { Ref: 'MyStreamRoleArn' },
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn.length
      ).to.equal(0);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if IAM role is imported', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          role: { 'Fn::ImportValue': 'ExportedRoleId' },
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn.length
      ).to.equal(0);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role reference is set in provider', () => {
      const roleLogicalId = 'RoleLogicalId';
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      awsCompileStreamEvents.serverless.service.provider.role = {
        'Fn::GetAtt': [roleLogicalId, 'Arn'],
      };

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.include(roleLogicalId);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    it('should not throw error if custom IAM role name reference is set in provider', () => {
      const roleLogicalId = 'RoleLogicalId';
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              // doesn't matter if DynamoDB or Kinesis stream
              stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
            },
          ],
        },
      };

      // pretend that the default IamRoleLambdaExecution is not in place
      awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
        null;

      awsCompileStreamEvents.serverless.service.provider.role = roleLogicalId;

      expect(() => {
        awsCompileStreamEvents.compileStreamEvents();
      }).to.not.throw(Error);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .FirstEventSourceMappingDynamodbFoo.DependsOn
      ).to.include(roleLogicalId);
      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution
      ).to.equal(null);
    });

    describe('when a DynamoDB stream ARN is given', () => {
      it('should create event source mappings when a DynamoDB stream ARN is given', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  batchSize: 1,
                  startingPosition: 'STARTING_POSITION_ONE',
                  enabled: false,
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/bar/stream/2',
                  batchWindow: 15,
                  maximumRetryAttempts: 4,
                },
              },
              {
                stream: 'arn:aws:dynamodb:region:account:table/baz/stream/3',
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/buzz/stream/4',
                  bisectBatchOnFunctionError: true,
                  batchWindow: 0,
                  maximumRecordAgeInSeconds: 120,
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/5',
                  destinations: {
                    onFailure: 'arn:aws:sns:region:account:snstopic',
                  },
                },
              },
            ],
          },
        };

        awsCompileStreamEvents.compileStreamEvents();

        // event 1
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Properties.BatchSize
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.batchSize
        );
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Properties.StartingPosition
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
            .startingPosition
        );
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Properties.Enabled
        ).to.equal(false);

        // event 2
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[1].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.MaximumBatchingWindowInSeconds
        ).to.equal(15);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.MaximumRetryAttempts
        ).to.equal(4);

        // event 3
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[2].stream);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.BisectBatchOnFunctionError
        ).to.equal(undefined);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.MaximumRecordAgeInSeconds
        ).to.equal(undefined);

        // event 4
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[3].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.BisectBatchOnFunctionError
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.MaximumRecordAgeInSeconds
        ).to.equal(120);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBuzz.Properties.MaximumBatchingWindowInSeconds
        ).to.equal(0);

        // event 5
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFizz.Properties.DestinationConfig.OnFailure
            .Destination
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.destinations
            .onFailure
        );
      });

      it('should allow specifying DynamoDB and Kinesis streams as CFN reference types', () => {
        awsCompileStreamEvents.serverless.service.resources.Parameters = {
          SomeDdbTableStreamArn: {
            Type: 'String',
          },
          ForeignKinesisStreamArn: {
            Type: 'String',
          },
        };
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: { 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] },
                  type: 'dynamodb',
                },
              },
              {
                stream: {
                  arn: { 'Fn::ImportValue': 'ForeignKinesis' },
                  type: 'kinesis',
                },
              },
              {
                stream: {
                  arn: {
                    'Fn::Join': [
                      ':',
                      [
                        'arn',
                        'aws',
                        'kinesis',
                        {
                          Ref: 'AWS::Region',
                        },
                        {
                          Ref: 'AWS::AccountId',
                        },
                        'stream/MyStream',
                      ],
                    ],
                  },
                  type: 'kinesis',
                },
              },
              {
                stream: {
                  arn: { Ref: 'SomeDdbTableStreamArn' },
                  type: 'dynamodb',
                },
              },
              {
                stream: {
                  arn: { Ref: 'ForeignKinesisStreamArn' },
                  type: 'kinesis',
                },
              },
            ],
          },
        };

        awsCompileStreamEvents.compileStreamEvents();

        // dynamodb with Fn::GetAtt
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbSomeDdbTable.Properties.EventSourceArn
        ).to.deep.equal({ 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] });
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[0]
        ).to.deep.equal({
          Action: [
            'dynamodb:GetRecords',
            'dynamodb:GetShardIterator',
            'dynamodb:DescribeStream',
            'dynamodb:ListStreams',
          ],
          Effect: 'Allow',
          Resource: [
            {
              'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'],
            },
            {
              Ref: 'SomeDdbTableStreamArn',
            },
          ],
        });

        // kinesis with Fn::ImportValue
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisForeignKinesis.Properties.EventSourceArn
        ).to.deep.equal({ 'Fn::ImportValue': 'ForeignKinesis' });

        // kinesis with Fn::Join
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisMyStream.Properties.EventSourceArn
        ).to.deep.equal({
          'Fn::Join': [
            ':',
            [
              'arn',
              'aws',
              'kinesis',
              {
                Ref: 'AWS::Region',
              },
              {
                Ref: 'AWS::AccountId',
              },
              'stream/MyStream',
            ],
          ],
        });

        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1]
        ).to.deep.equal({
          Effect: 'Allow',
          Action: [
            'kinesis:GetRecords',
            'kinesis:GetShardIterator',
            'kinesis:DescribeStream',
            'kinesis:ListStreams',
          ],
          Resource: [
            {
              'Fn::ImportValue': 'ForeignKinesis',
            },
            {
              'Fn::Join': [
                ':',
                [
                  'arn',
                  'aws',
                  'kinesis',
                  {
                    Ref: 'AWS::Region',
                  },
                  {
                    Ref: 'AWS::AccountId',
                  },
                  'stream/MyStream',
                ],
              ],
            },
            {
              Ref: 'ForeignKinesisStreamArn',
            },
          ],
        });
      });

      it('should allow specifying OnFailure destinations as CFN reference types', () => {
        awsCompileStreamEvents.serverless.service.resources.Parameters = {
          SomeSNSArn: {
            Type: 'String',
          },
          ForeignSQSArn: {
            Type: 'String',
          },
        };
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { 'Fn::GetAtt': ['SomeSNS', 'Arn'] },
                      type: 'sns',
                    },
                  },
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/bar/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { 'Fn::ImportValue': 'ForeignSQS' },
                      type: 'sqs',
                    },
                  },
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/baz/stream/1',
                  destinations: {
                    onFailure: {
                      arn: {
                        'Fn::Join': [
                          ':',
                          [
                            'arn',
                            'aws',
                            'sqs',
                            {
                              Ref: 'AWS::Region',
                            },
                            {
                              Ref: 'AWS::AccountId',
                            },
                            'MyQueue',
                          ],
                        ],
                      },
                      type: 'sqs',
                    },
                  },
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/buzz/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { Ref: 'SomeSNSArn' },
                      type: 'sns',
                    },
                  },
                },
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { Ref: 'ForeignSQSArn' },
                      type: 'sqs',
                    },
                  },
                },
              },
            ],
          },
        };

        awsCompileStreamEvents.compileStreamEvents();

        // sns with Fn::GetAtt
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbFoo.Properties.DestinationConfig.OnFailure
            .Destination
        ).to.deep.equal({ 'Fn::GetAtt': ['SomeSNS', 'Arn'] });
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1]
        ).to.deep.equal({
          Action: ['sns:Publish'],
          Effect: 'Allow',
          Resource: [
            {
              'Fn::GetAtt': ['SomeSNS', 'Arn'],
            },
            {
              Ref: 'SomeSNSArn',
            },
          ],
        });

        // sqs with Fn::ImportValue
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBar.Properties.DestinationConfig.OnFailure
            .Destination
        ).to.deep.equal({ 'Fn::ImportValue': 'ForeignSQS' });

        // sqs with Fn::Join
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingDynamodbBaz.Properties.DestinationConfig.OnFailure
            .Destination
        ).to.deep.equal({
          'Fn::Join': [
            ':',
            [
              'arn',
              'aws',
              'sqs',
              {
                Ref: 'AWS::Region',
              },
              {
                Ref: 'AWS::AccountId',
              },
              'MyQueue',
            ],
          ],
        });

        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[2]
        ).to.deep.equal({
          Effect: 'Allow',
          Action: ['sqs:ListQueues', 'sqs:SendMessage'],
          Resource: [
            {
              'Fn::ImportValue': 'ForeignSQS',
            },
            {
              'Fn::Join': [
                ':',
                [
                  'arn',
                  'aws',
                  'sqs',
                  {
                    Ref: 'AWS::Region',
                  },
                  {
                    Ref: 'AWS::AccountId',
                  },
                  'MyQueue',
                ],
              ],
            },
            {
              Ref: 'ForeignSQSArn',
            },
          ],
        });
      });

      it('fails if Ref/dynamic stream ARN is used without defining it to the CF parameters', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: { Ref: 'SomeDdbTableStreamArn' },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if Ref/dynamic onFailure ARN is used without defining it to the CF parameters', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { Ref: 'ForeignSQSArn' },
                    },
                  },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if Fn::GetAtt/dynamic stream ARN is used without a type', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: { 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if Fn::GetAtt/dynamic onFailure ARN is used without a type', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  destinations: {
                    onFailure: {
                      arn: { 'Fn::GetAtt': ['SomeSNS', 'Arn'] },
                    },
                  },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if onFailure ARN is given as a variable type other than string or object', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  destinations: {
                    onFailure: 3,
                  },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if no arn key is given for a dynamic onFailure ARN', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  destinations: {
                    onFailure: {
                      notarn: ['SomeSNS', 'Arn'],
                    },
                  },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('fails if destinations structure is wrong', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
                  destinations: {
                    notOnFailure: {
                      arn: {
                        'Fn::GetAtt': ['SomeSNS', 'Arn'],
                        'batchSize': 1,
                      },
                    },
                  },
                },
              },
            ],
          },
        };

        expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
      });

      it('should add the necessary IAM role statements', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
              },
              {
                stream: {
                  arn: 'arn:aws:dynamodb:region:account:table/bar/stream/2',
                  destinations: {
                    onFailure: 'arn:aws:sns:region:account:snstopic',
                  },
                },
              },
            ],
          },
        };

        const iamRoleStatements = [
          {
            Effect: 'Allow',
            Action: [
              'dynamodb:GetRecords',
              'dynamodb:GetShardIterator',
              'dynamodb:DescribeStream',
              'dynamodb:ListStreams',
            ],
            Resource: [
              'arn:aws:dynamodb:region:account:table/foo/stream/1',
              'arn:aws:dynamodb:region:account:table/bar/stream/2',
            ],
          },
          {
            Effect: 'Allow',
            Action: ['sns:Publish'],
            Resource: ['arn:aws:sns:region:account:snstopic'],
          },
        ];

        awsCompileStreamEvents.compileStreamEvents();

        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement
        ).to.deep.equal(iamRoleStatements);
      });
    });

    describe('when a Kinesis stream ARN is given', () => {
      it('should create event source mappings when a Kinesis stream ARN is given', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/foo',
                  batchSize: 1,
                  startingPosition: 'STARTING_POSITION_ONE',
                  enabled: false,
                  parallelizationFactor: 10,
                },
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/bar',
                  batchWindow: 15,
                  maximumRetryAttempts: 5,
                },
              },
              {
                stream: 'arn:aws:kinesis:region:account:stream/baz',
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/buzz',
                  bisectBatchOnFunctionError: true,
                  maximumRecordAgeInSeconds: 180,
                },
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:table/fizz/stream/5',
                  destinations: {
                    onFailure: 'arn:aws:sns:region:account:snstopic',
                  },
                },
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/abc',
                  consumer: true,
                },
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/xyz',
                  consumer: 'arn:aws:kinesis:region:account:stream/xyz/consumer/foobar:1558544531',
                },
              },
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/def',
                  consumer: false,
                },
              },
            ],
          },
        };

        awsCompileStreamEvents.compileStreamEvents();

        // event 1
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Properties.BatchSize
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.batchSize
        );
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Properties.StartingPosition
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
            .startingPosition
        );
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Properties.ParallelizationFactor
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
            .parallelizationFactor
        );
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFoo.Properties.Enabled
        ).to.equal(false);

        // event 2
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[1].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.ParallelizationFactor
        ).to.equal(undefined);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.MaximumBatchingWindowInSeconds
        ).to.equal(15);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBar.Properties.MaximumRetryAttempts
        ).to.equal(5);

        // event 3
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[2].stream);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.BisectBatchOnFunctionError
        ).to.equal(undefined);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBaz.Properties.MaximumRecordAgeInSeconds
        ).to.equal(undefined);

        // event 4
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[3].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.ParallelizationFactor
        ).to.equal(undefined);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.BisectBatchOnFunctionError
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisBuzz.Properties.MaximumRecordAgeInSeconds
        ).to.equal(180);

        // event 5
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisFizz.Properties.DestinationConfig.OnFailure
            .Destination
        ).to.equal(
          awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.destinations
            .onFailure
        );

        // event 6
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.DependsOn
        ).to.eql(['IamRoleLambdaExecution', 'FirstabcConsumerStreamConsumer']);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.Properties.EventSourceArn
        ).to.eql({ Ref: 'FirstabcConsumerStreamConsumer' });
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisAbc.Properties.Enabled
        ).to.equal(true);

        // event 7
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.Properties.EventSourceArn
        ).to.equal('arn:aws:kinesis:region:account:stream/xyz/consumer/foobar:1558544531');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisXyz.Properties.Enabled
        ).to.equal(true);

        // event 8
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Type
        ).to.equal('AWS::Lambda::EventSourceMapping');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.DependsOn
        ).to.include('IamRoleLambdaExecution');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.EventSourceArn
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[7].stream.arn);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.BatchSize
        ).to.equal(10);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.StartingPosition
        ).to.equal('TRIM_HORIZON');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.Enabled
        ).to.equal(true);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.BisectBatchOnFunctionError
        ).to.equal(undefined);
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstEventSourceMappingKinesisDef.Properties.MaximumRecordAgeInSeconds
        ).to.equal(undefined);
      });

      it('should create stream consumer when a Kinesis stream with consumer "true" is given', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: {
                  arn: 'arn:aws:kinesis:region:account:stream/abc',
                  consumer: true,
                },
              },
            ],
          },
        };

        awsCompileStreamEvents.compileStreamEvents();

        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstabcConsumerStreamConsumer.Type
        ).to.equal('AWS::Kinesis::StreamConsumer');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstabcConsumerStreamConsumer.Properties.ConsumerName
        ).to.equal('firstabcConsumer');
        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.FirstabcConsumerStreamConsumer.Properties.StreamARN
        ).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
      });

      it('should add the necessary IAM role statements', () => {
        awsCompileStreamEvents.serverless.service.functions = {
          first: {
            events: [
              {
                stream: 'arn:aws:kinesis:region:account:stream/foo',
              },
              {
                stream: 'arn:aws:kinesis:region:account:stream/bar',
              },
              {
                stream: {
                  type: 'kinesis',
                  arn: 'arn:aws:kinesis:region:account:stream/fizz',
                  consumer: true,
                },
              },
              {
                stream: {
                  type: 'kinesis',
                  arn: 'arn:aws:kinesis:region:account:stream/buzz',
                  consumer: 'arn:aws:kinesis:region:account:stream/buzz/consumer/abc:1558544531',
                },
              },
            ],
          },
        };

        const iamRoleStatements = [
          {
            Effect: 'Allow',
            Action: [
              'kinesis:GetRecords',
              'kinesis:GetShardIterator',
              'kinesis:DescribeStream',
              'kinesis:ListStreams',
            ],
            Resource: [
              'arn:aws:kinesis:region:account:stream/foo',
              'arn:aws:kinesis:region:account:stream/bar',
            ],
          },
          {
            Effect: 'Allow',
            Action: [
              'kinesis:GetRecords',
              'kinesis:GetShardIterator',
              'kinesis:DescribeStreamSummary',
              'kinesis:ListShards',
            ],
            Resource: [
              'arn:aws:kinesis:region:account:stream/fizz',
              'arn:aws:kinesis:region:account:stream/buzz',
            ],
          },
          {
            Effect: 'Allow',
            Action: ['kinesis:SubscribeToShard'],
            Resource: [
              { Ref: 'FirstfizzConsumerStreamConsumer' },
              'arn:aws:kinesis:region:account:stream/buzz/consumer/abc:1558544531',
            ],
          },
        ];

        awsCompileStreamEvents.compileStreamEvents();

        expect(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement
        ).to.deep.equal(iamRoleStatements);
      });
    });

    it('should not create event source mapping when stream events are not given', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [],
        },
      };

      awsCompileStreamEvents.compileStreamEvents();

      // should be 1 because we've mocked the IamRoleLambdaExecution above
      expect(
        Object.keys(
          awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
            .Resources
        ).length
      ).to.equal(1);
    });

    it('should not add the IAM role statements when stream events are not given', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [],
        },
      };

      awsCompileStreamEvents.compileStreamEvents();

      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
          .IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement.length
      ).to.equal(0);
    });

    it('should remove all non-alphanumerics from stream names for the resource logical ids', () => {
      awsCompileStreamEvents.serverless.service.functions = {
        first: {
          events: [
            {
              stream: 'arn:aws:kinesis:region:account:stream/some-long-name',
            },
          ],
        },
      };

      awsCompileStreamEvents.compileStreamEvents();

      expect(
        awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
      ).to.have.any.keys('FirstEventSourceMappingKinesisSomelongname');
    });
  });
});

describe('AwsCompileStreamEvents #2', () => {
  describe('with provisioned concurrency', () => {
    let naming;
    let eventSourceMappingResource;

    before(async () => {
      const { awsNaming, cfTemplate } = await runServerless({
        fixture: 'function',
        configExt: {
          functions: {
            foo: {
              provisionedConcurrency: 1,
              events: [{ stream: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream' }],
            },
          },
        },
        command: 'package',
      });
      naming = awsNaming;
      const streamLogicalId = awsNaming.getStreamLogicalId('foo', 'kinesis', 'myStream');
      eventSourceMappingResource = cfTemplate.Resources[streamLogicalId];
    });

    it('should reference provisioned alias', () => {
      expect(eventSourceMappingResource.Properties.FunctionName).to.deep.equal({
        'Fn::Join': [
          ':',
          [
            {
              'Fn::GetAtt': ['FooLambdaFunction', 'Arn'],
            },
            'provisioned',
          ],
        ],
      });
    });

    it('should depend on provisioned alias', () => {
      const aliasLogicalId = naming.getLambdaProvisionedConcurrencyAliasLogicalId('foo');
      expect(eventSourceMappingResource.DependsOn).to.include(aliasLogicalId);
    });
  });
  describe('with custom checkpoint enabled', () => {
    let eventSourceMappingResource;

    before(async () => {
      const { awsNaming, cfTemplate } = await runServerless({
        fixture: 'function',
        configExt: {
          functions: {
            foo: {
              events: [
                {
                  stream: {
                    arn: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream',
                    functionResponseType: 'ReportBatchItemFailures',
                  },
                },
              ],
            },
          },
        },
        command: 'package',
      });
      const streamLogicalId = awsNaming.getStreamLogicalId('foo', 'kinesis', 'myStream');
      eventSourceMappingResource = cfTemplate.Resources[streamLogicalId];
    });

    it('should use functionResponseTypes', () => {
      expect(eventSourceMappingResource.Properties.FunctionResponseTypes).to.include.members([
        'ReportBatchItemFailures',
      ]);
    });
  });
});
