Repository: Quramy/prisma-fabbrica
Pull request #1 (2022-11-17 13:32:27)

feat: Generate scalar fields


Patch:
diff --git a/package-lock.json b/package-lock.json
index c1d7a2d..752b596 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1856,6 +1856,11 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
+    "node_modules/any-base": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
+      "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
+    },
     "node_modules/anymatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@@ -5369,6 +5374,26 @@
         "node": ">=8"
       }
     },
+    "node_modules/short-uuid": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz",
+      "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==",
+      "dependencies": {
+        "any-base": "^1.1.0",
+        "uuid": "^8.3.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/short-uuid/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/signal-exit": {
       "version": "3.0.7",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6106,6 +6131,7 @@
       "dependencies": {
         "@prisma/generator-helper": "^4.2.0",
         "@prisma/internals": "^4.2.0",
+        "short-uuid": "4.2.2",
         "talt": "2.3.1"
       },
       "bin": {
@@ -7309,6 +7335,7 @@
         "@prisma/internals": "^4.2.0",
         "@types/jest": "29.2.1",
         "jest": "29.2.2",
+        "short-uuid": "4.2.2",
         "talt": "2.3.1",
         "ts-jest": "29.0.3",
         "typescript": "*"
@@ -7539,6 +7566,11 @@
         "color-convert": "^2.0.1"
       }
     },
+    "any-base": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
+      "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
+    },
     "anymatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@@ -10085,6 +10117,22 @@
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
     },
+    "short-uuid": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz",
+      "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==",
+      "requires": {
+        "any-base": "^1.1.0",
+        "uuid": "^8.3.2"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "8.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+        }
+      }
+    },
     "signal-exit": {
       "version": "3.0.7",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
diff --git a/packages/prisma-fabbrica/package.json b/packages/prisma-fabbrica/package.json
index cad7dd2..04cd767 100644
--- a/packages/prisma-fabbrica/package.json
+++ b/packages/prisma-fabbrica/package.json
@@ -4,7 +4,10 @@
   "description": "",
   "main": "./lib/index.js",
   "types": "./lib/index.d.ts",
-  "files": ["lib", "bin.js"],
+  "files": [
+    "lib",
+    "bin.js"
+  ],
   "bin": {
     "prisma-fabbrica": "./bin.js"
   },
@@ -16,6 +19,7 @@
   "dependencies": {
     "@prisma/generator-helper": "^4.2.0",
     "@prisma/internals": "^4.2.0",
+    "short-uuid": "4.2.2",
     "talt": "2.3.1"
   },
   "peerDependencies": {
@@ -28,7 +32,11 @@
     "ts-jest": "29.0.3",
     "typescript": "*"
   },
-  "keywords": ["prisma", "generator", "factory"],
+  "keywords": [
+    "prisma",
+    "generator",
+    "factory"
+  ],
   "author": "Quramy",
   "license": "MIT"
 }
