Skip to content

Commit 2a6cc90

Browse files
test: improve coverage for network, env, logs, volumes
- network-options.test.ts: cover 3 warning branches (external DOCKER_HOST, split-filesystem hint, DinD path-prefix hint) - environment-builder.test.ts: cover large-env warning path when envAll=true and env size exceeds threshold - log-parser-ipv6.test.ts: cover IPv6 bracketed dest parsing, bare IP (no port), non-numeric port in parseAuditJsonlLine - workspace-mounts.test.ts: cover binary staging path, missing binary, and buildCustomVolumeMounts edge cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7684ab6 commit 2a6cc90

4 files changed

Lines changed: 802 additions & 0 deletions

File tree

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
import { validateNetworkOptions } from './network-options';
2+
3+
// Mock the logger
4+
jest.mock('../../logger', () => ({
5+
logger: {
6+
error: jest.fn(),
7+
warn: jest.fn(),
8+
info: jest.fn(),
9+
debug: jest.fn(),
10+
},
11+
}));
12+
13+
// Mock option-parsers for checkDockerHost and resolveDockerHostPathPrefix
14+
jest.mock('../../option-parsers', () => ({
15+
checkDockerHost: jest.fn(),
16+
resolveDockerHostPathPrefix: jest.fn(),
17+
}));
18+
19+
// Mock preflight for domain resolution
20+
jest.mock('../preflight', () => ({
21+
resolveAllowedDomains: jest.fn(),
22+
resolveBlockedDomains: jest.fn(),
23+
}));
24+
25+
// Mock network-setup for network config
26+
jest.mock('../network-setup', () => ({
27+
resolveNetworkConfig: jest.fn(),
28+
}));
29+
30+
import { logger } from '../../logger';
31+
import { checkDockerHost, resolveDockerHostPathPrefix } from '../../option-parsers';
32+
import { resolveAllowedDomains, resolveBlockedDomains } from '../preflight';
33+
import { resolveNetworkConfig } from '../network-setup';
34+
35+
const mockCheckDockerHost = checkDockerHost as jest.Mock;
36+
const mockResolveDockerHostPathPrefix = resolveDockerHostPathPrefix as jest.Mock;
37+
const mockResolveAllowedDomains = resolveAllowedDomains as jest.Mock;
38+
const mockResolveBlockedDomains = resolveBlockedDomains as jest.Mock;
39+
const mockResolveNetworkConfig = resolveNetworkConfig as jest.Mock;
40+
41+
function makeDefaultMocks() {
42+
mockCheckDockerHost.mockReturnValue({ valid: true });
43+
mockResolveDockerHostPathPrefix.mockReturnValue({
44+
dockerHostPathPrefix: undefined,
45+
autoApplied: false,
46+
dindHint: false,
47+
});
48+
mockResolveAllowedDomains.mockReturnValue({
49+
allowedDomains: ['example.com'],
50+
localhostResult: { enableHostAccess: false, hostPorts: [] },
51+
resolvedCopilotApiTarget: undefined,
52+
resolvedCopilotApiBasePath: undefined,
53+
});
54+
mockResolveBlockedDomains.mockReturnValue([]);
55+
mockResolveNetworkConfig.mockReturnValue({
56+
upstreamProxy: undefined,
57+
dnsServers: ['8.8.8.8'],
58+
dnsOverHttps: undefined,
59+
});
60+
}
61+
62+
describe('validateNetworkOptions', () => {
63+
beforeEach(() => {
64+
jest.clearAllMocks();
65+
makeDefaultMocks();
66+
});
67+
68+
describe('happy path', () => {
69+
it('returns all resolved values from dependencies', () => {
70+
const result = validateNetworkOptions({ allowDomains: 'example.com' });
71+
72+
expect(result.dockerHostCheck).toEqual({ valid: true });
73+
expect(result.allowedDomains).toEqual(['example.com']);
74+
expect(result.blockedDomains).toEqual([]);
75+
expect(result.dnsServers).toEqual(['8.8.8.8']);
76+
expect(result.upstreamProxy).toBeUndefined();
77+
expect(result.dnsOverHttps).toBeUndefined();
78+
expect(result.localhostResult).toBeDefined();
79+
});
80+
81+
it('passes options to resolveAllowedDomains and resolveNetworkConfig', () => {
82+
const options = { allowDomains: 'github.com', dnsServers: '1.1.1.1' };
83+
validateNetworkOptions(options);
84+
85+
expect(mockResolveAllowedDomains).toHaveBeenCalledWith(options);
86+
expect(mockResolveNetworkConfig).toHaveBeenCalledWith(options);
87+
});
88+
89+
it('passes dockerHostCheck and dockerHostPathPrefix to resolveDockerHostPathPrefix', () => {
90+
const options = { dockerHostPathPrefix: '/host' };
91+
mockResolveDockerHostPathPrefix.mockReturnValue({
92+
dockerHostPathPrefix: '/host',
93+
autoApplied: false,
94+
dindHint: false,
95+
});
96+
97+
const result = validateNetworkOptions(options);
98+
99+
expect(mockResolveDockerHostPathPrefix).toHaveBeenCalledWith(
100+
{ valid: true },
101+
'/host',
102+
);
103+
expect(result.dockerHostPathPrefixResolution.dockerHostPathPrefix).toBe('/host');
104+
});
105+
});
106+
107+
describe('external DOCKER_HOST warnings (lines 50-53)', () => {
108+
it('emits two warnings when DOCKER_HOST is external (checkDockerHost valid=false)', () => {
109+
mockCheckDockerHost.mockReturnValue({
110+
valid: false,
111+
error: 'DOCKER_HOST is set to an external daemon',
112+
});
113+
mockResolveDockerHostPathPrefix.mockReturnValue({
114+
dockerHostPathPrefix: '/host',
115+
autoApplied: false,
116+
dindHint: false,
117+
});
118+
119+
validateNetworkOptions({});
120+
121+
expect(logger.warn).toHaveBeenCalledWith(
122+
expect.stringContaining('External DOCKER_HOST detected'),
123+
);
124+
expect(logger.warn).toHaveBeenCalledWith(
125+
expect.stringContaining('original DOCKER_HOST'),
126+
);
127+
});
128+
129+
it('does not emit external-host warnings when DOCKER_HOST is valid', () => {
130+
validateNetworkOptions({});
131+
132+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
133+
expect(warnCalls.some((m: string) => m.includes('External DOCKER_HOST'))).toBe(false);
134+
});
135+
});
136+
137+
describe('missing path-prefix warning (line 62)', () => {
138+
it('emits split-filesystem warning when external host and no prefix', () => {
139+
mockCheckDockerHost.mockReturnValue({ valid: false, error: 'external daemon' });
140+
mockResolveDockerHostPathPrefix.mockReturnValue({
141+
dockerHostPathPrefix: undefined,
142+
autoApplied: false,
143+
dindHint: false,
144+
});
145+
146+
validateNetworkOptions({});
147+
148+
expect(logger.warn).toHaveBeenCalledWith(
149+
expect.stringContaining('split runner/daemon filesystem'),
150+
);
151+
});
152+
153+
it('does not emit split-filesystem warning when prefix is provided', () => {
154+
mockCheckDockerHost.mockReturnValue({ valid: false, error: 'external daemon' });
155+
mockResolveDockerHostPathPrefix.mockReturnValue({
156+
dockerHostPathPrefix: '/host',
157+
autoApplied: false,
158+
dindHint: false,
159+
});
160+
161+
validateNetworkOptions({});
162+
163+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
164+
expect(warnCalls.some((m: string) => m.includes('split runner/daemon filesystem'))).toBe(false);
165+
});
166+
167+
it('does not emit split-filesystem warning when docker host is valid', () => {
168+
// valid=true AND no prefix: should NOT warn about split filesystem
169+
mockResolveDockerHostPathPrefix.mockReturnValue({
170+
dockerHostPathPrefix: undefined,
171+
autoApplied: false,
172+
dindHint: false,
173+
});
174+
175+
validateNetworkOptions({});
176+
177+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
178+
expect(warnCalls.some((m: string) => m.includes('split runner/daemon filesystem'))).toBe(false);
179+
});
180+
});
181+
182+
describe('DinD hint warnings (lines 66-78)', () => {
183+
it('emits four DinD hint warnings when dindHint=true and no prefix', () => {
184+
mockResolveDockerHostPathPrefix.mockReturnValue({
185+
dockerHostPathPrefix: undefined,
186+
autoApplied: false,
187+
dindHint: true,
188+
});
189+
190+
validateNetworkOptions({});
191+
192+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
193+
expect(warnCalls.some((m: string) => m.includes('Non-standard DOCKER_HOST unix socket'))).toBe(true);
194+
expect(warnCalls.some((m: string) => m.includes('runner and Docker daemon have separate root filesystems'))).toBe(true);
195+
expect(warnCalls.some((m: string) => m.includes('docker-host-path-prefix'))).toBe(true);
196+
});
197+
198+
it('does not emit DinD hint warnings when dindHint=false', () => {
199+
mockResolveDockerHostPathPrefix.mockReturnValue({
200+
dockerHostPathPrefix: undefined,
201+
autoApplied: false,
202+
dindHint: false,
203+
});
204+
205+
validateNetworkOptions({});
206+
207+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
208+
expect(warnCalls.some((m: string) => m.includes('Non-standard DOCKER_HOST'))).toBe(false);
209+
});
210+
211+
it('does not emit DinD hint warnings when prefix is provided despite dindHint=true', () => {
212+
mockResolveDockerHostPathPrefix.mockReturnValue({
213+
dockerHostPathPrefix: '/tmp/gh-aw',
214+
autoApplied: false,
215+
dindHint: true,
216+
});
217+
218+
validateNetworkOptions({});
219+
220+
const warnCalls = (logger.warn as jest.Mock).mock.calls.map((c: string[]) => c[0]);
221+
expect(warnCalls.some((m: string) => m.includes('Non-standard DOCKER_HOST'))).toBe(false);
222+
});
223+
});
224+
225+
describe('return value shape', () => {
226+
it('includes resolvedCopilotApiTarget and resolvedCopilotApiBasePath', () => {
227+
mockResolveAllowedDomains.mockReturnValue({
228+
allowedDomains: ['api.github.com'],
229+
localhostResult: { enableHostAccess: false, hostPorts: [] },
230+
resolvedCopilotApiTarget: 'https://api.github.com',
231+
resolvedCopilotApiBasePath: '/v1',
232+
});
233+
234+
const result = validateNetworkOptions({});
235+
236+
expect(result.resolvedCopilotApiTarget).toBe('https://api.github.com');
237+
expect(result.resolvedCopilotApiBasePath).toBe('/v1');
238+
});
239+
240+
it('includes upstream proxy configuration', () => {
241+
mockResolveNetworkConfig.mockReturnValue({
242+
upstreamProxy: { host: 'proxy.corp.example.com', port: 3128 },
243+
dnsServers: ['8.8.8.8'],
244+
dnsOverHttps: undefined,
245+
});
246+
247+
const result = validateNetworkOptions({});
248+
249+
expect(result.upstreamProxy).toEqual({ host: 'proxy.corp.example.com', port: 3128 });
250+
});
251+
252+
it('includes DNS-over-HTTPS when configured', () => {
253+
mockResolveNetworkConfig.mockReturnValue({
254+
upstreamProxy: undefined,
255+
dnsServers: ['8.8.8.8'],
256+
dnsOverHttps: 'https://1.1.1.1/dns-query',
257+
});
258+
259+
const result = validateNetworkOptions({});
260+
261+
expect(result.dnsOverHttps).toBe('https://1.1.1.1/dns-query');
262+
});
263+
});
264+
});

0 commit comments

Comments
 (0)