v0.2.0: Git-native architecture (#1)

This commit was merged in pull request #1.
This commit is contained in:
2026-05-29 12:59:45 +00:00
parent 9cf5c000d9
commit 6e637e4af0
50 changed files with 5852 additions and 135 deletions
+128
View File
@@ -0,0 +1,128 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
import { fileExists, ensureDir, readFile, writeFile, readJSON, writeJSON, listFiles, copyFile, getProjectRoot } from "../utils/file.js";
describe("file utilities", () => {
let tempDir: string;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ci-file-test-"));
});
afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
describe("fileExists", () => {
it("returns true for existing files", () => {
const filePath = path.join(tempDir, "test.txt");
fs.writeFileSync(filePath, "hello");
expect(fileExists(filePath)).toBe(true);
});
it("returns false for non-existent files", () => {
expect(fileExists(path.join(tempDir, "nonexistent.txt"))).toBe(false);
});
});
describe("ensureDir", () => {
it("creates directories recursively", () => {
const dirPath = path.join(tempDir, "a", "b", "c");
ensureDir(dirPath);
expect(fs.existsSync(dirPath)).toBe(true);
});
it("is idempotent", () => {
const dirPath = path.join(tempDir, "test");
ensureDir(dirPath);
ensureDir(dirPath);
expect(fs.existsSync(dirPath)).toBe(true);
});
});
describe("readFile / writeFile", () => {
it("reads and writes files", () => {
const filePath = path.join(tempDir, "test.txt");
writeFile(filePath, "hello world");
expect(readFile(filePath)).toBe("hello world");
});
it("creates parent directories when writing", () => {
const filePath = path.join(tempDir, "subdir", "test.txt");
writeFile(filePath, "content");
expect(readFile(filePath)).toBe("content");
});
it("returns null for non-existent files", () => {
expect(readFile(path.join(tempDir, "nonexistent.txt"))).toBeNull();
});
});
describe("readJSON / writeJSON", () => {
it("reads and writes JSON", () => {
const filePath = path.join(tempDir, "data.json");
const data = { name: "test", value: 42 };
writeJSON(filePath, data);
const result = readJSON(filePath);
expect(result).toEqual(data);
});
it("returns null for non-existent files", () => {
expect(readJSON(path.join(tempDir, "nonexistent.json"))).toBeNull();
});
});
describe("listFiles", () => {
it("lists files in directory", () => {
fs.writeFileSync(path.join(tempDir, "a.txt"), "a");
fs.writeFileSync(path.join(tempDir, "b.txt"), "b");
fs.writeFileSync(path.join(tempDir, "c.json"), "{}");
const files = listFiles(tempDir);
expect(files).toHaveLength(3);
expect(files).toContain("a.txt");
});
it("filters files by pattern", () => {
fs.writeFileSync(path.join(tempDir, "a.txt"), "a");
fs.writeFileSync(path.join(tempDir, "b.json"), "{}");
const txtFiles = listFiles(tempDir, /\.txt$/);
expect(txtFiles).toHaveLength(1);
expect(txtFiles).toContain("a.txt");
});
it("returns empty array for non-existent directory", () => {
expect(listFiles(path.join(tempDir, "nonexistent"))).toEqual([]);
});
});
describe("copyFile", () => {
it("copies files to new location", () => {
const src = path.join(tempDir, "src.txt");
const dest = path.join(tempDir, "dest", "copy.txt");
fs.writeFileSync(src, "content");
copyFile(src, dest);
expect(fs.existsSync(dest)).toBe(true);
expect(fs.readFileSync(dest, "utf-8")).toBe("content");
});
});
describe("getProjectRoot", () => {
it("finds project root with package.json", () => {
fs.writeFileSync(path.join(tempDir, "package.json"), "{}");
expect(getProjectRoot(path.join(tempDir, "subdir"))).toBe(tempDir);
});
it("finds project root with .ci directory", () => {
fs.mkdirSync(path.join(tempDir, ".ci"));
expect(getProjectRoot(path.join(tempDir, "nested", "dir"))).toBe(tempDir);
});
it("returns start path when no marker found", () => {
const result = getProjectRoot(tempDir);
expect(result).toBe(tempDir);
});
});
});