1
0
Fork 0
mirror of https://github.com/imjasonh/govulncheck-action synced 2026-07-08 08:45:48 +00:00
govulncheck-action/test/summary.test.js
Jason Hall 6f111796c8
Improve test coverage to 99.64%
Added comprehensive test cases to achieve near-perfect test coverage:

## Coverage Improvements
- **Statements**: 99.65% (from 76.2%)
- **Branches**: 81.06% (from 55.02%)
- **Functions**: 100% (from 78.57%)
- **Lines**: 99.64% (from 76.97%)

## New Test Files
- `test/summary.test.js`: Complete test suite for summary generation
- `test/index-main.test.js`: Test for main module execution

## Enhanced Test Cases
- Added tests for large output truncation
- Added tests for OSV details with CVE aliases
- Added tests for vulnerabilities without OSV IDs
- Added tests for fixed version recommendations
- Added tests for module call sites and suggested edits
- Added tests for multi-line JSON parsing
- Added tests for error handling edge cases

The only remaining uncovered line is the main module execution check,
which is inherently difficult to test in a unit testing environment.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-07 01:44:32 -04:00

238 lines
No EOL
7.5 KiB
JavaScript

const SummaryGenerator = require('../lib/summary');
describe('SummaryGenerator', () => {
let summaryGenerator;
let mockCore;
let mockParser;
let mockSummary;
beforeEach(() => {
mockSummary = {
addHeading: jest.fn().mockReturnThis(),
addEOL: jest.fn().mockReturnThis(),
addRaw: jest.fn().mockReturnThis(),
addList: jest.fn().mockReturnThis(),
addTable: jest.fn().mockReturnThis(),
addLink: jest.fn().mockReturnThis(),
addSeparator: jest.fn().mockReturnThis(),
write: jest.fn().mockResolvedValue()
};
mockCore = {
summary: mockSummary
};
mockParser = {
extractUniqueModules: jest.fn(),
extractCallSites: jest.fn()
};
summaryGenerator = new SummaryGenerator(mockCore);
});
describe('generateSummary', () => {
it('should generate summary with no vulnerabilities', async () => {
mockParser.extractUniqueModules.mockReturnValue([]);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary([], mockParser, '.');
expect(mockSummary.addHeading).toHaveBeenCalledWith('🔍 Govulncheck Security Report', 1);
expect(mockSummary.addRaw).toHaveBeenCalledWith('✅ No vulnerabilities found!');
expect(mockSummary.write).toHaveBeenCalled();
});
it('should generate summary with vulnerabilities sorted by OSV ID', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-5678',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }],
fixed_version: 'v1.2.3'
},
osvDetails: {
summary: 'Second vulnerability'
}
},
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }],
fixed_version: 'v1.2.3'
},
osvDetails: {
summary: 'First vulnerability'
}
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable']);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
// Check that table was called
const tableCall = mockSummary.addTable.mock.calls[0][0];
expect(tableCall[1][0]).toBe('GO-2023-1234'); // First row should be sorted first
expect(tableCall[2][0]).toBe('GO-2023-5678'); // Second row should be sorted second
});
it('should include CVE aliases in vulnerability table', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }],
fixed_version: 'v1.2.3'
},
osvDetails: {
summary: 'Critical vulnerability',
aliases: ['CVE-2023-1234', 'GHSA-xxxx-yyyy', 'CVE-2023-5678']
}
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable']);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
const tableCall = mockSummary.addTable.mock.calls[0][0];
expect(tableCall[1][3]).toBe('CVE: CVE-2023-1234, CVE-2023-5678');
});
it('should generate vulnerable code locations section', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }]
}
}
];
const callSites = [
{
filename: 'main.go',
line: 42,
vulnerableFunction: 'vulnerable.Function',
osv: 'GO-2023-1234'
},
{
filename: 'utils.go',
line: 10,
vulnerableFunction: 'helper.Process',
osv: 'GO-2023-5678'
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable']);
mockParser.extractCallSites.mockReturnValue(callSites);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
expect(mockSummary.addHeading).toHaveBeenCalledWith('🚨 Vulnerable Code Locations', 2);
expect(mockSummary.addHeading).toHaveBeenCalledWith('📄 main.go', 3);
expect(mockSummary.addRaw).toHaveBeenCalledWith('• Line 42: calls vulnerable.Function');
expect(mockSummary.addLink).toHaveBeenCalledWith('GO-2023-1234', 'https://pkg.go.dev/vuln/GO-2023-1234');
});
it('should handle call sites in subdirectory', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }]
}
}
];
const callSites = [
{
filename: 'main.go',
line: 42,
vulnerableFunction: 'vulnerable.Function',
osv: 'GO-2023-1234'
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable']);
mockParser.extractCallSites.mockReturnValue(callSites);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, 'subdir');
expect(mockSummary.addHeading).toHaveBeenCalledWith('📄 subdir/main.go', 3);
});
it('should generate recommendations with latest fixed versions', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }],
fixed_version: 'v1.2.0'
}
},
{
finding: {
osv: 'GO-2023-5678',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }],
fixed_version: 'v1.2.3'
}
},
{
finding: {
osv: 'GO-2023-9999',
trace: [{ module: 'another.com/package', version: 'v2.0.0' }],
fixed_version: 'v2.1.0'
}
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable', 'another.com/package']);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
expect(mockSummary.addList).toHaveBeenCalledWith([
'Update another.com/package to version v2.1.0 or later',
'Update example.com/vulnerable to version v1.2.3 or later'
]);
});
it('should handle vulnerabilities without fixed versions', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234',
trace: [{ module: 'example.com/vulnerable', version: 'v1.0.0' }]
}
}
];
mockParser.extractUniqueModules.mockReturnValue(['example.com/vulnerable']);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
expect(mockSummary.addRaw).toHaveBeenCalledWith('No specific version recommendations available.');
});
it('should handle vulnerabilities without trace data', async () => {
const vulnerabilities = [
{
finding: {
osv: 'GO-2023-1234'
}
}
];
mockParser.extractUniqueModules.mockReturnValue([]);
mockParser.extractCallSites.mockReturnValue([]);
await summaryGenerator.generateSummary(vulnerabilities, mockParser, '.');
expect(mockSummary.write).toHaveBeenCalled();
});
});
});