mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 06:18:55 +00:00
fix(auth): reject resource mode outside resources command
This commit is contained in:
@@ -242,8 +242,8 @@ class AuthCommands {
|
|||||||
/**
|
/**
|
||||||
* Reset default profile - delegates to default-command.ts
|
* Reset default profile - delegates to default-command.ts
|
||||||
*/
|
*/
|
||||||
async handleResetDefault(): Promise<void> {
|
async handleResetDefault(args: string[] = []): Promise<void> {
|
||||||
return handleResetDefault(this.getContext());
|
return handleResetDefault(this.getContext(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,7 +297,7 @@ class AuthCommands {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'reset-default':
|
case 'reset-default':
|
||||||
await this.handleResetDefault();
|
await this.handleResetDefault(commandArgs);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'current':
|
case 'current':
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from '../resume-lane-diagnostics';
|
} from '../resume-lane-diagnostics';
|
||||||
import { isAccountContextMetadata, resolveAccountContextPolicy } from '../account-context';
|
import { isAccountContextMetadata, resolveAccountContextPolicy } from '../account-context';
|
||||||
import { isProfileLocalSharedResourceMode } from '../shared-resource-policy';
|
import { isProfileLocalSharedResourceMode } from '../shared-resource-policy';
|
||||||
import { CommandContext, parseArgs } from './types';
|
import { CommandContext, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
|
|
||||||
interface BackupManifest {
|
interface BackupManifest {
|
||||||
target: string;
|
target: string;
|
||||||
@@ -37,7 +37,11 @@ function copyDirectoryIfPresent(sourcePath: string, targetPath: string): boolean
|
|||||||
|
|
||||||
export async function handleBackup(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleBackup(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { profileName, json } = parseArgs(args);
|
const parsed = parseArgs(args);
|
||||||
|
const { profileName, json } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth backup <profile|default> [--json]',
|
||||||
|
});
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
} from '../shared-resource-policy';
|
} from '../shared-resource-policy';
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, parseArgs } from './types';
|
import { CommandContext, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
import { stripAmbientProviderCredentials } from './create-command-env';
|
import { stripAmbientProviderCredentials } from './create-command-env';
|
||||||
import { isUnifiedMode } from '../../config/config-loader-facade';
|
import { isUnifiedMode } from '../../config/config-loader-facade';
|
||||||
|
|
||||||
@@ -40,29 +40,13 @@ function sanitizeProfileNameForInstance(name: string): string {
|
|||||||
*/
|
*/
|
||||||
export async function handleCreate(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleCreate(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const {
|
const parsed = parseArgs(args);
|
||||||
profileName,
|
const { profileName, force, shareContext, contextGroup, deeperContinuity, bare } = parsed;
|
||||||
force,
|
|
||||||
shareContext,
|
|
||||||
contextGroup,
|
|
||||||
deeperContinuity,
|
|
||||||
bare,
|
|
||||||
mode,
|
|
||||||
unknownFlags,
|
|
||||||
} = parseArgs(args);
|
|
||||||
|
|
||||||
const unsupportedOptions = [...(unknownFlags ?? []), ...(mode !== undefined ? ['--mode'] : [])];
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
if (unsupportedOptions.length > 0) {
|
usage:
|
||||||
const unknownList = unsupportedOptions.map((flag) => `"${flag}"`).join(', ');
|
'ccs auth create <profile> [--force] [--bare] [--share-context] [--context-group <name>] [--deeper-continuity]',
|
||||||
console.log(fail(`Unknown option(s): ${unknownList}`));
|
});
|
||||||
console.log('');
|
|
||||||
console.log(
|
|
||||||
`Usage: ${color('ccs auth create <profile> [--force] [--bare] [--share-context] [--context-group <name>] [--deeper-continuity]', 'command')}`
|
|
||||||
);
|
|
||||||
console.log(`Help: ${color('ccs auth --help', 'command')}`);
|
|
||||||
console.log('');
|
|
||||||
exitWithError(`Unknown option(s): ${unknownList}`, ExitCode.PROFILE_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { initUI, color, dim, ok, fail } from '../../utils/ui';
|
|||||||
|
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, parseArgs } from './types';
|
import { CommandContext, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
import { isUnifiedMode } from '../../config/config-loader-facade';
|
import { isUnifiedMode } from '../../config/config-loader-facade';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,7 +16,11 @@ import { isUnifiedMode } from '../../config/config-loader-facade';
|
|||||||
*/
|
*/
|
||||||
export async function handleDefault(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleDefault(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { profileName } = parseArgs(args);
|
const parsed = parseArgs(args);
|
||||||
|
const { profileName } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth default <profile>',
|
||||||
|
});
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
@@ -48,8 +52,12 @@ export async function handleDefault(ctx: CommandContext, args: string[]): Promis
|
|||||||
/**
|
/**
|
||||||
* Handle the reset-default command (clear the custom default)
|
* Handle the reset-default command (clear the custom default)
|
||||||
*/
|
*/
|
||||||
export async function handleResetDefault(ctx: CommandContext): Promise<void> {
|
export async function handleResetDefault(ctx: CommandContext, args: string[] = []): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
|
const parsed = parseArgs(args);
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth reset-default',
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use unified or legacy based on config mode
|
// Use unified or legacy based on config mode
|
||||||
|
|||||||
@@ -10,14 +10,24 @@ import { resolveAccountContextPolicy, formatAccountContextPolicy } from '../acco
|
|||||||
import { resolveSharedResourcePolicy } from '../shared-resource-policy';
|
import { resolveSharedResourcePolicy } from '../shared-resource-policy';
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, ListOutput, parseArgs, formatRelativeTime } from './types';
|
import {
|
||||||
|
CommandContext,
|
||||||
|
ListOutput,
|
||||||
|
parseArgs,
|
||||||
|
formatRelativeTime,
|
||||||
|
rejectUnsupportedAuthOptions,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the list command
|
* Handle the list command
|
||||||
*/
|
*/
|
||||||
export async function handleList(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleList(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { verbose, json } = parseArgs(args);
|
const parsed = parseArgs(args);
|
||||||
|
const { verbose, json } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth list [--verbose] [--json]',
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get profiles from both legacy (profiles.json) and unified config (config.yaml)
|
// Get profiles from both legacy (profiles.json) and unified config (config.yaml)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { InteractivePrompt } from '../../utils/prompt';
|
|||||||
|
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, parseArgs } from './types';
|
import { CommandContext, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
import { isUnifiedMode } from '../../config/config-loader-facade';
|
import { isUnifiedMode } from '../../config/config-loader-facade';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +19,11 @@ import { isUnifiedMode } from '../../config/config-loader-facade';
|
|||||||
*/
|
*/
|
||||||
export async function handleRemove(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleRemove(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { profileName, yes } = parseArgs(args);
|
const parsed = parseArgs(args);
|
||||||
|
const { profileName, yes } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth remove <profile> [--yes]',
|
||||||
|
});
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '../shared-resource-policy';
|
} from '../shared-resource-policy';
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, parseArgs } from './types';
|
import { CommandContext, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
|
|
||||||
function formatMode(mode: SharedResourceMode): string {
|
function formatMode(mode: SharedResourceMode): string {
|
||||||
return mode === 'profile-local' ? 'profile-local' : 'shared';
|
return mode === 'profile-local' ? 'profile-local' : 'shared';
|
||||||
@@ -22,7 +22,12 @@ function modeDescription(mode: SharedResourceMode): string {
|
|||||||
|
|
||||||
export async function handleResources(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleResources(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { profileName, mode, json } = parseArgs(args);
|
const parsed = parseArgs(args, { allowMode: true });
|
||||||
|
const { profileName, mode, json } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth resources <profile> [--mode shared|profile-local] [--json]',
|
||||||
|
allowMode: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { resolveConfiguredPlainCcsResumeLane } from '../resume-lane-diagnostics'
|
|||||||
import { resolveSharedResourcePolicy } from '../shared-resource-policy';
|
import { resolveSharedResourcePolicy } from '../shared-resource-policy';
|
||||||
import { exitWithError } from '../../errors';
|
import { exitWithError } from '../../errors';
|
||||||
import { ExitCode } from '../../errors/exit-codes';
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
import { CommandContext, ProfileOutput, parseArgs } from './types';
|
import { CommandContext, ProfileOutput, parseArgs, rejectUnsupportedAuthOptions } from './types';
|
||||||
|
|
||||||
function formatHistorySummary(history: ReturnType<typeof summarizeAccountHistory>): string {
|
function formatHistorySummary(history: ReturnType<typeof summarizeAccountHistory>): string {
|
||||||
const scope = history.projects_shared ? 'shared projects' : 'profile-local projects';
|
const scope = history.projects_shared ? 'shared projects' : 'profile-local projects';
|
||||||
@@ -26,7 +26,11 @@ function formatHistorySummary(history: ReturnType<typeof summarizeAccountHistory
|
|||||||
*/
|
*/
|
||||||
export async function handleShow(ctx: CommandContext, args: string[]): Promise<void> {
|
export async function handleShow(ctx: CommandContext, args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
const { profileName, json } = parseArgs(args);
|
const parsed = parseArgs(args);
|
||||||
|
const { profileName, json } = parsed;
|
||||||
|
rejectUnsupportedAuthOptions(parsed, {
|
||||||
|
usage: 'ccs auth show <profile> [--json]',
|
||||||
|
});
|
||||||
|
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
console.log(fail('Profile name is required'));
|
console.log(fail('Profile name is required'));
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
import ProfileRegistry from '../profile-registry';
|
import ProfileRegistry from '../profile-registry';
|
||||||
import { InstanceManager } from '../../management/instance-manager';
|
import { InstanceManager } from '../../management/instance-manager';
|
||||||
|
import { exitWithError } from '../../errors';
|
||||||
|
import { ExitCode } from '../../errors/exit-codes';
|
||||||
|
import { color, fail } from '../../utils/ui';
|
||||||
|
|
||||||
// Re-export for backward compatibility
|
// Re-export for backward compatibility
|
||||||
export { formatRelativeTime } from '../../utils/time';
|
export { formatRelativeTime } from '../../utils/time';
|
||||||
@@ -83,10 +86,36 @@ export interface CommandContext {
|
|||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function rejectUnsupportedAuthOptions(
|
||||||
|
parsed: Pick<AuthCommandArgs, 'mode' | 'unknownFlags'>,
|
||||||
|
options: { usage: string; allowMode?: boolean }
|
||||||
|
): void {
|
||||||
|
const unsupportedOptions = [
|
||||||
|
...(parsed.unknownFlags ?? []),
|
||||||
|
...(!options.allowMode && parsed.mode !== undefined ? ['--mode'] : []),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (unsupportedOptions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unknownList = unsupportedOptions.map((flag) => `"${flag}"`).join(', ');
|
||||||
|
console.log(fail(`Unknown option(s): ${unknownList}`));
|
||||||
|
console.log('');
|
||||||
|
console.log(`Usage: ${color(options.usage, 'command')}`);
|
||||||
|
console.log(`Help: ${color('ccs auth --help', 'command')}`);
|
||||||
|
console.log('');
|
||||||
|
exitWithError(`Unknown option(s): ${unknownList}`, ExitCode.PROFILE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParseArgsOptions {
|
||||||
|
allowMode?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse command arguments from raw args array
|
* Parse command arguments from raw args array
|
||||||
*/
|
*/
|
||||||
export function parseArgs(args: string[]): AuthCommandArgs {
|
export function parseArgs(args: string[], options: ParseArgsOptions = {}): AuthCommandArgs {
|
||||||
let profileName: string | undefined;
|
let profileName: string | undefined;
|
||||||
let contextGroup: string | undefined;
|
let contextGroup: string | undefined;
|
||||||
let mode: string | undefined;
|
let mode: string | undefined;
|
||||||
@@ -101,7 +130,10 @@ export function parseArgs(args: string[]): AuthCommandArgs {
|
|||||||
'--deeper-continuity',
|
'--deeper-continuity',
|
||||||
'--bare',
|
'--bare',
|
||||||
]);
|
]);
|
||||||
const knownValueFlags = new Set(['--context-group', '--mode']);
|
const knownValueFlags = new Set(['--context-group']);
|
||||||
|
if (options.allowMode) {
|
||||||
|
knownValueFlags.add('--mode');
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
const arg = args[i];
|
const arg = args[i];
|
||||||
@@ -118,7 +150,7 @@ export function parseArgs(args: string[]): AuthCommandArgs {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg === '--mode') {
|
if (options.allowMode && arg === '--mode') {
|
||||||
const next = args[i + 1];
|
const next = args[i + 1];
|
||||||
if (!next || next.startsWith('-')) {
|
if (!next || next.startsWith('-')) {
|
||||||
mode = '';
|
mode = '';
|
||||||
@@ -135,7 +167,7 @@ export function parseArgs(args: string[]): AuthCommandArgs {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg.startsWith('--mode=')) {
|
if (options.allowMode && arg.startsWith('--mode=')) {
|
||||||
mode = arg.slice('--mode='.length);
|
mode = arg.slice('--mode='.length);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,19 +64,27 @@ describe('auth command args parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('parses shared resource mode value for resources command', () => {
|
it('parses shared resource mode value for resources command', () => {
|
||||||
const parsed = parseArgs(['work', '--mode', 'profile-local']);
|
const parsed = parseArgs(['work', '--mode', 'profile-local'], { allowMode: true });
|
||||||
|
|
||||||
expect(parsed.profileName).toBe('work');
|
expect(parsed.profileName).toBe('work');
|
||||||
expect(parsed.mode).toBe('profile-local');
|
expect(parsed.mode).toBe('profile-local');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses inline shared resource mode value', () => {
|
it('parses inline shared resource mode value', () => {
|
||||||
const parsed = parseArgs(['work', '--mode=shared']);
|
const parsed = parseArgs(['work', '--mode=shared'], { allowMode: true });
|
||||||
|
|
||||||
expect(parsed.profileName).toBe('work');
|
expect(parsed.profileName).toBe('work');
|
||||||
expect(parsed.mode).toBe('shared');
|
expect(parsed.mode).toBe('shared');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('treats shared resource mode as unknown unless the command opts in', () => {
|
||||||
|
const parsed = parseArgs(['work', '--mode', 'shared']);
|
||||||
|
|
||||||
|
expect(parsed.profileName).toBe('work');
|
||||||
|
expect(parsed.mode).toBeUndefined();
|
||||||
|
expect(parsed.unknownFlags).toEqual(['--mode']);
|
||||||
|
});
|
||||||
|
|
||||||
it('tracks unknown flags and keeps positional profile intact', () => {
|
it('tracks unknown flags and keeps positional profile intact', () => {
|
||||||
const parsed = parseArgs(['--foo', 'bar', 'work']);
|
const parsed = parseArgs(['--foo', 'bar', 'work']);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,38 @@ import ProfileRegistry from '../../src/auth/profile-registry';
|
|||||||
import InstanceManager from '../../src/management/instance-manager';
|
import InstanceManager from '../../src/management/instance-manager';
|
||||||
import { handleResources } from '../../src/auth/commands/resources-command';
|
import { handleResources } from '../../src/auth/commands/resources-command';
|
||||||
import { handleCreate } from '../../src/auth/commands/create-command';
|
import { handleCreate } from '../../src/auth/commands/create-command';
|
||||||
|
import { handleList } from '../../src/auth/commands/list-command';
|
||||||
|
import { handleShow } from '../../src/auth/commands/show-command';
|
||||||
|
import { handleBackup } from '../../src/auth/commands/backup-command';
|
||||||
|
import { handleRemove } from '../../src/auth/commands/remove-command';
|
||||||
|
import { handleDefault, handleResetDefault } from '../../src/auth/commands/default-command';
|
||||||
|
|
||||||
|
async function expectProfileExit(action: () => Promise<void>): Promise<string> {
|
||||||
|
const originalExit = process.exit;
|
||||||
|
const originalLog = console.log;
|
||||||
|
const originalError = console.error;
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
process.exit = ((code?: number) => {
|
||||||
|
throw new Error(`process.exit(${code ?? 0})`);
|
||||||
|
}) as typeof process.exit;
|
||||||
|
console.log = (...args: unknown[]) => {
|
||||||
|
lines.push(args.map(String).join(' '));
|
||||||
|
};
|
||||||
|
console.error = (...args: unknown[]) => {
|
||||||
|
lines.push(args.map(String).join(' '));
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await expect(action()).rejects.toThrow('process.exit(7)');
|
||||||
|
} finally {
|
||||||
|
process.exit = originalExit;
|
||||||
|
console.log = originalLog;
|
||||||
|
console.error = originalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
describe('auth resources command', () => {
|
describe('auth resources command', () => {
|
||||||
let tempRoot = '';
|
let tempRoot = '';
|
||||||
@@ -143,38 +175,37 @@ describe('auth resources command', () => {
|
|||||||
it('rejects --mode on auth create instead of silently ignoring it', async () => {
|
it('rejects --mode on auth create instead of silently ignoring it', async () => {
|
||||||
const registry = new ProfileRegistry();
|
const registry = new ProfileRegistry();
|
||||||
const instanceMgr = new InstanceManager();
|
const instanceMgr = new InstanceManager();
|
||||||
const originalExit = process.exit;
|
|
||||||
const originalLog = console.log;
|
|
||||||
const originalError = console.error;
|
|
||||||
const lines: string[] = [];
|
|
||||||
|
|
||||||
process.exit = ((code?: number) => {
|
const output = await expectProfileExit(() =>
|
||||||
throw new Error(`process.exit(${code ?? 0})`);
|
handleCreate(
|
||||||
}) as typeof process.exit;
|
{
|
||||||
console.log = (...args: unknown[]) => {
|
registry,
|
||||||
lines.push(args.map(String).join(' '));
|
instanceMgr,
|
||||||
};
|
version: 'test',
|
||||||
console.error = (...args: unknown[]) => {
|
},
|
||||||
lines.push(args.map(String).join(' '));
|
['work', '--mode', 'profile-local']
|
||||||
};
|
)
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
expect(output).toContain('Unknown option(s): "--mode"');
|
||||||
await expect(
|
});
|
||||||
handleCreate(
|
|
||||||
{
|
it('rejects --mode on non-resources auth commands instead of silently ignoring it', async () => {
|
||||||
registry,
|
const registry = new ProfileRegistry();
|
||||||
instanceMgr,
|
const instanceMgr = new InstanceManager();
|
||||||
version: 'test',
|
const ctx = { registry, instanceMgr, version: 'test' };
|
||||||
},
|
const cases: Array<[string, () => Promise<void>]> = [
|
||||||
['work', '--mode', 'profile-local']
|
['list', () => handleList(ctx, ['--mode', 'shared'])],
|
||||||
)
|
['show', () => handleShow(ctx, ['work', '--mode=shared'])],
|
||||||
).rejects.toThrow('process.exit(7)');
|
['backup', () => handleBackup(ctx, ['work', '--mode', 'profile-local'])],
|
||||||
} finally {
|
['remove', () => handleRemove(ctx, ['work', '--mode', 'shared'])],
|
||||||
process.exit = originalExit;
|
['default', () => handleDefault(ctx, ['work', '--mode', 'shared'])],
|
||||||
console.log = originalLog;
|
['reset-default', () => handleResetDefault(ctx, ['--mode', 'shared'])],
|
||||||
console.error = originalError;
|
];
|
||||||
|
|
||||||
|
for (const [commandName, action] of cases) {
|
||||||
|
const output = await expectProfileExit(action);
|
||||||
|
expect(`${commandName}\n${output}`).toContain('Unknown option(s): "--mode"');
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(lines.join('\n')).toContain('Unknown option(s): "--mode"');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user