mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 14:22:02 +00:00
319 lines
11 KiB
TypeScript
319 lines
11 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import {
|
|
generateSyntheticDiffForNewFile,
|
|
appendUntrackedFileDiffs,
|
|
listAllFilesInDirectory,
|
|
generateDiffsForNonGitDirectory,
|
|
getGitRepositoryDiffs,
|
|
} from '../src/diff';
|
|
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import os from 'os';
|
|
|
|
describe('diff.ts', () => {
|
|
let tempDir: string;
|
|
|
|
beforeEach(async () => {
|
|
// Create a temporary directory for each test
|
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'git-utils-test-'));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
// Clean up temporary directory
|
|
try {
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
} catch (error) {
|
|
// Ignore cleanup errors
|
|
}
|
|
});
|
|
|
|
describe('generateSyntheticDiffForNewFile', () => {
|
|
it('should generate diff for binary file', async () => {
|
|
const fileName = 'test.png';
|
|
const filePath = path.join(tempDir, fileName);
|
|
await fs.writeFile(filePath, Buffer.from([0x89, 0x50, 0x4e, 0x47]));
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('new file mode 100644');
|
|
expect(diff).toContain(`Binary file ${fileName} added`);
|
|
});
|
|
|
|
it('should generate diff for large text file', async () => {
|
|
const fileName = 'large.txt';
|
|
const filePath = path.join(tempDir, fileName);
|
|
// Create a file > 1MB
|
|
const largeContent = 'x'.repeat(1024 * 1024 + 100);
|
|
await fs.writeFile(filePath, largeContent);
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('[File too large to display:');
|
|
expect(diff).toMatch(/\d+KB\]/);
|
|
});
|
|
|
|
it('should generate diff for small text file with trailing newline', async () => {
|
|
const fileName = 'test.txt';
|
|
const filePath = path.join(tempDir, fileName);
|
|
const content = 'line 1\nline 2\nline 3\n';
|
|
await fs.writeFile(filePath, content);
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('new file mode 100644');
|
|
expect(diff).toContain('--- /dev/null');
|
|
expect(diff).toContain(`+++ b/${fileName}`);
|
|
expect(diff).toContain('@@ -0,0 +1,3 @@');
|
|
expect(diff).toContain('+line 1');
|
|
expect(diff).toContain('+line 2');
|
|
expect(diff).toContain('+line 3');
|
|
expect(diff).not.toContain('\\ No newline at end of file');
|
|
});
|
|
|
|
it('should generate diff for text file without trailing newline', async () => {
|
|
const fileName = 'no-newline.txt';
|
|
const filePath = path.join(tempDir, fileName);
|
|
const content = 'line 1\nline 2';
|
|
await fs.writeFile(filePath, content);
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('+line 1');
|
|
expect(diff).toContain('+line 2');
|
|
expect(diff).toContain('\\ No newline at end of file');
|
|
});
|
|
|
|
it('should generate diff for empty file', async () => {
|
|
const fileName = 'empty.txt';
|
|
const filePath = path.join(tempDir, fileName);
|
|
await fs.writeFile(filePath, '');
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('@@ -0,0 +1,0 @@');
|
|
});
|
|
|
|
it('should generate diff for single line file', async () => {
|
|
const fileName = 'single.txt';
|
|
const filePath = path.join(tempDir, fileName);
|
|
await fs.writeFile(filePath, 'single line\n');
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain('@@ -0,0 +1,1 @@');
|
|
expect(diff).toContain('+single line');
|
|
});
|
|
|
|
it('should handle file not found error', async () => {
|
|
const fileName = 'nonexistent.txt';
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, fileName);
|
|
|
|
expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`);
|
|
expect(diff).toContain('[Unable to read file content]');
|
|
});
|
|
|
|
it('should handle directory path gracefully', async () => {
|
|
const dirName = 'some-directory';
|
|
const dirPath = path.join(tempDir, dirName);
|
|
await fs.mkdir(dirPath);
|
|
|
|
const diff = await generateSyntheticDiffForNewFile(tempDir, dirName);
|
|
|
|
expect(diff).toContain(`diff --git a/${dirName} b/${dirName}`);
|
|
expect(diff).toContain('new file mode 040000');
|
|
expect(diff).toContain('[Directory]');
|
|
});
|
|
});
|
|
|
|
describe('appendUntrackedFileDiffs', () => {
|
|
it('should return existing diff when no untracked files', async () => {
|
|
const existingDiff = 'diff --git a/test.txt b/test.txt\n';
|
|
const files = [
|
|
{ status: 'M', path: 'test.txt' },
|
|
{ status: 'A', path: 'new.txt' },
|
|
];
|
|
|
|
const result = await appendUntrackedFileDiffs(tempDir, existingDiff, files);
|
|
|
|
expect(result).toBe(existingDiff);
|
|
});
|
|
|
|
it('should append synthetic diffs for untracked files', async () => {
|
|
const existingDiff = 'existing diff\n';
|
|
const untrackedFile = 'untracked.txt';
|
|
const filePath = path.join(tempDir, untrackedFile);
|
|
await fs.writeFile(filePath, 'content\n');
|
|
|
|
const files = [
|
|
{ status: 'M', path: 'modified.txt' },
|
|
{ status: '?', path: untrackedFile },
|
|
];
|
|
|
|
const result = await appendUntrackedFileDiffs(tempDir, existingDiff, files);
|
|
|
|
expect(result).toContain('existing diff');
|
|
expect(result).toContain(`diff --git a/${untrackedFile} b/${untrackedFile}`);
|
|
expect(result).toContain('+content');
|
|
});
|
|
|
|
it('should handle multiple untracked files', async () => {
|
|
const file1 = 'file1.txt';
|
|
const file2 = 'file2.txt';
|
|
await fs.writeFile(path.join(tempDir, file1), 'file1\n');
|
|
await fs.writeFile(path.join(tempDir, file2), 'file2\n');
|
|
|
|
const files = [
|
|
{ status: '?', path: file1 },
|
|
{ status: '?', path: file2 },
|
|
];
|
|
|
|
const result = await appendUntrackedFileDiffs(tempDir, '', files);
|
|
|
|
expect(result).toContain(`diff --git a/${file1} b/${file1}`);
|
|
expect(result).toContain(`diff --git a/${file2} b/${file2}`);
|
|
expect(result).toContain('+file1');
|
|
expect(result).toContain('+file2');
|
|
});
|
|
});
|
|
|
|
describe('listAllFilesInDirectory', () => {
|
|
it('should list files in empty directory', async () => {
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
expect(files).toEqual([]);
|
|
});
|
|
|
|
it('should list files in flat directory', async () => {
|
|
await fs.writeFile(path.join(tempDir, 'file1.txt'), 'content');
|
|
await fs.writeFile(path.join(tempDir, 'file2.js'), 'code');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(2);
|
|
expect(files).toContain('file1.txt');
|
|
expect(files).toContain('file2.js');
|
|
});
|
|
|
|
it('should list files in nested directories', async () => {
|
|
await fs.mkdir(path.join(tempDir, 'subdir'));
|
|
await fs.writeFile(path.join(tempDir, 'root.txt'), '');
|
|
await fs.writeFile(path.join(tempDir, 'subdir', 'nested.txt'), '');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(2);
|
|
expect(files).toContain('root.txt');
|
|
expect(files).toContain('subdir/nested.txt');
|
|
});
|
|
|
|
it('should skip node_modules directory', async () => {
|
|
await fs.mkdir(path.join(tempDir, 'node_modules'));
|
|
await fs.writeFile(path.join(tempDir, 'app.js'), '');
|
|
await fs.writeFile(path.join(tempDir, 'node_modules', 'package.js'), '');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(1);
|
|
expect(files).toContain('app.js');
|
|
expect(files).not.toContain('node_modules/package.js');
|
|
});
|
|
|
|
it('should skip common build directories', async () => {
|
|
await fs.mkdir(path.join(tempDir, 'dist'));
|
|
await fs.mkdir(path.join(tempDir, 'build'));
|
|
await fs.mkdir(path.join(tempDir, '.next'));
|
|
await fs.writeFile(path.join(tempDir, 'source.ts'), '');
|
|
await fs.writeFile(path.join(tempDir, 'dist', 'output.js'), '');
|
|
await fs.writeFile(path.join(tempDir, 'build', 'output.js'), '');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(1);
|
|
expect(files).toContain('source.ts');
|
|
});
|
|
|
|
it('should skip hidden files except .env', async () => {
|
|
await fs.writeFile(path.join(tempDir, '.hidden'), '');
|
|
await fs.writeFile(path.join(tempDir, '.env'), '');
|
|
await fs.writeFile(path.join(tempDir, 'visible.txt'), '');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(2);
|
|
expect(files).toContain('.env');
|
|
expect(files).toContain('visible.txt');
|
|
expect(files).not.toContain('.hidden');
|
|
});
|
|
|
|
it('should skip .git directory', async () => {
|
|
await fs.mkdir(path.join(tempDir, '.git'));
|
|
await fs.writeFile(path.join(tempDir, '.git', 'config'), '');
|
|
await fs.writeFile(path.join(tempDir, 'README.md'), '');
|
|
|
|
const files = await listAllFilesInDirectory(tempDir);
|
|
|
|
expect(files).toHaveLength(1);
|
|
expect(files).toContain('README.md');
|
|
});
|
|
});
|
|
|
|
describe('generateDiffsForNonGitDirectory', () => {
|
|
it('should generate diffs for all files in directory', async () => {
|
|
await fs.writeFile(path.join(tempDir, 'file1.txt'), 'content1\n');
|
|
await fs.writeFile(path.join(tempDir, 'file2.js'), "console.log('hi');\n");
|
|
|
|
const result = await generateDiffsForNonGitDirectory(tempDir);
|
|
|
|
expect(result.files).toHaveLength(2);
|
|
expect(result.files.every((f) => f.status === '?')).toBe(true);
|
|
expect(result.diff).toContain('diff --git a/file1.txt b/file1.txt');
|
|
expect(result.diff).toContain('diff --git a/file2.js b/file2.js');
|
|
expect(result.diff).toContain('+content1');
|
|
expect(result.diff).toContain("+console.log('hi');");
|
|
});
|
|
|
|
it('should return empty result for empty directory', async () => {
|
|
const result = await generateDiffsForNonGitDirectory(tempDir);
|
|
|
|
expect(result.files).toEqual([]);
|
|
expect(result.diff).toBe('');
|
|
});
|
|
|
|
it('should mark all files as untracked', async () => {
|
|
await fs.writeFile(path.join(tempDir, 'test.txt'), 'test');
|
|
|
|
const result = await generateDiffsForNonGitDirectory(tempDir);
|
|
|
|
expect(result.files).toHaveLength(1);
|
|
expect(result.files[0].status).toBe('?');
|
|
expect(result.files[0].statusText).toBe('New');
|
|
});
|
|
});
|
|
|
|
describe('getGitRepositoryDiffs', () => {
|
|
it('should treat non-git directory as all new files', async () => {
|
|
await fs.writeFile(path.join(tempDir, 'file.txt'), 'content\n');
|
|
|
|
const result = await getGitRepositoryDiffs(tempDir);
|
|
|
|
expect(result.hasChanges).toBe(true);
|
|
expect(result.files).toHaveLength(1);
|
|
expect(result.files[0].status).toBe('?');
|
|
expect(result.diff).toContain('diff --git a/file.txt b/file.txt');
|
|
});
|
|
|
|
it('should return no changes for empty non-git directory', async () => {
|
|
const result = await getGitRepositoryDiffs(tempDir);
|
|
|
|
expect(result.hasChanges).toBe(false);
|
|
expect(result.files).toEqual([]);
|
|
expect(result.diff).toBe('');
|
|
});
|
|
});
|
|
});
|