Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions packages/seed/e2e/e2e.paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { test as _test, type TestFunction, expect } from "vitest";
import { adapterEntries } from "#test/adapters.js";
import { setupProject } from "#test/setupProject.js";

for (const [dialect, adapter] of adapterEntries) {
const computeName = (name: string) => `e2e > ${dialect} > ${name}`;
const test = (name: string, fn: TestFunction) => {
// eslint-disable-next-line vitest/expect-expect, vitest/valid-title
_test.concurrent(computeName(name), fn);
};

test("basic path connection", async () => {
const { db } = await setupProject({
adapter,
databaseSchema: `
create table board (
id uuid not null primary key,
name text not null
);
create table "column" (
id uuid not null primary key,
name text not null,
board_id uuid not null references board(id)
);
create table item (
id uuid not null primary key,
name text not null,
column_id uuid not null references "column"(id),
board_id uuid not null references board(id)
);
`,
seedScript: `
import { createSeedClient } from '#seed'

const seed = await createSeedClient()

await seed.boards([{
columns: [{
items: [{}, {}]
}]
}])
`,
});

const boards = await db.query<{ id: string }>("select * from board");
const columns = await db.query('select * from "column"');
const items = await db.query("select * from item");

expect(boards).toHaveLength(1);
expect(columns).toHaveLength(1);
expect(items).toEqual([
expect.objectContaining({ board_id: boards[0].id }),
expect.objectContaining({ board_id: boards[0].id }),
]);
});

test("multiple path connections", async () => {
const { db } = await setupProject({
adapter,
databaseSchema: `
create table board (
id uuid not null primary key,
name text not null
);
create table "column" (
id uuid not null primary key,
name text not null,
board_id uuid not null references board(id)
);
create table item (
id uuid not null primary key,
name text not null,
column_id uuid not null references "column"(id),
board_id uuid not null references board(id)
);
`,
seedScript: `
import { createSeedClient } from '#seed'

const seed = await createSeedClient()

await seed.boards((x) => x(2, {
columns: (x) => x(2, {
items: (x) => x(2)
})
}))
`,
});

const boards = await db.query<{ id: string }>("select * from board");
const columns = await db.query('select * from "column"');
const items = await db.query("select * from item");

expect(boards).toHaveLength(2);
expect(columns).toHaveLength(4);
expect(items).toEqual([
expect.objectContaining({ board_id: boards[0].id }),
expect.objectContaining({ board_id: boards[0].id }),
expect.objectContaining({ board_id: boards[0].id }),
expect.objectContaining({ board_id: boards[0].id }),
expect.objectContaining({ board_id: boards[1].id }),
expect.objectContaining({ board_id: boards[1].id }),
expect.objectContaining({ board_id: boards[1].id }),
expect.objectContaining({ board_id: boards[1].id }),
]);
});

test("connect option overrides path connection", async () => {
const { db } = await setupProject({
adapter,
databaseSchema: `
create table board (
id uuid not null primary key,
name text not null
);
create table "column" (
id uuid not null primary key,
name text not null,
board_id uuid not null references board(id)
);
create table item (
id uuid not null primary key,
name text not null,
column_id uuid not null references "column"(id),
board_id uuid not null references board(id)
);
`,
seedScript: `
import { createSeedClient } from '#seed'

const seed = await createSeedClient()

const store = await seed.boards([{ name: 'connected board' }])

await seed.boards([{
columns: [{
items: [{}, {}]
}]
}], { connect: store })
`,
});

const boards = await db.query<{ id: string; name: string }>(
"select * from board",
);
const columns = await db.query('select * from "column"');
const items = await db.query("select * from item");

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const connectedBoard = boards.find((b) => b.name === "connected board")!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const pathBoard = boards.find((b) => b.name !== "connected board")!;

expect(boards).toHaveLength(2);
expect(columns).toEqual([
expect.objectContaining({
board_id: pathBoard.id,
}),
]);
expect(items).toEqual([
expect.objectContaining({ board_id: connectedBoard.id }),
expect.objectContaining({ board_id: connectedBoard.id }),
]);
});
}
18 changes: 17 additions & 1 deletion packages/seed/src/core/plan/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@ export class Plan implements IPlan {
inputs,
}: PlanInputs & {
ctx?: {
connectPath?: Array<{ model: string; rowId: number }>;
index?: number;
path?: Array<number | string>;
};
},
options: Required<GenerateOptions>,
) {
const connectPath = ctx?.connectPath ?? [];
const path = ctx?.path ?? [model];
const userModels = options.models;
const modelStructure = this.dataModel.models[model];
Expand Down Expand Up @@ -195,12 +197,24 @@ export class Plan implements IPlan {
if (parentField === undefined) {
const connectFallback = userModels[parentModelName].connect;
if (connectFallback) {
return connectFallback({
const parent = connectFallback({
$store: this.ctx.store._store,
store: this.store._store,
index,
seed: `${modelSeed}/${field.name}`,
});
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (parent !== undefined) {
return parent;
}
}

// if the connect store is empty for this model, we attempt the path connection strategy
const candidate = connectPath.findLast(
(p) => p.model === parentModelName,
);
if (candidate) {
return this.store._store[parentModelName][candidate.rowId];
}
}

Expand Down Expand Up @@ -340,6 +354,7 @@ export class Plan implements IPlan {
}),
});

const rowId = this.store._store[model].length - 1;
for (const field of fields.children) {
const childModelName = field.type;
const childField = inputsData[field.name] as ChildField | undefined;
Expand Down Expand Up @@ -409,6 +424,7 @@ export class Plan implements IPlan {
{
ctx: {
path: [...path, index, field.name],
connectPath: [...connectPath, { model, rowId }],
},
model: childModelName,
inputs: childInputs,
Expand Down
1 change: 0 additions & 1 deletion packages/seed/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export default defineProject({
name: pkg.name,
root,
testTimeout: 120_000,
maxConcurrency: 7,
},
esbuild: {
target: "es2022",
Expand Down