120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { FsTaskRepository } from "@/storage/FsTaskRepository";
|
|
import { InMemoryFileSystem } from "../helpers/InMemoryFileSystem";
|
|
import { ITaskRepository } from "@/ports";
|
|
import { Task } from "@/model/task";
|
|
|
|
describe("FsTaskRepository", () => {
|
|
let fs: InMemoryFileSystem;
|
|
let repo: ITaskRepository;
|
|
|
|
const folder = "/tasks";
|
|
|
|
const sampleTask = {
|
|
title: "Fix login bug",
|
|
description: "Token validation error",
|
|
status: "todo" as const,
|
|
priority: "high" as const,
|
|
tags: ["bug", "frontend"],
|
|
assignee: "ivanov",
|
|
};
|
|
|
|
beforeEach(() => {
|
|
fs = new InMemoryFileSystem();
|
|
repo = new FsTaskRepository(fs, ".task.md");
|
|
});
|
|
|
|
it("creates a task and writes it as a .task.md file", async () => {
|
|
const task = await repo.create(folder, sampleTask);
|
|
|
|
expect(task.id).toBeTruthy();
|
|
expect(task.title).toBe("Fix login bug");
|
|
expect(task.created).toBeTruthy();
|
|
expect(task.updated).toBe(task.created);
|
|
|
|
const files = await fs.readdir(folder);
|
|
expect(files).toHaveLength(1);
|
|
expect(files[0]).toMatch(/\.task\.md$/);
|
|
|
|
const content = await fs.readFile(`${folder}/${files[0]}`);
|
|
expect(content).toContain("Fix login bug");
|
|
expect(content).toContain("status: todo");
|
|
});
|
|
|
|
it("list returns created tasks sorted by updated desc", async () => {
|
|
const a = await repo.create(folder, { ...sampleTask, title: "A" });
|
|
await delay(10);
|
|
const b = await repo.create(folder, { ...sampleTask, title: "B" });
|
|
|
|
const tasks = await repo.list(folder);
|
|
expect(tasks).toHaveLength(2);
|
|
expect(tasks[0].title).toBe("B");
|
|
expect(tasks[1].title).toBe("A");
|
|
});
|
|
|
|
it("getById returns the correct task", async () => {
|
|
const created = await repo.create(folder, sampleTask);
|
|
const found = await repo.getById(folder, created.id);
|
|
expect(found).toEqual(created);
|
|
});
|
|
|
|
it("getById returns undefined for unknown id", async () => {
|
|
const found = await repo.getById(folder, "nonexistent");
|
|
expect(found).toBeUndefined();
|
|
});
|
|
|
|
it("update modifies the file and updates timestamp", async () => {
|
|
const task = await repo.create(folder, sampleTask);
|
|
await delay(5);
|
|
const updated: Task = { ...task, title: "Fixed!", status: "done" };
|
|
|
|
await repo.update(folder, updated);
|
|
const found = await repo.getById(folder, task.id);
|
|
|
|
expect(found!.title).toBe("Fixed!");
|
|
expect(found!.status).toBe("done");
|
|
expect(new Date(found!.updated).getTime()).toBeGreaterThan(
|
|
new Date(task.updated).getTime(),
|
|
);
|
|
});
|
|
|
|
it("delete removes the task file", async () => {
|
|
const task = await repo.create(folder, sampleTask);
|
|
await repo.delete(folder, task.id);
|
|
|
|
const tasks = await repo.list(folder);
|
|
expect(tasks).toHaveLength(0);
|
|
});
|
|
|
|
it("delete is a no-op for unknown id", async () => {
|
|
await expect(repo.delete(folder, "nope")).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("list returns empty array for non-existent folder", async () => {
|
|
const tasks = await repo.list("/nonexistent");
|
|
expect(tasks).toEqual([]);
|
|
});
|
|
|
|
it("filters files by extension", async () => {
|
|
fs.writeFile(`${folder}/notes.txt`, "hello");
|
|
fs.writeFile(`${folder}/chore.md`, "---\nid: x\n---\n");
|
|
|
|
await repo.create(folder, sampleTask);
|
|
|
|
const tasks = await repo.list(folder);
|
|
expect(tasks).toHaveLength(1);
|
|
});
|
|
|
|
it("skips unparseable .task.md files silently", async () => {
|
|
fs.writeFile(`${folder}/corrupt.task.md`, "not valid frontmatter");
|
|
|
|
await repo.create(folder, sampleTask);
|
|
const tasks = await repo.list(folder);
|
|
expect(tasks).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
function delay(ms: number): Promise<void> {
|
|
return new Promise((r) => setTimeout(r, ms));
|
|
}
|