TradeMe/tractor

View on GitHub
packages/file-structure/src/actions/save-item.spec.ts

Summary

Maintainability
A
0 mins
Test Coverage
// Test setup:
import { getPort, expect } from '@tractor/unit-test';

// Dependencies:
import { TractorError } from '@tractor/error-handler';
import * as fs from 'graceful-fs';
import fetch from 'node-fetch';
import * as path from 'path';
import { promisify } from 'util';
import { Directory } from '../structure/directory';
import { File } from '../structure/file';
import { FileStructure } from '../structure/file-structure';

// Under test:
import { startTestServer } from '../../test/test-server';

describe('@tractor/file-structure - actions/save-item:', () => {
    it('should save a file', async () => {
        const readFile = promisify(fs.readFile);
        // tslint:disable-next-line:max-classes-per-file
        class TestFile extends File { }
        TestFile.prototype.extension = '.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-file'));
        fileStructure.addFileType(TestFile);
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs/file.ext`, {
            body: JSON.stringify({ data: '🚜' }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const contents = await readFile(path.join(fileStructure.path, 'file.ext'));
            expect(contents.toString()).to.equal('🚜');
        } catch {
            expect(true).to.equal('`readFile` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should save a new file with multiple extensions', async () => {
        const readFile = promisify(fs.readFile);
        // tslint:disable-next-line:max-classes-per-file
        class TestMultiFile extends File { }
        TestMultiFile.prototype.extension = '.multi.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-file-multi-extension'));
        fileStructure.addFileType(TestMultiFile);
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs/file.multi.ext`, {
            body: JSON.stringify({ data: '🚜' }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const contents = await readFile(path.join(fileStructure.path, 'file.multi.ext'));
            expect(contents.toString()).to.equal('🚜');
        } catch {
            expect(true).to.equal('`readFile` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should throw if it is an unknown file type', async () => {
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-file-unknown-extension'));
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs/file.ext`, {
            body: JSON.stringify({ data: '🚜' }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const { error } = await response.json();

        expect(error).to.equal(`Could not save "${path.join(fileStructure.path, 'file.ext')}" as it is not a supported file type.`);

        await close();
    });

    it('should increment the name if a file already exists', async () => {
        const readFile = promisify(fs.readFile);
        // tslint:disable-next-line:max-classes-per-file
        class TestFile extends File { }
        TestFile.prototype.extension = '.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-file-rename'));
        fileStructure.addFileType(TestFile);
        const file = new File(path.join(fileStructure.path, 'file.ext'), fileStructure);
        await file.save('🚜');
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs${file.url}`, {
            body: JSON.stringify({ data: '🚜' }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const contents = await readFile(path.join(fileStructure.path, 'file (1).ext'));
            expect(contents.toString()).to.equal('🚜');
        } catch {
            expect(true).to.equal('`readFile` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should overwrite an existing file', async () => {
        const readFile = promisify(fs.readFile);
        // tslint:disable-next-line:max-classes-per-file
        class TestFile extends File { }
        TestFile.prototype.extension = '.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-overwrite-file'));
        fileStructure.addFileType(TestFile);
        const file = new File(path.join(fileStructure.path, 'file.ext'), fileStructure);
        await file.save('🚜');
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs${file.url}`, {
            body: JSON.stringify({ data: '🔥', overwrite: true }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const contents = await readFile(file.path);
            expect(contents.toString()).to.equal('🔥');
        } catch {
            expect(true).to.equal('`readFile` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should save a directory', async () => {
        const readdir = promisify(fs.readdir);
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-directory'));
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs/directory`, {
            body: JSON.stringify({ }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const dir = await readdir(path.join(fileStructure.path, 'directory'));
            expect(dir).to.deep.equal([]);
        } catch {
            expect(true).to.equal('`readdir` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should increment the name if a directory already exists', async () => {
        const readdir = promisify(fs.readdir);
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-directory-rename'));
        const directory = new Directory(path.join(fileStructure.path, 'directory'), fileStructure);
        await directory.save();
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const response = await fetch(`http://localhost:${port}/fs${directory.url}`, {
            body: JSON.stringify({ }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });
        const data = await response.text();

        expect(data).to.equal('OK');
        try {
            const dir = await readdir(path.join(fileStructure.path, 'directory (1)'));
            expect(dir).to.deep.equal([]);
        } catch {
            expect(true).to.equal('`readdir` should not throw');
        }

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should handle known TractorErrors', async () => {
        // tslint:disable-next-line:max-classes-per-file
        class TestFile extends File { }
        TestFile.prototype.extension = '.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-known-errors'));
        fileStructure.addFileType(TestFile);
        const file = new TestFile(path.join(fileStructure.path, 'file.ext'), fileStructure);
        await file.save('🚜');
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        const expectedError = new TractorError(`Cannot save "${file.path}". Something went wrong.`);
        jest.spyOn(file, 'save').mockImplementation(async () => {
            throw expectedError;
        });

        const response = await fetch(`http://localhost:${port}/fs${file.url}`, {
            body: JSON.stringify({ data: '🔥', overwrite: true }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });

        const { error } = await response.json();
        expect(error).to.equal(expectedError.message);

        await close();
        await fileStructure.structure.rimraf();
    });

    it('should handle unknown errors', async () => {
        // tslint:disable-next-line:max-classes-per-file
        class TestFile extends File { }
        TestFile.prototype.extension = '.ext';
        const fileStructure = new FileStructure(path.resolve(__dirname, '../../fixtures/actions-save-unknown-errors'));
        fileStructure.addFileType(TestFile);
        const file = new TestFile(path.join(fileStructure.path, 'file.ext'), fileStructure);
        await file.save('🚜');
        const port = await getPort();
        const close = await startTestServer(fileStructure, port);

        jest.spyOn(file, 'save').mockImplementation(async () => {
            throw new Error();
        });

        const response = await fetch(`http://localhost:${port}/fs${file.url}`, {
            body: JSON.stringify({ data: '🔥', overwrite: true }),
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'PUT'
        });

        const { error } = await response.json();
        expect(error).to.equal(`Could not save "${file.path}"`);

        await close();
        await fileStructure.structure.rimraf();
    });
});