mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
This changes things up a bit so that files are handled by uuid instead of path. The allows for files to be renamed without breaking things. Also some improvements with persistence of the view state for each file is made.
778 lines
23 KiB
TypeScript
778 lines
23 KiB
TypeScript
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2022 The Pybricks Authors
|
|
|
|
import 'fake-indexeddb/auto';
|
|
import Dexie from 'dexie';
|
|
import 'dexie-observable';
|
|
import { AsyncSaga, uuid } from '../../test';
|
|
import { createCountFunc } from '../utils/iter';
|
|
import {
|
|
FD,
|
|
FileOpenMode,
|
|
fileStorageClose,
|
|
fileStorageCopyFile,
|
|
fileStorageDeleteFile,
|
|
fileStorageDidClose,
|
|
fileStorageDidCopyFile,
|
|
fileStorageDidDeleteFile,
|
|
fileStorageDidDumpAllFiles,
|
|
fileStorageDidFailToCopyFile,
|
|
fileStorageDidFailToDeleteFile,
|
|
fileStorageDidFailToDumpAllFiles,
|
|
fileStorageDidFailToInitialize,
|
|
fileStorageDidFailToOpen,
|
|
fileStorageDidFailToRead,
|
|
fileStorageDidFailToReadFile,
|
|
fileStorageDidFailToRenameFile,
|
|
fileStorageDidFailToWrite,
|
|
fileStorageDidFailToWriteFile,
|
|
fileStorageDidInitialize,
|
|
fileStorageDidOpen,
|
|
fileStorageDidRead,
|
|
fileStorageDidReadFile,
|
|
fileStorageDidRenameFile,
|
|
fileStorageDidWrite,
|
|
fileStorageDidWriteFile,
|
|
fileStorageDumpAllFiles,
|
|
fileStorageOpen,
|
|
fileStorageRead,
|
|
fileStorageReadFile,
|
|
fileStorageRenameFile,
|
|
fileStorageWrite,
|
|
fileStorageWriteFile,
|
|
} from './actions';
|
|
import fileStorage from './sagas';
|
|
import { FileMetadata, FileStorageDb } from '.';
|
|
|
|
beforeEach(() => {
|
|
// deterministic UUID generator for repeatable tests
|
|
const nextId = createCountFunc();
|
|
Dexie.Observable.createUUID = () => uuid(nextId());
|
|
});
|
|
|
|
afterEach(async () => {
|
|
jest.restoreAllMocks();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
const request = indexedDB.deleteDatabase('test');
|
|
request.addEventListener('success', resolve);
|
|
request.addEventListener('error', reject);
|
|
});
|
|
|
|
localStorage.clear();
|
|
});
|
|
|
|
/**
|
|
* helper function that writes test file to storage for later use in a test
|
|
* @param saga The saga.
|
|
* @returns The test file metadata and test file contents.
|
|
*/
|
|
async function setUpTestFile(saga: AsyncSaga): Promise<[FileMetadata, string]> {
|
|
const testFilePath = 'test.file';
|
|
const testFileId = uuid(0);
|
|
const testFileContents = 'test file contents';
|
|
const testFileContentsSha256 =
|
|
'c4fa968a745586faaa030054f51fb1cafd5e9ae25fa6b137ac6477715fdc81b1';
|
|
|
|
const testFile: FileMetadata = {
|
|
uuid: testFileId,
|
|
path: testFilePath,
|
|
sha256: testFileContentsSha256,
|
|
viewState: null,
|
|
};
|
|
|
|
saga.put(fileStorageOpen(testFilePath, 'w', true));
|
|
|
|
const didOpen = await saga.take();
|
|
|
|
if (!fileStorageDidOpen.matches(didOpen)) {
|
|
fail(didOpen);
|
|
}
|
|
|
|
saga.put(fileStorageWrite(didOpen.fd, testFileContents));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidWrite(didOpen.fd));
|
|
|
|
saga.put(fileStorageClose(didOpen.fd));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(didOpen.fd));
|
|
|
|
return [testFile, testFileContents];
|
|
}
|
|
|
|
describe('initialize', () => {
|
|
it('should migrate old program from local storage during initialization', async () => {
|
|
const oldProgramKey = 'program';
|
|
const oldProgramContents = '# test program';
|
|
const oldProgramContentsSha256 =
|
|
'31c21eb39c9276341d9364f6d4bcac46a4aa3768bc2626f8aa742c46e3e0fdd6';
|
|
|
|
// add item to localStorage to simulate an existing program
|
|
localStorage.setItem(oldProgramKey, oldProgramContents);
|
|
expect(localStorage.getItem(oldProgramKey)).toBe(oldProgramContents);
|
|
|
|
const saga = new AsyncSaga(fileStorage, {
|
|
fileStorage: new FileStorageDb('test'),
|
|
});
|
|
|
|
// initialization should remove the localStorage entry and add add it to
|
|
// new storage backend
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidInitialize([
|
|
{
|
|
uuid: uuid(0),
|
|
path: 'main.py',
|
|
sha256: oldProgramContentsSha256,
|
|
viewState: null,
|
|
},
|
|
]),
|
|
);
|
|
expect(localStorage.getItem(oldProgramKey)).toBeNull();
|
|
|
|
await saga.end();
|
|
});
|
|
|
|
it('should catch error', async () => {
|
|
const testError = new Error('test error');
|
|
|
|
jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
|
|
throw testError;
|
|
});
|
|
|
|
const saga = new AsyncSaga(fileStorage, {
|
|
fileStorage: new FileStorageDb('test'),
|
|
});
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToInitialize(testError),
|
|
);
|
|
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('open', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
});
|
|
|
|
it('should fail to open for reading if file does not exist', async () => {
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToOpen(
|
|
'test.file',
|
|
new Error("file 'test.file' does not exist"),
|
|
),
|
|
);
|
|
});
|
|
|
|
describe('should open file for writing if file does not exist', () => {
|
|
beforeEach(async () => {
|
|
saga.put(fileStorageOpen('test.file', 'w', true));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 0 as FD),
|
|
);
|
|
});
|
|
|
|
describe('should fail to open if file is already open for writing', () => {
|
|
it.each<FileOpenMode>(['r', 'w'])('mode: %o', async (mode) => {
|
|
saga.put(fileStorageOpen('test.file', mode, true));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToOpen(
|
|
'test.file',
|
|
new Error("file 'test.file' is already in use"),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
saga.put(fileStorageClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(0 as FD));
|
|
});
|
|
});
|
|
|
|
describe('should open file if file exists', () => {
|
|
beforeEach(async () => {
|
|
await setUpTestFile(saga);
|
|
});
|
|
|
|
it.each<FileOpenMode>(['r', 'w'])('mode: %o', async (mode) => {
|
|
saga.put(fileStorageOpen('test.file', mode, false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should allow multiple readers', async () => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 2 as FD),
|
|
);
|
|
});
|
|
|
|
it('should fail to open for writing if already open for reading', async () => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'w', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToOpen(
|
|
'test.file',
|
|
new Error("file 'test.file' is already in use"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should allow calling close multiple times', async () => {
|
|
saga.put(fileStorageOpen('test.file', 'w', true));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 0 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(0 as FD));
|
|
|
|
saga.put(fileStorageClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(0 as FD));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('read', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
});
|
|
|
|
describe('should read an open file', () => {
|
|
it.each<FileOpenMode>(['r', 'w'])('mode: %o', async (mode) => {
|
|
const [, contents] = await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', mode, false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageRead(1 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidRead(1 as FD, contents),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('should fail to read a closed file', () => {
|
|
it.each<FileOpenMode>(['r', 'w'])('mode: %o', async (mode) => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', mode, false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageClose(1 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(1 as FD));
|
|
|
|
saga.put(fileStorageRead(1 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToRead(
|
|
1 as FD,
|
|
new Error('file descriptor 1 is not open'),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('write', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
});
|
|
|
|
it('should write a file open for writing', async () => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'w', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageWrite(1 as FD, 'new contents'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidWrite(1 as FD));
|
|
});
|
|
|
|
it('should fail to write a file open for reading', async () => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageWrite(1 as FD, 'new contents'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToWrite(
|
|
1 as FD,
|
|
new Error('file descriptor 1 is not open for writing'),
|
|
),
|
|
);
|
|
});
|
|
|
|
describe('should fail to write a closed file', () => {
|
|
it.each<FileOpenMode>(['r', 'w'])('mode: %o', async (mode) => {
|
|
await setUpTestFile(saga);
|
|
|
|
saga.put(fileStorageOpen('test.file', mode, false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageClose(1 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(1 as FD));
|
|
|
|
saga.put(fileStorageWrite(1 as FD, 'new contents'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToWrite(
|
|
1 as FD,
|
|
new Error('file descriptor 1 is not open'),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('readFile', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
|
|
saga.put(fileStorageReadFile('test.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageOpen('test.file', 'r', false),
|
|
);
|
|
});
|
|
|
|
it('should forward open error', async () => {
|
|
const error = new Error('open test file failed');
|
|
saga.put(fileStorageDidFailToOpen('test.file', error));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToReadFile('test.file', error),
|
|
);
|
|
});
|
|
|
|
describe('should open file', () => {
|
|
beforeEach(async () => {
|
|
saga.put(fileStorageDidOpen('test.file', uuid(0), 0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageRead(0 as FD));
|
|
});
|
|
|
|
it('should forward read error', async () => {
|
|
const error = new Error('test fail to read');
|
|
saga.put(fileStorageDidFailToRead(0 as FD, error));
|
|
|
|
// file should be closed before we get fileStorageDidFailToReadFile
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageClose(0 as FD));
|
|
|
|
saga.put(fileStorageDidClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToReadFile('test.file', error),
|
|
);
|
|
});
|
|
|
|
it('should return file contents', async () => {
|
|
const contents = 'test read contents';
|
|
saga.put(fileStorageDidRead(0 as FD, contents));
|
|
|
|
// file should be closed before we get fileStorageDidReadFile
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageClose(0 as FD));
|
|
|
|
saga.put(fileStorageDidClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidReadFile('test.file', contents),
|
|
);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('writeFile', () => {
|
|
let saga: AsyncSaga;
|
|
const contents = 'test write file contents';
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
|
|
saga.put(fileStorageWriteFile('test.file', contents));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageOpen('test.file', 'w', true),
|
|
);
|
|
});
|
|
|
|
it('should forward open error', async () => {
|
|
const error = new Error('open test file failed');
|
|
saga.put(fileStorageDidFailToOpen('test.file', error));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToWriteFile('test.file', error),
|
|
);
|
|
});
|
|
|
|
describe('should open file', () => {
|
|
beforeEach(async () => {
|
|
saga.put(fileStorageDidOpen('test.file', uuid(0), 0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageWrite(0 as FD, contents),
|
|
);
|
|
});
|
|
|
|
it('should forward write error', async () => {
|
|
const error = new Error('test fail to write');
|
|
saga.put(fileStorageDidFailToWrite(0 as FD, error));
|
|
|
|
// file should be closed before we get fileStorageDidFailToWriteFile
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageClose(0 as FD));
|
|
|
|
saga.put(fileStorageDidClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToWriteFile('test.file', error),
|
|
);
|
|
});
|
|
|
|
it('should return', async () => {
|
|
saga.put(fileStorageDidWrite(0 as FD));
|
|
|
|
// file should be closed before we get fileStorageDidWriteFile
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageClose(0 as FD));
|
|
|
|
saga.put(fileStorageDidClose(0 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidWriteFile('test.file', uuid(0)),
|
|
);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('copyFile', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
await setUpTestFile(saga);
|
|
});
|
|
|
|
it('should fail if file does not exist', async () => {
|
|
saga.put(fileStorageCopyFile('other.file', 'new.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToCopyFile(
|
|
'other.file',
|
|
new Error("file 'other.file' does not exist"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should fail if new file is open', async () => {
|
|
saga.put(fileStorageOpen('new.file', 'w', true));
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('new.file', uuid(1), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageCopyFile('test.file', 'new.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToCopyFile(
|
|
'test.file',
|
|
new Error("file 'new.file' is in use"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should fail if new file exists', async () => {
|
|
saga.put(fileStorageOpen('new.file', 'w', true));
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('new.file', uuid(1), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageClose(1 as FD));
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(1 as FD));
|
|
|
|
saga.put(fileStorageCopyFile('test.file', 'new.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToCopyFile(
|
|
'test.file',
|
|
new Error("file 'new.file' already exists"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should copy file', async () => {
|
|
saga.put(fileStorageCopyFile('test.file', 'new.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidCopyFile('test.file'));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('deleteFile', () => {
|
|
let saga: AsyncSaga;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
await setUpTestFile(saga);
|
|
});
|
|
|
|
it('should fail if file does not exist', async () => {
|
|
saga.put(fileStorageDeleteFile('other.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToDeleteFile(
|
|
'other.file',
|
|
new Error("file 'other.file' does not exist"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should fail if file is open', async () => {
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
saga.put(fileStorageDeleteFile('test.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToDeleteFile(
|
|
'test.file',
|
|
new Error("file 'test.file' is in use"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should remove file', async () => {
|
|
saga.put(fileStorageDeleteFile('test.file'));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidDeleteFile('test.file'),
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('renameFile', () => {
|
|
let saga: AsyncSaga;
|
|
let testFile: FileMetadata;
|
|
const newPath = 'new.file';
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
[testFile] = await setUpTestFile(saga);
|
|
});
|
|
|
|
it('should fail if file does not exist', async () => {
|
|
saga.put(fileStorageRenameFile('other.file', newPath));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToRenameFile(
|
|
'other.file',
|
|
new Error("file 'other.file' does not exist"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should fail if file is open', async () => {
|
|
saga.put(fileStorageOpen('test.file', 'r', false));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen('test.file', uuid(0), 1 as FD),
|
|
);
|
|
saga.put(fileStorageRenameFile('test.file', newPath));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToRenameFile(
|
|
'test.file',
|
|
new Error("file 'test.file' is in use"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should fail if new path already exists', async () => {
|
|
// there are two paths here that result in the same error
|
|
// the first is if the file is open, e.g. in another tab
|
|
|
|
saga.put(fileStorageOpen(newPath, 'w', true));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidOpen(newPath, uuid(1), 1 as FD),
|
|
);
|
|
|
|
saga.put(fileStorageRenameFile('test.file', newPath));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToRenameFile(
|
|
'test.file',
|
|
new Error("file 'new.file' already exists"),
|
|
),
|
|
);
|
|
|
|
// the second is if the file is not open but still exists in storage
|
|
|
|
saga.put(fileStorageClose(1 as FD));
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidClose(1 as FD));
|
|
|
|
saga.put(fileStorageRenameFile('test.file', newPath));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToRenameFile(
|
|
'test.file',
|
|
new Error("file 'new.file' already exists"),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('should change file', async () => {
|
|
saga.put(fileStorageRenameFile(testFile.path, newPath));
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidRenameFile(testFile.path),
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|
|
|
|
describe('dump all files', () => {
|
|
let saga: AsyncSaga;
|
|
let testFile: FileMetadata;
|
|
let testFileContents: string;
|
|
|
|
beforeEach(async () => {
|
|
saga = new AsyncSaga(fileStorage, { fileStorage: new FileStorageDb('test') });
|
|
|
|
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
|
[testFile, testFileContents] = await setUpTestFile(saga);
|
|
});
|
|
|
|
it('should archive file', async () => {
|
|
saga.put(fileStorageDumpAllFiles());
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidDumpAllFiles([
|
|
{ path: testFile.path, contents: testFileContents },
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('should catch error', async () => {
|
|
const testError = new Error('test error');
|
|
|
|
jest.spyOn(Dexie.prototype, 'transaction').mockImplementation(() => {
|
|
throw testError;
|
|
});
|
|
|
|
saga.put(fileStorageDumpAllFiles());
|
|
|
|
await expect(saga.take()).resolves.toEqual(
|
|
fileStorageDidFailToDumpAllFiles(testError),
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await saga.end();
|
|
});
|
|
});
|