prisma-mock 1.1.0-alpha.1 → 1.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,9 +14,30 @@ yarn add prisma-mock --dev
14
14
 
15
15
  ## Usage
16
16
 
17
- ### Basic Example
17
+ ### Prisma 7
18
18
 
19
- Simple example of how to create a prisma mock instance:
19
+ With Prisma 7, you typically generate your client to a custom output directory (such as `./generated/client`), and import the generated `Prisma` namespace from your generated files. Use the `prisma-mock/client` entry point and pass the `Prisma` namespace when
20
+ initializing your mock client.
21
+
22
+ > **Note:** You will also need to configure and use the [DMMF Generator](#dmmf-generator) to produce the necessary metadata for mocking.
23
+
24
+ ```js
25
+ import createPrismaMock from "prisma-mock/client"
26
+ import { Prisma } from "./generated/client"
27
+ import * as dmmf from "./generated/dmmf"
28
+
29
+ let client
30
+
31
+ beforeEach(() => {
32
+ client = createPrismaMock(Prisma, {
33
+ datamodel: dmmf,
34
+ })
35
+ })
36
+ ```
37
+
38
+ ### Prisma 6 and below
39
+
40
+ Simple example of how to create a prisma mock instance using the default (node_module) export:
20
41
 
21
42
  ```js
22
43
  import createPrismaMock from "prisma-mock"
@@ -97,7 +118,11 @@ createPrismaMock<P extends PrismaClient = PrismaClient>(
97
118
  caseInsensitive?: boolean
98
119
  enableIndexes?: boolean
99
120
  }
100
- ): P & { $getInternalState: () => Required<PrismaMockData<P>> }
121
+ ): P & {
122
+ $getInternalState: () => Required<PrismaMockData<P>>
123
+ $setInternalState: (state: Required<PrismaMockData<P>>) => void
124
+ $clear: () => void
125
+ }
101
126
  ```
102
127
 
103
128
  ### Parameters
@@ -117,6 +142,8 @@ createPrismaMock<P extends PrismaClient = PrismaClient>(
117
142
  Returns a mock Prisma client with all standard model methods plus:
118
143
 
119
144
  - `$getInternalState()`: Method to access the internal data state for testing/debugging
145
+ - `$setInternalState(state)`: Method to replace the entire internal data state. Useful for resetting state to a specific snapshot or setting up complex test scenarios. The state is deep copied to avoid reference issues.
146
+ - `$clear()`: Method to reset the internal state back to the initial state
120
147
 
121
148
  ### Client Export (`prisma-mock/client`)
122
149
 
@@ -130,7 +157,11 @@ createPrismaMock<PClient extends PrismaClient, P extends typeof Prisma = typeof
130
157
  caseInsensitive?: boolean
131
158
  enableIndexes?: boolean
132
159
  }
133
- ): PClient & { $getInternalState: () => Required<PrismaMockData<PClient>> }
160
+ ): PClient & {
161
+ $getInternalState: () => Required<PrismaMockData<PClient>>
162
+ $setInternalState: (state: Required<PrismaMockData<PClient>>) => void
163
+ $clear: () => void
164
+ }
134
165
  ```
135
166
 
136
167
  #### Parameters
@@ -284,6 +315,7 @@ const client = createPrismaMock(Prisma, {
284
315
  ### Attribute Functions ✅
285
316
 
286
317
  - `autoincrement()`
318
+ - `auto()` (MongoDB ObjectId)
287
319
  - `cuid()`
288
320
  - `uuid()`
289
321
  - `now()`
@@ -331,10 +363,6 @@ The following features are planned but not yet implemented:
331
363
  - `isEmpty`
332
364
  - `equals`
333
365
 
334
- ### Attributes
335
-
336
- - `auto()`
337
-
338
366
  ### Referential Actions
339
367
 
340
368
  - `onDelete: Restrict`
package/lib/client.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { Prisma, PrismaClient } from "@prisma/client";
2
2
  import { MockPrismaOptions, PrismaMockData } from "./types";
3
3
  declare function createPrismaMock<PClient extends PrismaClient, P extends typeof Prisma = typeof Prisma>(prisma: P, options?: MockPrismaOptions<PClient, P>): PClient & {
4
4
  $getInternalState: () => Required<PrismaMockData<PClient>>;
5
+ $setInternalState: (state: Required<PrismaMockData<PClient>>) => void;
5
6
  $clear: () => void;
6
7
  };
7
8
  export default createPrismaMock;
package/lib/client.js CHANGED
@@ -22,9 +22,10 @@ function createPrismaMock(prisma, options = {
22
22
  enableIndexes: true,
23
23
  data: {}
24
24
  }) {
25
+ let internalState = options.data ? (0, deepCopy_1.deepCopy)(options.data) : {};
25
26
  // Reference object to hold the mock data state
26
27
  let ref = {
27
- data: options.data ? (0, deepCopy_1.deepCopy)(options.data) : {},
28
+ data: internalState,
28
29
  };
29
30
  // Initialize the mock client (either use provided one or create new)
30
31
  let client = options.mockClient ? options.mockClient : {};
@@ -126,8 +127,12 @@ function createPrismaMock(prisma, options = {
126
127
  });
127
128
  // Add method to access internal state for testing/debugging
128
129
  client['$getInternalState'] = () => ref.data;
130
+ client['$setInternalState'] = (state) => {
131
+ internalState = (0, deepCopy_1.deepCopy)(state);
132
+ ref.data = internalState;
133
+ };
129
134
  client['$clear'] = () => {
130
- ref.data = options.data ? (0, deepCopy_1.deepCopy)(options.data) : {};
135
+ ref.data = internalState;
131
136
  };
132
137
  // @ts-ignore
133
138
  return client;
@@ -6,11 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const autoincrement_1 = __importDefault(require("./autoincrement"));
7
7
  const cuid_1 = __importDefault(require("./cuid"));
8
8
  const now_1 = __importDefault(require("./now"));
9
+ const objectId_1 = __importDefault(require("./objectId"));
9
10
  const uuid_1 = __importDefault(require("./uuid"));
10
11
  function createHandleDefault() {
11
12
  // const registry = new Map<string, (string, Prisma.DMMF.Field, PrismaMockData) => any>();
12
13
  const registry = new Map();
13
14
  registry.set("autoincrement", (0, autoincrement_1.default)());
15
+ registry.set("auto", (0, objectId_1.default)());
14
16
  registry.set("cuid", (0, cuid_1.default)());
15
17
  registry.set("uuid", (0, uuid_1.default)());
16
18
  registry.set("now", now_1.default);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Generates MongoDB ObjectId-like strings (24 hex chars).
3
+ * Used for @default(auto()) with @db.ObjectId
4
+ */
5
+ declare const createObjectId: () => () => string;
6
+ export default createObjectId;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Generates MongoDB ObjectId-like strings (24 hex chars).
5
+ * Used for @default(auto()) with @db.ObjectId
6
+ */
7
+ const createObjectId = () => {
8
+ let counter = 0;
9
+ return () => {
10
+ const hex = "0123456789abcdef";
11
+ let result = "";
12
+ for (let i = 0; i < 22; i++) {
13
+ result += hex[Math.floor(Math.random() * 16)];
14
+ }
15
+ result += (++counter).toString(16).padStart(2, "0");
16
+ return result;
17
+ };
18
+ };
19
+ exports.default = createObjectId;
package/lib/delegate.js CHANGED
@@ -133,7 +133,7 @@ const createDelegate = ({ ref, prisma, datamodel = prisma.dmmf.datamodel, caseIn
133
133
  if (isCreating && (field.isUnique || field.isId)) {
134
134
  const existing = findOne({ where: { [field.name]: inputFieldData } });
135
135
  if (existing) {
136
- (0, errors_1.throwKnownError)(prisma, `Unique constraint failed on the fields: (\`${field.name}\`)`, { code: 'P2002', meta: { target: [field.name] } });
136
+ (0, errors_1.throwKnownError)(prisma, `Unique constraint failed on the fields: (\`${field.name}\`)`, { code: 'P2002', meta: { modelName: model.name, target: [field.name] } });
137
137
  }
