Skip to content

Commit b6375a9

Browse files
Mossakaclaude
andcommitted
test(cli): add predownloadCommand unit tests for coverage
Add 5 tests covering the predownloadCommand async function: successful pull of all images, api-proxy inclusion, process.exit on failure, continued pulling after partial failure, and non-Error rejection handling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9668394 commit b6375a9

File tree

1 file changed

+96
-1
lines changed

1 file changed

+96
-1
lines changed

src/commands/predownload.test.ts

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { resolveImages, PredownloadOptions } from './predownload';
1+
import { resolveImages, predownloadCommand, PredownloadOptions } from './predownload';
2+
3+
// Mock execa
4+
jest.mock('execa', () => {
5+
const mockExeca = jest.fn().mockResolvedValue({ stdout: '', stderr: '' });
6+
return { __esModule: true, default: mockExeca };
7+
});
8+
9+
// eslint-disable-next-line @typescript-eslint/no-require-imports
10+
const execa = require('execa').default as jest.Mock;
211

312
describe('predownload', () => {
413
describe('resolveImages', () => {
@@ -54,4 +63,90 @@ describe('predownload', () => {
5463
]);
5564
});
5665
});
66+
67+
describe('predownloadCommand', () => {
68+
const defaults: PredownloadOptions = {
69+
imageRegistry: 'ghcr.io/github/gh-aw-firewall',
70+
imageTag: 'latest',
71+
agentImage: 'default',
72+
enableApiProxy: false,
73+
};
74+
75+
beforeEach(() => {
76+
execa.mockReset();
77+
execa.mockResolvedValue({ stdout: '', stderr: '' });
78+
});
79+
80+
it('should pull all resolved images', async () => {
81+
await predownloadCommand(defaults);
82+
83+
expect(execa).toHaveBeenCalledTimes(2);
84+
expect(execa).toHaveBeenCalledWith(
85+
'docker',
86+
['pull', 'ghcr.io/github/gh-aw-firewall/squid:latest'],
87+
{ stdio: 'inherit' },
88+
);
89+
expect(execa).toHaveBeenCalledWith(
90+
'docker',
91+
['pull', 'ghcr.io/github/gh-aw-firewall/agent:latest'],
92+
{ stdio: 'inherit' },
93+
);
94+
});
95+
96+
it('should pull api-proxy when enabled', async () => {
97+
await predownloadCommand({ ...defaults, enableApiProxy: true });
98+
99+
expect(execa).toHaveBeenCalledTimes(3);
100+
expect(execa).toHaveBeenCalledWith(
101+
'docker',
102+
['pull', 'ghcr.io/github/gh-aw-firewall/api-proxy:latest'],
103+
{ stdio: 'inherit' },
104+
);
105+
});
106+
107+
it('should exit with code 1 when a pull fails', async () => {
108+
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
109+
throw new Error('process.exit called');
110+
});
111+
execa
112+
.mockResolvedValueOnce({ stdout: '', stderr: '' })
113+
.mockRejectedValueOnce(new Error('pull failed'));
114+
115+
await expect(predownloadCommand(defaults)).rejects.toThrow(
116+
'process.exit called',
117+
);
118+
119+
expect(mockExit).toHaveBeenCalledWith(1);
120+
mockExit.mockRestore();
121+
});
122+
123+
it('should continue pulling remaining images after a failure', async () => {
124+
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
125+
throw new Error('process.exit called');
126+
});
127+
execa.mockRejectedValueOnce(new Error('pull failed')).mockResolvedValueOnce({ stdout: '', stderr: '' });
128+
129+
await expect(predownloadCommand(defaults)).rejects.toThrow(
130+
'process.exit called',
131+
);
132+
133+
// Both images should have been attempted
134+
expect(execa).toHaveBeenCalledTimes(2);
135+
mockExit.mockRestore();
136+
});
137+
138+
it('should handle non-Error rejection', async () => {
139+
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
140+
throw new Error('process.exit called');
141+
});
142+
execa.mockRejectedValueOnce('string error').mockResolvedValueOnce({ stdout: '', stderr: '' });
143+
144+
await expect(predownloadCommand(defaults)).rejects.toThrow(
145+
'process.exit called',
146+
);
147+
148+
expect(mockExit).toHaveBeenCalledWith(1);
149+
mockExit.mockRestore();
150+
});
151+
});
57152
});

0 commit comments

Comments
 (0)