import * as chai from 'chai'; import { expect } from 'chai'; import * as sinon from 'sinon'; import * as chaiAsPromised from 'chai-as-promised'; import { BalenaSettingsPermissionError } from '..'; chai.use(chaiAsPromised); import { createStorage } from '../lib/stores/local-storage'; import { getStorage } from '..'; const IS_BROWSER = typeof window !== 'undefined'; let dataDirectory: string | undefined; let fs: typeof import('fs'); let path: typeof import('path'); if (!IS_BROWSER) { // eslint-disable-next-line @typescript-eslint/no-require-imports fs = require('fs'); // eslint-disable-next-line @typescript-eslint/no-require-imports path = require('path'); const settings = // eslint-disable-next-line @typescript-eslint/no-require-imports require('balena-settings-client') as typeof import('balena-settings-client'); dataDirectory = settings.get('dataDirectory'); } const localStorage = createStorage(dataDirectory); const storage = getStorage({ dataDirectory }); describe('Storage:', () => { beforeEach(() => storage.clear()); after(() => storage.clear()); describe('given numbers', () => { it('should be able to save a float number', () => storage.set('pi', 3.14).then(() => { return expect(storage.get('pi')).to.eventually.equal(3.14); })); it('should be able to save an integer number', () => storage.set('age', 34).then(() => { return expect(storage.get('age')).to.eventually.equal(34); })); it('should be able to save a string number containin numbers as a string', () => storage.set('pi', 'pi=3.14').then(() => { return expect(storage.get('pi')).to.eventually.equal('pi=3.14'); })); }); describe('given objects', () => { it('should be able to save a plain object', () => storage.set('pi', { value: 3.14 }).then(() => { return expect(storage.get('pi')).to.eventually.deep.equal({ value: 3.14, }); })); it('should be able to save an empty object', () => storage.set('empty', {}).then(() => { return expect(storage.get('empty')).to.eventually.deep.equal({}); })); }); describe('.set()', () => { describe('given a key does not exist', () => { it('should be able to set a value', () => { return expect(storage.get('foobar')) .to.eventually.equal(undefined) .then(() => storage.set('foobar', 'Hello')) .then(() => { return expect(storage.get('foobar')).to.eventually.equal('Hello'); }); }); }); describe('given a key already exist', () => { beforeEach(() => storage.set('foobar', 'Hello')); afterEach(() => storage.remove('foobar')); it('should be able to change the value', () => { return expect(storage.get('foobar')) .to.eventually.equal('Hello') .then(() => storage.set('foobar', 'World')) .then(() => expect(storage.get('foobar')).to.eventually.equal('World'), ); }); }); }); describe('.get()', () => { describe('given a key does not exist', () => { it('should eventually be undefined', () => expect(storage.get('foobar')).to.eventually.equal(undefined)); }); describe('given a key already exist', () => { beforeEach(() => storage.set('foobar', 'Hello')); it('should eventually be the value', () => expect(storage.get('foobar')).to.eventually.equal('Hello')); }); describe('given getItem throws an error', () => { let getItemStub: any; beforeEach(() => { getItemStub = sinon.stub(localStorage, 'getItem'); getItemStub.throws(new Error('ENOENT')); }); afterEach(() => { getItemStub.restore(); }); it('should eventually be undefined', () => expect(storage.get('foobar')).to.eventually.equal(undefined)); }); describe('given an externally saved value', () => { // this test is node-specific if (IS_BROWSER) { return; } describe('with expected file access', () => { let fooPath: string; beforeEach(() => { fooPath = path.join(dataDirectory!, 'foo'); fs.writeFileSync(fooPath, 'hello world'); }); afterEach(() => { fs.unlinkSync(fooPath); }); it('should be able to read back', () => expect(storage.get('foo')).to.eventually.equal('hello world')); }); describe('with no read access', () => { let fsReadFileStub: any; beforeEach(() => { fsReadFileStub = sinon.stub(fs.promises, 'readFile'); fsReadFileStub.rejects({ code: 'EACCES' }); }); afterEach(() => { fsReadFileStub.restore(); }); it('should raise an error', () => { return expect(storage.get('bar')).to.eventually.be.rejectedWith( BalenaSettingsPermissionError, ); }); }); }); }); describe('.has()', () => { describe('given a key does not exist', () => { it('should eventually be false', () => expect(storage.has('foobar')).to.eventually.equal(false)); }); describe('given a key already exist', () => { beforeEach(() => storage.set('foobar', 'Hello')); it('should eventually be true', () => expect(storage.has('foobar')).to.eventually.equal(true)); }); }); describe('.remove()', () => { describe('given a key does not exist', () => { it('should do nothing', async () => { await expect(storage.has('foobar')).to.eventually.equal(false); return storage .remove('foobar') .then(() => expect(storage.has('foobar')).to.eventually.equal(false)); }); }); describe('given a key already exist', () => { beforeEach(() => storage.set('foobar', 'Hello')); it('should remove the key', () => { return expect(storage.has('foobar')) .to.eventually.equal(true) .then(() => storage.remove('foobar')) .then(() => expect(storage.has('foobar')).to.eventually.equal(false)); }); }); }); });