diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index da626d3786..0a91508400 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -46,6 +46,7 @@ "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/types": "workspace:^", "better-sqlite3": "^8.0.0", diff --git a/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts b/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts index 562acb3a8a..a7fe1815b2 100644 --- a/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts +++ b/packages/backend-test-utils/src/filesystem/MockDirectory.test.ts @@ -16,7 +16,11 @@ import fs from 'fs-extra'; import os from 'os'; -import { relative as relativePath } from 'path'; +import { + join as joinPath, + resolve as resolvePath, + relative as relativePath, +} from 'path'; import { MockDirectory } from './MockDirectory'; describe('MockDirectory', () => { @@ -27,7 +31,9 @@ describe('MockDirectory', () => { it('should resolve paths', () => { expect(mockDir.path).toEqual(expect.any(String)); expect(relativePath(mockDir.path, mockDir.resolve('a'))).toBe('a'); - expect(relativePath(mockDir.path, mockDir.resolve('a/b/c'))).toBe('a/b/c'); + expect(relativePath(mockDir.path, mockDir.resolve('a/b/c'))).toBe( + joinPath('a', 'b', 'c'), + ); }); it('should remove itself', async () => { @@ -267,14 +273,15 @@ describe('MockDirectory', () => { }); it('should reject non-child paths', async () => { + const path = resolvePath('/root/a.txt'); await expect(mockDir.setContent({ '/root/a.txt': 'a' })).rejects.toThrow( - "Provided path must resolve to a child path of the mock directory, got '/root/a.txt'", + `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); await expect(mockDir.addContent({ '/root/a.txt': 'a' })).rejects.toThrow( - "Provided path must resolve to a child path of the mock directory, got '/root/a.txt'", + `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); await expect(mockDir.content({ path: '/root/a.txt' })).rejects.toThrow( - "Provided path must resolve to a child path of the mock directory, got '/root/a.txt'", + `Provided path must resolve to a child path of the mock directory, got '${path}'`, ); }); diff --git a/packages/backend-test-utils/src/filesystem/MockDirectory.ts b/packages/backend-test-utils/src/filesystem/MockDirectory.ts index 5553982484..371a315f22 100644 --- a/packages/backend-test-utils/src/filesystem/MockDirectory.ts +++ b/packages/backend-test-utils/src/filesystem/MockDirectory.ts @@ -28,6 +28,7 @@ import { win32, posix, } from 'path'; +import { isError } from '@backstage/errors'; /** * The content of a mock directory represented by a nested object structure. @@ -334,7 +335,16 @@ export class MockDirectory { * Removes the mock directory and all its contents. */ remove = async (): Promise => { - await fs.rm(this.#root, { recursive: true, force: true }); + try { + await fs.rm(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 }); + } else { + throw error; + } + } }; #transformInput(input: MockDirectoryContent[string]): MockEntry[] { diff --git a/yarn.lock b/yarn.lock index dc8e58da96..20922a9959 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3642,6 +3642,7 @@ __metadata: "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/types": "workspace:^" "@types/supertest": ^2.0.8