initial commit
[squeep-mystery-box] / test / lib / mystery-box.js
diff --git a/test/lib/mystery-box.js b/test/lib/mystery-box.js
new file mode 100644 (file)
index 0000000..8d228e6
--- /dev/null
@@ -0,0 +1,166 @@
+/* eslint-env mocha */
+/* eslint-disable capitalized-comments */
+'use strict';
+
+const assert = require('assert');
+const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
+const MysteryBox = require('../../lib/mystery-box');
+const stubLogger = require('../stub-logger');
+
+describe('MysteryBox', function () {
+  const noExpectedException = 'did not get expected exception';
+  let mb, options, object;
+  beforeEach(function () {
+    options = {
+      encryptionSecret: 'this is not a very good secret',
+    };
+  });
+  afterEach(function () {
+    sinon.restore();
+  });
+
+  describe('constructor', function () {
+    it('needs a secret', async function () {
+      options = {};
+      try {
+        mb = new MysteryBox(stubLogger, options);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert.strictEqual(e.message, 'missing encryption secret', noExpectedException);
+      }
+    });
+
+    it('covers options', function () {
+      try {
+        mb = new MysteryBox(stubLogger);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert.strictEqual(e.message, 'missing encryption secret', noExpectedException);
+      }
+    });
+
+    it('covers bad flags', function () {
+      options.defaultFlags = 300;
+      try {
+        mb = new MysteryBox(stubLogger, options);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof RangeError, noExpectedException);
+      }
+    });
+
+    it('covers missing ciphers', function () {
+      sinon.stub(MysteryBox._test.crypto, 'getCiphers').returns(['rot13']);
+      try {
+        mb = new MysteryBox(stubLogger, options);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert.strictEqual(e.message, 'no supported versions available', noExpectedException);
+      }
+    });
+  }); // constructor
+
+  describe('pack, unpack', function () {
+    beforeEach(function () {
+      mb = new MysteryBox(stubLogger, options);
+    });
+  
+    it('covers packing unsupported version', async function () {
+      try {
+        await mb.pack({}, 0);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof RangeError, noExpectedException);
+      }
+    });
+
+    it('covers unpacking unsupported version', async function () {
+      const badBuffer = Buffer.alloc(128);
+      badBuffer.writeUInt8(0, 0); // No such thing as version 0
+      const badPayload = badBuffer.toString('base64');
+      try {
+        await mb.unpack(badPayload);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof RangeError, noExpectedException);
+      }
+    });
+
+    it('encrypts and decrypts default version', async function () {
+      this.slow(500);
+      object = {
+        foo: 'bar',
+        baz: 'quux',
+        flarp: 13,
+      };
+      const encryptedResult = await mb.pack(object);
+      const decryptedResult = await mb.unpack(encryptedResult);
+      assert.deepStrictEqual(decryptedResult, object);
+    });
+
+    it('encrypts and decrypts default version, buffer contents', async function () {
+      this.slow(500);
+      object = Buffer.from('a fine little buffer');
+      const encryptedResult = await mb.pack(object);
+      const decryptedResult = await mb.unpack(encryptedResult);
+      assert.deepStrictEqual(decryptedResult, object);
+    });
+
+    it('encrypts and decrypts all available versions +brotli', async function () {
+      Object.keys(mb.versionParameters).map((v) => Number(v)).forEach(async (version) => {
+        object = {
+          foo: 'bar',
+          baz: 'quux',
+          flarp: 13,
+        };
+        const encryptedResult = await mb.pack(object, version, 0x00);
+        const decryptedResult = await mb.unpack(encryptedResult);
+        assert.deepStrictEqual(decryptedResult, object, `${version} results not symmetric`);
+      });
+    });
+
+    it('encrypts and decrypts all available versions +flate', async function () {
+      Object.keys(mb.versionParameters).map((v) => Number(v)).forEach(async (version) => {
+        object = {
+          foo: 'bar',
+          baz: 'quux',
+          flarp: 13,
+        };
+        const encryptedResult = await mb.pack(object, version, 0x01);
+        const decryptedResult = await mb.unpack(encryptedResult);
+        assert.deepStrictEqual(decryptedResult, object, `${version} results not symmetric`);
+      });
+    });
+
+    it('handles large object +brotli', async function () {
+      this.timeout(5000);
+      this.slow(2000);
+      const firstChar = 32, lastChar = 126;
+      const rnd = () => {
+        return Math.floor(firstChar + (lastChar - firstChar + 1) * Math.random());
+      }
+      object = {
+        longProperty: 'x'.repeat(384 * 1024).split('').map(() => String.fromCharCode(rnd())).join(''),
+      };
+      const encryptedResult = await mb.pack(object, mb.bestVersion, 0x00);
+      const decryptedResult = await mb.unpack(encryptedResult);
+      assert.deepStrictEqual(decryptedResult, object);
+    });
+
+    it('handles large object +flate', async function () {
+      this.timeout(5000);
+      this.slow(2000);
+      const firstChar = 32, lastChar = 126;
+      const rnd = () => {
+        return Math.floor(firstChar + (lastChar - firstChar + 1) * Math.random());
+      }
+      object = {
+        longProperty: 'x'.repeat(384 * 1024).split('').map(() => String.fromCharCode(rnd())).join(''),
+      };
+      const encryptedResult = await mb.pack(object, mb.bestVersion, 0x01);
+      const decryptedResult = await mb.unpack(encryptedResult);
+      assert.deepStrictEqual(decryptedResult, object);
+    });
+  }); // pack, unpack
+
+}); // MysteryBox
\ No newline at end of file