From f66f60aa0e5c25d8783126c477f4da2d7c8197ed Mon Sep 17 00:00:00 2001 From: hypercross Date: Mon, 20 Apr 2026 01:29:59 +0800 Subject: [PATCH] test: add comprehensive tests for csvToModule - Add unit tests for accessor-based output, circular references, and reverse reference resolution in `csvToModule`. - Extract fixture loading logic into `test-utils.ts`. - Refactor `loader.test.ts` to use the new test utilities. --- src/csv-loader/loader.test.ts | 395 +----------------------------- src/csv-loader/module-gen.test.ts | 381 ++++++++++++++++++++++++++++ src/csv-loader/test-utils.ts | 8 + 3 files changed, 391 insertions(+), 393 deletions(-) create mode 100644 src/csv-loader/module-gen.test.ts create mode 100644 src/csv-loader/test-utils.ts diff --git a/src/csv-loader/loader.test.ts b/src/csv-loader/loader.test.ts index 3d80816..d6398e5 100644 --- a/src/csv-loader/loader.test.ts +++ b/src/csv-loader/loader.test.ts @@ -1,14 +1,8 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { parseCsv } from "./loader"; import * as path from "path"; -import * as fs from "fs"; -import { csvToModule } from "./module-gen"; - -const fixturesDir = path.join(__dirname, "fixtures"); - -function readFixture(name: string): string { - return fs.readFileSync(path.join(fixturesDir, name), "utf-8"); -} +import { fixturesDir, readFixture } from "./test-utils"; +import fs from "fs"; describe("parseCsv - basic parsing", () => { it("should parse a simple CSV with primitive types", () => { @@ -770,245 +764,6 @@ describe("parseCsv - resolveReferences: false", () => { }); }); -describe("csvToModule - accessor-based output", () => { - it("should emit accessor function for tables without references", () => { - const csv = ["name,age", "string,number", "Alice,30"].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("export default function getData()"); - expect(result.js).not.toContain("import "); - expect(result.js).not.toContain("Lookup"); - }); - - it("should emit accessor function for tables with references", () => { - const csv = ["id,customer", "string,@users", "1,1"].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("import _users from './users.csv'"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("_usersLookup"); - expect(result.js).toContain("_resolved = _raw;"); - }); - - it("should emit accessor function for tables with array references", () => { - const csv = ["id,items", "string,@parts[]", "1,[1; 2]"].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("import _parts from './parts.csv'"); - expect(result.js).toContain("_partsLookup"); - expect(result.js).toContain(".map(id =>"); - }); - - it("should emit multiple imports for multiple reference tables", () => { - const csv = [ - "id,customer,items", - "string,@users,@parts[]", - "1,1,[1; 2]", - ].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("import _users from './users.csv'"); - expect(result.js).toContain("import _parts from './parts.csv'"); - expect(result.js).toContain("_usersLookup"); - expect(result.js).toContain("_partsLookup"); - }); - - it("should generate function type in dts for tables with references", () => { - const csv = ["id,customer", "string,@users", "1,1"].join("\n"); - - const result = csvToModule(csv, { - emitTypes: true, - resourceName: "orders", - }); - - expect(result.dts).toContain("declare function getData(): ordersTable"); - expect(result.dts).toContain("export default getData"); - expect(result.dts).not.toContain("declare const data"); - }); - - it("should generate function type in dts for tables without references", () => { - const csv = ["name,age", "string,number", "Alice,30"].join("\n"); - - const result = csvToModule(csv, { - emitTypes: true, - resourceName: "people", - }); - - expect(result.dts).toContain("declare function getData(): peopleTable"); - expect(result.dts).toContain("export default getData"); - expect(result.dts).not.toContain("declare const data"); - }); - - it("should handle nested references in tuples", () => { - const csv = [ - "id,info", - "string,[ref: @users; note: string]", - "1,[ref: 1; note: urgent]", - ].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("import _users from './users.csv'"); - expect(result.js).toContain("_usersLookup"); - }); -}); - -describe("csvToModule - circular reference support", () => { - it("should emit accessor for self-referencing table without self-import", () => { - const csv = readFixture("self_ref.csv"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("_self_refLookup"); - expect(result.js).toContain("_self_refLookup = new Map(_raw.map"); - expect(result.js).toContain( - "parent: _self_refLookup.get(String(row.parent))", - ); - }); - - it("should emit accessor for cross-referencing tables", () => { - const csv = readFixture("circular_a.csv"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "circular_a.csv"), - }); - - expect(result.js).toContain("import _circular_b from './circular_b.csv'"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("_circular_bLookup"); - expect(result.js).toContain("related:"); - }); - - it("should emit accessor for self-referencing table with nested 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 = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); - expect(result.js).toContain("_self_refLookup"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("parent_info:"); - expect(result.js).toContain( - "_self_refLookup.get(String(row.parent_info[0]))", - ); - }); - - it("should emit accessor for self-referencing table with nested reference in union with fallback", () => { - const csv = [ - "id,name,ref_or_val", - "string,string,@self_ref | string", - "1,Root,2", - "2,Child,none", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); - expect(result.js).toContain("_self_refLookup"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("ref_or_val:"); - expect(result.js).toContain( - "_self_refLookup.get(String(row.ref_or_val)) ?? row.ref_or_val", - ); - }); - - it("should emit accessor for self-referencing table with nested reference array in tuple", () => { - const csv = [ - "id,name,children", - "string,string,[kids: @self_ref[]]", - "1,Root,[[2]]", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); - expect(result.js).toContain("_self_refLookup"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("children:"); - }); - - it("should generate correct type definition for self-referencing table using local singular type", () => { - const csv = readFixture("self_ref.csv"); - - const result = csvToModule(csv, { - emitTypes: true, - resourceName: "nodes", - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.dts).toContain("declare function getData(): nodesTable"); - expect(result.dts).toContain("readonly parent: Nodes"); - expect(result.dts).not.toContain( - "import type { Self_ref } from './self_ref.csv'", - ); - expect(result.dts).not.toContain("Self_ref"); - }); - - it("should emit accessor for cross-referencing tables with array references", () => { - const csv = readFixture("circular_a.csv"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "circular_a.csv"), - }); - - expect(result.js).toContain("import _circular_b from './circular_b.csv'"); - expect(result.js).toContain("export default function getData()"); - expect(result.js).toContain("_circular_bLookup"); - expect(result.js).toContain(".map(id =>"); - }); - - it("should emit accessor for nested cross-reference in tuple", () => { - const csv = [ - "id,name,info", - "string,string,[ref: @circular_b; note: string]", - "1,A,[ref: 1; note: linked]", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "circular_a.csv"), - }); - - expect(result.js).toContain("import _circular_b from './circular_b.csv'"); - expect(result.js).toContain("_circular_bLookup"); - expect(result.js).toContain("export default function getData()"); - }); - - it("should generate union fallback with ?? for non-reference union members", () => { - const csv = ["id,value", "string,@users | string", "1,1", "2,unknown"].join( - "\n", - ); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("?? row.value"); - }); -}); - describe("parseCsv - reverse reference resolution", () => { it("should resolve reverse reference from comment declaration", () => { // Create a temporary orders CSV with a plain string foreign key @@ -1281,149 +1036,3 @@ describe("parseCsv - reverse reference with resolveReferences: false", () => { ).not.toThrow(); }); }); - -describe("csvToModule - reverse reference output", () => { - it("should emit reverse lookup code for reverse references", () => { - const csv = [ - "id,name", - "string,string", - "# orders := ~orders(customer)", - "1,Alice", - ].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("import _orders from './orders.csv'"); - expect(result.js).toContain("_ordersBy_customer"); - expect(result.js).toContain("orders:"); - expect(result.js).toContain("_ordersBy_customer.get(String(row.id))"); - }); - - it("should emit null fallback for optional reverse references", () => { - const csv = [ - "id,name", - "string,string", - "# orders := ~orders(customer)?", - "1,Alice", - ].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain( - "_ordersBy_customer.get(String(row.id)) || null", - ); - }); - - it("should emit empty array fallback for non-optional reverse references", () => { - const csv = [ - "id,name", - "string,string", - "# orders := ~orders(customer)", - "1,Alice", - ].join("\n"); - - const result = csvToModule(csv, { emitTypes: false }); - - expect(result.js).toContain("_ordersBy_customer.get(String(row.id)) || []"); - }); - - it("should handle self-referencing reverse reference without self-import", () => { - const csv = [ - "id,name", - "string,string", - "# children := ~self_ref(parent)", - "1,Root", - "2,Child", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "self_ref.csv"), - }); - - expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); - expect(result.js).toContain("_self_refBy_parent"); - expect(result.js).toContain("for (const r of _raw)"); - expect(result.js).toContain("children:"); - }); - - it("should generate correct type definition for reverse references", () => { - const csv = [ - "id,name", - "string,string", - "# orders := ~orders(customer)", - "1,Alice", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: true, - resourceName: "users", - currentFilePath: path.join(fixturesDir, "test.csv"), - }); - - expect(result.dts).toContain("readonly orders: Orders[]"); - expect(result.dts).toContain("import type { Orders }"); - }); - - it("should generate nullable type for optional reverse references", () => { - const csv = [ - "id,name", - "string,string", - "# orders := ~orders(customer)?", - "1,Alice", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: true, - resourceName: "users", - currentFilePath: path.join(fixturesDir, "test.csv"), - }); - - expect(result.dts).toContain("readonly orders: Orders[] | null"); - }); - - it("should combine forward and reverse references to the same table", () => { - // When the current table references itself via both @ and ~, - // no self-import is needed - const csv = [ - "id,name,manager", - "string,string,@users", - "# reports := ~users(manager)", - "1,Alice,2", - "2,Bob,", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "users.csv"), - }); - - // Forward reference uses lookup built from _raw (self) - expect(result.js).toContain("_usersLookup"); - // Reverse reference uses reverse lookup built from _raw (self) - expect(result.js).toContain("_usersBy_manager"); - // No self-import since the current file IS users.csv - expect(result.js).not.toContain("import _users from './users.csv'"); - }); - - it("should import referenced table when forward and reverse reference a different table", () => { - const csv = [ - "id,name,creator", - "string,string,@users", - "# reviews := ~users(reviewer)", - "1,Doc,1", - ].join("\n"); - - const result = csvToModule(csv, { - emitTypes: false, - currentFilePath: path.join(fixturesDir, "test.csv"), - }); - - // Forward reference uses lookup - expect(result.js).toContain("_usersLookup"); - // Reverse reference uses reverse lookup - expect(result.js).toContain("_usersBy_reviewer"); - // users is a different table, so it should be imported - expect(result.js).toContain("import _users from './users.csv'"); - }); -}); diff --git a/src/csv-loader/module-gen.test.ts b/src/csv-loader/module-gen.test.ts new file mode 100644 index 0000000..19728bf --- /dev/null +++ b/src/csv-loader/module-gen.test.ts @@ -0,0 +1,381 @@ +import { describe, it, expect } from "vitest"; +import { csvToModule } from "./module-gen"; +import * as path from "path"; +import { fixturesDir, readFixture } from "./test-utils"; + +describe("csvToModule - accessor-based output", () => { + it("should emit accessor function for tables without references", () => { + const csv = ["name,age", "string,number", "Alice,30"].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("export default function getData()"); + expect(result.js).not.toContain("import "); + expect(result.js).not.toContain("Lookup"); + }); + + it("should emit accessor function for tables with references", () => { + const csv = ["id,customer", "string,@users", "1,1"].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("import _users from './users.csv'"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("_usersLookup"); + expect(result.js).toContain("_resolved = _raw;"); + }); + + it("should emit accessor function for tables with array references", () => { + const csv = ["id,items", "string,@parts[]", "1,[1; 2]"].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("import _parts from './parts.csv'"); + expect(result.js).toContain("_partsLookup"); + expect(result.js).toContain(".map(id =>"); + }); + + it("should emit multiple imports for multiple reference tables", () => { + const csv = [ + "id,customer,items", + "string,@users,@parts[]", + "1,1,[1; 2]", + ].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("import _users from './users.csv'"); + expect(result.js).toContain("import _parts from './parts.csv'"); + expect(result.js).toContain("_usersLookup"); + expect(result.js).toContain("_partsLookup"); + }); + + it("should generate function type in dts for tables with references", () => { + const csv = ["id,customer", "string,@users", "1,1"].join("\n"); + + const result = csvToModule(csv, { + emitTypes: true, + resourceName: "orders", + }); + + expect(result.dts).toContain("declare function getData(): ordersTable"); + expect(result.dts).toContain("export default getData"); + expect(result.dts).not.toContain("declare const data"); + }); + + it("should generate function type in dts for tables without references", () => { + const csv = ["name,age", "string,number", "Alice,30"].join("\n"); + + const result = csvToModule(csv, { + emitTypes: true, + resourceName: "people", + }); + + expect(result.dts).toContain("declare function getData(): peopleTable"); + expect(result.dts).toContain("export default getData"); + expect(result.dts).not.toContain("declare const data"); + }); + + it("should handle nested references in tuples", () => { + const csv = [ + "id,info", + "string,[ref: @users; note: string]", + "1,[ref: 1; note: urgent]", + ].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("import _users from './users.csv'"); + expect(result.js).toContain("_usersLookup"); + }); +}); + +describe("csvToModule - circular reference support", () => { + it("should emit accessor for self-referencing table without self-import", () => { + const csv = readFixture("self_ref.csv"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("_self_refLookup"); + expect(result.js).toContain("_self_refLookup = new Map(_raw.map"); + expect(result.js).toContain( + "parent: _self_refLookup.get(String(row.parent))", + ); + }); + + it("should emit accessor for cross-referencing tables", () => { + const csv = readFixture("circular_a.csv"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "circular_a.csv"), + }); + + expect(result.js).toContain("import _circular_b from './circular_b.csv'"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("_circular_bLookup"); + expect(result.js).toContain("related:"); + }); + + it("should emit accessor for self-referencing table with nested 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 = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); + expect(result.js).toContain("_self_refLookup"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("parent_info:"); + expect(result.js).toContain( + "_self_refLookup.get(String(row.parent_info[0]))", + ); + }); + + it("should emit accessor for self-referencing table with nested reference in union with fallback", () => { + const csv = [ + "id,name,ref_or_val", + "string,string,@self_ref | string", + "1,Root,2", + "2,Child,none", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); + expect(result.js).toContain("_self_refLookup"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("ref_or_val:"); + expect(result.js).toContain( + "_self_refLookup.get(String(row.ref_or_val)) ?? row.ref_or_val", + ); + }); + + it("should emit accessor for self-referencing table with nested reference array in tuple", () => { + const csv = [ + "id,name,children", + "string,string,[kids: @self_ref[]]", + "1,Root,[[2]]", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); + expect(result.js).toContain("_self_refLookup"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("children:"); + }); + + it("should generate correct type definition for self-referencing table using local singular type", () => { + const csv = readFixture("self_ref.csv"); + + const result = csvToModule(csv, { + emitTypes: true, + resourceName: "nodes", + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.dts).toContain("declare function getData(): nodesTable"); + expect(result.dts).toContain("readonly parent: Nodes"); + expect(result.dts).not.toContain( + "import type { Self_ref } from './self_ref.csv'", + ); + expect(result.dts).not.toContain("Self_ref"); + }); + + it("should emit accessor for cross-referencing tables with array references", () => { + const csv = readFixture("circular_a.csv"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "circular_a.csv"), + }); + + expect(result.js).toContain("import _circular_b from './circular_b.csv'"); + expect(result.js).toContain("export default function getData()"); + expect(result.js).toContain("_circular_bLookup"); + expect(result.js).toContain(".map(id =>"); + }); + + it("should emit accessor for nested cross-reference in tuple", () => { + const csv = [ + "id,name,info", + "string,string,[ref: @circular_b; note: string]", + "1,A,[ref: 1; note: linked]", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "circular_a.csv"), + }); + + expect(result.js).toContain("import _circular_b from './circular_b.csv'"); + expect(result.js).toContain("_circular_bLookup"); + expect(result.js).toContain("export default function getData()"); + }); + + it("should generate union fallback with ?? for non-reference union members", () => { + const csv = ["id,value", "string,@users | string", "1,1", "2,unknown"].join( + "\n", + ); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("?? row.value"); + }); +}); + +describe("csvToModule - reverse reference output", () => { + it("should emit reverse lookup code for reverse references", () => { + const csv = [ + "id,name", + "string,string", + "# orders := ~orders(customer)", + "1,Alice", + ].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("import _orders from './orders.csv'"); + expect(result.js).toContain("_ordersBy_customer"); + expect(result.js).toContain("orders:"); + expect(result.js).toContain("_ordersBy_customer.get(String(row.id))"); + }); + + it("should emit null fallback for optional reverse references", () => { + const csv = [ + "id,name", + "string,string", + "# orders := ~orders(customer)?", + "1,Alice", + ].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain( + "_ordersBy_customer.get(String(row.id)) || null", + ); + }); + + it("should emit empty array fallback for non-optional reverse references", () => { + const csv = [ + "id,name", + "string,string", + "# orders := ~orders(customer)", + "1,Alice", + ].join("\n"); + + const result = csvToModule(csv, { emitTypes: false }); + + expect(result.js).toContain("_ordersBy_customer.get(String(row.id)) || []"); + }); + + it("should handle self-referencing reverse reference without self-import", () => { + const csv = [ + "id,name", + "string,string", + "# children := ~self_ref(parent)", + "1,Root", + "2,Child", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "self_ref.csv"), + }); + + expect(result.js).not.toContain("import _self_ref from './self_ref.csv'"); + expect(result.js).toContain("_self_refBy_parent"); + expect(result.js).toContain("for (const r of _raw)"); + expect(result.js).toContain("children:"); + }); + + it("should generate correct type definition for reverse references", () => { + const csv = [ + "id,name", + "string,string", + "# orders := ~orders(customer)", + "1,Alice", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: true, + resourceName: "users", + currentFilePath: path.join(fixturesDir, "test.csv"), + }); + + expect(result.dts).toContain("readonly orders: Orders[]"); + expect(result.dts).toContain("import type { Orders }"); + }); + + it("should generate nullable type for optional reverse references", () => { + const csv = [ + "id,name", + "string,string", + "# orders := ~orders(customer)?", + "1,Alice", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: true, + resourceName: "users", + currentFilePath: path.join(fixturesDir, "test.csv"), + }); + + expect(result.dts).toContain("readonly orders: Orders[] | null"); + }); + + it("should combine forward and reverse references to the same table", () => { + const csv = [ + "id,name,manager", + "string,string,@users", + "# reports := ~users(manager)", + "1,Alice,2", + "2,Bob,", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "users.csv"), + }); + + expect(result.js).toContain("_usersLookup"); + expect(result.js).toContain("_usersBy_manager"); + expect(result.js).not.toContain("import _users from './users.csv'"); + }); + + it("should import referenced table when forward and reverse reference a different table", () => { + const csv = [ + "id,name,creator", + "string,string,@users", + "# reviews := ~users(reviewer)", + "1,Doc,1", + ].join("\n"); + + const result = csvToModule(csv, { + emitTypes: false, + currentFilePath: path.join(fixturesDir, "test.csv"), + }); + + expect(result.js).toContain("_usersLookup"); + expect(result.js).toContain("_usersBy_reviewer"); + expect(result.js).toContain("import _users from './users.csv'"); + }); +}); diff --git a/src/csv-loader/test-utils.ts b/src/csv-loader/test-utils.ts new file mode 100644 index 0000000..7aa0313 --- /dev/null +++ b/src/csv-loader/test-utils.ts @@ -0,0 +1,8 @@ +import * as path from "path"; +import * as fs from "fs"; + +export const fixturesDir = path.join(__dirname, "fixtures"); + +export function readFixture(name: string): string { + return fs.readFileSync(path.join(fixturesDir, name), "utf-8"); +}