test: refactor csv-loader tests into separate files
Split the monolithic `src/csv-loader/loader.test.ts` into multiple specialized test files to improve maintainability and readability: - `parseCsv-basic.test.ts`: Primitive types, arrays, and tuples - `parseCsv-caching.test.ts`: Table caching logic - `parseCsv-circular.test.ts`: Circular reference detection - `parseCsv-combinators.test.ts`: References in unions and tuples - `parseCsv-noResolveRefs.
This commit is contained in:
parent
d0ed1dc92d
commit
3e768f5c83
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
|
||||||
|
describe("parseCsv - basic parsing", () => {
|
||||||
|
it("should parse a simple CSV with primitive types", () => {
|
||||||
|
const csv = [
|
||||||
|
"name,age,active",
|
||||||
|
"string,number,boolean",
|
||||||
|
"Alice,30,true",
|
||||||
|
"Bob,25,false",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0]).toEqual({ name: "Alice", age: 30, active: true });
|
||||||
|
expect(result.data[1]).toEqual({ name: "Bob", age: 25, active: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse CSV with int and float columns", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,count,price",
|
||||||
|
"int,int,float",
|
||||||
|
"1,5,9.99",
|
||||||
|
"2,3,4.50",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0]).toEqual({ id: 1, count: 5, price: 9.99 });
|
||||||
|
expect(result.data[1]).toEqual({ id: 2, count: 3, price: 4.5 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse CSV with non-ASCII characters and comments", () => {
|
||||||
|
const csv = [
|
||||||
|
'# id: unique intent state ID (e.g. "仙人掌怪-boost")',
|
||||||
|
"id",
|
||||||
|
"string",
|
||||||
|
"仙人掌怪-boost",
|
||||||
|
"仙人掌怪-defend",
|
||||||
|
"仙人掌怪-attack",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(3);
|
||||||
|
expect(result.data[0]).toEqual({ id: "仙人掌怪-boost" });
|
||||||
|
expect(result.data[1]).toEqual({ id: "仙人掌怪-defend" });
|
||||||
|
expect(result.data[2]).toEqual({ id: "仙人掌怪-attack" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse CSV with string literal columns (unquoted in CSV)", () => {
|
||||||
|
const csv = [
|
||||||
|
"name,status",
|
||||||
|
"string,'on' | 'off'",
|
||||||
|
"Alice,on",
|
||||||
|
"Bob,off",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0]).toEqual({ name: "Alice", status: "on" });
|
||||||
|
expect(result.data[1]).toEqual({ name: "Bob", status: "off" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse CSV with array columns", () => {
|
||||||
|
const csv = [
|
||||||
|
"name,tags",
|
||||||
|
"string,string[]",
|
||||||
|
"Alice,[dev; admin]",
|
||||||
|
"Bob,[user]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0]).toEqual({ name: "Alice", tags: ["dev", "admin"] });
|
||||||
|
expect(result.data[1]).toEqual({ name: "Bob", tags: ["user"] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse CSV with tuple columns", () => {
|
||||||
|
const csv = [
|
||||||
|
"name,coords",
|
||||||
|
"string,[number; number]",
|
||||||
|
"Alice,[1; 2]",
|
||||||
|
"Bob,[3; 4]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0]).toEqual({ name: "Alice", coords: [1, 2] });
|
||||||
|
expect(result.data[1]).toEqual({ name: "Bob", coords: [3, 4] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should require at least 2 rows (header + schema)", () => {
|
||||||
|
const csv = "name,age\nstring,number";
|
||||||
|
expect(() => parseCsv(csv, { emitTypes: false })).not.toThrow();
|
||||||
|
|
||||||
|
const csv1Row = "name,age";
|
||||||
|
expect(() => parseCsv(csv1Row, { emitTypes: false })).toThrow(
|
||||||
|
"at least 2 rows",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw if header and schema count mismatch", () => {
|
||||||
|
const csv = "name,age\nstring";
|
||||||
|
expect(() => parseCsv(csv, { emitTypes: false })).toThrow("does not match");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir, readFixture } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - caching", () => {
|
||||||
|
it("should cache referenced table and not re-read on subsequent references", () => {
|
||||||
|
const usersCsv = readFixture("users.csv");
|
||||||
|
|
||||||
|
const csv = ["id,creator,reviewer", "string,@users,@users", "1,1,2"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const creator = result.data[0].creator as Record<string, unknown>;
|
||||||
|
const reviewer = result.data[0].reviewer as Record<string, unknown>;
|
||||||
|
expect(creator).not.toEqual(reviewer);
|
||||||
|
expect(creator.id).toBe("1");
|
||||||
|
expect(reviewer.id).toBe("2");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir, readFixture } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - circular reference detection", () => {
|
||||||
|
it("should detect self-referencing circular reference", () => {
|
||||||
|
const csv = ["id,name,parent", "string,string,@self_ref", "1,Root,2"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "self_ref.csv"),
|
||||||
|
}),
|
||||||
|
).toThrow(/Circular reference detected/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should detect mutual circular reference (A -> B -> A)", () => {
|
||||||
|
const csv = readFixture("circular_a.csv");
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "circular_a.csv"),
|
||||||
|
}),
|
||||||
|
).toThrow(/Circular reference detected/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow same table referenced from multiple columns without circular reference", () => {
|
||||||
|
const usersCsv = readFixture("users.csv");
|
||||||
|
const csv = ["id,creator,reviewer", "string,@users,@users", "1,1,2"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].creator).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(result.data[0].reviewer).toEqual({
|
||||||
|
id: "2",
|
||||||
|
name: "Bob",
|
||||||
|
email: "bob@example.com",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - references in combinatory schemas", () => {
|
||||||
|
it("should resolve reference inside a tuple", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,info",
|
||||||
|
"string,[ref: @users; note: string]",
|
||||||
|
"1,[ref: 1; note: urgent]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
const info = result.data[0].info as unknown[];
|
||||||
|
expect(info).toHaveLength(2);
|
||||||
|
expect(info[0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(info[1]).toBe("urgent");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve reference array inside a tuple", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,info",
|
||||||
|
"string,[refs: @users[]; note: string]",
|
||||||
|
"1,[refs: [1; 2]; note: test]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
const info = result.data[0].info as unknown[];
|
||||||
|
expect(info).toHaveLength(2);
|
||||||
|
const refs = info[0] as Record<string, unknown>[];
|
||||||
|
expect(refs).toHaveLength(2);
|
||||||
|
expect(refs[0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(refs[1]).toEqual({ id: "2", name: "Bob", email: "bob@example.com" });
|
||||||
|
expect(info[1]).toBe("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve array of tuples containing references", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,pairs",
|
||||||
|
"string,[@users; number][]",
|
||||||
|
"1,[[1; 10]; [2; 20]]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
const pairs = result.data[0].pairs as unknown[][];
|
||||||
|
expect(pairs).toHaveLength(2);
|
||||||
|
expect(pairs[0]).toHaveLength(2);
|
||||||
|
expect(pairs[0][0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(pairs[0][1]).toBe(10);
|
||||||
|
expect(pairs[1][0]).toEqual({
|
||||||
|
id: "2",
|
||||||
|
name: "Bob",
|
||||||
|
email: "bob@example.com",
|
||||||
|
});
|
||||||
|
expect(pairs[1][1]).toBe(20);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve reference in union (@users | string)", () => {
|
||||||
|
const csv = ["id,value", "string,@users | string", "1,1", "2,unknown"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0].value).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(result.data[1].value).toBe("unknown");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve reference in union (@users[] | string)", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,value",
|
||||||
|
"string,@users[] | string",
|
||||||
|
"1,[1; 2]",
|
||||||
|
"2,none",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
const arr = result.data[0].value as Record<string, unknown>[];
|
||||||
|
expect(arr).toHaveLength(2);
|
||||||
|
expect(arr[0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(result.data[1].value).toBe("none");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve array of reference unions (@users | @parts)[]", () => {
|
||||||
|
const csv = ["id,items", "string,(@users | @parts)[]", "1,[1; 2]"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve named tuple with reference and other fields", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,details",
|
||||||
|
"string,[owner: @users; count: number]",
|
||||||
|
"1,[owner: 1; count: 5]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
const details = result.data[0].details as unknown[];
|
||||||
|
expect(details).toHaveLength(2);
|
||||||
|
expect(details[0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(details[1]).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir, readFixture } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - resolveReferences: false", () => {
|
||||||
|
it("should store IDs instead of resolved objects for reference fields", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,customer,items",
|
||||||
|
"string,@users,@parts[]",
|
||||||
|
"1,1,[1; 2]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].customer).toBe("1");
|
||||||
|
expect(result.data[0].items).toEqual(["1", "2"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should populate referenceFields with metadata", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,customer,items",
|
||||||
|
"string,@users,@parts[]",
|
||||||
|
"1,1,[1; 2]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.referenceFields).toHaveLength(2);
|
||||||
|
expect(result.referenceFields[0]).toEqual({
|
||||||
|
name: "customer",
|
||||||
|
tableName: "users",
|
||||||
|
isArray: false,
|
||||||
|
schema: expect.objectContaining({
|
||||||
|
type: "reference",
|
||||||
|
tableName: "users",
|
||||||
|
isArray: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(result.referenceFields[1]).toEqual({
|
||||||
|
name: "items",
|
||||||
|
tableName: "parts",
|
||||||
|
isArray: true,
|
||||||
|
schema: expect.objectContaining({
|
||||||
|
type: "reference",
|
||||||
|
tableName: "parts",
|
||||||
|
isArray: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not load referenced CSV files", () => {
|
||||||
|
const csv = ["id,customer", "string,@nonexistent", "1,someid"].join("\n");
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should store IDs for nested references in tuples", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,info",
|
||||||
|
"string,[ref: @users; note: string]",
|
||||||
|
"1,[ref: 1; note: urgent]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect((result.data[0].info as unknown[])[0]).toBe("1");
|
||||||
|
expect((result.data[0].info as unknown[])[1]).toBe("urgent");
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("users");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should store IDs for references in unions", () => {
|
||||||
|
const csv = ["id,value", "string,@users | string", "1,1", "2,unknown"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].value).toBe("1");
|
||||||
|
expect(result.data[1].value).toBe("unknown");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not throw for self-referencing table when resolveReferences is false", () => {
|
||||||
|
const csv = readFixture("self_ref.csv");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "self_ref.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0].parent).toBe("2");
|
||||||
|
expect(result.data[1].parent).toBe("1");
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("self_ref");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not throw for cross-referencing tables when resolveReferences is false", () => {
|
||||||
|
const csv = readFixture("circular_a.csv");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "circular_a.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
expect(result.data[0].related).toEqual(["1"]);
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("circular_b");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should store IDs for nested self-reference in tuple", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name,parent_info",
|
||||||
|
"string,string,[parent: @self_ref; role: string]",
|
||||||
|
"1,Root,[parent: 2; role: admin]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "self_ref.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const parentInfo = result.data[0].parent_info as unknown[];
|
||||||
|
expect(parentInfo[0]).toBe("2");
|
||||||
|
expect(parentInfo[1]).toBe("admin");
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("self_ref");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should store IDs for self-reference in union", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name,ref_or_val",
|
||||||
|
"string,string,@self_ref | string",
|
||||||
|
"1,Root,2",
|
||||||
|
"2,Child,none",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "self_ref.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].ref_or_val).toBe("2");
|
||||||
|
expect(result.data[1].ref_or_val).toBe("none");
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("self_ref");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should store IDs for self-reference array in tuple", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name,children",
|
||||||
|
"string,string,[@self_ref[]]",
|
||||||
|
"1,Root,[[2]]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "self_ref.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const children = result.data[0].children as unknown[];
|
||||||
|
expect(children[0]).toEqual(["2"]);
|
||||||
|
expect(result.referenceFields).toHaveLength(1);
|
||||||
|
expect(result.referenceFields[0].tableName).toBe("self_ref");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import { fixturesDir } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - refBaseDir option", () => {
|
||||||
|
it("should use refBaseDir to resolve reference paths", () => {
|
||||||
|
const csv = ["id,customer", "string,@users", "1,1"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
refBaseDir: fixturesDir,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].customer).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir, readFixture } from "../test-utils";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
describe("parseCsv - reference resolution", () => {
|
||||||
|
it("should resolve single reference to another CSV table", () => {
|
||||||
|
const usersCsv = readFixture("users.csv");
|
||||||
|
const result = parseCsv(usersCsv, { emitTypes: false });
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(3);
|
||||||
|
expect(result.data[0]).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve reference values using parseCsv with referenced tables", () => {
|
||||||
|
const ordersCsv = [
|
||||||
|
"id,customer,total",
|
||||||
|
"string,@users,number",
|
||||||
|
"1,1,100",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(ordersCsv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "orders.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
expect(result.data[0].customer).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
expect(result.data[0].total).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve array reference values", () => {
|
||||||
|
const ordersCsv = [
|
||||||
|
"id,items,total",
|
||||||
|
"string,@parts[],number",
|
||||||
|
"1,[1; 2],35.5",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(ordersCsv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "orders.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
const items = result.data[0].items as Record<string, unknown>[];
|
||||||
|
expect(items).toHaveLength(2);
|
||||||
|
expect(items[0]).toEqual({ id: "1", name: "Widget", price: 10.5 });
|
||||||
|
expect(items[1]).toEqual({ id: "2", name: "Gadget", price: 25 });
|
||||||
|
expect(result.data[0].total).toBe(35.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve mixed single and array references", () => {
|
||||||
|
const ordersCsv = [
|
||||||
|
"id,customer,items,total",
|
||||||
|
"string,@users,@parts[],number",
|
||||||
|
"1,1,[1; 2],35.5",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(ordersCsv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "orders.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
expect(result.data[0].customer).toEqual({
|
||||||
|
id: "1",
|
||||||
|
name: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
});
|
||||||
|
const items = result.data[0].items as Record<string, unknown>[];
|
||||||
|
expect(items).toHaveLength(2);
|
||||||
|
expect(items[0]).toEqual({ id: "1", name: "Widget", price: 10.5 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error for reference to non-existent ID", () => {
|
||||||
|
const ordersCsv = ["id,customer", "string,@users", "1,999"].join("\n");
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(ordersCsv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "orders.csv"),
|
||||||
|
}),
|
||||||
|
).toThrow(/not found/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error for reference to non-existent table", () => {
|
||||||
|
const csv = ["id,ref", "string,@nonexistent", "1,someid"].join("\n");
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
}),
|
||||||
|
).toThrow(/Failed to load referenced table/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should collect reference table names", () => {
|
||||||
|
const csv = ["id,customer,items", "string,@users,@parts[]", "1,1,[1]"].join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.references.has("users")).toBe(true);
|
||||||
|
expect(result.references.has("parts")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use custom primary key", () => {
|
||||||
|
const nameCsv = [
|
||||||
|
"code,name",
|
||||||
|
"string,string",
|
||||||
|
"US,United States",
|
||||||
|
"UK,United Kingdom",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const nameCsvPath = path.join(fixturesDir, "countries.csv");
|
||||||
|
fs.writeFileSync(nameCsvPath, nameCsv);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const refCsv = ["id,country", "string,@countries", "1,US"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(refCsv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "ref.csv"),
|
||||||
|
defaultPrimaryKey: "code",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data[0].country).toEqual({
|
||||||
|
code: "US",
|
||||||
|
name: "United States",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
fs.unlinkSync(nameCsvPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir } from "../test-utils";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
describe("parseCsv - reverse reference resolution", () => {
|
||||||
|
it("should resolve reverse reference from comment declaration", () => {
|
||||||
|
const ordersCsvPath = path.join(fixturesDir, "rev_orders.csv");
|
||||||
|
const ordersContent = [
|
||||||
|
"id,customer,total",
|
||||||
|
"string,string,number",
|
||||||
|
"1,1,100",
|
||||||
|
"2,1,50",
|
||||||
|
"3,2,75",
|
||||||
|
].join("\n");
|
||||||
|
fs.writeFileSync(ordersCsvPath, ordersContent);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~rev_orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
"2,Bob",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
const aliceOrders = result.data[0].orders as Record<string, unknown>[];
|
||||||
|
expect(aliceOrders).toHaveLength(2);
|
||||||
|
expect(aliceOrders[0]).toEqual({ id: "1", customer: "1", total: 100 });
|
||||||
|
expect(aliceOrders[1]).toEqual({ id: "2", customer: "1", total: 50 });
|
||||||
|
const bobOrders = result.data[1].orders as Record<string, unknown>[];
|
||||||
|
expect(bobOrders).toHaveLength(1);
|
||||||
|
expect(bobOrders[0].id).toBe("3");
|
||||||
|
} finally {
|
||||||
|
fs.unlinkSync(ordersCsvPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array for reverse reference with no matches", () => {
|
||||||
|
const ordersCsvPath = path.join(fixturesDir, "rev_orders.csv");
|
||||||
|
const ordersContent = [
|
||||||
|
"id,customer,total",
|
||||||
|
"string,string,number",
|
||||||
|
"1,1,100",
|
||||||
|
"2,1,50",
|
||||||
|
].join("\n");
|
||||||
|
fs.writeFileSync(ordersCsvPath, ordersContent);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~rev_orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
"99,Nobody",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0].orders).toHaveLength(2);
|
||||||
|
expect(result.data[1].orders).toEqual([]);
|
||||||
|
} finally {
|
||||||
|
fs.unlinkSync(ordersCsvPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null for optional reverse reference with no matches", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~orders(customer)?",
|
||||||
|
"99,Nobody",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.data).toHaveLength(1);
|
||||||
|
expect(result.data[0].orders).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should populate reverseReferences in result", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.reverseReferences).toHaveLength(1);
|
||||||
|
expect(result.reverseReferences[0]).toEqual({
|
||||||
|
fieldName: "orders",
|
||||||
|
tableName: "orders",
|
||||||
|
foreignKey: "customer",
|
||||||
|
isOptional: false,
|
||||||
|
schema: {
|
||||||
|
type: "reverseReference",
|
||||||
|
tableName: "orders",
|
||||||
|
foreignKey: "customer",
|
||||||
|
isOptional: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include reverse reference tables in references set", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.references.has("orders")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support multiple reverse reference declarations", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~orders(customer)",
|
||||||
|
"# parts := ~parts(user)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.reverseReferences).toHaveLength(2);
|
||||||
|
expect(result.reverseReferences[0].fieldName).toBe("orders");
|
||||||
|
expect(result.reverseReferences[1].fieldName).toBe("parts");
|
||||||
|
expect(result.data[0]).toHaveProperty("orders");
|
||||||
|
expect(result.data[0]).toHaveProperty("parts");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore comment lines that are not reverse reference declarations", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# This is just a comment",
|
||||||
|
"# orders := ~orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.reverseReferences).toHaveLength(1);
|
||||||
|
expect(result.data[0]).toHaveProperty("orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle multiple comment lines including plain comments and reverse references", () => {
|
||||||
|
const orderCsvPath = path.join(fixturesDir, "order.csv");
|
||||||
|
const orderContent = [
|
||||||
|
"id,user,total",
|
||||||
|
"string,string,number",
|
||||||
|
"o01,u01,100",
|
||||||
|
"o02,u01,50",
|
||||||
|
"o03,u02,75",
|
||||||
|
].join("\n");
|
||||||
|
fs.writeFileSync(orderCsvPath, orderContent);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const csv = [
|
||||||
|
"# id: id of user",
|
||||||
|
"# orders: list of related orders",
|
||||||
|
"# orders := ~order(user)",
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"u01,Alice",
|
||||||
|
"u02,Bob",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.reverseReferences).toHaveLength(1);
|
||||||
|
expect(result.reverseReferences[0].fieldName).toBe("orders");
|
||||||
|
expect(result.data).toHaveLength(2);
|
||||||
|
expect(result.data[0].id).toBe("u01");
|
||||||
|
expect(result.data[1].id).toBe("u02");
|
||||||
|
const aliceOrders = result.data[0].orders as Record<string, unknown>[];
|
||||||
|
expect(aliceOrders).toHaveLength(2);
|
||||||
|
expect(aliceOrders[0]).toEqual({ id: "o01", user: "u01", total: 100 });
|
||||||
|
expect(aliceOrders[1]).toEqual({ id: "o02", user: "u01", total: 50 });
|
||||||
|
const bobOrders = result.data[1].orders as Record<string, unknown>[];
|
||||||
|
expect(bobOrders).toHaveLength(1);
|
||||||
|
expect(bobOrders[0].id).toBe("o03");
|
||||||
|
} finally {
|
||||||
|
fs.unlinkSync(orderCsvPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parseCsv - reverse reference with resolveReferences: false", () => {
|
||||||
|
it("should populate referenceFields for reverse references", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# orders := ~orders(customer)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withForeignKey = result.referenceFields.filter(
|
||||||
|
(f) => f.foreignKey === "customer",
|
||||||
|
);
|
||||||
|
expect(withForeignKey).toHaveLength(1);
|
||||||
|
expect(withForeignKey[0]).toEqual({
|
||||||
|
name: "orders",
|
||||||
|
tableName: "orders",
|
||||||
|
isArray: true,
|
||||||
|
foreignKey: "customer",
|
||||||
|
schema: expect.objectContaining({
|
||||||
|
type: "reverseReference",
|
||||||
|
tableName: "orders",
|
||||||
|
foreignKey: "customer",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not load referenced CSV files for reverse references", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,name",
|
||||||
|
"string,string",
|
||||||
|
"# nonexistent := ~nonexistent(some_key)",
|
||||||
|
"1,Alice",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
parseCsv(csv, {
|
||||||
|
emitTypes: false,
|
||||||
|
resolveReferences: false,
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { parseCsv } from "../loader";
|
||||||
|
import * as path from "path";
|
||||||
|
import { fixturesDir } from "../test-utils";
|
||||||
|
|
||||||
|
describe("parseCsv - type generation", () => {
|
||||||
|
it("should generate type definition with emitTypes enabled", () => {
|
||||||
|
const csv = ["name,age", "string,number", "Alice,30"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: true, resourceName: "people" });
|
||||||
|
|
||||||
|
expect(result.typeDefinition).toBeDefined();
|
||||||
|
expect(result.typeDefinition).toContain("peopleTable");
|
||||||
|
expect(result.typeDefinition).toContain("readonly name: string");
|
||||||
|
expect(result.typeDefinition).toContain("readonly age: number");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include reference imports in type definition", () => {
|
||||||
|
const csv = ["id,customer", "string,@users", "1,1"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: true,
|
||||||
|
resourceName: "orders",
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.typeDefinition).toBeDefined();
|
||||||
|
expect(result.typeDefinition).toContain("Users");
|
||||||
|
expect(result.typeDefinition).toContain("users.csv");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not generate type definition when emitTypes is false", () => {
|
||||||
|
const csv = ["name,age", "string,number", "Alice,30"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, { emitTypes: false });
|
||||||
|
expect(result.typeDefinition).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate correct type for reference column", () => {
|
||||||
|
const csv = ["id,customer", "string,@users", "1,1"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: true,
|
||||||
|
resourceName: "orders",
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.typeDefinition).toContain("readonly customer: Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate correct type for array reference column", () => {
|
||||||
|
const csv = ["id,items", "string,@parts[]", "1,[1]"].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: true,
|
||||||
|
resourceName: "orders",
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.typeDefinition).toContain("readonly items: Parts[]");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate correct type for reference in tuple", () => {
|
||||||
|
const csv = [
|
||||||
|
"id,info",
|
||||||
|
"string,[ref: @users; note: string]",
|
||||||
|
"1,[ref: 1; note: test]",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const result = parseCsv(csv, {
|
||||||
|
emitTypes: true,
|
||||||
|
resourceName: "data",
|
||||||
|
currentFilePath: path.join(fixturesDir, "test.csv"),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.typeDefinition).toContain("Users");
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue