Implement Phase 1: Foundation
- Set up TypeScript project with strict mode - Configure tsup build system (ESM output) - Configure vitest for testing (36 tests passing) - Configure ESLint with flat config for TypeScript - Implement Commander.js CLI with all 4 commands: - init: Configure API keys with validation - new: Create new Ralph Method project (stub) - validate: Validate PROMPT.md files - research: Research topics via Perplexity (stub) - Implement config management: - Keys stored in ~/.ralph-generator/config.json - File permissions set to 600 - Environment variables override file config - Implement logging utility with verbosity levels - Implement atomic file writes utility All Phase 1 acceptance criteria met. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
87
src/__tests__/files.test.ts
Normal file
87
src/__tests__/files.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { join } from 'path';
|
||||
import { mkdir, rm, writeFile as fsWriteFile, readFile } from 'fs/promises';
|
||||
import { tmpdir } from 'os';
|
||||
import { fileExists, readFileContent, writeFileAtomic, readJsonFile, writeJsonFile, ensureDir } from '../utils/files.js';
|
||||
|
||||
describe('File Utilities', () => {
|
||||
const testDir = join(tmpdir(), `ralph-files-test-${Date.now()}`);
|
||||
|
||||
beforeEach(async () => {
|
||||
await mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('ensureDir', () => {
|
||||
it('creates directory if it does not exist', async () => {
|
||||
const newDir = join(testDir, 'new', 'nested', 'dir');
|
||||
await ensureDir(newDir);
|
||||
expect(await fileExists(newDir)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not throw if directory exists', async () => {
|
||||
await expect(ensureDir(testDir)).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fileExists', () => {
|
||||
it('returns true for existing file', async () => {
|
||||
const filePath = join(testDir, 'test.txt');
|
||||
await fsWriteFile(filePath, 'test');
|
||||
expect(await fileExists(filePath)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for non-existing file', async () => {
|
||||
expect(await fileExists(join(testDir, 'nonexistent.txt'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readFileContent', () => {
|
||||
it('reads file content', async () => {
|
||||
const filePath = join(testDir, 'read-test.txt');
|
||||
await fsWriteFile(filePath, 'hello world');
|
||||
const content = await readFileContent(filePath);
|
||||
expect(content).toBe('hello world');
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeFileAtomic', () => {
|
||||
it('writes file atomically', async () => {
|
||||
const filePath = join(testDir, 'atomic.txt');
|
||||
await writeFileAtomic(filePath, 'atomic content');
|
||||
const content = await readFile(filePath, 'utf-8');
|
||||
expect(content).toBe('atomic content');
|
||||
});
|
||||
|
||||
it('creates parent directories', async () => {
|
||||
const filePath = join(testDir, 'new', 'deep', 'file.txt');
|
||||
await writeFileAtomic(filePath, 'nested');
|
||||
expect(await fileExists(filePath)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readJsonFile', () => {
|
||||
it('reads and parses JSON file', async () => {
|
||||
const filePath = join(testDir, 'data.json');
|
||||
await fsWriteFile(filePath, JSON.stringify({ name: 'test', value: 42 }));
|
||||
const data = await readJsonFile<{ name: string; value: number }>(filePath);
|
||||
expect(data.name).toBe('test');
|
||||
expect(data.value).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeJsonFile', () => {
|
||||
it('writes JSON file with formatting', async () => {
|
||||
const filePath = join(testDir, 'output.json');
|
||||
await writeJsonFile(filePath, { key: 'value' });
|
||||
const content = await readFile(filePath, 'utf-8');
|
||||
expect(content).toContain('"key"');
|
||||
expect(content).toContain('"value"');
|
||||
// Check it's formatted (has newlines)
|
||||
expect(content).toContain('\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user