prisma-mock 0.9.3 → 0.11.0-alpha.1

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
@@ -188,7 +188,7 @@ TODO (has, hasEvery, hasSome, isEmpty, equals)
188
188
 
189
189
  - @@id
190
190
  - @default
191
- - @unique (TODO: no error if duplicate)
191
+ - @unique
192
192
  - @@unique (TODO: no error if duplicate)
193
193
  - @relation
194
194
  - @updatedAt: Partially supported, value is set at creation (TODO: update value on update)
package/lib/index.js CHANGED
@@ -33,11 +33,11 @@ const shallowCompare_1 = require("./utils/shallowCompare");
33
33
  const deepEqual_1 = require("./utils/deepEqual");
34
34
  const deepCopy_1 = require("./utils/deepCopy");
35
35
  const getNestedValue_1 = __importDefault(require("./utils/getNestedValue"));
36
+ const indexes_1 = __importDefault(require("./indexes"));
36
37
  function IsFieldDefault(f) {
37
38
  return f.name !== undefined;
38
39
  }
39
- const throwKnownError = (message, cause) => {
40
- const code = "P2025";
40
+ const throwKnownError = (message, { code = "P2025", meta } = {}) => {
41
41
  const clientVersion = client_1.Prisma.prismaVersion.client;
42
42
  // PrismaClientKnownRequestError prototype changed in version 4.7.0
43
43
  // from: constructor(message: string, code: string, clientVersion: string, meta?: any)
@@ -56,15 +56,14 @@ const throwKnownError = (message, cause) => {
56
56
  // @ts-ignore
57
57
  clientVersion);
58
58
  }
59
- error.meta = {
60
- cause,
61
- };
59
+ error.meta = meta;
62
60
  throw error;
63
61
  };
64
62
  const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel, client = (0, jest_mock_extended_1.mockDeep)(), options = {
65
63
  caseInsensitive: false,
66
64
  }) => {
67
65
  const manyToManyData = {};
66
+ const indexes = (0, indexes_1.default)();
68
67
  // let data = options.data || {}
69
68
  // const datamodel = options.datamodel || Prisma.dmmf.datamodel
70
69
  const caseInsensitive = options.caseInsensitive || false;
@@ -74,6 +73,7 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
74
73
  return name.substr(0, 1).toLowerCase() + name.substr(1);
75
74
  };
76
75
  const removeMultiFieldIds = (model, data) => {
76
+ /// [tableName][field][value] = Array
77
77
  const c = getCamelCase(model.name);
78
78
  const idFields = model.idFields || model.primaryKey?.fields;
79
79
  const removeId = (ids) => {
@@ -150,8 +150,9 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
150
150
  try {
151
151
  return await actions(client);
152
152
  }
153
- catch {
153
+ catch (error) {
154
154
  data = snapshot;
155
+ throw error;
155
156
  }
156
157
  }
157
158
  });