138
138
  }
139
139
  // Handle relation fields (object type)
@@ -509,7 +509,9 @@ const createDelegate = ({ ref, prisma, datamodel = prisma.dmmf.datamodel, caseIn
509
509
  const findOrThrow = (args) => {
510
510
  const found = findOne(args);
511
511
  if (!found) {
512
- (0, errors_1.throwKnownError)(prisma, `No ${prop.slice(0, 1).toUpperCase()}${prop.slice(1)} found`);
512
+ (0, errors_1.throwKnownError)(prisma, `No ${prop.slice(0, 1).toUpperCase()}${prop.slice(1)} found`, {
513
+ meta: { cause: "No record was found for a query.", modelName: model.name },
514
+ });
513
515
  }
514
516
  return found;
515
517
  };
@@ -824,7 +826,7 @@ const createDelegate = ({ ref, prisma, datamodel = prisma.dmmf.datamodel, caseIn
824
826
  if (!hasMatch) {
825
827
  if (args.skipForeignKeysChecks)
826
828
  return;
827
- (0, errors_1.throwKnownError)(prisma, "An operation failed because it depends on one or more records that were required but not found. Record to update not found.", { meta: { cause: "Record to update not found." } });
829
+ (0, errors_1.throwKnownError)(prisma, "An operation failed because it depends on one or more records that were required but not found. Record to update not found.", { meta: { cause: "No record was found for an update.", modelName: model.name } });
828
830
  }
829
831
  ref.data = {
830
832
  ...ref.data,
@@ -1063,7 +1065,7 @@ const createDelegate = ({ ref, prisma, datamodel = prisma.dmmf.datamodel, caseIn
1063
1065
  delete: (args) => {
1064
1066
  const item = findOne(args);
1065
1067
  if (!item) {
1066
- (0, errors_1.throwKnownError)(prisma, "An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.", { meta: { cause: "Record to delete does not exist." } });
1068
+ (0, errors_1.throwKnownError)(prisma, "An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.", { meta: { cause: "No record was found for a delete.", modelName: model.name } });
1067
1069
  }
1068
1070
  const deleted = deleteMany(args);
1069
1071
  if (deleted.length) {
package/lib/index.d.ts CHANGED
@@ -5,6 +5,7 @@ type MockPrismaOptions<P extends PrismaClient = PrismaClient> = Omit<MockPrismaO
5
5
  };
6
6
  export default function createPrismaClient<P extends PrismaClient = PrismaClient>(options?: MockPrismaOptions<P>): P & {
7
7
  $getInternalState: () => Required<Partial<{ [key in import("./types").IsTable<Uncapitalize<import("./types").IsString<keyof P>>>]: import("./types").PrismaList<P, key>; }>>;
8
+ $setInternalState: (state: Required<Partial<{ [key in import("./types").IsTable<Uncapitalize<import("./types").IsString<keyof P>>>]: import("./types").PrismaList<P, key>; }>>) => void;
8
9
  $clear: () => void;
9
10
  };
10
11
  export {};
package/lib/legacy.d.ts CHANGED
@@ -73,5 +73,74 @@ export default function createPrismaClient(data?: PrismaMockData<PrismaClient>,
73
73
  id: string;
74
74
  }>[];
75
75
  }>>;
76
+ $setInternalState: (state: Required<Partial<{
77
+ account: Partial<{
78
+ name: string;
79
+ id: number;
80
+ sort: number;
81
+ }>[];
82
+ user: Partial<{
83
+ name: string;
84
+ id: number;
85
+ sort: number;
86
+ accountId: number;
87
+ role: import(".prisma/client").$Enums.Role;
88
+ clicks: number;
89
+ deleted: boolean;
90
+ uniqueField: string;
91
+ age: number;
92
+ }>[];
93
+ answers: Partial<{
94
+ id: number;
95
+ title: string;
96
+ }>[];
97
+ element: Partial<{
98
+ value: string;
99
+ userId: number;
100
+ e_id: number;
101
+ json: Prisma.JsonValue;
102
+ }>[];
103
+ stripe: Partial<{
104
+ id: number;
105
+ sort: number;
106
+ accountId: number;
107
+ customerId: string;
108
+ active: boolean;
109
+ }>[];
110
+ userAnswers: Partial<{
111
+ value: string;
112
+ answerId: number;
113
+ userId: number;
114
+ }>[];
115
+ document: Partial<{
116
+ name: string;
117
+ id: string;
118
+ }>[];
119
+ post: Partial<{
120
+ id: number;
121
+ title: string;
122
+ published: boolean;
123
+ authorId: number;
124
+ updated: Date;
125
+ created: Date;
126
+ }>[];
127
+ pet: Partial<{
128
+ name: string;
129
+ id: number;
130
+ ownerId: number;
131
+ }>[];
132
+ toy: Partial<{
133
+ name: string;
134
+ id: number;
135
+ ownerId: number;
136
+ }>[];
137
+ transaction: Partial<{
138
+ id: string;
139
+ initiator: string;
140
+ }>[];
141
+ dbGeneratedId: Partial<{
142
+ id: string;
143
+ }>[];
144
+ }>>) => void;
76
145
  $clear: () => void;
77
146
  };
@@ -40,6 +40,8 @@ function createMatch({ prisma, getFieldRelationshipWhere, getDelegateForFieldNam
40
40
  if (info?.relationName) {
41
41
  const childName = (0, fieldHelpers_1.getCamelCase)(info.type);
42
42
  let childWhere = {};
43
+ let useIsFilter = false;
44
+ let useIsNotFilter = false;
43
45
  if (filter.every) {
44
46
  childWhere = filter.every;
45
47
  }
@@ -49,6 +51,14 @@ function createMatch({ prisma, getFieldRelationshipWhere, getDelegateForFieldNam
49
51
  else if (filter.none) {
50
52
  childWhere = filter.none;
51
53
  }
54
+ else if ("is" in filter) {
55
+ useIsFilter = true;
56
+ childWhere = filter.is === null ? {} : filter.is;
57
+ }
58
+ else if ("isNot" in filter) {
59
+ useIsNotFilter = true;
60
+ childWhere = filter.isNot === null ? {} : filter.isNot;
61
+ }
52
62
  else {
53
63
  childWhere = filter;
54
64
  }
@@ -57,10 +67,37 @@ function createMatch({ prisma, getFieldRelationshipWhere, getDelegateForFieldNam
57
67
  });
58
68
  const delegate = getDelegateForFieldName(childName);
59
69
  const joinWhere = getFieldRelationshipWhere(item, info, submodel);
70
+ if (useIsFilter) {
71
+ if (filter.is === null) {
72
+ if (!joinWhere)
73
+ return true;
74
+ const res = delegate.findMany({ where: joinWhere });
75
+ return res.length === 0;
76
+ }
77
+ if (!joinWhere)
78
+ return false;
79
+ const res = delegate.findMany({
80
+ where: { AND: [childWhere, joinWhere] },
81
+ });
82
+ return res.length > 0;
83
+ }
84
+ if (useIsNotFilter) {
85
+ if (filter.isNot === null) {
86
+ if (!joinWhere)
87
+ return false;
88
+ const res = delegate.findMany({ where: joinWhere });
89
+ return res.length > 0;
90
+ }
91
+ if (!joinWhere)
92
+ return true;
93
+ const res = delegate.findMany({
94
+ where: { AND: [childWhere, joinWhere] },
95
+ });
96
+ return res.length === 0;
97
+ }
60
98
  if (!joinWhere) {
61
99
  return false;
62
100
  }
63
- // return true
64
101
  const res = delegate.findMany({
65
102
  where: {
66
103
  AND: [
@@ -70,17 +107,12 @@ function createMatch({ prisma, getFieldRelationshipWhere, getDelegateForFieldNam
70
107
  }
71
108
  });
72
109
  if (filter.every) {
73
- // const all = data[childName].filter(
74
- // matchFnc(getFieldRelationshipWhere(item, info)),
75
- // )
76
110
  const where = getFieldRelationshipWhere(item, info, model);
77
111
  if (!where)
78
112
  return false;
79
113
  const all = delegate.findMany({
80
114
  where,
81
115
  });
82
- // For "every": all related records must match the condition
83
- // If no related records exist, "every" is vacuously true
84
116
  if (all.length === 0)
85
117
  return true;
86
118
  return res.length === all.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-mock",
3
- "version": "1.1.0-alpha.1",
3
+ "version": "1.1.0-alpha.3",
4
4
  "description": "Mock prisma for unit testing database",
5
5
  "main": "lib/index.js",
6
6
  "repository": {
@@ -45,11 +45,13 @@
45
45
  "preversion": "jest && tsc",
46
46
  "lint": "tsc --noEmit",
47
47
  "build": "tsc",
48
- "test": "jest",
48
+ "test": "prisma generate && npm run generate:mongodb && jest",
49
49
  "generate": "prisma generate",
50
+ "generate:mongodb": "DATABASE_URL_MONGODB=mongodb://localhost:27017/test prisma generate --schema=prisma/schema-mongodb.prisma",
50
51
  "release": "yarn build && changeset publish && git push --follow-tags",
51
52
  "watch": "tsc --watch",
52
53
  "test:postgres": "env-cmd -e postgres jest --maxWorkers=1",
54
+ "test:mongodb": "npm run generate:mongodb && jest __tests__/mongodb.test.ts",
53
55
  "changeset": "changeset",
54
56
  "changeset:add": "changeset add",
55
57
  "changeset:validate:ci": "changeset status --since=origin/main --verbose"