chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Git Diff Tests
|
||||
*/
|
||||
|
||||
import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest';
|
||||
import { extractDiff, validateOnBranch } from '../../../src/codeScan/git/diff';
|
||||
import { GitError } from '../../../src/types/codeScan';
|
||||
import type { SimpleGit, StatusResult } from 'simple-git';
|
||||
|
||||
// Define a partial mock type for the SimpleGit methods we use
|
||||
type MockSimpleGit = Pick<SimpleGit, 'status' | 'branch' | 'diff' | 'log'> & {
|
||||
status: Mock;
|
||||
branch: Mock;
|
||||
diff: Mock;
|
||||
log: Mock;
|
||||
};
|
||||
|
||||
// Mock simple-git with a factory that returns a mock instance
|
||||
const mockGit: MockSimpleGit = {
|
||||
status: vi.fn(),
|
||||
branch: vi.fn(),
|
||||
diff: vi.fn(),
|
||||
log: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock('simple-git', () => ({
|
||||
default: vi.fn(() => mockGit),
|
||||
}));
|
||||
|
||||
describe('Git Diff', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('validateOnBranch', () => {
|
||||
it('should return current branch name when on a branch', async () => {
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test-branch',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
const branchName = await validateOnBranch(mockGit as unknown as SimpleGit);
|
||||
|
||||
expect(branchName).toBe('feature/test-branch');
|
||||
});
|
||||
|
||||
it('should throw GitError when in detached HEAD state', async () => {
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: null,
|
||||
detached: true,
|
||||
} as StatusResult);
|
||||
|
||||
await expect(validateOnBranch(mockGit as unknown as SimpleGit)).rejects.toThrow(GitError);
|
||||
await expect(validateOnBranch(mockGit as unknown as SimpleGit)).rejects.toThrow(
|
||||
'Not on a branch',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractDiff', () => {
|
||||
it('should extract diff comparing against main branch', async () => {
|
||||
const mockDiff = `diff --git a/file.ts b/file.ts
|
||||
index 123..456 100644
|
||||
--- a/file.ts
|
||||
+++ b/file.ts
|
||||
@@ -1,3 +1,4 @@
|
||||
+const newLine = 'test';
|
||||
const existingLine = 'existing';`;
|
||||
|
||||
// Mock status (for validateOnBranch)
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
// Mock main branch exists
|
||||
mockGit.branch.mockResolvedValue({
|
||||
all: ['main', 'feature/test'],
|
||||
branches: {},
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as any);
|
||||
mockGit.diff.mockResolvedValue(mockDiff);
|
||||
|
||||
const result = await extractDiff('/fake/repo');
|
||||
|
||||
expect(result).toEqual({
|
||||
diff: mockDiff,
|
||||
baseBranch: 'main',
|
||||
});
|
||||
expect(mockGit.diff).toHaveBeenCalledWith(['main...HEAD']);
|
||||
});
|
||||
|
||||
it('should fall back to master branch if main does not exist', async () => {
|
||||
const mockDiff = 'diff content';
|
||||
|
||||
// Mock status (for validateOnBranch)
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
// Mock main branch does not exist, master does
|
||||
mockGit.branch.mockResolvedValue({
|
||||
all: ['master', 'feature/test'],
|
||||
branches: {},
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as any);
|
||||
mockGit.diff.mockResolvedValue(mockDiff);
|
||||
|
||||
const result = await extractDiff('/fake/repo');
|
||||
|
||||
expect(result).toEqual({
|
||||
diff: mockDiff,
|
||||
baseBranch: 'master',
|
||||
});
|
||||
expect(mockGit.diff).toHaveBeenCalledWith(['master...HEAD']);
|
||||
});
|
||||
|
||||
it('should throw GitError when neither main nor master exists', async () => {
|
||||
// Mock status (for validateOnBranch)
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
// Mock neither branch exists - should throw error
|
||||
mockGit.branch.mockResolvedValue({
|
||||
all: ['feature/test'],
|
||||
branches: {},
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as any);
|
||||
|
||||
await expect(extractDiff('/fake/repo')).rejects.toThrow(GitError);
|
||||
});
|
||||
|
||||
it('should throw GitError if no changes detected', async () => {
|
||||
// Mock status (for validateOnBranch)
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
mockGit.branch.mockResolvedValue({
|
||||
all: ['main', 'feature/test'],
|
||||
branches: {},
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as any);
|
||||
|
||||
// Mock empty diff
|
||||
mockGit.diff.mockResolvedValue('');
|
||||
|
||||
await expect(extractDiff('/fake/repo')).rejects.toThrow(GitError);
|
||||
await expect(extractDiff('/fake/repo')).rejects.toThrow('No changes detected');
|
||||
});
|
||||
|
||||
it('should throw GitError if diff extraction fails', async () => {
|
||||
// Mock status (for validateOnBranch)
|
||||
mockGit.status.mockResolvedValue({
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as StatusResult);
|
||||
|
||||
mockGit.branch.mockResolvedValue({
|
||||
all: ['main', 'feature/test'],
|
||||
branches: {},
|
||||
current: 'feature/test',
|
||||
detached: false,
|
||||
} as any);
|
||||
|
||||
mockGit.diff.mockRejectedValue(new Error('git diff failed'));
|
||||
|
||||
await expect(extractDiff('/fake/repo')).rejects.toThrow(GitError);
|
||||
await expect(extractDiff('/fake/repo')).rejects.toThrow('Failed to extract diff');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user