Home Reference Source Test

__tests__/functions/transfer/transfer/artifact/artifact.test.js

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

import Artifact from '@functions/transfer/transfer/artifact';

import S3 from 'aws-sdk/clients/s3';
import AWS from 'aws-sdk-mock';
import faker from 'faker';
import Zip from 'node-zip';
import fs from 'fs';

describe('Artifact', () => {
  const bucketName = faker.random.uuid();

  const objectKey = faker.random.uuid();

  const secretAccessKey = faker.random.uuid();

  const sessionToken = faker.random.uuid();

  const accessKeyId = faker.random.uuid();

  const credentials = { secretAccessKey, sessionToken, accessKeyId };

  const location = { bucketName, objectKey };

  describe('#id', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.id).toEqual(expect.stringMatching(/^[-a-zA-Z0-9]+$/));
    });
  });

  describe('#filename', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.filename).toEqual(`${artifact.id}.zip`);
    });
  });

  describe('#filepath', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.filepath).toEqual(`/tmp/${artifact.id}.zip`);
    });
  });

  describe('#dir', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.dir).toEqual(expect.stringMatching(/^\/tmp\/[-a-zA-Z0-9]+$/));
    });
  });

  describe('#bucketName', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.bucketName).toEqual(bucketName);
    });
  });

  describe('#objectKey', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.objectKey).toEqual(objectKey);
    });
  });

  describe('#secretAccessKey', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.secretAccessKey).toEqual(secretAccessKey);
    });
  });

  describe('#sessionToken', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.sessionToken).toEqual(sessionToken);
    });
  });

  describe('#accessKeyId', () => {
    it('returns the correct value', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.accessKeyId).toEqual(accessKeyId);
    });
  });

  describe('#ready', () => {
    const artifact = new Artifact(location, credentials);

    const { dir, filepath } = artifact;

    const key = faker.random.word();

    const value = faker.random.number();

    const jsonFile = `${faker.random.uuid()}.json`;

    const data = JSON.stringify({ [key]: value });

    describe('when the artifact readys successfully', () => {
      beforeEach(() => {
        const zipFile = new Zip();
        zipFile.file(jsonFile, data);
        const zipped = zipFile.generate({ base64: false, compression: 'DEFLATE' });
        AWS.mock('S3', 'getObject', (params, cb) => {
          expect(params).toEqual(
            expect.objectContaining({
              Bucket: bucketName,
              Key: objectKey,
            })
          );
          cb(null, { Body: Buffer.from(zipped, 'binary') });
        });
      });

      afterEach(() => {
        fs.unlinkSync(filepath);
        fs.unlinkSync(`${dir}/${jsonFile}`);
        fs.rmdirSync(dir);
        AWS.restore('S3', 'getObject');
      });

      it('fetches, loads and unzips correctly', () => expect(artifact.ready()).resolves.toBe(true));
    });

    describe('when the artifact fails to ready', () => {
      const invalidArtifact = new Artifact({}, credentials);

      it('rejects with an error', () =>
        expect(invalidArtifact.ready()).rejects.toEqual(expect.any(Error)));
    });
  });

  describe('#match', () => {
    describe('when files are matched', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const relativePath = 'output';

      const path = `${dir}/${relativePath}`;

      const filename = faker.system.fileName();

      const selector = '**/*';

      const absolutePath = `${path}/${filename}`;

      beforeEach(() => {
        fs.mkdirSync(dir);
        fs.mkdirSync(path);
        fs.writeFileSync(absolutePath, '', { encoding: 'utf8' });
      });

      afterEach(() => {
        fs.unlinkSync(absolutePath);
        fs.rmdirSync(path);
        fs.rmdirSync(dir);
      });

      it('should return the correct files (without path)', async () => {
        const [file] = await artifact.match(selector);
        expect(file.key).toEqual(`/${relativePath}/${filename}`);
        expect(file.absolutePath).toEqual(absolutePath);
      });

      it('should return the correct files (with path)', async () => {
        const [file] = await artifact.match(selector, relativePath);
        expect(file.key).toEqual(`/${filename}`);
        expect(file.absolutePath).toEqual(absolutePath);
      });
    });
  });

  describe('#get', () => {
    describe('when the file exists', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const filename = faker.random.uuid();

      const data = JSON.stringify({ [faker.random.word()]: faker.random.number() });

      beforeEach(() => {
        fs.mkdirSync(dir);
        fs.writeFileSync(`${dir}/${filename}`, data, { encoding: 'utf8' });
      });

      afterEach(() => {
        fs.unlinkSync(`${dir}/${filename}`);
        fs.rmdirSync(dir);
      });

      it('should return the file', () => {
        expect(artifact.get(filename)).toEqual(data);
      });
    });

    describe('when the file does not exist', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const filename = faker.random.uuid();

      beforeEach(() => {
        fs.mkdirSync(dir);
      });

      afterEach(() => {
        fs.rmdirSync(dir);
      });

      it('throws an error', () => {
        expect(() => artifact.get(filename)).toThrow(Error);
      });
    });
  });

  describe('#attribute', () => {
    describe('when the file and key exists', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const filename = faker.random.uuid();

      const key = faker.random.word();

      const value = faker.random.number();

      const data = JSON.stringify({ [key]: value });

      beforeEach(() => {
        fs.mkdirSync(dir);
        fs.writeFileSync(`${dir}/${filename}`, data, { encoding: 'utf8' });
      });

      afterEach(() => {
        fs.unlinkSync(`${dir}/${filename}`);
        fs.rmdirSync(dir);
      });

      it('should return the file', () => {
        expect(artifact.attribute(filename, key)).toEqual(value);
      });
    });

    describe('when the files does not exist', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const filename = faker.random.uuid();

      const key = faker.random.word();

      beforeEach(() => {
        fs.mkdirSync(dir);
      });

      afterEach(() => {
        fs.rmdirSync(dir);
      });

      it('should throw an error', () => {
        expect(() => artifact.attribute(filename, key)).toThrow(Error);
      });
    });

    describe('when the key is not present', () => {
      const artifact = new Artifact(location, credentials);

      const { dir } = artifact;

      const filename = faker.random.uuid();

      const key = faker.random.word();

      const data = JSON.stringify({});

      beforeEach(() => {
        fs.mkdirSync(dir);
        fs.writeFileSync(`${dir}/${filename}`, data, { encoding: 'utf8' });
      });

      afterEach(() => {
        fs.unlinkSync(`${dir}/${filename}`);
        fs.rmdirSync(dir);
      });

      it('should throw an error', () => {
        expect(() => artifact.attribute(filename, key)).toThrow(Error);
      });
    });
  });

  describe('#unzip', () => {
    describe('when the artifact can be unzipped', () => {
      const artifact = new Artifact(location, credentials);

      const { filepath } = artifact;

      const key = `${faker.random.uuid()}.json`;

      const data = JSON.stringify({ [faker.random.word()]: faker.random.number() });

      beforeEach(() => {
        const zipFile = new Zip();
        zipFile.file(key, data);
        const zipped = zipFile.generate({ base64: false, compression: 'DEFLATE' });
        fs.writeFileSync(filepath, zipped, 'binary');
      });

      afterEach(() => {
        fs.unlinkSync(filepath);
      });

      it('should unzip the file', async () => {
        const success = await artifact.unzip();
        expect(success).toBe(true);
        expect(artifact.get(key)).toEqual(data);
      });
    });

    describe('when the artifact cannot be unzipped', () => {
      const artifact = new Artifact(location, credentials);

      const { filepath } = artifact;

      beforeEach(() => {
        fs.writeFileSync(filepath, '', 'utf8');
      });

      afterEach(() => {
        fs.unlinkSync(filepath);
      });

      it('reject with an error', async () =>
        expect(artifact.unzip()).rejects.toEqual(expect.any(Error)));
    });
  });

  describe('#write', () => {
    const artifact = new Artifact(location, credentials);

    const data = JSON.stringify({ [faker.random.word()]: faker.random.number() });

    it('writes a file to the correct location', () =>
      expect(artifact.write(data)).resolves.toEqual(true));

    afterEach(() => {
      fs.unlinkSync(artifact.filepath);
    });
  });

  describe('#fetch', () => {
    describe('when the artifact can be retrieved', () => {
      const artifact = new Artifact(location, credentials);

      const data = JSON.stringify({ [faker.random.word()]: faker.random.number() });

      beforeEach(() => {
        AWS.mock('S3', 'getObject', (params, cb) => {
          expect(params).toEqual(
            expect.objectContaining({
              Bucket: bucketName,
              Key: objectKey,
            })
          );
          cb(null, { Body: Buffer.from(data) });
        });
      });

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

      it('should retrieve the correct data', () =>
        expect(artifact.fetch()).resolves.toEqual(Buffer.from(data)));
    });

    describe('when the artifact cannot be retrieved', () => {
      const artifact = new Artifact(location, credentials);

      beforeEach(() => {
        AWS.mock('S3', 'getObject', (params, cb) => {
          cb(new Error('TEST'));
        });
      });

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

      it('should reject with an error', () =>
        expect(artifact.fetch()).rejects.toEqual(expect.any(Error)));
    });
  });

  describe('#client', () => {
    it('returns an S3 client', () => {
      const artifact = new Artifact(location, credentials);
      expect(artifact.client).toBeInstanceOf(S3);
    });
  });
});

