import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { writeFileSync, unlinkSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import {
loadCsv,
describeCsv,
filterRows,
aggregateColumn,
groupBy,
sortRows,
} from "../src/csv.js";
const tmpDir = join(import.meta.dirname, ".tmp");
const testCsvPath = join(tmpDir, "test.csv");
const TEST_CSV = `name,age,city,score
Alice,30,NYC,95
Bob,25,LA,87
Charlie,35,NYC,92
Diana,28,Chicago,78
Eve,30,LA,91`;
beforeAll(() => {
mkdirSync(tmpDir, { recursive: true });
writeFileSync(testCsvPath, TEST_CSV);
});
afterAll(() => {
try {
unlinkSync(testCsvPath);
} catch {}
});
describe("loadCsv", () => {
it("loads CSV with correct headers and row count", () => {
const data = loadCsv(testCsvPath);
expect(data.headers).toEqual(["name", "age", "city", "score"]);
expect(data.rowCount).toBe(5);
});
});
describe("describeCsv", () => {
it("describes columns with types", () => {
const data = loadCsv(testCsvPath);
const desc = describeCsv(data);
expect(desc).toContain("Rows: 5");
expect(desc).toContain("age: numeric");
expect(desc).toContain("name: string");
});
});
describe("filterRows", () => {
it("filters with eq operator", () => {
const data = loadCsv(testCsvPath);
const result = filterRows(data, "city", "eq", "NYC");
expect(result).toHaveLength(2);
expect(result[0].name).toBe("Alice");
});
it("filters with gt operator", () => {
const data = loadCsv(testCsvPath);
const result = filterRows(data, "age", "gt", "29");
expect(result).toHaveLength(3);
});
it("filters with contains operator", () => {
const data = loadCsv(testCsvPath);
const result = filterRows(data, "name", "contains", "li");
expect(result).toHaveLength(2); // Alice, Charlie
});
});
describe("aggregateColumn", () => {
it("computes sum", () => {
const data = loadCsv(testCsvPath);
expect(aggregateColumn(data, "score", "sum")).toBe("443");
});
it("computes avg", () => {
const data = loadCsv(testCsvPath);
expect(Number(aggregateColumn(data, "score", "avg"))).toBeCloseTo(88.6);
});
it("computes min/max", () => {
const data = loadCsv(testCsvPath);
expect(aggregateColumn(data, "score", "min")).toBe("78");
expect(aggregateColumn(data, "score", "max")).toBe("95");
});
it("computes median", () => {
const data = loadCsv(testCsvPath);
expect(aggregateColumn(data, "score", "median")).toBe("91");
});
});
describe("groupBy", () => {
it("groups and sums", () => {
const data = loadCsv(testCsvPath);
const result = groupBy(data, "city", "score", "sum");
expect(Number(result["NYC"])).toBe(187);
expect(Number(result["LA"])).toBe(178);
});
it("groups and counts", () => {
const data = loadCsv(testCsvPath);
const result = groupBy(data, "city", "score", "count");
expect(result["NYC"]).toBe("2");
expect(result["Chicago"]).toBe("1");
});
});
describe("sortRows", () => {
it("sorts numerically ascending", () => {
const data = loadCsv(testCsvPath);
const sorted = sortRows(data.rows, "score", "asc");
expect(sorted[0].name).toBe("Diana");
expect(sorted[4].name).toBe("Alice");
});
it("sorts descending", () => {
const data = loadCsv(testCsvPath);
const sorted = sortRows(data.rows, "score", "desc");
expect(sorted[0].name).toBe("Alice");
});
});