diff --git a/packages/prisma-fabbrica/src/scalar/gen.ts b/packages/prisma-fabbrica/src/scalar/gen.ts
new file mode 100644
index 0000000..d225d26
--- /dev/null
+++ b/packages/prisma-fabbrica/src/scalar/gen.ts
@@ -0,0 +1,30 @@
+import short from "short-uuid";
+import { ScalarFieldValueGenerator } from "./types";
+
+const scalarFieldValueGenerator: ScalarFieldValueGenerator = {
+  Boolean: () => true,
+  String: ({ fieldName, isId, isUnique }) => {
+    if (isId || isUnique) {
+      return short.generate();
+    }
+    return `${fieldName} field`;
+  },
+  Int: ({ isId, isUnique }) => {
+    if (isId || isUnique) {
+      return Date.now();
+    }
+    return 10;
+  },
+  Float: () => 3.14,
+  BigInt: () => BigInt(12_345_678),
+  Decimal: () => {
+    throw new Error("Not support Decimal field generation");
+  },
+  DateTime: () => new Date(),
+  Bytes: () => Buffer.from([0x80, 0xff]),
+  JSON: () => ({
+    key: "value",
+  }),
+};
+
+export default scalarFieldValueGenerator;
diff --git a/packages/prisma-fabbrica/src/scalar/index.ts b/packages/prisma-fabbrica/src/scalar/index.ts
new file mode 100644
index 0000000..eea524d
--- /dev/null
+++ b/packages/prisma-fabbrica/src/scalar/index.ts
@@ -0,0 +1,1 @@
+export * from "./types";
diff --git a/packages/prisma-fabbrica/src/scalar/types.ts b/packages/prisma-fabbrica/src/scalar/types.ts
new file mode 100644
index 0000000..9305232
--- /dev/null
+++ b/packages/prisma-fabbrica/src/scalar/types.ts
@@ -0,0 +1,18 @@
+export type ScalarFieldGenerateOptions = {
+  readonly modelName: string;
+  readonly fieldName: string;
+  readonly isUnique: boolean;
+  readonly isId: boolean;
+};
+
+export interface ScalarFieldValueGenerator {
+  Boolean: (options: ScalarFieldGenerateOptions) => boolean;
+  String: (options: ScalarFieldGenerateOptions) => string;
+  Int: (options: ScalarFieldGenerateOptions) => number;
+  Float: (options: ScalarFieldGenerateOptions) => number;
+  BigInt: (options: ScalarFieldGenerateOptions) => bigint;
+  Decimal: (options: ScalarFieldGenerateOptions) => unknown;
+  DateTime: (options: ScalarFieldGenerateOptions) => Date;
+  Bytes: (options: ScalarFieldGenerateOptions) => Buffer;
+  JSON: (options: ScalarFieldGenerateOptions) => any;
+}
diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts
index 06d1613..90f3cff 100644
--- a/packages/prisma-fabbrica/src/templates/index.ts
+++ b/packages/prisma-fabbrica/src/templates/index.ts
@@ -2,9 +2,39 @@ import { DMMF } from "@prisma/generator-helper";
 import ts from "typescript";
 import { template } from "talt";
 
