mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 14:15:56 +00:00
fix(commands): await async command validation
This commit is contained in:
1 parent
fc4b77bc52
commit
b98335c162
2 files changed
+55
-2
No files matched your search
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
export interface CommandExecutionContract<TParsedArgs, TExecutionResult> {
|
export interface CommandExecutionContract<TParsedArgs, TExecutionResult> {
|
||||||
parse(rawArgs: string[]): TParsedArgs;
|
parse(rawArgs: string[]): TParsedArgs;
|
||||||
validate(parsedArgs: TParsedArgs): void;
|
validate(parsedArgs: TParsedArgs): void | Promise<void>;
|
||||||
execute(parsedArgs: TParsedArgs): Promise<TExecutionResult> | TExecutionResult;
|
execute(parsedArgs: TParsedArgs): Promise<TExecutionResult> | TExecutionResult;
|
||||||
render(
|
render(
|
||||||
result: TExecutionResult,
|
result: TExecutionResult,
|
||||||
@@ -23,7 +23,7 @@ export async function runCommandWithContract<TParsedArgs, TExecutionResult>(
|
|||||||
contract: CommandExecutionContract<TParsedArgs, TExecutionResult>
|
contract: CommandExecutionContract<TParsedArgs, TExecutionResult>
|
||||||
): Promise<{ parsedArgs: TParsedArgs; result: TExecutionResult }> {
|
): Promise<{ parsedArgs: TParsedArgs; result: TExecutionResult }> {
|
||||||
const parsedArgs = contract.parse(rawArgs);
|
const parsedArgs = contract.parse(rawArgs);
|
||||||
contract.validate(parsedArgs);
|
await contract.validate(parsedArgs);
|
||||||
const result = await contract.execute(parsedArgs);
|
const result = await contract.execute(parsedArgs);
|
||||||
await contract.render(result, { rawArgs, parsedArgs });
|
await contract.render(result, { rawArgs, parsedArgs });
|
||||||
return { parsedArgs, result };
|
return { parsedArgs, result };
|
||||||
|
|||||||
@@ -56,4 +56,57 @@ describe('runCommandWithContract', () => {
|
|||||||
await expect(promise).rejects.toThrow('validation failed');
|
await expect(promise).rejects.toThrow('validation failed');
|
||||||
expect(lifecycle).toEqual(['parse', 'validate']);
|
expect(lifecycle).toEqual(['parse', 'validate']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('awaits async validate before execute and render', async () => {
|
||||||
|
const lifecycle: string[] = [];
|
||||||
|
|
||||||
|
const result = await runCommandWithContract(['--flag'], {
|
||||||
|
parse: (rawArgs) => {
|
||||||
|
lifecycle.push('parse');
|
||||||
|
return { parsed: true, value: rawArgs[0] };
|
||||||
|
},
|
||||||
|
validate: async () => {
|
||||||
|
lifecycle.push('validate:start');
|
||||||
|
await Promise.resolve();
|
||||||
|
lifecycle.push('validate:end');
|
||||||
|
},
|
||||||
|
execute: (parsedArgs) => {
|
||||||
|
lifecycle.push('execute');
|
||||||
|
return { output: parsedArgs.value.toUpperCase() };
|
||||||
|
},
|
||||||
|
render: () => {
|
||||||
|
lifecycle.push('render');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(lifecycle).toEqual(['parse', 'validate:start', 'validate:end', 'execute', 'render']);
|
||||||
|
expect(result.result).toEqual({ output: '--FLAG' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('short-circuits after async validate failure', async () => {
|
||||||
|
const lifecycle: string[] = [];
|
||||||
|
|
||||||
|
const promise = runCommandWithContract([], {
|
||||||
|
parse: () => {
|
||||||
|
lifecycle.push('parse');
|
||||||
|
return { valid: false };
|
||||||
|
},
|
||||||
|
validate: async () => {
|
||||||
|
lifecycle.push('validate:start');
|
||||||
|
await Promise.resolve();
|
||||||
|
lifecycle.push('validate:reject');
|
||||||
|
throw new Error('async validation failed');
|
||||||
|
},
|
||||||
|
execute: () => {
|
||||||
|
lifecycle.push('execute');
|
||||||
|
return { ok: true };
|
||||||
|
},
|
||||||
|
render: () => {
|
||||||
|
lifecycle.push('render');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(promise).rejects.toThrow('async validation failed');
|
||||||
|
expect(lifecycle).toEqual(['parse', 'validate:start', 'validate:reject']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user