|
1 | 1 | 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, parseMemoryLimit, validateFormat, validateApiProxyConfig, buildRateLimitConfig, validateRateLimitFlags, hasRateLimitOptions, collectRulesetFile, validateApiTargetInAllowedDomains, DEFAULT_OPENAI_API_TARGET, DEFAULT_ANTHROPIC_API_TARGET, DEFAULT_COPILOT_API_TARGET, emitApiProxyTargetWarnings, formatItem, program, parseAgentTimeout, applyAgentTimeout, handlePredownloadAction } from './cli'; |
| 2 | +import { parseEnvironmentVariables, parseDomains, parseDomainsFile, escapeShellArg, joinShellArgs, parseVolumeMounts, isValidIPv4, isValidIPv6, parseDnsServers, parseDnsOverHttps, validateAgentImage, isAgentImagePreset, AGENT_IMAGE_PRESETS, processAgentImageOption, processLocalhostKeyword, validateSkipPullWithBuildLocal, validateAllowHostPorts, parseMemoryLimit, validateFormat, validateApiProxyConfig, buildRateLimitConfig, validateRateLimitFlags, hasRateLimitOptions, collectRulesetFile, validateApiTargetInAllowedDomains, DEFAULT_OPENAI_API_TARGET, DEFAULT_ANTHROPIC_API_TARGET, DEFAULT_COPILOT_API_TARGET, emitApiProxyTargetWarnings, formatItem, program, parseAgentTimeout, applyAgentTimeout, handlePredownloadAction, resolveApiTargetsToAllowedDomains } from './cli'; |
3 | 3 | import { redactSecrets } from './redact-secrets'; |
4 | 4 | import * as fs from 'fs'; |
5 | 5 | import * as path from 'path'; |
@@ -1812,6 +1812,135 @@ describe('cli', () => { |
1812 | 1812 | }); |
1813 | 1813 | }); |
1814 | 1814 |
|
| 1815 | + describe('resolveApiTargetsToAllowedDomains', () => { |
| 1816 | + it('should add copilot-api-target option to allowed domains', () => { |
| 1817 | + const domains: string[] = ['github.com']; |
| 1818 | + resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'custom.copilot.com' }, domains); |
| 1819 | + expect(domains).toContain('custom.copilot.com'); |
| 1820 | + expect(domains).toContain('https://custom.copilot.com'); |
| 1821 | + }); |
| 1822 | + |
| 1823 | + it('should add openai-api-target option to allowed domains', () => { |
| 1824 | + const domains: string[] = ['github.com']; |
| 1825 | + resolveApiTargetsToAllowedDomains({ openaiApiTarget: 'custom.openai.com' }, domains); |
| 1826 | + expect(domains).toContain('custom.openai.com'); |
| 1827 | + expect(domains).toContain('https://custom.openai.com'); |
| 1828 | + }); |
| 1829 | + |
| 1830 | + it('should add anthropic-api-target option to allowed domains', () => { |
| 1831 | + const domains: string[] = ['github.com']; |
| 1832 | + resolveApiTargetsToAllowedDomains({ anthropicApiTarget: 'custom.anthropic.com' }, domains); |
| 1833 | + expect(domains).toContain('custom.anthropic.com'); |
| 1834 | + expect(domains).toContain('https://custom.anthropic.com'); |
| 1835 | + }); |
| 1836 | + |
| 1837 | + it('should prefer option flag over env var', () => { |
| 1838 | + const domains: string[] = []; |
| 1839 | + const env = { COPILOT_API_TARGET: 'env.copilot.com' }; |
| 1840 | + resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'flag.copilot.com' }, domains, env); |
| 1841 | + expect(domains).toContain('flag.copilot.com'); |
| 1842 | + expect(domains).not.toContain('env.copilot.com'); |
| 1843 | + }); |
| 1844 | + |
| 1845 | + it('should fall back to env var when option flag is not set', () => { |
| 1846 | + const domains: string[] = []; |
| 1847 | + const env = { COPILOT_API_TARGET: 'env.copilot.com' }; |
| 1848 | + resolveApiTargetsToAllowedDomains({}, domains, env); |
| 1849 | + expect(domains).toContain('env.copilot.com'); |
| 1850 | + expect(domains).toContain('https://env.copilot.com'); |
| 1851 | + }); |
| 1852 | + |
| 1853 | + it('should read OPENAI_API_TARGET from env when flag not set', () => { |
| 1854 | + const domains: string[] = []; |
| 1855 | + const env = { OPENAI_API_TARGET: 'env.openai.com' }; |
| 1856 | + resolveApiTargetsToAllowedDomains({}, domains, env); |
| 1857 | + expect(domains).toContain('env.openai.com'); |
| 1858 | + }); |
| 1859 | + |
| 1860 | + it('should read ANTHROPIC_API_TARGET from env when flag not set', () => { |
| 1861 | + const domains: string[] = []; |
| 1862 | + const env = { ANTHROPIC_API_TARGET: 'env.anthropic.com' }; |
| 1863 | + resolveApiTargetsToAllowedDomains({}, domains, env); |
| 1864 | + expect(domains).toContain('env.anthropic.com'); |
| 1865 | + }); |
| 1866 | + |
| 1867 | + it('should not duplicate a domain already in the list', () => { |
| 1868 | + const domains: string[] = ['custom.copilot.com']; |
| 1869 | + resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'custom.copilot.com' }, domains); |
| 1870 | + const count = domains.filter(d => d === 'custom.copilot.com').length; |
| 1871 | + expect(count).toBe(1); |
| 1872 | + }); |
| 1873 | + |
| 1874 | + it('should not duplicate the https:// form if already in the list', () => { |
| 1875 | + const domains: string[] = ['github.com', 'https://custom.copilot.com']; |
| 1876 | + resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'custom.copilot.com' }, domains); |
| 1877 | + const count = domains.filter(d => d === 'https://custom.copilot.com').length; |
| 1878 | + expect(count).toBe(1); |
| 1879 | + }); |
| 1880 | + |
| 1881 | + it('should preserve an existing https:// prefix without doubling it', () => { |
| 1882 | + const domains: string[] = []; |
| 1883 | + resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'https://custom.copilot.com' }, domains); |
| 1884 | + expect(domains).toContain('https://custom.copilot.com'); |
| 1885 | + const count = domains.filter(d => d === 'https://custom.copilot.com').length; |
| 1886 | + expect(count).toBe(1); |
| 1887 | + }); |
| 1888 | + |
| 1889 | + it('should handle http:// prefix without adding another https://', () => { |
| 1890 | + const domains: string[] = []; |
| 1891 | + resolveApiTargetsToAllowedDomains({ openaiApiTarget: 'http://internal.openai.com' }, domains); |
| 1892 | + expect(domains).toContain('http://internal.openai.com'); |
| 1893 | + }); |
| 1894 | + |
| 1895 | + it('should add all three targets when all are specified', () => { |
| 1896 | + const domains: string[] = []; |
| 1897 | + resolveApiTargetsToAllowedDomains( |
| 1898 | + { |
| 1899 | + copilotApiTarget: 'copilot.internal', |
| 1900 | + openaiApiTarget: 'openai.internal', |
| 1901 | + anthropicApiTarget: 'anthropic.internal', |
| 1902 | + }, |
| 1903 | + domains |
| 1904 | + ); |
| 1905 | + expect(domains).toContain('copilot.internal'); |
| 1906 | + expect(domains).toContain('openai.internal'); |
| 1907 | + expect(domains).toContain('anthropic.internal'); |
| 1908 | + }); |
| 1909 | + |
| 1910 | + it('should call debug with auto-added domains', () => { |
| 1911 | + const domains: string[] = []; |
| 1912 | + const debugMessages: string[] = []; |
| 1913 | + resolveApiTargetsToAllowedDomains( |
| 1914 | + { copilotApiTarget: 'copilot.internal' }, |
| 1915 | + domains, |
| 1916 | + {}, |
| 1917 | + (msg) => debugMessages.push(msg) |
| 1918 | + ); |
| 1919 | + expect(debugMessages.some(m => m.includes('copilot.internal'))).toBe(true); |
| 1920 | + }); |
| 1921 | + |
| 1922 | + it('should not call debug when no api targets are set', () => { |
| 1923 | + const domains: string[] = []; |
| 1924 | + const debugMessages: string[] = []; |
| 1925 | + resolveApiTargetsToAllowedDomains({}, domains, {}, (msg) => debugMessages.push(msg)); |
| 1926 | + expect(debugMessages).toHaveLength(0); |
| 1927 | + }); |
| 1928 | + |
| 1929 | + it('should return the same allowedDomains array reference', () => { |
| 1930 | + const domains: string[] = []; |
| 1931 | + const returned = resolveApiTargetsToAllowedDomains({ copilotApiTarget: 'x.com' }, domains); |
| 1932 | + expect(returned).toBe(domains); |
| 1933 | + }); |
| 1934 | + |
| 1935 | + it('should ignore empty env var values', () => { |
| 1936 | + const domains: string[] = []; |
| 1937 | + const env = { COPILOT_API_TARGET: ' ', OPENAI_API_TARGET: '' }; |
| 1938 | + resolveApiTargetsToAllowedDomains({}, domains, env); |
| 1939 | + // Whitespace-only and empty values are filtered out |
| 1940 | + expect(domains).toHaveLength(0); |
| 1941 | + }); |
| 1942 | + }); |
| 1943 | + |
1815 | 1944 | describe('formatItem', () => { |
1816 | 1945 | it('should format item with description on same line when term fits', () => { |
1817 | 1946 | const result = formatItem('-v', 'verbose output', 20, 2, 2, 80); |
|
0 commit comments