+function camelize(pascal: string) {
+  return pascal[0].toLowerCase() + pascal.slice(1);
+}
+
+export function findPrsimaCreateInputTypeFromModelName(document: DMMF.Document, modelName: string) {
+  const inputType = document.schema.inputObjectTypes.prisma.find(x => x.name === `${modelName}CreateInput`);
+  if (!inputType) throw new Error("");
+  return inputType;
+}
+
+export function filterScalarFields(inputType: DMMF.InputType) {
+  return inputType.fields.filter(
+    field =>
+      field.inputTypes.length > 0 && field.inputTypes.every(childInputType => childInputType.location === "scalar"),
+  );
+}
+
+export function filterNonNullScalarFields(inputType: DMMF.InputType) {
+  return filterScalarFields(inputType).filter(field => !field.isNullable);
+}
+
+export function filterObjectTypeFields(inputType: DMMF.InputType) {
+  return inputType.fields.filter(
+    field =>
+      field.inputTypes.length > 0 &&
+      field.inputTypes.every(childInputType => childInputType.location === "inputObjectTypes"),
+  );
+}
+
 export const header = template.sourceFile`
   import { Prisma } from "@prisma/client";
   import { getClient } from "@quramy/prisma-fabbrica";
+  import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen";
   
   type Resolver<T extends Record<string, unknown>> =
     | T
@@ -22,21 +52,127 @@ export const header = template.sourceFile`
   const defineFnMap = new Map<unknown, (options: any) => unknown>();
 `;
 
-function camerize(pascal: string) {
-  return pascal[0].toLowerCase() + pascal.slice(1);
-}
+export const scalarFieldType = (
+  modelName: string,
+  fieldName: string,
+  inputType: DMMF.SchemaArgInputType,
+): ts.TypeNode => {
+  if (inputType.location !== "scalar") {
+    throw new Error("Invalid call. This function is allowed for only scalar field.");
+  }
+  switch (inputType.type) {
+    case "Boolean":
+      return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
+    case "String":
+      return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
+    case "Int":
+    case "Float":
+      return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
+    case "BigInt":
+      return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword);
+    case "Decimal":
+      return template.typeNode`Prisma.Decimal`();
+    case "DateTime":
+      return template.typeNode`Date`();
+    case "Bytes":
+      return template.typeNode`Buffer`();
+    case "JSON":
+      // FIXME Is the folloing type right?
+      // return template.typeNode`Prisma.Json`();
+      return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
+    default:
+      throw new Error(`Unknown scalar type "${inputType.type}" for ${modelName}.${fieldName} .`);
+  }
+};
 
-const defineModelFactoryOptions = (modelName: string) =>
+export const modelScalarFields = (modelName: string, inputType: DMMF.InputType) =>
+  template.statement<ts.TypeAliasDeclaration>`
+    type MODEL_SCALAR_FIELDS = TYPE_RHS;
+  `({
+    MODEL_SCALAR_FIELDS: ts.factory.createIdentifier(`${modelName}ScalarFields`),
+    TYPE_RHS: ts.factory.createTypeLiteralNode(
+      filterNonNullScalarFields(inputType).map(field =>
+        ts.factory.createPropertySignature(
+          undefined,
+          field.name,
+          undefined,
+          scalarFieldType(modelName, field.name, field.inputTypes[0]),
+        ),
+      ),
+    ),
+  });
+
+export const modelFactoryDefineInput = (modelName: string, inputTpue: DMMF.InputType) =>
+  template.statement<ts.TypeAliasDeclaration>`
+    type MODEL_FACTORY_DEFINE_INPUT = TYPE_RHS;
+  `({
+    MODEL_FACTORY_DEFINE_INPUT: ts.factory.createIdentifier(`${modelName}FactoryDefineInput`),
+    TYPE_RHS: ts.factory.createTypeLiteralNode([
+      ...filterScalarFields(inputTpue).map(field =>
+        ts.factory.createPropertySignature(
+          undefined,
+          field.name,
+          ts.factory.createToken(ts.SyntaxKind.QuestionToken),
+          scalarFieldType(modelName, field.name, field.inputTypes[0]),
+        ),
+      ),
+      ...filterObjectTypeFields(inputTpue).map(field =>
+        ts.factory.createPropertySignature(
+          undefined,
+          field.name,
+          !field.isRequired ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
+          ts.factory.createTypeReferenceNode(
+            template.expression<ts.Identifier>`Prisma.TYPE_ID`({
+              TYPE_ID: ts.factory.createIdentifier(field.inputTypes[0].type as string),
+            }),
+          ),
+        ),
+      ),
+    ]),
+  });
+
+export const defineModelFactoryOptions = (modelName: string) =>
   template.statement<ts.TypeAliasDeclaration>`
     type DEFINE_MODEL_FACTORY_OPTIONS = {
-      defaultAttrs: Resolver<Prisma.MODEL_CREATE_INPUT>;
+      defaultAttrs: Resolver<MODEL_FACTORY_DEFINE_INPUT>;
     };
   `({
     DEFINE_MODEL_FACTORY_OPTIONS: ts.factory.createIdentifier(`Define${modelName}FactoryOptions`),
-    MODEL_CREATE_INPUT: ts.factory.createIdentifier(`${modelName}CreateInput`),
+    MODEL_FACTORY_DEFINE_INPUT: ts.factory.createIdentifier(`${modelName}FactoryDefineInput`),
+  });
+
+export const autoGenrateModelScalars = (modelName: string, inputType: DMMF.InputType, model: DMMF.Model) =>
+  template.statement<ts.FunctionDeclaration>`
+    function AUTO_GENRATE_MODEL_SCALARS(): MODEL_SCALAR_FIELDS {
+      return OBJECT_VALUE;
+    }
+  `({
+    AUTO_GENRATE_MODEL_SCALARS: ts.factory.createIdentifier(`autoGenrate${modelName}Scalars`),
+    MODEL_SCALAR_FIELDS: ts.factory.createIdentifier(`${modelName}ScalarFields`),
+    OBJECT_VALUE: ts.factory.createObjectLiteralExpression(
+      filterNonNullScalarFields(inputType).map(field =>
+        ts.factory.createPropertyAssignment(
+          ts.factory.createIdentifier(field.name),
+          template.expression`scalarFieldValueGenerator.SCALAR_TYPE({ modelName: MODEL_NAME, fieldName: FIELD_NAME, isId: IS_ID, isUnique: IS_UNIQUE })`(
+            {
+              SCALAR_TYPE: ts.factory.createIdentifier(field.inputTypes[0].type as string),
+              MODEL_NAME: ts.factory.createStringLiteral(modelName),
+              FIELD_NAME: ts.factory.createStringLiteral(field.name),
+              IS_ID: model.fields.find(f => f.name === field.name)!.isId
+                ? ts.factory.createTrue()
+                : ts.factory.createFalse(),
+              IS_UNIQUE: model.fields.find(f => f.name === field.name)!.isUnique
+                ? ts.factory.createTrue()
+                : ts.factory.createFalse(),
+            },
+          ),
+        ),
+      ),
+      true,
+    ),
   });
 
-const defineModelFactory = (modelName: string) =>
+export const defineModelFactory = (modelName: string) =>
   template.statement<ts.FunctionDeclaration>`
     function DEFINE_MODEL_FACTORY({
       defaultAttrs: defaultAttributesResolver
@@ -44,20 +180,22 @@ const defineModelFactory = (modelName: string) =>
       const create = async (
         inputAttributes: Partial<Prisma.MODEL_CREATE_INPUT> = {}
       ) => {
+        const scalarsAttributes = AUTO_GENRATE_MODEL_SCALARS()
         const defaultAttributes = await resolveValue(defaultAttributesResolver);
-        const data = { ...defaultAttributes, ...inputAttributes };
+        const data = { ...scalarsAttributes, ...defaultAttributes, ...inputAttributes };
         return await getClient().MODEL_KEY.create({ data });
       };
       return { create };
     }
   `({
-    MODEL_KEY: ts.factory.createIdentifier(camerize(modelName)),
+    MODEL_KEY: ts.factory.createIdentifier(camelize(modelName)),
     DEFINE_MODEL_FACTORY: ts.factory.createIdentifier(`define${modelName}Factory`),
     DEFINE_MODEL_FACTORY_OPTIONS: ts.factory.createIdentifier(`Define${modelName}FactoryOptions`),
     MODEL_CREATE_INPUT: ts.factory.createIdentifier(`${modelName}CreateInput`),
+    AUTO_GENRATE_MODEL_SCALARS: ts.factory.createIdentifier(`autoGenrate${modelName}Scalars`),
   });
 
-const defineFnMapSet = (modelName: string) =>
+export const defineFnMapSet = (modelName: string) =>
   template.statement<ts.ExpressionStatement>`
     defineFnMap.set(MODEL_NAME, DEFINE_MODEL_FACTORY);
   `({
@@ -65,7 +203,7 @@ const defineFnMapSet = (modelName: string) =>
     DEFINE_MODEL_FACTORY: ts.factory.createIdentifier(`define${modelName}Factory`),
   });
 
-const defineFactoryOverload = (modelName: string) =>
+export const defineFactoryOverload = (modelName: string) =>
   template.statement<ts.FunctionDeclaration>`
     export function defineFactory(
       name: MODEL_NAME,
@@ -77,7 +215,7 @@ const defineFactoryOverload = (modelName: string) =>
     DEFINE_MODEL_FACTORY_OPTIONS: ts.factory.createIdentifier(`Define${modelName}FactoryOptions`),
   });
 
