Files
promptfoo--promptfoo/test/codeScans/util/github.test.ts
T
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

170 lines
4.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
ALL_CLEAR_MESSAGE,
hasPrPostableFindings,
prepareComments,
} from '../../../src/codeScan/util/github';
import { CodeScanSeverity, type Comment } from '../../../src/types/codeScan';
describe('prepareComments', () => {
it('should return empty arrays and empty review body when no comments', () => {
const result = prepareComments([], undefined, undefined);
expect(result.lineComments).toEqual([]);
expect(result.generalComments).toEqual([]);
expect(result.reviewBody).toBe('');
});
it('should prepend "All Clear" when only severity=none comments exist', () => {
const comments: Comment[] = [
{
file: null,
line: null,
severity: CodeScanSeverity.NONE,
finding: 'No vulnerabilities found',
},
];
const review = 'Scanned 10 files. No issues detected.';
const result = prepareComments(comments, review, 'medium');
expect(result.reviewBody).toContain(ALL_CLEAR_MESSAGE);
expect(result.reviewBody).toContain('Scanned 10 files');
});
it('should separate line-specific from general comments', () => {
const comments: Comment[] = [
{
file: 'src/test.ts',
line: 42,
severity: CodeScanSeverity.HIGH,
finding: 'SQL injection vulnerability',
},
{
file: null,
line: null,
severity: CodeScanSeverity.MEDIUM,
finding: 'General security issue',
},
{
file: 'src/file-only.ts',
line: null,
severity: CodeScanSeverity.LOW,
finding: 'File-level security issue',
},
];
const result = prepareComments(comments, 'Review text', 'low');
expect(result.lineComments).toHaveLength(1);
expect(result.lineComments[0].file).toBe('src/test.ts');
expect(result.generalComments).toHaveLength(2);
expect(result.generalComments.map((comment) => comment.file)).toEqual([
null,
'src/file-only.ts',
]);
});
it.each([
null,
0,
-1,
1.5,
])('should route findings with a non-inlineable line (%s) to general comments', (line) => {
const comment: Comment = {
file: 'src/example.ts',
line,
severity: CodeScanSeverity.HIGH,
finding: 'File-scoped issue',
};
const result = prepareComments([comment], 'Review', 'low');
expect(result.lineComments).toEqual([]);
expect(result.generalComments).toEqual([comment]);
});
it('should filter out severity=none from general comments', () => {
const comments: Comment[] = [
{
file: null,
line: null,
severity: CodeScanSeverity.NONE,
finding: 'All clear message',
},
{
file: null,
line: null,
severity: CodeScanSeverity.LOW,
finding: 'Actual issue',
},
];
const result = prepareComments(comments, 'Review', 'low');
expect(result.generalComments).toHaveLength(1);
expect(result.generalComments[0].severity).toBe(CodeScanSeverity.LOW);
});
it('should recognize fileless findings as PR-postable but reject severity=none comments', () => {
expect(
hasPrPostableFindings([
{
file: null,
line: null,
severity: CodeScanSeverity.HIGH,
finding: 'General security issue',
},
]),
).toBe(true);
expect(
hasPrPostableFindings([
{
file: 'src/test.ts',
line: 42,
severity: CodeScanSeverity.NONE,
finding: 'No issue found',
},
]),
).toBe(false);
});
it('should append severity threshold', () => {
const result = prepareComments([], 'Review text', 'medium');
expect(result.reviewBody).toContain('Minimum severity threshold');
expect(result.reviewBody).toContain('Medium');
});
it('should sort comments by severity descending', () => {
const comments: Comment[] = [
{ severity: CodeScanSeverity.LOW, finding: 'Low issue', file: 'a.ts', line: 1 },
{ severity: CodeScanSeverity.CRITICAL, finding: 'Critical issue', file: 'b.ts', line: 2 },
{ severity: CodeScanSeverity.MEDIUM, finding: 'Medium issue', file: 'c.ts', line: 3 },
];
const result = prepareComments(comments, 'Review', undefined);
expect(result.lineComments[0].severity).toBe(CodeScanSeverity.CRITICAL);
expect(result.lineComments[1].severity).toBe(CodeScanSeverity.MEDIUM);
expect(result.lineComments[2].severity).toBe(CodeScanSeverity.LOW);
});
it('should not prepend "All Clear" when vulnerabilities exist', () => {
const comments: Comment[] = [
{
severity: CodeScanSeverity.HIGH,
finding: 'Real vulnerability',
file: 'test.ts',
line: 10,
},
];
const result = prepareComments(comments, 'Found issues', 'low');
expect(result.reviewBody).not.toContain(ALL_CLEAR_MESSAGE);
expect(result.reviewBody).toContain('Found issues');
});
});