@@ -232,6 +233,12 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
232
233
  model.fields.forEach((field) => {
233
234
  if (d[field.name]) {
234
235
  const c = d[field.name];
236
+ if (isCreating && (field.isUnique || field.isId)) {
237
+ const existing = findOne({ where: { [field.name]: c } });
238
+ if (existing) {
239
+ throwKnownError(`Unique constraint failed on the fields: (\`${field.name}\`)`, { code: 'P2002', meta: { target: [field.name] } });
240
+ }
241
+ }
235
242
  if (field.kind === "object") {
236
243
  if (c.set) {
237
244
  const { [field.name]: { set }, ...rest } = d;
@@ -331,11 +338,14 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
331
338
  });
332
339
  }
333
340
  if (c.create || c.createMany) {
341
+ const otherModel = datamodel.models.find((model) => {
342
+ return model.name === field.type;
343
+ });
334
344
  const { [field.name]: create, ...rest } = d;
335
345
  d = rest;
336
346
  // @ts-ignore
337
347
  const name = getCamelCase(field.type);
338
- const delegate = Delegate(name, model);
348
+ const delegate = Delegate(name, otherModel);
339
349
  const joinfield = getJoinField(field);
340
350
  if (field.relationFromFields.length > 0) {
341
351
  const item = delegate.create({
@@ -548,13 +558,7 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
548
558
  return matchAnd(item, filter);
549
559
  }
550
560
  if (child === "NOT") {
551
- if (Array.isArray(filter)) {
552
- return !matchOr(item, filter);
553
- }
554
- item; //?
555
- filter; //?
556
- matchItems(item, filter); //?
557
- return !matchItems(item, filter);
561
+ return matchNot(item, filter);
558
562
  }
559
563
  if (filter == null || filter === undefined) {
560
564
  if (filter === null) {
@@ -596,6 +600,7 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
596
600
  if (!joinWhere) {
597
601
  return false;
598
602
  }
603
+ // return true
599
604
  const res = delegate.findMany({
600
605
  where: {
601
606
  AND: [
@@ -642,6 +647,9 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
642
647
  if (val === undefined) {
643
648
  return false;
644
649
  }
650
+ if (val === null) {
651
+ return false;
652
+ }
645
653
  let match = true;
646
654
  const matchFilter = { ...filter };
647
655
  if (caseInsensitive ||
@@ -766,6 +774,33 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
766
774
  }
767
775
  return true;
768
776
  };
777
+ const matchNot = (item, where) => {
778
+ if (Array.isArray(where)) {
779
+ let hasNull = false;
780
+ let res = !where.some((w) => {
781
+ for (let child in w) {
782
+ if (item[child] === null) {
783
+ hasNull = true;
784
+ return false;
785
+ }
786
+ if (matchItem(child, item, w)) {
787
+ return true;
788
+ }
789
+ }
790
+ return false;
791
+ });
792
+ return hasNull ? caseInsensitive : res;
793
+ }
794
+ for (let child in where) {
795
+ if (item[child] === null) {
796
+ return false;
797
+ }
798
+ if (!matchItem(child, item, where)) {
799
+ return true;
800
+ }
801
+ }
802
+ return false;
803
+ };
769
804
  const matchAnd = (item, where) => {
770
805
  return where.filter((child) => matchItems(item, child)).length === where.length;
771
806
  };
@@ -795,9 +830,19 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
795
830
  return found;
796
831
  };
797
832
  const findMany = (args) => {
798
- let res = data[prop]
799
- .filter(matchFnc(args?.where))
800
- .map(includes(args));
833
+ const match = matchFnc(args?.where);
834
+ const inc = includes(args);
835
+ // let res = data[prop]
836
+ // .filter(matchFnc(args?.where))
837
+ // .map(includes(args))
838
+ let items = indexes.getIndexedItems(prop, args?.where) || data[prop];
839
+ let res = [];
840
+ for (const item of items) {
841
+ if (match(item)) {
842
+ const i = inc(item);
843
+ res.push(i);
844
+ }
845
+ }
801
846
  if (args?.distinct) {
802
847
  let values = {};
803
848
  res = res.filter((item) => {
@@ -858,10 +903,12 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
858
903
  if (matchFnc(args.where)(e)) {
859
904
  let data = nestedUpdate(args, false, e);
860
905
  nbUpdated++;
861
- return {
906
+ const newItem = {
862
907
  ...e,
863
908
  ...data,
864
909
  };
910
+ indexes.updateItem(prop, newItem);
911
+ return newItem;
865
912
  }
866
913
  return e;
867
914
  });
@@ -885,7 +932,9 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
885
932
  where[field.name] = d[field.name];
886
933
  }
887
934
  }
888
- return findOne({ where, ...args });
935
+ const item = findOne({ where, ...args });
936
+ indexes.updateItem(prop, item);
937
+ return item;
889
938
  };
890
939
  const createMany = (args) => {
891
940
  const createdItems = [];
@@ -923,19 +972,23 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
923
972
  // Referential Actions
924
973
  deleted.forEach((item) => {
925
974
  model.fields.forEach((field) => {
975
+ indexes.deleteItemByField(prop, field, item);
926
976
  const joinfield = getJoinField(field);
927
977
  if (!joinfield)
928
978
  return;
929
979
  const delegate = Delegate(getCamelCase(field.type), model);
930
980
  if (joinfield.relationOnDelete === "SetNull") {
931
- delegate.update({
932
- where: {
933
- [joinfield.relationFromFields[0]]: item[joinfield.relationToFields[0]],
934
- },
935
- data: {
936
- [joinfield.relationFromFields[0]]: null,
937
- },
938
- });
981
+ try {
982
+ delegate.update({
983
+ where: {
984
+ [joinfield.relationFromFields[0]]: item[joinfield.relationToFields[0]],
985
+ },
986
+ data: {
987
+ [joinfield.relationFromFields[0]]: null,
988
+ },
989
+ });
990
+ }
991
+ catch (e) { }
939
992
  }
940
993
  else if (joinfield.relationOnDelete === "Cascade") {
941
994
  try {
@@ -1037,18 +1090,24 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
1037
1090
  };
1038
1091
  const update = (args) => {
1039
1092
  let updatedItem;
1093
+ let hasMatch = false;
1094
+ const match = matchFnc(args.where);
1040
1095
  const newItems = data[prop].map((e) => {
1041
- if (matchFnc(args.where)(e)) {
1096
+ if (match(e)) {
1097
+ hasMatch = true;
1042
1098
  let data = nestedUpdate(args, false, e);
1043
- data; //?
1044
1099
  updatedItem = {
1045
1100
  ...e,
1046
1101
  ...data,
1047
1102
  };
1103
+ indexes.updateItem(prop, updatedItem);
1048
1104
  return updatedItem;
1049
1105
  }
1050
1106
  return e;
1051
1107
  });
1108
+ if (!hasMatch) {
1109
+ throwKnownError("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." } });
1110
+ }
1052
1111
  data = {
1053
1112
  ...data,
1054
1113
  [prop]: newItems,
@@ -1076,7 +1135,7 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
1076
1135
  delete: (args) => {
1077
1136
  const item = findOne(args);
1078
1137
  if (!item) {
1079
- throwKnownError("An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.", "Record to delete does not exist.");
1138
+ throwKnownError("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." } });
1080
1139
  }
1081
1140
  const deleted = deleteMany(args);
1082
1141
  if (deleted.length) {
@@ -1132,6 +1191,12 @@ const createPrismaMock = (data = {}, datamodel = client_1.Prisma.dmmf.datamodel,
1132
1191
  };
1133
1192
  }
1134
1193
  data = removeMultiFieldIds(model, data);
1194
+ model.fields.forEach((field) => {
1195
+ indexes.addIndexFieldIfNeeded(c, field);
1196
+ });
1197
+ data[c].forEach((item) => {
1198
+ indexes.updateItem(c, item);
1199
+ });
1135
1200
  const objs = Delegate(c, model);
1136
1201
  Object.keys(objs).forEach((fncName) => {
1137
1202
  if (fncName.indexOf("_") === 0)
@@ -0,0 +1,7 @@
1
+ import { Prisma } from "@prisma/client";
2
+ export default function createIndexes(): {
3
+ addIndexFieldIfNeeded: (tableName: string, field: Prisma.DMMF.Field) => void;
4
+ getIndexedItems: (tableName: string, where: any) => any;
5
+ updateItem: (tableName: string, item: any) => void;
6
+ deleteItemByField: (tableName: string, field: Prisma.DMMF.Field, item: any) => void;
7
+ };
package/lib/indexes.js ADDED
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function createIndexes() {
4
+ let items = {};
5
+ let indexedFieldNames = {};
6
+ let fields = {};
7
+ let idFieldNames = {};
8
+ const addIndexFieldIfNeeded = (tableName, field) => {
9
+ if (!indexedFieldNames[tableName]) {
10
+ indexedFieldNames[tableName] = [];
11
+ }
12
+ if (!fields[tableName]) {
13
+ fields[tableName] = {};
14
+ }
15
+ if (!idFieldNames[tableName]) {
16
+ idFieldNames[tableName] = [];
17
+ }
18
+ let thisFields = fields[tableName];
19
+ let thisFieldNames = indexedFieldNames[tableName];
20
+ let thisIdFieldNames = idFieldNames[tableName];
21
+ if (thisFieldNames.includes(field.name))
22
+ return;
23
+ if (field.isId || field.isUnique) {
24
+ thisFieldNames.push(field.name);
25
+ }
26
+ if (field.isId) {
27
+ thisIdFieldNames.push(field.name);
28
+ }
29
+ if (!!field.relationFromFields?.length) {
30
+ const fieldName = field.relationFromFields[0];
31
+ thisFieldNames.push(fieldName);
32
+ }
33
+ thisFields[field.name] = field;
34
+ };
35
+ const getIndexedItems = (tableName, where) => {
36
+ for (const field in where) {
37
+ if (field === "AND") {
38
+ const subWhere = where.AND;
39
+ if (Array.isArray(subWhere)) {
40
+ for (const subWhereItem of subWhere) {
41
+ const items = getIndexedItems(tableName, subWhereItem);
42
+ if (items) {
43
+ return items;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ if (indexedFieldNames[tableName]) {
49
+ if (indexedFieldNames[tableName].includes(field)) {
50
+ return items[tableName]?.[field]?.[where[field]];
51
+ }
52
+ }
53
+ }
54
+ };
55
+ const updateItem = (tableName, item) => {
56
+ if (!items[tableName]) {
57
+ items[tableName] = {};
58
+ }
59
+ if (!indexedFieldNames[tableName]) {
60
+ throw new Error(`No indexed fields for table ${tableName}`);
61
+ }
62
+ for (const fieldName of indexedFieldNames[tableName]) {
63
+ if (!items[tableName][fieldName]) {
64
+ items[tableName][fieldName] = {};
65
+ }
66
+ if (!items[tableName][fieldName][item[fieldName]]) {
67
+ items[tableName][fieldName][item[fieldName]] = [item];
68
+ }
69
+ else {
70
+ const field = fields[tableName][fieldName];
71
+ if (field && (field.isId || field.isUnique)) {
72
+ items[tableName][fieldName][item[fieldName]] = [item];
73
+ }
74
+ else {
75
+ const array = items[tableName][fieldName][item[fieldName]];
76
+ if (array.length === 0) {
77
+ array.push(item);
78
+ }
79
+ else {
80
+ const thisIdFieldNames = idFieldNames[tableName] || [];
81
+ let hasFound = false;
82
+ for (let i = 0; i < array.length; i++) {
83
+ const oldItem = array[i];
84
+ for (const thisIdFieldName of thisIdFieldNames) {
85
+ if (item[thisIdFieldName] === oldItem[thisIdFieldName]) {
86
+ hasFound = true;
87
+ array[i] = item;
88
+ return;
89
+ }
90
+ }
91
+ }
92
+ if (!hasFound) {
93
+ array.push(item);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ };
100
+ const deleteItemByField = (tableName, field, item) => {
101
+ if (indexedFieldNames[tableName]) {
102
+ if (indexedFieldNames[tableName].includes(field.name)) {
103
+ delete items[tableName]?.[field.name]?.[item[field.name]];
104
+ }
105
+ }
106
+ };
107
+ return {
108
+ addIndexFieldIfNeeded,
109
+ getIndexedItems,
110
+ updateItem,
111
+ deleteItemByField
112
+ };
113
+ }
114
+ exports.default = createIndexes;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const indexes_1 = __importDefault(require("./indexes"));
8
+ test("createIndexes", () => {
9
+ const indexes = (0, indexes_1.default)();
10
+ indexes.addIndexFieldIfNeeded("User", {
11
+ name: "id",
12
+ isId: true
13
+ });
14
+ indexes.addIndexFieldIfNeeded("User", {
15
+ name: "account",
16
+ relationFromFields: ["accountId"]
17
+ });
18
+ indexes.updateItem("User", {
19
+ id: 1,
20
+ name: "Alice",
21
+ accountId: 1
22
+ });
23
+ const items1 = indexes.getIndexedItems("User", {
24
+ accountId: 1
25
+ });
26
+ expect(items1).toMatchInlineSnapshot(`
27
+ Array [
28
+ Object {
29
+ "accountId": 1,
30
+ "id": 1,
31
+ "name": "Alice",
32
+ },
33
+ ]
34
+ `);
35
+ indexes.updateItem("User", {
36
+ id: 1,
37
+ name: "Alice 2",
38
+ accountId: 1
39
+ });
40
+ const items2 = indexes.getIndexedItems("User", {
41
+ accountId: 1
42
+ });
43
+ expect(items2).toMatchInlineSnapshot(`
44
+ Array [
45
+ Object {
46
+ "accountId": 1,
47
+ "id": 1,
48
+ "name": "Alice 2",
49
+ },
50
+ ]
51
+ `);
52
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-mock",
3
- "version": "0.9.3",
3
+ "version": "0.11.0-alpha.1",
4
4
  "description": "Mock prisma for unit testing database",
5
5
  "main": "lib/index.js",
6
6
  "repository": "https://github.com/demonsters/prisma-mock",
@@ -21,7 +21,7 @@
21
21
  "uuid": "^9.0.0"
22
22
  },
23
23
  "scripts": {
24
- "preversion": "tsc",
24
+ "preversion": "jest && tsc",
25
25
  "build": "tsc",
26
26
  "test": "jest",
27
27
  "watch": "tsc --watch",