Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/ssl-bump.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@ describe('SSL Bump', () => {
expect.arrayContaining(['-t', 'tmpfs', 'tmpfs']),
);
});

it('should handle non-Error throw from OpenSSL command', async () => {
mockExeca.mockImplementation((cmd: string) => {
if (cmd === 'mount') {
return Promise.reject(new Error('mount not available'));
}
// Reject with a string instead of an Error object
return Promise.reject('unexpected string rejection');
});

await expect(generateSessionCa({ workDir: tempDir })).rejects.toThrow(
'Failed to generate SSL Bump CA: unexpected string rejection'
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion matches the full error string exactly, which is brittle if the implementation changes wording/punctuation while still preserving the intended behavior. Consider asserting on key substrings (e.g., regex containing both Failed to generate SSL Bump CA and unexpected string rejection) to keep the test focused on behavior rather than exact formatting.

Suggested change
'Failed to generate SSL Bump CA: unexpected string rejection'
/Failed to generate SSL Bump CA[\s\S]*unexpected string rejection/

Copilot uses AI. Check for mistakes.
);
});
});

describe('initSslDb', () => {
Expand Down Expand Up @@ -253,6 +267,54 @@ describe('SSL Bump', () => {

expect(result).toBe(sslDbPath);
});

it('should silently catch EEXIST when index.txt already exists', async () => {
// Pre-create the directory structure and files
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
fs.writeFileSync(path.join(sslDbPath, 'index.txt'), 'existing');
fs.writeFileSync(path.join(sslDbPath, 'size'), '42\n');

// Should not throw — EEXIST is silently caught by the 'wx' flag handler
const result = await initSslDb(tempDir);
expect(result).toBe(sslDbPath);

// Verify existing content was preserved (not overwritten)
expect(fs.readFileSync(path.join(sslDbPath, 'index.txt'), 'utf-8')).toBe('existing');
expect(fs.readFileSync(path.join(sslDbPath, 'size'), 'utf-8')).toBe('42\n');
});

it('should re-throw non-EEXIST errors from writeFileSync', async () => {
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two non-EEXIST tests duplicate setup/teardown patterns (create structure → chmod → expect reject → restore). Consider extracting a small helper (e.g., withReadOnlyDir(sslDbPath, fn)) to reduce repetition and make the intent of each case (which write fails) stand out.

Copilot uses AI. Check for mistakes.
// Create ssl_db directory, then make it read-only so writeFileSync fails with EACCES
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Make ssl_db read-only so index.txt creation fails with EACCES
fs.chmodSync(sslDbPath, 0o555);

try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserting via toThrow(/EACCES/) depends on the error message text, which can vary by OS/runtime. A more stable assertion is to check the error shape (e.g., code: 'EACCES') using await expect(...).rejects.toMatchObject({ code: 'EACCES' }) (or whatever the implementation throws), which reduces cross-platform brittleness.

Copilot uses AI. Check for mistakes.
} finally {
// Restore permissions for cleanup
fs.chmodSync(sslDbPath, 0o700);
}
});

it('should re-throw non-EEXIST errors from size file writeFileSync', async () => {
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two non-EEXIST tests duplicate setup/teardown patterns (create structure → chmod → expect reject → restore). Consider extracting a small helper (e.g., withReadOnlyDir(sslDbPath, fn)) to reduce repetition and make the intent of each case (which write fails) stand out.

Copilot uses AI. Check for mistakes.
// Create full structure with index.txt, then make dir read-only
// so only the size file creation fails
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Pre-create index.txt so it hits EEXIST (silently caught), then
// size file creation will fail because dir is read-only
fs.writeFileSync(path.join(sslDbPath, 'index.txt'), '');
fs.chmodSync(sslDbPath, 0o555);

try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserting via toThrow(/EACCES/) depends on the error message text, which can vary by OS/runtime. A more stable assertion is to check the error shape (e.g., code: 'EACCES') using await expect(...).rejects.toMatchObject({ code: 'EACCES' }) (or whatever the implementation throws), which reduces cross-platform brittleness.

Copilot uses AI. Check for mistakes.
} finally {
fs.chmodSync(sslDbPath, 0o700);
Comment on lines +288 to +315
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests rely on chmodSync causing EACCES, which is not portable (notably unreliable on Windows, and can behave differently under elevated/CI permissions). This can make the suite fail or become flaky across environments. Prefer mocking fs.writeFileSync to throw an error with code: 'EACCES' for the specific call(s), or conditionally skip these tests on platforms where POSIX permissions don’t apply.

Suggested change
// Create ssl_db directory, then make it read-only so writeFileSync fails with EACCES
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Make ssl_db read-only so index.txt creation fails with EACCES
fs.chmodSync(sslDbPath, 0o555);
try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
} finally {
// Restore permissions for cleanup
fs.chmodSync(sslDbPath, 0o700);
}
});
it('should re-throw non-EEXIST errors from size file writeFileSync', async () => {
// Create full structure with index.txt, then make dir read-only
// so only the size file creation fails
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Pre-create index.txt so it hits EEXIST (silently caught), then
// size file creation will fail because dir is read-only
fs.writeFileSync(path.join(sslDbPath, 'index.txt'), '');
fs.chmodSync(sslDbPath, 0o555);
try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
} finally {
fs.chmodSync(sslDbPath, 0o700);
// Create ssl_db directory
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Mock writeFileSync so that creating index.txt fails with EACCES
const originalWriteFileSync = fs.writeFileSync;
const writeSpy = jest
.spyOn(fs, 'writeFileSync')
.mockImplementation((file: any, data: any, options?: any): any => {
if (typeof file === 'string' && file === path.join(sslDbPath, 'index.txt')) {
const err: any = new Error('EACCES');
err.code = 'EACCES';
throw err;
}
return (originalWriteFileSync as any)(file, data, options);
});
try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
} finally {
writeSpy.mockRestore();
}
});
it('should re-throw non-EEXIST errors from size file writeFileSync', async () => {
// Create full structure with index.txt so it hits EEXIST (silently caught),
// then simulate a failure only for the size file creation
const sslDbPath = path.join(tempDir, 'ssl_db');
fs.mkdirSync(path.join(sslDbPath, 'certs'), { recursive: true });
// Pre-create index.txt
fs.writeFileSync(path.join(sslDbPath, 'index.txt'), '');
// Mock writeFileSync so that creating the size file fails with EACCES
const originalWriteFileSync = fs.writeFileSync;
const writeSpy = jest
.spyOn(fs, 'writeFileSync')
.mockImplementation((file: any, data: any, options?: any): any => {
if (typeof file === 'string' && file === path.join(sslDbPath, 'size')) {
const err: any = new Error('EACCES');
err.code = 'EACCES';
throw err;
}
return (originalWriteFileSync as any)(file, data, options);
});
try {
await expect(initSslDb(tempDir)).rejects.toThrow(/EACCES/);
} finally {
writeSpy.mockRestore();

Copilot uses AI. Check for mistakes.
}
});
});

describe('isOpenSslAvailable', () => {
Expand Down
Loading