balena-supervisor/test/unit/lib/json.spec.ts
Felipe Lalanne c1e6dadeb4 Create test/unit and test/integration folders
This sets up the new `test/unit` and `test/integration` folders
and starts classification of some of the test files.

Note that unit tests include, `fs-utils` and `system-info` tests.

While these tests interact with the filesystem, the implementation
of these modules is simple enough, and the tests are fast enough to
allow these tests to fall under the `unit` test category (according to
test/README)

Change-type: patch
2022-08-24 14:28:36 -04:00

160 lines
5.1 KiB
TypeScript

import { expect } from 'chai';
import { equals, diff, prune, shallowDiff } from '~/lib/json';
describe('JSON utils', () => {
describe('equals', () => {
it('should compare non-objects', () => {
expect(equals(0, 1)).to.be.false;
expect(equals(1111, 'a' as any)).to.be.false;
expect(equals(1111, 2222)).to.be.false;
expect(equals('aaa', 'bbb')).to.be.false;
expect(equals('aaa', 'aaa')).to.be.true;
expect(equals(null, null)).to.be.true;
expect(equals(null, undefined)).to.be.false;
expect(equals([], [])).to.be.true;
expect(equals([1, 2, 3], [1, 2, 3])).to.be.true;
expect(equals([1, 2, 3], [1, 2])).to.be.false;
expect(equals([], []), 'empty arrays').to.be.true;
});
it('should compare objects recursively', () => {
expect(equals({}, {}), 'empty objects').to.be.true;
expect(equals({ a: 1 }, { a: 1 }), 'single level objects').to.be.true;
expect(equals({ a: 1 }, { a: 2 }), 'differing value single level objects')
.to.be.false;
expect(equals({ a: 1 }, { b: 1 }), 'differing keys single level objects');
expect(
equals({ a: 1 }, { b: 1, c: 2 }),
'differing keys single level objects',
).to.be.false;
expect(equals({ a: { b: 1 } }, { a: { b: 1 } }), 'multiple level objects')
.to.be.true;
expect(
equals({ a: { b: 1 } }, { a: { b: 1, c: 2 } }),
'extra keys in multiple level objects',
).to.be.false;
expect(
equals({ a: { b: 1 }, c: 2 }, { a: { b: 1 } }),
'source object with extra keys',
).to.be.false;
expect(
equals({ a: { b: 1 } }, { a: { b: 1 }, c: 2 }),
'other object with extra keys',
).to.be.false;
expect(
equals({ a: { b: 1 }, c: 2 }, { a: { b: 1 }, c: 2 }),
'multiple level objects with extra keys',
).to.be.true;
expect(
equals({ a: { b: 1 }, d: 2 }, { a: { b: 1 }, c: 2 }),
'multiple level objects with same number of keys',
).to.be.false;
});
});
describe('diff', () => {
it('when comparing non-objects or arrays, always returns the target value', () => {
expect(diff(1, 2)).to.equal(2);
expect(diff(1, 'a' as any)).to.equal('a');
expect(diff(1.1, 2)).to.equal(2);
expect(diff('aaa', 'bbb')).to.equal('bbb');
expect(diff({}, 'bbb' as any)).to.equal('bbb');
expect(diff([1, 2, 3], [3, 4, 5])).to.deep.equal([3, 4, 5]);
});
it('when comparing objects, calculates differences recursively', () => {
// Reports all changes
expect(diff({ a: 1 }, { b: 1 })).to.deep.equal({ a: undefined, b: 1 });
// Only reports array changes if arrays are different
expect(diff({ a: [1, 2] }, { a: [1, 2] })).to.deep.equal({});
// Multiple key comparisons
expect(diff({ a: 1, b: 1 }, { b: 2 })).to.deep.equal({
a: undefined,
b: 2,
});
// Multiple target keys
expect(diff({ a: 1 }, { b: 2, c: 1 })).to.deep.equal({
a: undefined,
b: 2,
c: 1,
});
// Removing a branch
expect(diff({ a: 1, b: { c: 1 } }, { a: 1 })).to.deep.equal({
b: undefined,
});
// If the arrays are different, return target array
expect(diff({ a: [1, 2] }, { a: [2, 3] })).to.deep.equal({ a: [2, 3] });
// Value to object
expect(diff({ a: 1 }, { a: { c: 1 }, b: 2 })).to.deep.equal({
a: { c: 1 },
b: 2,
});
// Changes in nested object
expect(diff({ a: { c: 1 } }, { a: { c: 1, d: 2 }, b: 3 })).to.deep.equal({
a: { d: 2 },
b: 3,
});
// Multiple level nested objects with value removal
expect(
diff({ a: { c: { f: 1, g: 2 } } }, { a: { c: { f: 2 }, d: 2 }, b: 3 }),
).to.deep.equal({
a: { c: { f: 2, g: undefined }, d: 2 },
b: 3,
});
});
});
describe('shallowDiff', () => {
it('compares objects only to the given depth', () => {
expect(shallowDiff({ a: 1 }, { a: 1 }, 0)).to.deep.equal({ a: 1 });
expect(shallowDiff({ a: 1 }, { a: 1 }, 1)).to.deep.equal({});
expect(shallowDiff({ a: 1 }, { a: 1 }, 2)).to.deep.equal({});
expect(shallowDiff({ a: 1, b: 1 }, { a: 1 }, 1)).to.deep.equal({
b: undefined,
});
expect(
shallowDiff({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }, 1),
).to.deep.equal({});
expect(
shallowDiff({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2, d: 3 } }, 1),
).to.deep.equal({ b: { c: 2, d: 3 } });
expect(
shallowDiff({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2, d: 3 } }, 2),
).to.deep.equal({ b: { d: 3 } });
});
});
describe('prune', () => {
it('does not remove empty arrays or other "empty values"', () => {
expect(prune([])).to.deep.equal([]);
expect(prune([0])).to.deep.equal([0]);
expect(prune(0)).to.deep.equal(0);
expect(prune({ a: 0 })).to.deep.equal({ a: 0 });
expect(prune({ a: [] })).to.deep.equal({ a: [] });
expect(prune({ a: [], b: 0 })).to.deep.equal({ a: [], b: 0 });
});
it('removes empty branches from a json object', () => {
expect(prune({})).to.deep.equal({});
expect(prune({ a: {} })).to.deep.equal({});
expect(prune({ a: { b: {} } })).to.deep.equal({});
expect(prune({ a: 1, b: {} })).to.deep.equal({ a: 1 });
expect(prune({ a: 1, b: {}, c: { d: 2, e: {} } })).to.deep.equal({
a: 1,
c: { d: 2 },
});
expect(prune({ a: 1, b: {}, c: { d: 2, e: [] } })).to.deep.equal({
a: 1,
c: { d: 2, e: [] },
});
});
});
});