apiveritas
Version:
Lightweight CLI tool for consumer-driven API contract testing via JSON schema and payload comparisons.
167 lines (145 loc) • 5.73 kB
text/typescript
/**
* @file ExitHandler.ts
* @author Mario Galea
* @description
* This service class centralizes all process exit operations for the ApiVeritas CLI.
* It ensures consistent logging and exit code handling across different CLI execution paths,
* enabling more robust CI/CD integrations and easier debugging.
*
* Each method maps to a well-defined `ExitCode` to indicate why the CLI exited,
* supporting better automation and visibility into runtime outcomes.
*
*/
import { Logger } from '../utils/Logger';
/**
* Enum representing all possible process exit codes used by the CLI.
*/
export enum ExitCode {
/** Operation completed successfully */
SUCCESS = 0,
/** A general, unexpected error occurred */
GENERAL_ERROR = 1,
/** CLI was invoked with missing or invalid arguments */
INVALID_ARGS = 2,
/** Application configuration was missing or invalid */
CONFIG_ERROR = 3,
/** Test suite JSON file failed to load or parse */
TEST_SUITE_LOADING_ERROR = 4,
/** One or more API calls failed to complete successfully */
API_CALL_FAILURE = 5,
/** A payload comparison mismatch or schema diff was detected */
COMPARISON_FAILURE = 6,
/** Mock server failed to start, bind, or serve responses */
MOCK_SERVER_ERROR = 7,
}
/**
* Class responsible for managing application exits in a controlled, consistent manner.
* It wraps all exit points with structured logging and appropriate `ExitCode`s.
*/
export class ExitHandler {
private logger: Logger;
/**
* Constructs an ExitHandler with an injected logger.
* @param logger Logger instance used for printing exit-related messages.
*/
constructor(logger: Logger) {
this.logger = logger;
}
/**
* Generic method to terminate the process with a given exit code and log message.
* @param code The exit code to terminate the process with.
* @param message Optional message to log before exiting.
* @param level Logging level to use (`info`, `warn`, or `error`). Defaults to `info`.
* @throws This method never returns; it terminates the Node.js process.
*/
exit(code: ExitCode, message?: string, level: 'info' | 'warn' | 'error' = 'info'): never {
if (message) {
this.log(level, message);
} else {
this.log(level, `Exiting with code ${code}`);
}
process.exit(code);
}
/**
* Exits the application successfully.
* @param message Optional message to log before exiting.
* @throws This method terminates the process with `ExitCode.SUCCESS`.
*/
success(message?: string): never {
this.exit(ExitCode.SUCCESS, message ?? 'Operation completed successfully.', 'info');
}
/**
* Exits due to a general, unexpected error.
* @param message Optional error message to log.
* @throws This method terminates the process with `ExitCode.GENERAL_ERROR`.
*/
generalError(message?: string): never {
this.exit(ExitCode.GENERAL_ERROR, message ?? 'An unexpected error occurred.', 'error');
}
/**
* Exits when CLI arguments are missing or invalid.
* @param message Optional warning message to log.
* @throws This method terminates the process with `ExitCode.INVALID_ARGS`.
*/
invalidArgs(message?: string): never {
this.exit(ExitCode.INVALID_ARGS, message ?? 'Missing or invalid arguments provided.', 'warn');
}
/**
* Exits when the configuration is invalid or missing.
* @param message Optional error message to log.
* @throws This method terminates the process with `ExitCode.CONFIG_ERROR`.
*/
configError(message?: string): never {
this.exit(ExitCode.CONFIG_ERROR, message ?? 'Configuration error encountered.', 'error');
}
/**
* Exits when the test suite file cannot be loaded or parsed.
* @param message Optional error message to log.
* @throws This method terminates the process with `ExitCode.TEST_SUITE_LOADING_ERROR`.
*/
testSuiteLoadingError(message?: string): never {
this.exit(ExitCode.TEST_SUITE_LOADING_ERROR, message ?? 'Failed to load test suite.', 'error');
}
/**
* Exits when one or more API calls fail.
* @param message Optional error message to log.
* @throws This method terminates the process with `ExitCode.API_CALL_FAILURE`.
*/
apiCallFailure(message?: string): never {
this.exit(ExitCode.API_CALL_FAILURE, message ?? 'API call failed.', 'error');
}
/**
* Exits when a payload mismatch or contract violation is detected.
* @param message Optional warning message to log.
* @throws This method terminates the process with `ExitCode.COMPARISON_FAILURE`.
*/
comparisonFailure(message?: string): never {
this.exit(ExitCode.COMPARISON_FAILURE, message ?? 'Payload comparison failed.', 'warn');
}
/**
* Exits when the mock server fails to initialize or respond.
* @param message Optional error message to log.
* @throws This method terminates the process with `ExitCode.MOCK_SERVER_ERROR`.
*/
mockServerError(message?: string): never {
this.exit(ExitCode.MOCK_SERVER_ERROR, message ?? 'Mock server encountered an error.', 'error');
}
/**
* Internal method for logging messages at the appropriate log level.
* @param level Log level (`info`, `warn`, `error`)
* @param message Message to log.
*/
private log(level: 'info' | 'warn' | 'error', message: string): void {
switch (level) {
case 'info':
this.logger.info(message);
break;
case 'warn':
this.logger.warn(message);
break;
case 'error':
this.logger.error(message);
break;
}
}
}