balena-supervisor/test/unit/lib/fs-utils.spec.ts
Felipe Lalanne 988a1c9e9a Update @balena/lint to v7
This updates balena lint to the latest version to enable eslint support
and unblock Typescript updates. This is a huge number of changes as the
linting rules are much more strict now, requiring modifiying most files
in the codebase. This commit also bumps the test dependency `rewire` as
that was interfering with the update of balena-lint

Change-type: patch
2024-03-01 18:27:30 -03:00

170 lines
4.8 KiB
TypeScript

import { expect } from 'chai';
import * as path from 'path';
import { promises as fs } from 'fs';
import type { SinonSpy } from 'sinon';
import { spy } from 'sinon';
import mock = require('mock-fs');
import * as fsUtils from '~/lib/fs-utils';
import { pathOnRoot } from '~/lib/host-utils';
describe('lib/fs-utils', () => {
const testFileName1 = 'file.1';
const testFileName2 = 'file.2';
const testFile1 = pathOnRoot(testFileName1);
const testFile2 = pathOnRoot(testFileName2);
const mockFs = () => {
mock({
[testFile1]: mock.file({
content: 'foo',
mtime: new Date('2022-01-04T00:00:00'),
}),
[testFile2]: mock.file({
content: 'bar',
mtime: new Date('2022-01-04T00:00:00'),
}),
});
};
const unmockFs = () => {
mock.restore();
};
describe('writeAndSyncFile', () => {
before(mockFs);
after(unmockFs);
it('should write and sync string data', async () => {
await fsUtils.writeAndSyncFile(testFile1, 'foo bar');
expect(await fs.readFile(testFile1, 'utf-8')).to.equal('foo bar');
});
it('should write and sync buffers', async () => {
await fsUtils.writeAndSyncFile(testFile1, Buffer.from('bar foo'));
expect(await fs.readFile(testFile1, 'utf-8')).to.equal('bar foo');
});
});
describe('writeFileAtomic', () => {
before(() => {
spy(fs, 'rename');
mockFs();
});
after(() => {
(fs.rename as SinonSpy).restore();
unmockFs();
});
it('should write string data atomically', async () => {
await fsUtils.writeFileAtomic(testFile1, 'foo baz');
expect(await fs.readFile(testFile1, 'utf-8')).to.equal('foo baz');
expect(fs.rename).to.have.been.calledWith(`${testFile1}.new`, testFile1);
});
it('should write buffer data atomically', async () => {
await fsUtils.writeFileAtomic(testFile1, 'baz foo');
expect(await fs.readFile(testFile1, 'utf-8')).to.equal('baz foo');
expect(fs.rename).to.have.been.calledWith(`${testFile1}.new`, testFile1);
});
});
describe('safeRename', () => {
beforeEach(mockFs);
afterEach(unmockFs);
it('should rename a file', async () => {
await fsUtils.safeRename(testFile1, testFile1 + 'rename');
const dirContents = await fs.readdir(pathOnRoot());
expect(dirContents).to.have.length(2);
expect(dirContents).to.not.include(testFileName1);
expect(dirContents).to.include(testFileName1 + 'rename');
});
it('should replace an existing file', async () => {
await fsUtils.safeRename(testFile1, testFile2);
const dirContents = await fs.readdir(pathOnRoot());
expect(dirContents).to.have.length(1);
expect(dirContents).to.include(testFileName2);
expect(dirContents).to.not.include(testFileName1);
});
});
describe('exists', () => {
before(mockFs);
after(unmockFs);
it('should return whether a file exists', async () => {
expect(await fsUtils.exists(testFile1)).to.be.true;
await fs.unlink(testFile1).catch(() => {
/* noop */
});
expect(await fsUtils.exists(testFile1)).to.be.false;
});
});
describe('mkdirp', () => {
before(mockFs);
after(unmockFs);
it('should recursively create directories', async () => {
const directory = path.join(pathOnRoot('test1'), 'test2', 'test3');
await fsUtils.mkdirp(directory);
expect(() => fs.readdir(directory)).to.not.throw();
});
});
describe('unlinkAll', () => {
beforeEach(mockFs);
afterEach(unmockFs);
it('should unlink a single file', async () => {
await fsUtils.unlinkAll(testFile1);
expect(await fs.readdir(pathOnRoot())).to.not.include(testFileName1);
});
it('should unlink multiple files', async () => {
await fsUtils.unlinkAll(testFile1, testFile2);
expect(await fs.readdir(pathOnRoot())).to.have.length(0);
});
});
describe('touch', () => {
beforeEach(mockFs);
afterEach(unmockFs);
it('creates the file if it does not exist', async () => {
await fsUtils.touch('somefile');
expect(await fsUtils.exists('somefile')).to.be.true;
});
it('updates the file mtime if file already exists', async () => {
const statsBefore = await fs.stat(testFile1);
await fsUtils.touch(testFile1);
const statsAfter = await fs.stat(testFile1);
// Mtime should be different
expect(statsAfter.mtime.getTime()).to.not.equal(
statsBefore.mtime.getTime(),
);
});
it('allows setting a custom time for existing files', async () => {
const customTime = new Date('1981-11-24T12:00:00');
await fsUtils.touch(testFile1, customTime);
const statsAfter = await fs.stat(testFile1);
expect(statsAfter.mtime.getTime()).to.be.equal(customTime.getTime());
});
it('allows setting a custom time for newly created files', async () => {
const customTime = new Date('1981-11-24T12:00:00');
await fsUtils.touch('somefile', customTime);
const statsAfter = await fs.stat('somefile');
expect(statsAfter.mtime.getTime()).to.be.equal(customTime.getTime());
});
});
});