mirror of
https://github.com/imjasonh/govulncheck-action
synced 2026-07-21 06:17:10 +00:00
Add support for comma-separated and space-delimited working directories
- Parse multiple directories from working-directory input - Support both comma and space delimiters - Filter duplicate directories automatically - Skip non-existent directories with warnings - Aggregate and deduplicate vulnerabilities across all directories - Add comprehensive test coverage (79 tests total, all passing) - Update documentation in README and action.yml Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com>
This commit is contained in:
parent
d375553216
commit
9dacd0a386
6 changed files with 698 additions and 95 deletions
|
|
@ -70,14 +70,20 @@ describe('GitHub Action Integration', () => {
|
|||
});
|
||||
|
||||
it('should complete successfully with no vulnerabilities', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Running govulncheck...');
|
||||
expect(core.info).toHaveBeenCalledWith('Raw govulncheck output length: 0 characters');
|
||||
expect(core.info).toHaveBeenCalledWith('Processing 1 working directory: .');
|
||||
expect(core.info).toHaveBeenCalledWith('Running govulncheck in ....');
|
||||
expect(core.info).toHaveBeenCalledWith('Raw govulncheck output length in .: 0 characters');
|
||||
expect(core.info).toHaveBeenCalledWith('Raw output: ');
|
||||
|
||||
expect(mockGovulncheck.install).toHaveBeenCalled();
|
||||
|
|
@ -93,6 +99,10 @@ describe('GitHub Action Integration', () => {
|
|||
});
|
||||
|
||||
it('should handle vulnerabilities found', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const mockVulnerabilities = [
|
||||
{
|
||||
finding: {
|
||||
|
|
@ -113,19 +123,14 @@ describe('GitHub Action Integration', () => {
|
|||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.warning).toHaveBeenCalledWith('Found 2 vulnerabilities');
|
||||
expect(core.warning).toHaveBeenCalledWith('Found 2 unique vulnerabilities across all directories');
|
||||
expect(core.setOutput).toHaveBeenCalledWith('vulnerabilities-found', 'true');
|
||||
expect(core.setOutput).toHaveBeenCalledWith('vulnerability-count', '2');
|
||||
|
||||
expect(mockAnnotator.createAnnotations).toHaveBeenCalledWith(
|
||||
mockVulnerabilities,
|
||||
mockParser,
|
||||
'.'
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
vulnerabilities: mockVulnerabilities,
|
||||
hasVulnerabilities: true
|
||||
|
|
@ -133,30 +138,51 @@ describe('GitHub Action Integration', () => {
|
|||
});
|
||||
|
||||
it('should change to working directory if specified', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
core.getInput.mockReturnValue('./subfolder');
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(process.chdir).toHaveBeenCalledWith('./subfolder');
|
||||
// Should change to the subfolder absolute path
|
||||
expect(process.chdir).toHaveBeenCalled();
|
||||
const chdirCalls = process.chdir.mock.calls;
|
||||
expect(chdirCalls.some(call => call[0].includes('subfolder'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should not change directory for default value', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
core.getInput.mockReturnValue('.');
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(process.chdir).not.toHaveBeenCalled();
|
||||
// Should still call chdir for the original directory restoration
|
||||
// but not for changing to '.'
|
||||
const chdirCalls = process.chdir.mock.calls;
|
||||
const changeToNewDir = chdirCalls.some(call => call[0] !== originalChdir && call[0] !== process.cwd());
|
||||
expect(changeToNewDir).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle stderr warnings', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
mockGovulncheck.run.mockResolvedValue({
|
||||
output: '',
|
||||
errorOutput: 'warning: using database from 2023-01-01',
|
||||
|
|
@ -166,15 +192,20 @@ describe('GitHub Action Integration', () => {
|
|||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'govulncheck stderr: warning: using database from 2023-01-01'
|
||||
'govulncheck stderr in .: warning: using database from 2023-01-01'
|
||||
);
|
||||
});
|
||||
|
||||
it('should fail when go.sum is missing', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
mockGovulncheck.run.mockResolvedValue({
|
||||
output: '{"config": {}}',
|
||||
errorOutput: 'missing go.sum entry for module providing package golang.org/x/net/html',
|
||||
|
|
@ -184,11 +215,16 @@ describe('GitHub Action Integration', () => {
|
|||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
})).rejects.toThrow('govulncheck failed due to missing dependencies');
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('govulncheck failed in . due to missing dependencies');
|
||||
});
|
||||
|
||||
it('should fail when imports cannot be resolved', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
mockGovulncheck.run.mockResolvedValue({
|
||||
output: '{"config": {}}',
|
||||
errorOutput: 'could not import golang.org/x/net/html (invalid package name: "")',
|
||||
|
|
@ -198,24 +234,34 @@ describe('GitHub Action Integration', () => {
|
|||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
})).rejects.toThrow('govulncheck failed due to missing dependencies');
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('govulncheck failed in . due to missing dependencies');
|
||||
});
|
||||
|
||||
it('should handle errors and set action as failed', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const error = new Error('Failed to install govulncheck');
|
||||
mockGovulncheck.install.mockRejectedValue(error);
|
||||
|
||||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('Failed to install govulncheck');
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Failed to install govulncheck');
|
||||
});
|
||||
|
||||
it('should handle parsing errors', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const error = new Error('Invalid JSON output');
|
||||
mockParser.parse.mockImplementation(() => {
|
||||
throw error;
|
||||
|
|
@ -224,13 +270,18 @@ describe('GitHub Action Integration', () => {
|
|||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('Invalid JSON output');
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Invalid JSON output');
|
||||
});
|
||||
|
||||
it('should handle annotation errors gracefully', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const mockVulnerabilities = [{ finding: { osv: 'GO-2023-1234' } }];
|
||||
mockParser.parse.mockReturnValue(mockVulnerabilities);
|
||||
mockAnnotator.createAnnotations.mockRejectedValue(new Error('Annotation failed'));
|
||||
|
|
@ -238,13 +289,18 @@ describe('GitHub Action Integration', () => {
|
|||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('Annotation failed');
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Annotation failed');
|
||||
});
|
||||
|
||||
it('should truncate output logging when output is large', async () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const largeOutput = 'x'.repeat(6000);
|
||||
mockGovulncheck.run.mockResolvedValue({
|
||||
output: largeOutput,
|
||||
|
|
@ -255,10 +311,341 @@ describe('GitHub Action Integration', () => {
|
|||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Raw govulncheck output length: 6000 characters');
|
||||
expect(core.info).toHaveBeenCalledWith('Raw govulncheck output length in .: 6000 characters');
|
||||
expect(core.info).toHaveBeenCalledWith(`Raw output (first 1000 chars): ${'x'.repeat(1000)}...`);
|
||||
});
|
||||
|
||||
describe('Multiple Working Directories', () => {
|
||||
let mockFs;
|
||||
let originalCwd;
|
||||
|
||||
beforeEach(() => {
|
||||
originalCwd = process.cwd();
|
||||
mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
});
|
||||
|
||||
it('should process comma-separated directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2,dir3');
|
||||
|
||||
const mockVulns1 = [{ finding: { osv: 'GO-2023-1111' } }];
|
||||
const mockVulns2 = [{ finding: { osv: 'GO-2023-2222' } }];
|
||||
const mockVulns3 = [{ finding: { osv: 'GO-2023-3333' } }];
|
||||
|
||||
mockParser.parse
|
||||
.mockReturnValueOnce(mockVulns1)
|
||||
.mockReturnValueOnce(mockVulns2)
|
||||
.mockReturnValueOnce(mockVulns3);
|
||||
|
||||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Processing 3 working directories: dir1, dir2, dir3');
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(3);
|
||||
expect(result.vulnerabilities).toHaveLength(3);
|
||||
expect(result.hasVulnerabilities).toBe(true);
|
||||
});
|
||||
|
||||
it('should process space-delimited directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1 dir2 dir3');
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Processing 3 working directories: dir1, dir2, dir3');
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should process mixed comma and space delimited directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1, dir2 dir3,dir4');
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Processing 4 working directories: dir1, dir2, dir3, dir4');
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('should filter out duplicate directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2,dir1,dir3,dir2');
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Processing 3 working directories: dir1, dir2, dir3');
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should skip non-existent directories with a warning', async () => {
|
||||
core.getInput.mockReturnValue('existing-dir,non-existent-dir');
|
||||
|
||||
mockFs.statSync.mockImplementation((dir) => {
|
||||
if (dir.includes('non-existent')) {
|
||||
throw new Error('ENOENT: no such file or directory');
|
||||
}
|
||||
return { isDirectory: () => true };
|
||||
});
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(core.warning).toHaveBeenCalledWith('Directory not found, skipping: non-existent-dir');
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should filter duplicate vulnerabilities across directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2');
|
||||
|
||||
const vuln1 = { finding: { osv: 'GO-2023-1111' } };
|
||||
const vuln2 = { finding: { osv: 'GO-2023-2222' } };
|
||||
const vuln1Duplicate = { finding: { osv: 'GO-2023-1111' } };
|
||||
|
||||
mockParser.parse
|
||||
.mockReturnValueOnce([vuln1, vuln2])
|
||||
.mockReturnValueOnce([vuln1Duplicate]);
|
||||
|
||||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(result.vulnerabilities).toHaveLength(2);
|
||||
expect(core.info).toHaveBeenCalledWith('Total unique vulnerabilities across all directories: 2');
|
||||
});
|
||||
|
||||
it('should add workingDirectory context to vulnerabilities', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2');
|
||||
|
||||
const vuln1 = { finding: { osv: 'GO-2023-1111' } };
|
||||
const vuln2 = { finding: { osv: 'GO-2023-2222' } };
|
||||
|
||||
mockParser.parse
|
||||
.mockReturnValueOnce([vuln1])
|
||||
.mockReturnValueOnce([vuln2]);
|
||||
|
||||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(result.vulnerabilities[0].workingDirectory).toBe('dir1');
|
||||
expect(result.vulnerabilities[1].workingDirectory).toBe('dir2');
|
||||
});
|
||||
|
||||
it('should return to original directory after processing each directory', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2');
|
||||
const originalDir = process.cwd();
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
// Should return to original directory after each iteration
|
||||
expect(process.chdir).toHaveBeenCalledWith(originalDir);
|
||||
// Called once per directory (2 times)
|
||||
const chdirCalls = process.chdir.mock.calls.filter(call => call[0] !== originalDir);
|
||||
expect(chdirCalls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should install govulncheck only once for multiple directories', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2,dir3');
|
||||
|
||||
mockParser.parse.mockReturnValue([]);
|
||||
|
||||
await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
expect(mockGovulncheck.install).toHaveBeenCalledTimes(1);
|
||||
expect(mockGovulncheck.run).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should handle errors in one directory and continue with others', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2');
|
||||
|
||||
let callCount = 0;
|
||||
mockGovulncheck.run.mockImplementation(() => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
throw new Error('Error in dir1');
|
||||
}
|
||||
return Promise.resolve({
|
||||
output: '',
|
||||
errorOutput: '',
|
||||
exitCode: 0
|
||||
});
|
||||
});
|
||||
|
||||
await expect(run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
})).rejects.toThrow('Error in dir1');
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Error in dir1');
|
||||
});
|
||||
|
||||
it('should handle vulnerabilities without OSV IDs', async () => {
|
||||
core.getInput.mockReturnValue('dir1,dir2');
|
||||
|
||||
const vuln1 = { finding: { osv: 'GO-2023-1111' } };
|
||||
const vuln2 = { finding: {} }; // No OSV ID
|
||||
const vuln3 = { finding: { osv: 'GO-2023-3333' } };
|
||||
|
||||
mockParser.parse
|
||||
.mockReturnValueOnce([vuln1, vuln2])
|
||||
.mockReturnValueOnce([vuln3]);
|
||||
|
||||
const result = await run({
|
||||
govulncheck: mockGovulncheck,
|
||||
parser: mockParser,
|
||||
annotator: mockAnnotator,
|
||||
fs: mockFs
|
||||
});
|
||||
|
||||
// Should include all vulnerabilities since one doesn't have OSV ID
|
||||
expect(result.vulnerabilities).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseWorkingDirectories', () => {
|
||||
const { parseWorkingDirectories } = require('../index');
|
||||
|
||||
it('should parse comma-separated directories', () => {
|
||||
const result = parseWorkingDirectories('dir1,dir2,dir3');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3']);
|
||||
});
|
||||
|
||||
it('should parse space-delimited directories', () => {
|
||||
const result = parseWorkingDirectories('dir1 dir2 dir3');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3']);
|
||||
});
|
||||
|
||||
it('should parse mixed comma and space delimiters', () => {
|
||||
const result = parseWorkingDirectories('dir1, dir2 dir3,dir4');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3', 'dir4']);
|
||||
});
|
||||
|
||||
it('should filter out empty strings', () => {
|
||||
const result = parseWorkingDirectories('dir1,,dir2, ,dir3');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3']);
|
||||
});
|
||||
|
||||
it('should remove duplicates', () => {
|
||||
const result = parseWorkingDirectories('dir1,dir2,dir1,dir3,dir2');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3']);
|
||||
});
|
||||
|
||||
it('should trim whitespace from directory names', () => {
|
||||
const result = parseWorkingDirectories(' dir1 , dir2 , dir3 ');
|
||||
expect(result).toEqual(['dir1', 'dir2', 'dir3']);
|
||||
});
|
||||
|
||||
it('should return ["."] for empty input', () => {
|
||||
expect(parseWorkingDirectories('')).toEqual(['.']);
|
||||
expect(parseWorkingDirectories(null)).toEqual(['.']);
|
||||
expect(parseWorkingDirectories(undefined)).toEqual(['.']);
|
||||
});
|
||||
|
||||
it('should return ["."] for input with only whitespace', () => {
|
||||
const result = parseWorkingDirectories(' , , ');
|
||||
expect(result).toEqual(['.']);
|
||||
});
|
||||
|
||||
it('should handle a single directory', () => {
|
||||
const result = parseWorkingDirectories('single-dir');
|
||||
expect(result).toEqual(['single-dir']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('directoryExists', () => {
|
||||
const { directoryExists } = require('../index');
|
||||
|
||||
it('should return true for existing directories', () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => true })
|
||||
};
|
||||
|
||||
const result = directoryExists('/some/path', mockFs);
|
||||
expect(result).toBe(true);
|
||||
expect(mockFs.statSync).toHaveBeenCalledWith('/some/path');
|
||||
});
|
||||
|
||||
it('should return false for files', () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockReturnValue({ isDirectory: () => false })
|
||||
};
|
||||
|
||||
const result = directoryExists('/some/file.txt', mockFs);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for non-existent paths', () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockImplementation(() => {
|
||||
throw new Error('ENOENT: no such file or directory');
|
||||
})
|
||||
};
|
||||
|
||||
const result = directoryExists('/non/existent', mockFs);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle any error and return false', () => {
|
||||
const mockFs = {
|
||||
statSync: jest.fn().mockImplementation(() => {
|
||||
throw new Error('Permission denied');
|
||||
})
|
||||
};
|
||||
|
||||
const result = directoryExists('/no/access', mockFs);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue