Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement sequence counter #44

Merged
merged 3 commits into from
Nov 27, 2022
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
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,32 @@ const UserFactory = defineUserFactory({
await UserFactory.create()
```

### Use sequence for scalar fields

`seq` parameter provides sequential number which increments when called `.create()` .

```ts
const UserFactory = defineUserFactory({
defaultData: async ({ seq }) => ({
id: `user${seq.toString().padStart(3, "0")}`,
}),
});

await UserFactory.create(); // Insert with id: "user000"
await UserFactory.create(); // Insert with id: "user001"
await UserFactory.create(); // Insert with id: "user002"
```

And the sequential number can be reset via `resetSequence` .

```ts
/* your.testSetup.ts */

import { resetSequence } from "./__generated__/fabbrica";

beforeEach(() => resetSequence());
```

### Required relation

Sometimes, creating a model requires other model existence. For example, the following model `Post` belongs to other model `User`.
Expand Down
4 changes: 2 additions & 2 deletions examples/example-prj/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"clean": "echo nothing to do",
"migrate:test:ci": "DATABASE_URL=\"file:./test.db\" prisma migrate dev",
"generate": "prisma generate",
"test": "jest",
"test:ci": "DATABASE_URL=\"file:./test.db\" jest"
"test": "DATABASE_URL=\"file:./test.db\" jest --maxWorkers=1",
"test:ci": "DATABASE_URL=\"file:./test.db\" jest --maxWorkers=1"
},
"devDependencies": {
"@quramy/jest-prisma-node": "1.1.2",
Expand Down
9 changes: 6 additions & 3 deletions examples/example-prj/src/__generated__/fabbrica/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 20 additions & 11 deletions examples/example-prj/src/__generated__/fabbrica/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions examples/example-prj/src/connectOrCreate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineUserFactory, definePostFactory } from "./__generated__/fabbrica";

const prisma = jestPrisma.client;

const UserFactory = defineUserFactory();

const PostFactory = definePostFactory({
defaultData: async () => ({
author: {
connectOrCreate: {
where: {
id: "user001",
},
create: await UserFactory.buildCreateInput({
id: "user001",
}),
},
},
}),
});

describe("factories", () => {
describe("PostFactory", () => {
it("creates post record", async () => {
await PostFactory.create();
await expect(prisma.user.count()).resolves.toBe(1);
});

it("creates related user at most one", async () => {
await PostFactory.create();
await PostFactory.create();
await PostFactory.create();
await expect(prisma.user.count()).resolves.toBe(1);
});
});
});
29 changes: 0 additions & 29 deletions examples/example-prj/src/sample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,6 @@ const PostFactory = definePostFactory({
},
});

const PostFactoryAlt = definePostFactory({
defaultData: async () => ({
author: {
connectOrCreate: {
where: {
id: "user001",
},
create: await UserFactory.buildCreateInput({
id: "user001",
}),
},
},
}),
});

describe("factories", () => {
describe("UserFactory", () => {
it("creates records without input parameters", async () => {
Expand Down Expand Up @@ -58,18 +43,4 @@ describe("factories", () => {
expect(userWithPosts?.posts.length).toBe(3);
});
});

describe("PostFactoryAlt", () => {
it("creates post record", async () => {
await PostFactoryAlt.create();
await expect(prisma.user.count()).resolves.toBe(1);
});

it("creates related user at most one", async () => {
await PostFactoryAlt.create();
await PostFactoryAlt.create();
await PostFactoryAlt.create();
await expect(prisma.user.count()).resolves.toBe(1);
});
});
});
28 changes: 28 additions & 0 deletions examples/example-prj/src/sequence.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineUserFactory, resetSequence } from "./__generated__/fabbrica";

const prisma = jestPrisma.client;

const UserFactory = defineUserFactory({
defaultData: async ({ seq }) => ({
id: `user${seq.toString().padStart(3, "0")}`,
}),
});

describe("factories", () => {
describe("UserFactory", () => {
beforeEach(async () => {
resetSequence();
await UserFactory.create();
});

it("creates record with sequential id", async () => {
await expect(prisma.user.findUnique({ where: { id: "user000" } })).resolves.not.toBeNull();
});

it("expected that the sequential id is restarted", async () => {
await UserFactory.create();
await expect(prisma.user.findUnique({ where: { id: "user000" } })).resolves.not.toBeNull();
await expect(prisma.user.findUnique({ where: { id: "user001" } })).resolves.not.toBeNull();
});
});
});
1 change: 1 addition & 0 deletions packages/prisma-fabbrica/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./valueResolver";
export * from "./stringConverter";
export * from "./astShorthand";
export * from "./sequence";
15 changes: 15 additions & 0 deletions packages/prisma-fabbrica/src/helpers/sequence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
let sequenceCounterMap: WeakMap<any, number> = new Map();

export function resetSequence() {
sequenceCounterMap = new Map();
}

export function getSequenceCounter(key: any) {
const c = sequenceCounterMap.get(key);
if (c == null) {
sequenceCounterMap.set(key, 0);
return 0;
}
sequenceCounterMap.set(key, c + 1);
return c + 1;
}
29 changes: 29 additions & 0 deletions packages/prisma-fabbrica/src/helpers/valueResolver.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Resolver, normalizeResolver } from "./valueResolver";

type TestResolver = Resolver<{ value: string }, { seq: number }>;

describe(normalizeResolver, () => {
test("with plain object", async () => {
const r: TestResolver = {
value: "hoge",
};
const resolver = normalizeResolver<{ value: string }, { seq: number }>(r);
await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "hoge" });
});

test("with sync function", async () => {
const r: TestResolver = ({ seq }) => ({
value: `${seq}`,
});
const resolver = normalizeResolver<{ value: string }, { seq: number }>(r);
await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "1" });
});

test("with async function", async () => {
const r: TestResolver = async ({ seq }) => ({
value: `${await seq}`,
});
const resolver = normalizeResolver<{ value: string }, { seq: number }>(r);
await expect(resolver({ seq: 1 })).resolves.toEqual({ value: "1" });
});
});
15 changes: 11 additions & 4 deletions packages/prisma-fabbrica/src/helpers/valueResolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export type Resolver<T extends Record<string, unknown>> = T | (() => T) | (() => PromiseLike<T>);
export type Resolver<T extends Record<string, unknown>, S extends Record<string, unknown>> =
| T
| ((opt: S) => T)
| ((opt: S) => PromiseLike<T>);

export async function resolveValue<T extends Record<string, unknown>>(resolver: Resolver<T>) {
const fn = typeof resolver === "function" ? resolver : () => Promise.resolve(resolver);
return (await fn()) as T;
export function normalizeResolver<T extends Record<string, unknown>, S extends Record<string, unknown>>(
resolver: Resolver<T, S>,
): (opt: S) => Promise<T> {
return async (opt: S) => {
const fn = typeof resolver === "function" ? resolver : () => Promise.resolve(resolver);
return (await fn(opt)) as T;
};
}
4 changes: 4 additions & 0 deletions packages/prisma-fabbrica/src/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { resetClient, setClient, PrismaClientLike } from "./clientHolder";
import { resetSequence } from "./helpers";
export { resetSequence } from "./helpers";

export type InitializeOptions = {
readonly prisma: PrismaClientLike | (() => PrismaClientLike);
};

export function reset() {
resetClient();
resetSequence();
}

export function initialize(options: InitializeOptions) {
setClient(options.prisma);
resetSequence();
}
4 changes: 2 additions & 2 deletions packages/prisma-fabbrica/src/scalar/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const scalarFieldValueGenerator: ScalarFieldValueGenerator = {
}
return `${fieldName} field`;
},
Int: ({ isId, isUnique }) => {
Int: ({ isId, isUnique, seq }) => {
if (isId || isUnique) {
return Date.now();
return seq;
}
return 10;
},
Expand Down
1 change: 1 addition & 0 deletions packages/prisma-fabbrica/src/scalar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type ScalarFieldGenerateOptions = {
readonly fieldName: string;
readonly isUnique: boolean;
readonly isId: boolean;
readonly seq: number;
};

export interface ScalarFieldValueGenerator {
Expand Down
3 changes: 3 additions & 0 deletions packages/prisma-fabbrica/src/scripts/jest-prisma/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { initialize } from "../../initialize";
import { resetSequence } from "../../helpers";

beforeAll(() => {
if (typeof jestPrisma === "object") {
Expand All @@ -8,4 +9,6 @@ beforeAll(() => {
}
});

beforeEach(() => resetSequence());

declare var jestPrisma: any;
Loading