Skip to content

Commit 13f6940

Browse files
authored
fix(api-proxy): extract validateAnthropicCacheTailTtl for testability; add branch coverage tests
1 parent a4a8e79 commit 13f6940

2 files changed

Lines changed: 47 additions & 7 deletions

File tree

src/cli.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Command } from 'commander';
2-
import { parseEnvironmentVariables, parseDomains, parseDomainsFile, escapeShellArg, joinShellArgs, parseVolumeMounts, isValidIPv4, isValidIPv6, parseDnsServers, parseDnsOverHttps, validateAgentImage, isAgentImagePreset, AGENT_IMAGE_PRESETS, processAgentImageOption, processLocalhostKeyword, validateSkipPullWithBuildLocal, validateAllowHostPorts, validateAllowHostServicePorts, applyHostServicePortsConfig, parseMemoryLimit, validateFormat, validateApiProxyConfig, buildRateLimitConfig, validateRateLimitFlags, validateEnableOpenCodeFlag, hasRateLimitOptions, collectRulesetFile, validateApiTargetInAllowedDomains, DEFAULT_OPENAI_API_TARGET, DEFAULT_ANTHROPIC_API_TARGET, DEFAULT_COPILOT_API_TARGET, DEFAULT_GEMINI_API_TARGET, emitApiProxyTargetWarnings, emitCliProxyStatusLogs, warnClassicPATWithCopilotModel, formatItem, program, parseAgentTimeout, applyAgentTimeout, handlePredownloadAction, resolveApiTargetsToAllowedDomains, extractGhesDomainsFromEngineApiTarget, extractGhecDomainsFromServerUrl, checkDockerHost } from './cli';
2+
import { parseEnvironmentVariables, parseDomains, parseDomainsFile, escapeShellArg, joinShellArgs, parseVolumeMounts, isValidIPv4, isValidIPv6, parseDnsServers, parseDnsOverHttps, validateAgentImage, isAgentImagePreset, AGENT_IMAGE_PRESETS, processAgentImageOption, processLocalhostKeyword, validateSkipPullWithBuildLocal, validateAllowHostPorts, validateAllowHostServicePorts, applyHostServicePortsConfig, parseMemoryLimit, validateFormat, validateApiProxyConfig, buildRateLimitConfig, validateRateLimitFlags, validateEnableOpenCodeFlag, hasRateLimitOptions, collectRulesetFile, validateApiTargetInAllowedDomains, DEFAULT_OPENAI_API_TARGET, DEFAULT_ANTHROPIC_API_TARGET, DEFAULT_COPILOT_API_TARGET, DEFAULT_GEMINI_API_TARGET, emitApiProxyTargetWarnings, emitCliProxyStatusLogs, warnClassicPATWithCopilotModel, formatItem, program, parseAgentTimeout, applyAgentTimeout, handlePredownloadAction, resolveApiTargetsToAllowedDomains, extractGhesDomainsFromEngineApiTarget, extractGhecDomainsFromServerUrl, checkDockerHost, validateAnthropicCacheTailTtl } from './cli';
33
import { redactSecrets } from './redact-secrets';
44
import * as fs from 'fs';
55
import * as path from 'path';
@@ -2456,6 +2456,39 @@ describe('cli', () => {
24562456
});
24572457
});
24582458

2459+
describe('validateAnthropicCacheTailTtl', () => {
2460+
it('should not call process.exit when value is undefined', () => {
2461+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
2462+
validateAnthropicCacheTailTtl(undefined);
2463+
expect(mockExit).not.toHaveBeenCalled();
2464+
mockExit.mockRestore();
2465+
});
2466+
2467+
it('should not call process.exit for valid value "5m"', () => {
2468+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
2469+
validateAnthropicCacheTailTtl('5m');
2470+
expect(mockExit).not.toHaveBeenCalled();
2471+
mockExit.mockRestore();
2472+
});
2473+
2474+
it('should not call process.exit for valid value "1h"', () => {
2475+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
2476+
validateAnthropicCacheTailTtl('1h');
2477+
expect(mockExit).not.toHaveBeenCalled();
2478+
mockExit.mockRestore();
2479+
});
2480+
2481+
it('should call process.exit(1) for an invalid value', () => {
2482+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
2483+
const mockError = jest.spyOn(console, 'error').mockImplementation(() => {});
2484+
validateAnthropicCacheTailTtl('10m');
2485+
expect(mockError).toHaveBeenCalledWith('Invalid --anthropic-cache-tail-ttl value: "10m". Must be "5m" or "1h".');
2486+
expect(mockExit).toHaveBeenCalledWith(1);
2487+
mockError.mockRestore();
2488+
mockExit.mockRestore();
2489+
});
2490+
});
2491+
24592492
describe('formatItem', () => {
24602493
it('should format term with description when term fits within width', () => {
24612494
const result = formatItem('--flag', 'Description text', 20, 2, 2, 80);

src/cli.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,18 @@ export function validateApiProxyConfig(
318318
return { enabled: true, warnings, debugMessages };
319319
}
320320

321+
/**
322+
* Validates the value of --anthropic-cache-tail-ttl.
323+
* Exits the process with an error if the value is not "5m" or "1h".
324+
* @param value - The value provided for --anthropic-cache-tail-ttl (may be undefined)
325+
*/
326+
export function validateAnthropicCacheTailTtl(value: string | undefined): void {
327+
if (value !== undefined && value !== '5m' && value !== '1h') {
328+
console.error(`Invalid --anthropic-cache-tail-ttl value: "${value}". Must be "5m" or "1h".`);
329+
process.exit(1);
330+
}
331+
}
332+
321333
/**
322334
* Validates that a custom API proxy target hostname is covered by the allowed domains list.
323335
* Returns a warning message if the target domain is not in allowed domains, otherwise null.
@@ -1671,12 +1683,7 @@ program
16711683
}
16721684

16731685
// Validate --anthropic-cache-tail-ttl if provided
1674-
if (options.anthropicCacheTailTtl !== undefined &&
1675-
options.anthropicCacheTailTtl !== '5m' &&
1676-
options.anthropicCacheTailTtl !== '1h') {
1677-
console.error(`Invalid --anthropic-cache-tail-ttl value: "${options.anthropicCacheTailTtl}". Must be "5m" or "1h".`);
1678-
process.exit(1);
1679-
}
1686+
validateAnthropicCacheTailTtl(options.anthropicCacheTailTtl);
16801687

16811688
// Model aliases may be injected via config file (not a Commander option),
16821689
// so access through a Record cast with a proper type annotation.

0 commit comments

Comments
 (0)