describe('Artifact#toArtifact', () => {
  const bucketName = faker.random.uuid();

  const objectKey = faker.random.uuid();

  const secretAccessKey = faker.random.uuid();

  const sessionToken = faker.random.uuid();

  const accessKeyId = faker.random.uuid();

  const credentials = { secretAccessKey, sessionToken, accessKeyId };

  const name = faker.random.word();

  const artifact = { name, location: { s3Location: { bucketName, objectKey } } };

  it('correctly builds an artifact instance', () => {
    const obj = Artifact.toArtifact(artifact, credentials);
    expect(obj).toBeInstanceOf(Artifact);
    expect(obj.bucketName).toEqual(bucketName);
    expect(obj.objectKey).toEqual(objectKey);
    expect(obj.secretAccessKey).toEqual(secretAccessKey);
    expect(obj.sessionToken).toEqual(sessionToken);
    expect(obj.accessKeyId).toEqual(accessKeyId);
  });
});

describe('Artifact#toArtifactMapEntry', () => {
  const bucketName = faker.random.uuid();

  const objectKey = faker.random.uuid();

  const secretAccessKey = faker.random.uuid();

  const sessionToken = faker.random.uuid();

  const accessKeyId = faker.random.uuid();

  const credentials = { secretAccessKey, sessionToken, accessKeyId };

  const name = faker.random.word();

  const artifact = { name, location: { s3Location: { bucketName, objectKey } } };

  it('correctly builds an artifact instance', () => {
    const arr = Artifact.toArtifactMapEntry(artifact, credentials);
    expect(arr[0]).toEqual(name);
    expect(arr[1]).toBeInstanceOf(Artifact);
  });
});