diff --git a/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.spec.ts b/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.spec.ts index cb2066b..8f43911 100644 --- a/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.spec.ts +++ b/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.spec.ts @@ -1,27 +1,83 @@ -import { ExecutorContext } from '@nx/devkit'; - -import { PublishDevcontainerFeatureExecutorSchema } from './schema'; import executor from './executor'; +import { ExecutorContext, logger } from '@nx/devkit'; +import { execFileSync } from 'child_process'; -const options: PublishDevcontainerFeatureExecutorSchema = {}; -const context: ExecutorContext = { - root: '', - cwd: process.cwd(), - isVerbose: false, - projectGraph: { - nodes: {}, - dependencies: {}, - }, - projectsConfigurations: { - projects: {}, - version: 2, +jest.mock('@nx/devkit', () => ({ + logger: { + fatal: jest.fn(), + error: jest.fn(), }, - nxJsonConfiguration: {}, -}; +})); + +jest.mock('child_process', () => ({ + execFileSync: jest.fn(), +})); + +describe('runExecutor', () => { + const context: ExecutorContext = { + root: '/workspace-root', + cwd: '', + isVerbose: false, + projectName: 'test-project', + projectGraph: { + nodes: {}, + dependencies: {}, + }, + projectsConfigurations: { + projects: { + 'test-project': { + root: '/workspace-root/project-root', + targets: {}, + }, + }, + version: 2, + }, + nxJsonConfiguration: {}, + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should execute the publish command successfully', async () => { + (execFileSync as jest.Mock).mockImplementation(() => {}); + + const result = await executor({}, context); + + expect(result).toEqual({ success: true }); + expect(execFileSync).toHaveBeenCalledWith( + 'npx', + [ + 'devcontainer', + 'features', + 'publish', + '--registry', + 'ghcr.io', + '--namespace', + 'ebizbase/devcontainer-features', + '/workspace-root/project-root/src/test-project', + ], + { cwd: '/workspace-root', stdio: 'inherit' } + ); + }); + + it('should log an error and return failure when ProjectUtils throws', async () => { + const result = await executor({}, { ...context, projectName: undefined }); + expect(result).toEqual({ success: false }); + expect(logger.fatal).toHaveBeenCalledWith('No project name provided', expect.any(Error)); + expect(execFileSync).not.toHaveBeenCalled(); + }); + + it('should handle errors from execFileSync gracefully', async () => { + (execFileSync as jest.Mock).mockImplementation(() => { + throw new Error('Command failed'); + }); -describe('PublishDevcontainerFeature Executor', () => { - it('can run', async () => { - const output = await executor(options, context); - expect(output.success).toBe(true); + const result = await executor({}, context); + expect(result).toEqual({ success: false }); + expect(logger.fatal).toHaveBeenCalledWith( + 'Error while executing the publish command', + expect.any(Error) + ); }); }); diff --git a/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.ts b/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.ts index aacb23b..1073afc 100644 --- a/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.ts +++ b/tools/nx-internal/src/executors/publish-devcontainer-feature/executor.ts @@ -3,7 +3,10 @@ import { ProjectUtils } from '@ebizbase/nx-devkit'; import { execFileSync } from 'child_process'; import { PublishDevcontainerFeatureExecutorSchema } from './schema'; -const runExecutor: PromiseExecutor = async (_options, context) => { +const runExecutor: PromiseExecutor = async ( + _options, + context +) => { let projectUtils; try { projectUtils = new ProjectUtils(context); @@ -11,11 +14,25 @@ const runExecutor: PromiseExecutor = a logger.fatal('No project name provided', error); return { success: false }; } - const commands = ['npx', 'devcontainer', 'features', 'publish', '--registry', 'ghcr.io', '--namespace', 'ebizbase/devcontainer-features', projectUtils.getProjectRoot() + '/src/' + projectUtils.getProjectName()]; + const commands = [ + 'npx', + 'devcontainer', + 'features', + 'publish', + '--registry', + 'ghcr.io', + '--namespace', + 'ebizbase/devcontainer-features', + projectUtils.getProjectRoot() + '/src/' + projectUtils.getProjectName(), + ]; - execFileSync(commands[0], commands.slice(1), { cwd: context.root, stdio: 'inherit' }); - - return { success: true }; + try { + execFileSync(commands[0], commands.slice(1), { cwd: context.root, stdio: 'inherit' }); + return { success: true }; + } catch (error) { + logger.fatal('Error while executing the publish command', error); + return { success: false }; + } }; export default runExecutor; diff --git a/tools/nx-internal/src/executors/test-devcontainer-feature/executor.spec.ts b/tools/nx-internal/src/executors/test-devcontainer-feature/executor.spec.ts index 633fc05..8a7dc38 100644 --- a/tools/nx-internal/src/executors/test-devcontainer-feature/executor.spec.ts +++ b/tools/nx-internal/src/executors/test-devcontainer-feature/executor.spec.ts @@ -1,27 +1,82 @@ -import { ExecutorContext } from '@nx/devkit'; - -import { TestDevcontainerFeatureExecutorSchema } from './schema'; import executor from './executor'; +import { ExecutorContext, logger } from '@nx/devkit'; +import { execFileSync } from 'child_process'; -const options: TestDevcontainerFeatureExecutorSchema = {}; -const context: ExecutorContext = { - root: '', - cwd: process.cwd(), - isVerbose: false, - projectGraph: { - nodes: {}, - dependencies: {}, - }, - projectsConfigurations: { - projects: {}, - version: 2, +jest.mock('@nx/devkit', () => ({ + logger: { + fatal: jest.fn(), + error: jest.fn(), }, - nxJsonConfiguration: {}, -}; +})); + +jest.mock('child_process', () => ({ + execFileSync: jest.fn(), +})); + +describe('runExecutor', () => { + const context: ExecutorContext = { + root: '/workspace-root', + cwd: '', + isVerbose: false, + projectName: 'test-project', + projectGraph: { + nodes: {}, + dependencies: {}, + }, + projectsConfigurations: { + projects: { + 'test-project': { + root: '/workspace-root/project-root', + targets: {}, + }, + }, + version: 2, + }, + nxJsonConfiguration: {}, + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should execute the publish command successfully', async () => { + (execFileSync as jest.Mock).mockImplementation(() => {}); + + const result = await executor({}, context); + + expect(result).toEqual({ success: true }); + expect(execFileSync).toHaveBeenCalledWith( + 'npx', + [ + 'devcontainer', + 'features', + 'test', + '--skip-autogenerated', + '-p', + '/workspace-root/project-root', + '-f', + 'test-project', + '-i', + 'debian:bookworm-slim', + ], + { cwd: '/workspace-root', stdio: 'inherit' } + ); + }); + + it('should log an error and return failure when ProjectUtils throws', async () => { + const result = await executor({}, { ...context, projectName: undefined }); + expect(result).toEqual({ success: false }); + expect(logger.fatal).toHaveBeenCalledWith('No project name provided', expect.any(Error)); + expect(execFileSync).not.toHaveBeenCalled(); + }); + + it('should handle errors from execFileSync gracefully', async () => { + (execFileSync as jest.Mock).mockImplementation(() => { + throw new Error('Command failed'); + }); -describe('TestDevcontainerFeature Executor', () => { - it('can run', async () => { - const output = await executor(options, context); - expect(output.success).toBe(true); + const result = await executor({}, context); + expect(result).toEqual({ success: false }); + expect(logger.fatal).toHaveBeenCalledWith('Error running devcontainer test', expect.any(Error)); }); }); diff --git a/tools/nx-internal/src/executors/test-devcontainer-feature/executor.ts b/tools/nx-internal/src/executors/test-devcontainer-feature/executor.ts index e5eb8e3..7b5e019 100644 --- a/tools/nx-internal/src/executors/test-devcontainer-feature/executor.ts +++ b/tools/nx-internal/src/executors/test-devcontainer-feature/executor.ts @@ -3,7 +3,10 @@ import { TestDevcontainerFeatureExecutorSchema } from './schema'; import { ProjectUtils } from '@ebizbase/nx-devkit'; import { execFileSync } from 'child_process'; -const runExecutor: PromiseExecutor = async (options, context) => { +const runExecutor: PromiseExecutor = async ( + options, + context +) => { let projectUtils; try { projectUtils = new ProjectUtils(context); @@ -11,13 +14,30 @@ const runExecutor: PromiseExecutor = asyn logger.fatal('No project name provided', error); return { success: false }; } - const commands = ['npx', 'devcontainer', 'features', 'test','--skip-autogenerated', '-p', projectUtils.getProjectRoot(), '-f', projectUtils.getProjectName(), '-i', 'debian:bookworm-slim']; + const commands = [ + 'npx', + 'devcontainer', + 'features', + 'test', + '--skip-autogenerated', + '-p', + projectUtils.getProjectRoot(), + '-f', + projectUtils.getProjectName(), + '-i', + 'debian:bookworm-slim', + ]; if (options.filter) { commands.push('--filter', options.filter); } - execFileSync(commands[0], commands.slice(1), { cwd: context.root, stdio: 'inherit' }); - return { success: true }; + try { + execFileSync(commands[0], commands.slice(1), { cwd: context.root, stdio: 'inherit' }); + return { success: true }; + } catch (error) { + logger.fatal('Error running devcontainer test', error); + return { success: false }; + } }; export default runExecutor;