mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-15 02:54:07 +00:00
When duplicating a file users expect the file to be opened as in other applications. Fixes: https://github.com/pybricks/support/issues/1084
780 lines
23 KiB
TypeScript
780 lines
23 KiB
TypeScript
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2022-2023 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', uuid(1)),
|
|
);
|
|
});
|
|
|
|
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();
|
|
});
|
|
});
|