From 7d275f4587a641e13d8a9d9c503a55c6cdc25078 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 22 Sep 2023 11:48:12 +0200 Subject: [PATCH] backend-test-utils: refactor MockDirectory to be synchronous Signed-off-by: Patrik Oldsberg --- packages/backend-test-utils/api-report.md | 10 +- .../src/filesystem/MockDirectory.test.ts | 146 ++++++++---------- .../src/filesystem/MockDirectory.ts | 80 +++++----- 3 files changed, 108 insertions(+), 128 deletions(-) diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 0262ef339d..dff218edb1 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -36,17 +36,17 @@ export function isDockerDisabledForTests(): boolean; // @public export class MockDirectory { - addContent(root: MockDirectoryContent): Promise; - clear: () => Promise; + addContent(root: MockDirectoryContent): void; + clear: () => void; content( options?: MockDirectoryContentOptions, - ): Promise; + ): MockDirectoryContent | undefined; static create(options?: MockDirectoryCreateOptions): MockDirectory; static mockOsTmpDir(): MockDirectory; get path(): string; - remove: () => Promise; + remove: () => void; resolve(...paths: string[]): string; - setContent(root: MockDirectoryContent): Promise; + setContent(root: MockDirectoryContent): void; } // @public diff --git a/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts b/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts index a7fe1815b2..19c56119f0 100644 --- a/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts +++ b/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts @@ -38,19 +38,19 @@ describe('MockDirectory', () => { it('should remove itself', async () => { await expect(fs.pathExists(mockDir.path)).resolves.toBe(true); - await mockDir.remove(); + mockDir.remove(); await expect(fs.pathExists(mockDir.path)).resolves.toBe(false); }); - it('should populate a directory with text files', async () => { - await mockDir.setContent({ + it('should populate a directory with text files', () => { + mockDir.setContent({ 'a.txt': 'a', 'a/b.txt': 'b', 'a/b/c.txt': 'c', 'a/b/d.txt': 'd', }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'a.txt': 'a', a: { 'b.txt': 'b', @@ -62,15 +62,15 @@ describe('MockDirectory', () => { }); }); - it('should mix text and binary files', async () => { - await mockDir.setContent({ + it('should mix text and binary files', () => { + mockDir.setContent({ 'a.txt': 'a', 'a/b.txt': 'b', 'a/b/c.bin': Buffer.from([0xc]), 'a/b/d.bin': Buffer.from([0xd]), }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'a.txt': 'a', a: { 'b.txt': 'b', @@ -82,25 +82,25 @@ describe('MockDirectory', () => { }); }); - it('should be able to add content', async () => { - await mockDir.setContent({ + it('should be able to add content', () => { + mockDir.setContent({ 'a.txt': 'a', b: {}, }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'a.txt': 'a', b: {}, }); - await mockDir.addContent({ + mockDir.addContent({ 'b.txt': 'b', b: { 'c.txt': 'c', }, }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'a.txt': 'a', 'b.txt': 'b', b: { @@ -109,22 +109,22 @@ describe('MockDirectory', () => { }); }); - it('should replace existing files', async () => { - await mockDir.setContent({ + it('should replace existing files', () => { + mockDir.setContent({ 'a.txt': 'a', }); - await mockDir.addContent({ + mockDir.addContent({ 'a.txt': 'a2', }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'a.txt': 'a2', }); }); - it('should read content from sub dirs', async () => { - await mockDir.setContent({ + it('should read content from sub dirs', () => { + mockDir.setContent({ 'a.txt': 'a', 'b/b.txt': 'b', 'b/c/c.txt': 'c', @@ -140,77 +140,65 @@ describe('MockDirectory', () => { }, }; - await expect(mockDir.content()).resolves.toEqual(expected); - await expect(mockDir.content({ path: mockDir.path })).resolves.toEqual( - expected, - ); - await expect( - mockDir.content({ path: mockDir.resolve('.') }), - ).resolves.toEqual(expected); - await expect(mockDir.content({ path: 'b' })).resolves.toEqual(expected.b); - await expect(mockDir.content({ path: './b' })).resolves.toEqual(expected.b); - await expect( - mockDir.content({ path: mockDir.resolve('b') }), - ).resolves.toEqual(expected.b); - await expect(mockDir.content({ path: 'b/c' })).resolves.toEqual( + expect(mockDir.content()).toEqual(expected); + expect(mockDir.content({ path: mockDir.path })).toEqual(expected); + expect(mockDir.content({ path: mockDir.resolve('.') })).toEqual(expected); + expect(mockDir.content({ path: 'b' })).toEqual(expected.b); + expect(mockDir.content({ path: './b' })).toEqual(expected.b); + expect(mockDir.content({ path: mockDir.resolve('b') })).toEqual(expected.b); + expect(mockDir.content({ path: 'b/c' })).toEqual(expected.b.c); + expect(mockDir.content({ path: './b/c' })).toEqual(expected.b.c); + expect(mockDir.content({ path: mockDir.resolve('b/c') })).toEqual( expected.b.c, ); - await expect(mockDir.content({ path: './b/c' })).resolves.toEqual( + expect(mockDir.content({ path: mockDir.resolve('b', 'c') })).toEqual( expected.b.c, ); - await expect( - mockDir.content({ path: mockDir.resolve('b/c') }), - ).resolves.toEqual(expected.b.c); - await expect( - mockDir.content({ path: mockDir.resolve('b', 'c') }), - ).resolves.toEqual(expected.b.c); }); - it('should allow text reading to be configured', async () => { + it('should allow text reading to be configured', () => { const text = 'a'; const binary = Buffer.from('a', 'utf8'); - await mockDir.setContent({ + mockDir.setContent({ a: binary, 'a.txt': text, 'a.bin': binary, }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ a: binary, 'a.txt': text, 'a.bin': binary, }); - await expect(mockDir.content({ shouldReadAsText: false })).resolves.toEqual( - { - a: binary, - 'a.txt': binary, - 'a.bin': binary, - }, - ); + expect(mockDir.content({ shouldReadAsText: false })).toEqual({ + a: binary, + 'a.txt': binary, + 'a.bin': binary, + }); - await expect(mockDir.content({ shouldReadAsText: true })).resolves.toEqual({ + expect(mockDir.content({ shouldReadAsText: true })).toEqual({ a: text, 'a.txt': text, 'a.bin': text, }); - await expect( + expect( mockDir.content({ shouldReadAsText: path => path.length > 3 }), - ).resolves.toEqual({ + ).toEqual({ a: binary, 'a.txt': text, 'a.bin': text, }); }); - it('should provide a posix path to shouldReadAsText', async () => { + it('should provide a posix path to shouldReadAsText', () => { const shouldReadAsText = jest.fn().mockReturnValue(true); - await mockDir.setContent({ 'a/b/c': 'c' }); + mockDir.setContent({ 'a/b/c': 'c' }); - await expect(mockDir.content({ shouldReadAsText })).resolves.toEqual({ + expect(mockDir.content({ shouldReadAsText })).toEqual({ a: { b: { c: 'c' } }, }); expect(shouldReadAsText).toHaveBeenCalledWith( @@ -219,27 +207,27 @@ describe('MockDirectory', () => { ); }); - it('should not override directories', async () => { - await mockDir.setContent({ + it('should not override directories', () => { + mockDir.setContent({ 'a.txt': 'a', b: {}, }); - await expect( + expect(() => mockDir.addContent({ 'a.txt': {}, }), - ).rejects.toThrow('EEXIST'); + ).toThrow('EEXIST'); - await expect( + expect(() => mockDir.addContent({ b: 'b', }), - ).rejects.toThrow('EISDIR'); + ).toThrow('EISDIR'); }); - it('examples should work', async () => { - await mockDir.setContent({ + it('examples should work', () => { + mockDir.setContent({ 'test.txt': 'content', 'sub-dir': { 'file.txt': 'content', @@ -249,7 +237,7 @@ describe('MockDirectory', () => { 'binary-file': Buffer.from([0, 1, 2]), }); - await mockDir.addContent({ + mockDir.addContent({ 'test.txt': 'content', 'sub-dir': { 'file.txt': 'content', @@ -259,7 +247,7 @@ describe('MockDirectory', () => { 'binary-file': Buffer.from([0, 1, 2]), }); - await expect(mockDir.content()).resolves.toEqual({ + expect(mockDir.content()).toEqual({ 'test.txt': 'content', 'sub-dir': { 'file.txt': 'content', @@ -272,15 +260,15 @@ describe('MockDirectory', () => { }); }); - it('should reject non-child paths', async () => { + it('should reject non-child paths', () => { const path = resolvePath('/root/a.txt'); - await expect(mockDir.setContent({ '/root/a.txt': 'a' })).rejects.toThrow( + expect(() => mockDir.setContent({ '/root/a.txt': 'a' })).toThrow( `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); - await expect(mockDir.addContent({ '/root/a.txt': 'a' })).rejects.toThrow( + expect(() => mockDir.addContent({ '/root/a.txt': 'a' })).toThrow( `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); - await expect(mockDir.content({ path: '/root/a.txt' })).rejects.toThrow( + expect(() => mockDir.content({ path: '/root/a.txt' })).toThrow( `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); }); @@ -291,19 +279,19 @@ describe('MockDirectory', () => { describe('inner', () => { cleanupMockDir = MockDirectory.create(); - it('should populate a directory', async () => { - await cleanupMockDir.setContent({ + it('should populate a directory', () => { + cleanupMockDir.setContent({ 'a.txt': 'a', }); - await expect(cleanupMockDir.content()).resolves.toEqual({ + expect(cleanupMockDir.content()).toEqual({ 'a.txt': 'a', }); }); }); - it('should clean up after itself automatically', async () => { - await expect(cleanupMockDir.content()).resolves.toBeUndefined(); + it('should clean up after itself automatically', () => { + expect(cleanupMockDir.content()).toBeUndefined(); }); }); @@ -313,12 +301,12 @@ describe('MockDirectory', () => { describe('inner', () => { tmpDirMock = MockDirectory.mockOsTmpDir(); - it('should mock os.tmpdir()', async () => { + it('should mock os.tmpdir()', () => { expect(os.tmpdir()).toBe(tmpDirMock.path); }); }); - it('should restore os.tmpdir()', async () => { + it('should restore os.tmpdir()', () => { expect(os.tmpdir()).not.toBe(tmpDirMock.path); }); }); @@ -329,15 +317,15 @@ describe('MockDirectory', () => { describe('inner', () => { existingMockDir = MockDirectory.create({ root: __dirname }); // hardcore mode - it('should read existing directory', async () => { - await expect(existingMockDir.content()).resolves.toMatchObject({ + it('should read existing directory', () => { + expect(existingMockDir.content()).toMatchObject({ 'index.ts': expect.any(String), }); }); }); - it('should remove existing directory', async () => { - await expect(fs.pathExists(__dirname)).resolves.toBe(true); + it('should remove existing directory', () => { + expect(fs.pathExistsSync(__dirname)).toBe(true); }); }); }); diff --git a/packages/backend-test-utils/src/filesystem/MockDirectory.ts b/packages/backend-test-utils/src/filesystem/MockDirectory.ts index 371a315f22..b452fdcc08 100644 --- a/packages/backend-test-utils/src/filesystem/MockDirectory.ts +++ b/packages/backend-test-utils/src/filesystem/MockDirectory.ts @@ -124,9 +124,9 @@ export class MockDirectory { * describe('MySubject', () => { * const mockDir = MockDirectory.create(); * - * beforeEach(() => mockDir.clear()); + * beforeEach(mockDir.clear); * - * it('should work', async () => { + * it('should work', () => { * // ... use mockDir * }) * }) @@ -141,7 +141,7 @@ export class MockDirectory { const shouldCleanup = !options?.root || !fs.pathExistsSync(options.root); if (shouldCleanup) { - process.on('beforeExit', mocker.#removeSync); + process.on('beforeExit', mocker.remove); try { afterAll(mocker.remove); @@ -199,7 +199,7 @@ export class MockDirectory { * * @example * ```ts - * await mockDir.setContent({ + * mockDir.setContent({ * 'test.txt': 'content', * 'sub-dir': { * 'file.txt': 'content', @@ -210,8 +210,8 @@ export class MockDirectory { * }); * ``` */ - async setContent(root: MockDirectoryContent): Promise { - await this.remove(); + setContent(root: MockDirectoryContent): void { + this.remove(); return this.addContent(root); } @@ -221,7 +221,7 @@ export class MockDirectory { * * @example * ```ts - * await mockDir.addContent({ + * mockDir.addContent({ * 'test.txt': 'content', * 'sub-dir': { * 'file.txt': 'content', @@ -232,7 +232,7 @@ export class MockDirectory { * }); * ``` */ - async addContent(root: MockDirectoryContent): Promise { + addContent(root: MockDirectoryContent): void { const entries = this.#transformInput(root); for (const entry of entries) { @@ -244,10 +244,10 @@ export class MockDirectory { } if (entry.type === 'dir') { - await fs.ensureDir(fullPath); + fs.ensureDirSync(fullPath); } else if (entry.type === 'file') { - await fs.ensureDir(dirname(fullPath)); - await fs.writeFile(fullPath, entry.content); + fs.ensureDirSync(dirname(fullPath)); + fs.writeFileSync(fullPath, entry.content); } } } @@ -262,7 +262,7 @@ export class MockDirectory { * * @example * ```ts - * await expect(mockDir.content()).resolves.toEqual({ + * expect(mockDir.content()).toEqual({ * 'test.txt': 'content', * 'sub-dir': { * 'file.txt': 'content', @@ -275,9 +275,9 @@ export class MockDirectory { * }); * ``` */ - async content( + content( options?: MockDirectoryContentOptions, - ): Promise { + ): MockDirectoryContent | undefined { const shouldReadAsText = (typeof options?.shouldReadAsText === 'boolean' ? () => options?.shouldReadAsText @@ -291,33 +291,29 @@ export class MockDirectory { ); } - async function read( - path: string, - ): Promise { - if (!(await fs.pathExists(path))) { + function read(path: string): MockDirectoryContent | undefined { + if (!fs.pathExistsSync(path)) { return undefined; } - const entries = await fs.readdir(path, { withFileTypes: true }); + const entries = fs.readdirSync(path, { withFileTypes: true }); return Object.fromEntries( - await Promise.all( - entries.map(async entry => { - const fullPath = resolvePath(path, entry.name); + entries.map(entry => { + const fullPath = resolvePath(path, entry.name); - if (entry.isDirectory()) { - return [entry.name, await read(fullPath)]; - } - const content = await fs.readFile(fullPath); - const relativePosixPath = relativePath(root, fullPath) - .split(win32.sep) - .join(posix.sep); + if (entry.isDirectory()) { + return [entry.name, read(fullPath)]; + } + const content = fs.readFileSync(fullPath); + const relativePosixPath = relativePath(root, fullPath) + .split(win32.sep) + .join(posix.sep); - if (shouldReadAsText(relativePosixPath, content)) { - return [entry.name, content.toString('utf8')]; - } - return [entry.name, content]; - }), - ), + if (shouldReadAsText(relativePosixPath, content)) { + return [entry.name, content.toString('utf8')]; + } + return [entry.name, content]; + }), ); } @@ -327,20 +323,20 @@ export class MockDirectory { /** * Clears the content of the mock directory, ensuring that the directory itself exists. */ - clear = async (): Promise => { - await this.setContent({}); + clear = (): void => { + this.setContent({}); }; /** * Removes the mock directory and all its contents. */ - remove = async (): Promise => { + remove = (): void => { try { - await fs.rm(this.#root, { recursive: true, force: true }); + fs.rmSync(this.#root, { recursive: true, force: true }); } catch (error: unknown) { if (isError(error) && error.code === 'ENOTEMPTY') { // Windows can be a bit flaky, give it another go - await fs.rm(this.#root, { recursive: true, force: true }); + fs.rmSync(this.#root, { recursive: true, force: true }); } else { throw error; } @@ -372,8 +368,4 @@ export class MockDirectory { return entries; } - - #removeSync = () => { - fs.rmSync(this.#root, { recursive: true, force: true }); - }; }