-const defineFactoryImpl = template.statement`
+export const defineFactoryImpl = template.statement`
   export function defineFactory(name: unknown, options: unknown): unknown {
     const defineFn = defineFnMap.get(name);
     if (!defineFn) throw new Error("Invalid model name");
@@ -89,7 +227,10 @@ export function getSourceFile(document: DMMF.Document) {
   const statements = [
     ...header().statements,
     ...document.datamodel.models.flatMap(model => [
+      modelScalarFields(model.name, findPrsimaCreateInputTypeFromModelName(document, model.name)),
+      modelFactoryDefineInput(model.name, findPrsimaCreateInputTypeFromModelName(document, model.name)),
       defineModelFactoryOptions(model.name),
+      autoGenrateModelScalars(model.name, findPrsimaCreateInputTypeFromModelName(document, model.name), model),
       defineModelFactory(model.name),
     ]),
     ...document.datamodel.models.map(model => defineFnMapSet(model.name)),


Test patch:
diff --git a/examples/example-prj/src/sample.test.ts b/examples/example-prj/src/sample.test.ts
index e2e38c0..a31fba1 100644
--- a/examples/example-prj/src/sample.test.ts
+++ b/examples/example-prj/src/sample.test.ts
@@ -1,21 +1,42 @@
-import { v4 as uuid } from "uuid";
 import { defineFactory } from "./__generated__/factories";
 
 const UserFactory = defineFactory("User", {
-  defaultAttrs: () => ({
-    id: uuid(),
-    name: "",
+  defaultAttrs: {},
+});
+
+const PostFactory = defineFactory("Post", {
+  defaultAttrs: async () => ({
+    author: {
+      connect: {
+        id: (await UserFactory.create()).id,
+      },
+    },
   }),
 });
 
 const prisma = jestPrisma.client;
 
 describe("factories", () => {
-  beforeEach(async () => {
-    await UserFactory.create({ name: "quramy" });
-    await UserFactory.create({ name: "kurami" });
+  describe("UserFactory", () => {
+    it("creates records without input parameters", async () => {
+      await UserFactory.create();
+      await UserFactory.create();
+      await expect(prisma.user.count()).resolves.toBe(2);
+    });
+
+    it("creates record with input parameters", async () => {
+      await UserFactory.create({ id: "user001", name: "Quramy" });
+      const user = await prisma.user.findUnique({ where: { id: "user001" } });
+      expect(user).toEqual({ id: "user001", name: "Quramy" });
+    });
   });
-  test("created", async () => {
-    await expect(prisma.user.count()).resolves.toBe(2);
+
+  describe("PostFactory", () => {
+    it("creates required association", async () => {
+      await PostFactory.create();
+      const created = await prisma.post.findFirst({ include: { author: true } });
+      expect(created?.id).not.toBeFalsy();
+      expect(created?.author.id).not.toBeFalsy();
+    });
   });
 });
diff --git a/packages/prisma-fabbrica/src/__snapshots__/templates.test.ts.snap b/packages/prisma-fabbrica/src/__snapshots__/templates.test.ts.snap
index 14dd6b8..77c32de 100644
--- a/packages/prisma-fabbrica/src/__snapshots__/templates.test.ts.snap
+++ b/packages/prisma-fabbrica/src/__snapshots__/templates.test.ts.snap
@@ -3,19 +3,35 @@
 exports[`getSourceFile generates TypeScript AST 1`] = `
 "import { Prisma } from "@prisma/client";
 import { getClient } from "@quramy/prisma-fabbrica";
+import scalarFieldValueGenerator from "@quramy/prisma-fabbrica/lib/scalar/gen";
 type Resolver<T extends Record<string, unknown>> = T | (() => T) | (() => PromiseLike<T>);
 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;
 }
 const defineFnMap = new Map<unknown, (options: any) => unknown>();
+type UserScalarFields = {
+    id: number;
+    name: string;
+};
+type UserFactoryDefineInput = {
+    id?: number;
+    name?: string;
+};
 type DefineUserFactoryOptions = {
-    defaultAttrs: Resolver<Prisma.UserCreateInput>;
+    defaultAttrs: Resolver<UserFactoryDefineInput>;
 };
+function autoGenrateUserScalars(): UserScalarFields {
+    return {
+        id: scalarFieldValueGenerator.Int({ modelName: "User", fieldName: "id", isId: true, isUnique: false }),
+        name: scalarFieldValueGenerator.String({ modelName: "User", fieldName: "name", isId: false, isUnique: false })
+    };
+}
 function defineUserFactory({ defaultAttrs: defaultAttributesResolver }: DefineUserFactoryOptions) {
     const create = async (inputAttributes: Partial<Prisma.UserCreateInput> = {}) => {
+        const scalarsAttributes = autoGenrateUserScalars();
         const defaultAttributes = await resolveValue(defaultAttributesResolver);
-        const data = { ...defaultAttributes, ...inputAttributes };
+        const data = { ...scalarsAttributes, ...defaultAttributes, ...inputAttributes };
         return await getClient().user.create({ data });
     };
     return { create };
diff --git a/packages/prisma-fabbrica/src/templates.test.ts b/packages/prisma-fabbrica/src/templates.test.ts
index c8c89bc..607700c 100644
--- a/packages/prisma-fabbrica/src/templates.test.ts
+++ b/packages/prisma-fabbrica/src/templates.test.ts
@@ -1,21 +1,62 @@
 import { getDMMF } from "@prisma/internals";
-import { printNode } from "talt";
+import { template, printNode } from "talt";
 
-import { getSourceFile } from "./templates";
+import { modelScalarFields, getSourceFile, findPrsimaCreateInputTypeFromModelName } from "./templates";
 
-describe(getSourceFile, () => {
-  it("generates TypeScript AST", async () => {
+describe(modelScalarFields, () => {
+  it("generates literal type field type pattern", async () => {
     const dmmf = await getDMMF({
       datamodel: `
-      datasource db {
-        provider = "sqlite"
-        url      = "file:dev.db"
+        model TestModel {
+          id Int @id
+          boolField Boolean
+          strField String
+          floatField Float
+          dateTimeField DateTime
+        }
+      `,
+    });
+    const inputType = findPrsimaCreateInputTypeFromModelName(dmmf, "TestModel");
+    const expected = template.sourceFile`
+      type TestModelScalarFields = {
+        id: number;
+        boolField: boolean;
+        strField: string;
+        floatField: number;
+        dateTimeField: Date;
       }
-      model User {
-        id Int @id
-        name String
+    `();
+    expect(printNode(modelScalarFields("TestModel", inputType))).toBe(printNode(expected).trim());
+  });
+
+  it("does not generate for nullable field", async () => {
+    const dmmf = await getDMMF({
+      datamodel: `
+        model TestModel {
+          id Int @id
+          nullableField Int?
+        }
+      `,
+    });
+    const inputType = findPrsimaCreateInputTypeFromModelName(dmmf, "TestModel");
+    const expected = template.sourceFile`
+      type TestModelScalarFields = {
+        id: number;
       }
-    `,
+    `();
+    expect(printNode(modelScalarFields("TestModel", inputType))).toBe(printNode(expected).trim());
+  });
+});
+
+describe(getSourceFile, () => {
+  it("generates TypeScript AST", async () => {
+    const dmmf = await getDMMF({
+      datamodel: `
+        model User {
+          id Int @id
+          name String
+        }
+      `,
     });
     const sourceFile = getSourceFile(dmmf);
     expect(printNode(sourceFile)).toMatchSnapshot();