Home Reference Source Test

__tests__/functions/transfer/transfer/uploader.test.js

/* eslint-disable import/no-unresolved */

import Uploader from '@functions/transfer/transfer/uploader';
import Artifact from '@functions/transfer/transfer/artifact';
import Destination from '@functions/transfer/transfer/destination';

import faker from 'faker';
import AWS from 'aws-sdk-mock';
import Zip from 'node-zip';

describe('Uploader', () => {
  const artifactName = faker.random.word();

  const roleArnKey = faker.random.word();

  const roleArn = faker.internet.url();

  const bucketKey = faker.random.word();

  const bucket = faker.random.uuid();

  const jsonFile = JSON.stringify({ [roleArnKey]: roleArn, [bucketKey]: bucket });

  const jsonFileName = faker.system.fileName();

  const credentials = {
    AccessKeyId: faker.random.uuid(),
    SecretAccessKey: faker.random.uuid(),
    SessionToken: faker.random.uuid(),
  };

  beforeEach(() => {
    const zipFile = new Zip();
    zipFile.file(jsonFileName, jsonFile);
    AWS.mock('S3', 'getObject', (params, cb) => {
      process.nextTick(() => {
        const zipped = zipFile.generate({ base64: false, compression: 'DEFLATE' });
        cb(null, { Body: Buffer.from(zipped, 'binary') });
      });
    });
  });

  beforeEach(() => {
    AWS.mock('S3', 'putObject', (params, cb) => {
      process.nextTick(() => {
        cb(null, {});
      });
    });
  });

  beforeEach(() => {
    AWS.mock('STS', 'assumeRole', (params, cb) => {
      expect(params).toEqual(
        expect.objectContaining({
          RoleSessionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
          ExternalId: process.env.AWS_LAMBDA_FUNCTION_NAME,
          RoleArn: roleArn,
        })
      );
      cb(null, { Credentials: credentials });
    });
  });

  afterEach(() => {
    AWS.restore('S3', 'getObject');
    AWS.restore('STS', 'assumeRole');
  });

  const job = {
    data: {
      actionConfiguration: {
        configuration: {
          UserParameters: JSON.stringify([
            {
              roleArn: {
                'Fn::GetParam': [artifactName, jsonFileName, roleArnKey],
              },
              bucket: {
                'Fn::GetParam': [artifactName, jsonFileName, bucketKey],
              },
              src: [`${artifactName}::${jsonFileName}`],
            },
          ]),
        },
      },
      inputArtifacts: [
        {
          location: {
            s3Location: {
              bucketName: faker.random.word(),
              objectKey: faker.random.uuid(),
            },
          },
          name: artifactName,
        },
      ],
      artifactCredentials: {
        secretAccessKey: faker.random.uuid(),
        sessionToken: faker.random.uuid(),
        accessKeyId: faker.random.uuid(),
      },
    },
  };

  describe('#new', () => {
    describe('when the job is valid', () => {
      it('instantiates correctly', () => {
        const uploader = new Uploader(job);
        expect(uploader.data).toEqual(job.data);
        expect(uploader.parameters).toEqual(expect.any(Object));
        expect(uploader.artifacts).toEqual(
          expect.objectContaining({
            [artifactName]: expect.any(Artifact),
          })
        );
      });
    });

    describe('when there are no input artifacts', () => {
      const invalidJob = {
        data: {
          actionConfiguration: {
            configuration: {
              UserParameters: JSON.stringify({}),
            },
          },
          inputArtifacts: [],
        },
      };

      it('throws an error', () => expect(() => new Uploader(invalidJob)).toThrow(Error));
    });
  });

  describe('#userParameters', () => {
    describe('when data is valid', () => {
      it('returns the correct data object', () => {
        const uploader = new Uploader(job);
        return expect(uploader.userParameters()).resolves.toEqual(
          expect.arrayContaining([
            expect.objectContaining({
              roleArn: {
                'Fn::GetParam': [artifactName, jsonFileName, roleArnKey],
              },
              bucket: {
                'Fn::GetParam': [artifactName, jsonFileName, bucketKey],
              },
              src: [`${artifactName}::${jsonFileName}`],
            }),
          ])
        );
      });
    });

    describe('when there is no data', () => {
      it('throws an error', () => {
        const uploader = new Uploader(job);
        uploader.parameters = {};
        return expect(uploader.userParameters()).rejects.toEqual(expect.any(Error));
      });
    });

    describe('when data is not valid', () => {
      const invalidData = [
        {
          [faker.random.uuid()]: faker.random.number(),
        },
      ];

      it('throws an error', () => {
        const uploader = new Uploader(job);
        uploader.parameters = invalidData;
        return expect(uploader.userParameters()).rejects.toEqual(expect.any(Error));
      });
    });
  });

  describe('#destinations', () => {
    describe('when the job is valid', () => {
      it('returns an array of destination objects', () => {
        const uploader = new Uploader(job);
        return expect(uploader.destinations()).resolves.toEqual(
          expect.arrayContaining([expect.any(Destination)])
        );
      });
    });

    describe('when the job is invalid', () => {
      const invalidData = [
        {
          [faker.random.uuid()]: faker.random.number(),
        },
      ];

      it('throws an error', () => {
        const uploader = new Uploader(job);
        uploader.parameters = invalidData;
        return expect(uploader.destinations()).rejects.toEqual(expect.any(Error));
      });
    });
  });

  describe('#perform', () => {
    describe('when the destination can be uploaded', () => {
      const remoteLocation = faker.internet.url();

      const upload = jest.fn().mockImplementation(async () => remoteLocation);

      const uploader = new Uploader(job);

      jest.spyOn(uploader, 'destinations').mockImplementation(async () => [{ upload }]);

      it('returns an array of remote locations', async () => {
        const urls = await uploader.perform();
        expect(urls).toEqual(expect.arrayContaining([expect.stringMatching(remoteLocation)]));
        expect(upload).toHaveBeenCalled();
        expect(uploader.destinations).toHaveBeenCalled();
      });
    });
  });
});