@strapi/strapi 4.10.0 → 4.10.1-experimental.0

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.
Files changed (126) hide show
  1. package/coverage/clover.xml +1613 -0
  2. package/coverage/coverage-final.json +48 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +87 -0
  5. package/coverage/lcov-report/commands/__tests__/data-transfer/shared/index.html +116 -0
  6. package/coverage/lcov-report/commands/__tests__/data-transfer/shared/transfer.test.utils.js.html +133 -0
  7. package/coverage/lcov-report/commands/admin-create.js.html +424 -0
  8. package/coverage/lcov-report/commands/admin-reset.js.html +241 -0
  9. package/coverage/lcov-report/commands/generate-template.js.html +373 -0
  10. package/coverage/lcov-report/commands/index.html +146 -0
  11. package/coverage/lcov-report/commands/transfer/export.js.html +619 -0
  12. package/coverage/lcov-report/commands/transfer/import.js.html +562 -0
  13. package/coverage/lcov-report/commands/transfer/index.html +146 -0
  14. package/coverage/lcov-report/commands/transfer/transfer.js.html +532 -0
  15. package/coverage/lcov-report/commands/utils/helpers.js.html +430 -0
  16. package/coverage/lcov-report/commands/utils/index.html +116 -0
  17. package/coverage/lcov-report/core/registries/custom-fields.js.html +301 -0
  18. package/coverage/lcov-report/core/registries/index.html +116 -0
  19. package/coverage/lcov-report/core-api/controller/collection-type.js.html +418 -0
  20. package/coverage/lcov-report/core-api/controller/index.html +161 -0
  21. package/coverage/lcov-report/core-api/controller/index.js.html +220 -0
  22. package/coverage/lcov-report/core-api/controller/single-type.js.html +274 -0
  23. package/coverage/lcov-report/core-api/controller/transform.js.html +376 -0
  24. package/coverage/lcov-report/core-api/service/collection-type.js.html +325 -0
  25. package/coverage/lcov-report/core-api/service/index.html +161 -0
  26. package/coverage/lcov-report/core-api/service/index.js.html +220 -0
  27. package/coverage/lcov-report/core-api/service/pagination.js.html +460 -0
  28. package/coverage/lcov-report/core-api/service/single-type.js.html +301 -0
  29. package/coverage/lcov-report/favicon.png +0 -0
  30. package/coverage/lcov-report/index.html +386 -0
  31. package/coverage/lcov-report/load/filepath-to-prop-path.js.html +151 -0
  32. package/coverage/lcov-report/load/index.html +116 -0
  33. package/coverage/lcov-report/prettify.css +1 -0
  34. package/coverage/lcov-report/prettify.js +2 -0
  35. package/coverage/lcov-report/services/content-api/index.html +116 -0
  36. package/coverage/lcov-report/services/content-api/index.js.html +307 -0
  37. package/coverage/lcov-report/services/content-api/permissions/engine.js.html +100 -0
  38. package/coverage/lcov-report/services/content-api/permissions/index.html +131 -0
  39. package/coverage/lcov-report/services/content-api/permissions/index.js.html +529 -0
  40. package/coverage/lcov-report/services/content-api/permissions/providers/action.js.html +142 -0
  41. package/coverage/lcov-report/services/content-api/permissions/providers/condition.js.html +142 -0
  42. package/coverage/lcov-report/services/content-api/permissions/providers/index.html +146 -0
  43. package/coverage/lcov-report/services/content-api/permissions/providers/index.js.html +112 -0
  44. package/coverage/lcov-report/services/core-store.js.html +520 -0
  45. package/coverage/lcov-report/services/entity-service/attributes/index.html +131 -0
  46. package/coverage/lcov-report/services/entity-service/attributes/index.js.html +178 -0
  47. package/coverage/lcov-report/services/entity-service/attributes/transforms.js.html +145 -0
  48. package/coverage/lcov-report/services/entity-service/components.js.html +1246 -0
  49. package/coverage/lcov-report/services/entity-service/index.html +146 -0
  50. package/coverage/lcov-report/services/entity-service/index.js.html +1120 -0
  51. package/coverage/lcov-report/services/entity-service/params.js.html +112 -0
  52. package/coverage/lcov-report/services/entity-validator/__tests__/relations/utils/index.html +116 -0
  53. package/coverage/lcov-report/services/entity-validator/__tests__/relations/utils/relations.testdata.js.html +544 -0
  54. package/coverage/lcov-report/services/entity-validator/index.html +131 -0
  55. package/coverage/lcov-report/services/entity-validator/index.js.html +1231 -0
  56. package/coverage/lcov-report/services/entity-validator/validators.js.html +733 -0
  57. package/coverage/lcov-report/services/event-hub.js.html +319 -0
  58. package/coverage/lcov-report/services/fs.js.html +259 -0
  59. package/coverage/lcov-report/services/index.html +161 -0
  60. package/coverage/lcov-report/services/metrics/admin-user-hash.js.html +148 -0
  61. package/coverage/lcov-report/services/metrics/index.html +206 -0
  62. package/coverage/lcov-report/services/metrics/index.js.html +265 -0
  63. package/coverage/lcov-report/services/metrics/is-truthy.js.html +112 -0
  64. package/coverage/lcov-report/services/metrics/middleware.js.html +184 -0
  65. package/coverage/lcov-report/services/metrics/rate-limiter.js.html +166 -0
  66. package/coverage/lcov-report/services/metrics/sender.js.html +394 -0
  67. package/coverage/lcov-report/services/metrics/stringify-deep.js.html +151 -0
  68. package/coverage/lcov-report/services/utils/index.html +116 -0
  69. package/coverage/lcov-report/services/utils/upload-files.js.html +322 -0
  70. package/coverage/lcov-report/services/worker-queue.js.html +262 -0
  71. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  72. package/coverage/lcov-report/sorter.js +196 -0
  73. package/coverage/lcov-report/utils/convert-custom-field-type.js.html +151 -0
  74. package/coverage/lcov-report/utils/index.html +146 -0
  75. package/coverage/lcov-report/utils/machine-id.js.html +127 -0
  76. package/coverage/lcov-report/utils/url-from-segments.js.html +121 -0
  77. package/lib/commands/__tests__/commands.test.js +20 -0
  78. package/lib/commands/__tests__/commands.test.utils.js +16 -0
  79. package/lib/commands/actions/admin/create-user/__tests__/admin.create-user.test.js +450 -0
  80. package/lib/commands/actions/admin/reset-user-password/__tests__/admin.reset-user-password.test.js +145 -0
  81. package/lib/commands/actions/build/action.js +18 -0
  82. package/lib/commands/actions/build/command.js +15 -0
  83. package/lib/commands/actions/export/__tests__/export.test.js +175 -0
  84. package/lib/commands/actions/import/__tests__/import.test.js +143 -0
  85. package/lib/commands/actions/templates/generate/__tests__/templates.generate.js +118 -0
  86. package/lib/commands/actions/transfer/__tests__/transfer.test.js +178 -0
  87. package/lib/core/registries/__tests__/custom-fields.test.js +152 -0
  88. package/lib/core-api/__tests__/controller.test.js +39 -0
  89. package/lib/core-api/controller/__tests__/transform.test.js +226 -0
  90. package/lib/core-api/service/__tests__/index.test.js +127 -0
  91. package/lib/core-api/service/__tests__/pagination.test.js +275 -0
  92. package/lib/load/__tests__/filepath-to-prop-path.test.js +30 -0
  93. package/lib/middlewares/__tests__/errors.test.js +21 -0
  94. package/lib/services/__tests__/content-api-permissions.test.js +291 -0
  95. package/lib/services/__tests__/core-store.test.js +148 -0
  96. package/lib/services/__tests__/event-hub.test.js +126 -0
  97. package/lib/services/__tests__/fs.test.js +78 -0
  98. package/lib/services/__tests__/worker-queue.test.js +47 -0
  99. package/lib/services/entity-service/__tests__/entity-service-events.test.js +117 -0
  100. package/lib/services/entity-service/__tests__/entity-service.test.js +587 -0
  101. package/lib/services/entity-validator/__tests__/biginteger-validators.test.js +220 -0
  102. package/lib/services/entity-validator/__tests__/date-validators.test.js +183 -0
  103. package/lib/services/entity-validator/__tests__/datetime-validators.test.js +183 -0
  104. package/lib/services/entity-validator/__tests__/email-validators.test.js +56 -0
  105. package/lib/services/entity-validator/__tests__/enumeration-validators.test.js +43 -0
  106. package/lib/services/entity-validator/__tests__/float-validators.test.js +278 -0
  107. package/lib/services/entity-validator/__tests__/index.test.js +609 -0
  108. package/lib/services/entity-validator/__tests__/integer-validators.test.js +278 -0
  109. package/lib/services/entity-validator/__tests__/relations/attribute-level.test.js +123 -0
  110. package/lib/services/entity-validator/__tests__/relations/component-level.test.js +275 -0
  111. package/lib/services/entity-validator/__tests__/relations/dynamic-zone-level.test.js +159 -0
  112. package/lib/services/entity-validator/__tests__/relations/media-level.test.js +74 -0
  113. package/lib/services/entity-validator/__tests__/relations/utils/relations.testdata.js +153 -0
  114. package/lib/services/entity-validator/__tests__/string-validators.test.js +374 -0
  115. package/lib/services/entity-validator/__tests__/time-validators.test.js +183 -0
  116. package/lib/services/entity-validator/__tests__/timestamp-validators.test.js +204 -0
  117. package/lib/services/entity-validator/__tests__/uid-validators.test.js +229 -0
  118. package/lib/services/metrics/__tests__/admin-user-hash.test.js +41 -0
  119. package/lib/services/metrics/__tests__/index.test.js +157 -0
  120. package/lib/services/metrics/__tests__/is-truthy.js +33 -0
  121. package/lib/services/metrics/__tests__/middleware.test.js +60 -0
  122. package/lib/services/metrics/__tests__/rate-limiter.test.js +50 -0
  123. package/lib/services/metrics/__tests__/stringify-deep.test.js +27 -0
  124. package/lib/utils/__tests__/convert-custom-field-type.test.js +69 -0
  125. package/lib/utils/__tests__/url-from-segments.test.js +40 -0
  126. package/package.json +15 -15
@@ -0,0 +1,587 @@
1
+ 'use strict';
2
+
3
+ jest.mock('bcryptjs', () => ({ hashSync: () => 'secret-password' }));
4
+
5
+ const { EventEmitter } = require('events');
6
+ const { ValidationError } = require('@strapi/utils').errors;
7
+ const createEntityService = require('..');
8
+ const entityValidator = require('../../entity-validator');
9
+
10
+ jest.mock('../../utils/upload-files', () => jest.fn(() => Promise.resolve()));
11
+
12
+ describe('Entity service', () => {
13
+ global.strapi = {
14
+ getModel: jest.fn(() => ({})),
15
+ config: {
16
+ get() {
17
+ return [];
18
+ },
19
+ },
20
+ query: jest.fn(() => ({})),
21
+ };
22
+
23
+ describe('Decorator', () => {
24
+ test.each(['create', 'update', 'findMany', 'findOne', 'delete', 'count', 'findPage'])(
25
+ 'Can decorate',
26
+ async (method) => {
27
+ const instance = createEntityService({
28
+ strapi: {},
29
+ db: {},
30
+ eventHub: new EventEmitter(),
31
+ });
32
+
33
+ const methodFn = jest.fn();
34
+ const decorator = () => ({
35
+ [method]: methodFn,
36
+ });
37
+
38
+ instance.decorate(decorator);
39
+
40
+ const args = [{}, {}];
41
+ await instance[method](...args);
42
+ expect(methodFn).toHaveBeenCalled();
43
+ }
44
+ );
45
+ });
46
+
47
+ describe('Find', () => {
48
+ test('Returns first element for single types', async () => {
49
+ const data = {
50
+ id: 1,
51
+ title: 'Test',
52
+ };
53
+
54
+ const fakeQuery = {
55
+ findOne: jest.fn(() => Promise.resolve(data)),
56
+ };
57
+
58
+ const fakeDB = {
59
+ query: jest.fn(() => fakeQuery),
60
+ transaction: (cb) => cb(),
61
+ };
62
+
63
+ const fakeStrapi = {
64
+ getModel: jest.fn(() => {
65
+ return { kind: 'singleType', privateAttributes: [] };
66
+ }),
67
+ };
68
+
69
+ const instance = createEntityService({
70
+ strapi: fakeStrapi,
71
+ db: fakeDB,
72
+ eventHub: new EventEmitter(),
73
+ });
74
+
75
+ const result = await instance.findMany('test-model');
76
+
77
+ expect(fakeStrapi.getModel).toHaveBeenCalledTimes(1);
78
+ expect(fakeStrapi.getModel).toHaveBeenCalledWith('test-model');
79
+
80
+ expect(fakeDB.query).toHaveBeenCalledWith('test-model');
81
+ expect(fakeQuery.findOne).toHaveBeenCalledWith({});
82
+ expect(result).toEqual(data);
83
+ });
84
+ });
85
+
86
+ describe('Create', () => {
87
+ const fakeQuery = {
88
+ count: jest.fn(() => 0),
89
+ create: jest.fn(({ data }) => ({
90
+ id: 1,
91
+ ...data,
92
+ })),
93
+ findOne: jest.fn(),
94
+ };
95
+ const fakeModels = {};
96
+
97
+ beforeAll(() => {
98
+ global.strapi.getModel.mockImplementation((modelName) => fakeModels[modelName]);
99
+ global.strapi.query.mockImplementation(() => fakeQuery);
100
+ });
101
+ beforeEach(() => {
102
+ jest.clearAllMocks();
103
+ });
104
+ afterAll(() => {
105
+ global.strapi.getModel.mockImplementation(() => ({}));
106
+ });
107
+ describe('assign default values', () => {
108
+ let instance;
109
+ const entityUID = 'api::entity.entity';
110
+ const relationUID = 'api::relation.relation';
111
+
112
+ beforeAll(() => {
113
+ const fakeEntities = {
114
+ [relationUID]: {
115
+ 1: {
116
+ id: 1,
117
+ Name: 'TestRelation',
118
+ createdAt: '2022-09-28T15:11:22.995Z',
119
+ updatedAt: '2022-09-29T09:01:02.949Z',
120
+ publishedAt: null,
121
+ },
122
+ 2: {
123
+ id: 2,
124
+ Name: 'TestRelation2',
125
+ createdAt: '2022-09-28T15:11:22.995Z',
126
+ updatedAt: '2022-09-29T09:01:02.949Z',
127
+ publishedAt: null,
128
+ },
129
+ },
130
+ };
131
+
132
+ fakeModels[entityUID] = {
133
+ uid: entityUID,
134
+ kind: 'contentType',
135
+ modelName: 'test-model',
136
+ privateAttributes: [],
137
+ options: {},
138
+ attributes: {
139
+ attrStringDefaultRequired: {
140
+ type: 'string',
141
+ default: 'default value',
142
+ required: true,
143
+ },
144
+ attrStringDefault: { type: 'string', default: 'default value' },
145
+ attrBoolDefaultRequired: { type: 'boolean', default: true, required: true },
146
+ attrBoolDefault: { type: 'boolean', default: true },
147
+ attrIntDefaultRequired: { type: 'integer', default: 1, required: true },
148
+ attrIntDefault: { type: 'integer', default: 1 },
149
+ attrEnumDefaultRequired: {
150
+ type: 'enumeration',
151
+ enum: ['a', 'b', 'c'],
152
+ default: 'a',
153
+ required: true,
154
+ },
155
+ attrEnumDefault: {
156
+ type: 'enumeration',
157
+ enum: ['a', 'b', 'c'],
158
+ default: 'b',
159
+ },
160
+ attrPassword: { type: 'password' },
161
+ attrRelation: {
162
+ type: 'relation',
163
+ relation: 'oneToMany',
164
+ target: relationUID,
165
+ mappedBy: 'entity',
166
+ },
167
+ },
168
+ };
169
+ fakeModels[relationUID] = {
170
+ uid: relationUID,
171
+ kind: 'contentType',
172
+ modelName: 'relation',
173
+ attributes: {
174
+ Name: {
175
+ type: 'string',
176
+ default: 'default value',
177
+ required: true,
178
+ },
179
+ },
180
+ };
181
+ const fakeQuery = (uid) => ({
182
+ create: jest.fn(({ data }) => data),
183
+ count: jest.fn(({ where }) => {
184
+ return where.id.$in.filter((id) => Boolean(fakeEntities[uid][id])).length;
185
+ }),
186
+ });
187
+
188
+ const fakeDB = {
189
+ transaction: (cb) => cb(),
190
+ query: jest.fn((uid) => fakeQuery(uid)),
191
+ };
192
+
193
+ global.strapi.db = fakeDB;
194
+
195
+ instance = createEntityService({
196
+ strapi: global.strapi,
197
+ db: fakeDB,
198
+ eventHub: new EventEmitter(),
199
+ entityValidator,
200
+ });
201
+ });
202
+ afterAll(() => {
203
+ global.strapi.db = {
204
+ query: jest.fn(() => fakeQuery),
205
+ };
206
+ });
207
+ test('should create record with all default attributes', async () => {
208
+ const data = {};
209
+
210
+ await expect(instance.create(entityUID, { data })).resolves.toMatchObject({
211
+ attrStringDefaultRequired: 'default value',
212
+ attrStringDefault: 'default value',
213
+ attrBoolDefaultRequired: true,
214
+ attrBoolDefault: true,
215
+ attrIntDefaultRequired: 1,
216
+ attrIntDefault: 1,
217
+ attrEnumDefaultRequired: 'a',
218
+ attrEnumDefault: 'b',
219
+ });
220
+ });
221
+
222
+ test('should create record with default and required attributes', async () => {
223
+ const data = {
224
+ attrStringDefault: 'my value',
225
+ attrBoolDefault: false,
226
+ attrIntDefault: 2,
227
+ attrEnumDefault: 'c',
228
+ };
229
+
230
+ await expect(instance.create(entityUID, { data })).resolves.toMatchObject({
231
+ attrStringDefault: 'my value',
232
+ attrBoolDefault: false,
233
+ attrIntDefault: 2,
234
+ attrEnumDefault: 'c',
235
+ attrStringDefaultRequired: 'default value',
236
+ attrBoolDefaultRequired: true,
237
+ attrIntDefaultRequired: 1,
238
+ attrEnumDefaultRequired: 'a',
239
+ });
240
+ });
241
+
242
+ test('should create record with provided data', async () => {
243
+ const data = {
244
+ attrStringDefaultRequired: 'my value',
245
+ attrStringDefault: 'my value',
246
+ attrBoolDefaultRequired: true,
247
+ attrBoolDefault: true,
248
+ attrIntDefaultRequired: 10,
249
+ attrIntDefault: 10,
250
+ attrEnumDefaultRequired: 'c',
251
+ attrEnumDefault: 'a',
252
+ attrPassword: 'fooBar',
253
+ };
254
+
255
+ await expect(instance.create(entityUID, { data })).resolves.toMatchObject({
256
+ ...data,
257
+ attrPassword: 'secret-password',
258
+ });
259
+ });
260
+
261
+ test('should create record with valid relation', async () => {
262
+ const data = {
263
+ attrStringDefaultRequired: 'my value',
264
+ attrStringDefault: 'my value',
265
+ attrBoolDefaultRequired: true,
266
+ attrBoolDefault: true,
267
+ attrIntDefaultRequired: 10,
268
+ attrIntDefault: 10,
269
+ attrEnumDefaultRequired: 'c',
270
+ attrEnumDefault: 'a',
271
+ attrPassword: 'fooBar',
272
+ attrRelation: {
273
+ connect: [
274
+ {
275
+ id: 1,
276
+ },
277
+ ],
278
+ },
279
+ };
280
+
281
+ const res = instance.create(entityUID, { data });
282
+
283
+ await expect(res).resolves.toMatchObject({
284
+ ...data,
285
+ attrPassword: 'secret-password',
286
+ });
287
+ });
288
+
289
+ test('should fail to create a record with an invalid relation', async () => {
290
+ const data = {
291
+ attrStringDefaultRequired: 'my value',
292
+ attrStringDefault: 'my value',
293
+ attrBoolDefaultRequired: true,
294
+ attrBoolDefault: true,
295
+ attrIntDefaultRequired: 10,
296
+ attrIntDefault: 10,
297
+ attrEnumDefaultRequired: 'c',
298
+ attrEnumDefault: 'a',
299
+ attrPassword: 'fooBar',
300
+ attrRelation: {
301
+ connect: [
302
+ {
303
+ id: 3,
304
+ },
305
+ ],
306
+ },
307
+ };
308
+
309
+ const res = instance.create(entityUID, { data });
310
+ await expect(res).rejects.toThrowError(
311
+ new ValidationError(
312
+ `1 relation(s) of type api::relation.relation associated with this entity do not exist`
313
+ )
314
+ );
315
+ });
316
+ });
317
+
318
+ describe('with files', () => {
319
+ let instance;
320
+ beforeAll(() => {
321
+ fakeModels['test-model'] = {
322
+ uid: 'test-model',
323
+ kind: 'collectionType',
324
+ collectionName: 'test-model',
325
+ options: {},
326
+ attributes: {
327
+ name: {
328
+ type: 'string',
329
+ },
330
+ activity: {
331
+ displayName: 'activity',
332
+ type: 'component',
333
+ repeatable: true,
334
+ component: 'basic.activity',
335
+ },
336
+ },
337
+ modelType: 'contentType',
338
+ modelName: 'test-model',
339
+ };
340
+ fakeModels['basic.activity'] = {
341
+ collectionName: 'components_basic_activities',
342
+ info: {
343
+ displayName: 'activity',
344
+ },
345
+ options: {},
346
+ attributes: {
347
+ docs: {
348
+ allowedTypes: ['images', 'files', 'videos', 'audios'],
349
+ type: 'media',
350
+ multiple: true,
351
+ },
352
+ name: {
353
+ type: 'string',
354
+ },
355
+ },
356
+ uid: 'basic.activity',
357
+ category: 'basic',
358
+ modelType: 'component',
359
+ modelName: 'activity',
360
+ globalId: 'ComponentBasicActivity',
361
+ };
362
+
363
+ const fakeDB = {
364
+ query: jest.fn(() => fakeQuery),
365
+ };
366
+
367
+ const fakeStrapi = {
368
+ getModel: jest.fn((modelName) => fakeModels[modelName]),
369
+ query: jest.fn(() => fakeQuery),
370
+ db: {
371
+ dialect: {
372
+ client: 'sqlite',
373
+ },
374
+ },
375
+ };
376
+
377
+ global.strapi = fakeStrapi;
378
+
379
+ instance = createEntityService({
380
+ strapi: fakeStrapi,
381
+ db: fakeDB,
382
+ eventHub: new EventEmitter(),
383
+ entityValidator,
384
+ });
385
+ });
386
+ test('should create record with attached files', async () => {
387
+ const uploadFiles = require('../../utils/upload-files');
388
+ const data = {
389
+ name: 'demoEvent',
390
+ activity: [{ name: 'Powering the Aviation of the Future' }],
391
+ };
392
+ const files = {
393
+ 'activity.0.docs': {
394
+ size: 381924,
395
+ path: '/tmp/upload_4cab76a3a443b584a1fd3aa52e045130',
396
+ name: 'thisisajpeg.jpeg',
397
+ type: 'image/jpeg',
398
+ mtime: '2022-11-03T13:36:51.764Z',
399
+ },
400
+ };
401
+
402
+ fakeQuery.findOne.mockResolvedValue({ id: 1, ...data });
403
+
404
+ await instance.create('test-model', { data, files });
405
+
406
+ expect(global.strapi.getModel).toBeCalled();
407
+ expect(uploadFiles).toBeCalled();
408
+ expect(uploadFiles).toBeCalledTimes(1);
409
+ expect(uploadFiles).toBeCalledWith(
410
+ 'test-model',
411
+ {
412
+ id: 1,
413
+ name: 'demoEvent',
414
+ activity: [
415
+ {
416
+ id: 1,
417
+ __pivot: {
418
+ field: 'activity',
419
+ component_type: 'basic.activity',
420
+ },
421
+ },
422
+ ],
423
+ },
424
+ files
425
+ );
426
+ });
427
+ });
428
+ });
429
+
430
+ describe('Update', () => {
431
+ describe('assign default values', () => {
432
+ let instance;
433
+
434
+ const entityUID = 'api::entity.entity';
435
+ const relationUID = 'api::relation.relation';
436
+
437
+ const fakeEntities = {
438
+ [entityUID]: {
439
+ 0: {
440
+ id: 0,
441
+ Name: 'TestEntity',
442
+ createdAt: '2022-09-28T15:11:22.995Z',
443
+ updatedAt: '2022-09-29T09:01:02.949Z',
444
+ publishedAt: null,
445
+ },
446
+ },
447
+ [relationUID]: {
448
+ 1: {
449
+ id: 1,
450
+ Name: 'TestRelation',
451
+ createdAt: '2022-09-28T15:11:22.995Z',
452
+ updatedAt: '2022-09-29T09:01:02.949Z',
453
+ publishedAt: null,
454
+ },
455
+ 2: {
456
+ id: 2,
457
+ Name: 'TestRelation2',
458
+ createdAt: '2022-09-28T15:11:22.995Z',
459
+ updatedAt: '2022-09-29T09:01:02.949Z',
460
+ publishedAt: null,
461
+ },
462
+ },
463
+ };
464
+ const fakeModels = {
465
+ [entityUID]: {
466
+ kind: 'collectionType',
467
+ modelName: 'entity',
468
+ collectionName: 'entity',
469
+ uid: entityUID,
470
+ privateAttributes: [],
471
+ options: {},
472
+ info: {
473
+ singularName: 'entity',
474
+ pluralName: 'entities',
475
+ displayName: 'ENTITY',
476
+ },
477
+ attributes: {
478
+ Name: {
479
+ type: 'string',
480
+ },
481
+ addresses: {
482
+ type: 'relation',
483
+ relation: 'oneToMany',
484
+ target: relationUID,
485
+ mappedBy: 'entity',
486
+ },
487
+ },
488
+ },
489
+ [relationUID]: {
490
+ kind: 'contentType',
491
+ modelName: 'relation',
492
+ attributes: {
493
+ Name: {
494
+ type: 'string',
495
+ default: 'default value',
496
+ required: true,
497
+ },
498
+ },
499
+ },
500
+ };
501
+
502
+ beforeAll(() => {
503
+ const fakeQuery = (key) => ({
504
+ findOne: jest.fn(({ where }) => fakeEntities[key][where.id]),
505
+ count: jest.fn(({ where }) => {
506
+ let ret = 0;
507
+ where.id.$in.forEach((id) => {
508
+ const entity = fakeEntities[key][id];
509
+ if (!entity) return;
510
+ ret += 1;
511
+ });
512
+ return ret;
513
+ }),
514
+ update: jest.fn(({ where }) => ({
515
+ ...fakeEntities[key][where.id],
516
+ addresses: {
517
+ count: 1,
518
+ },
519
+ })),
520
+ });
521
+
522
+ const fakeDB = {
523
+ query: jest.fn((key) => fakeQuery(key)),
524
+ };
525
+
526
+ global.strapi = {
527
+ getModel: jest.fn((uid) => {
528
+ return fakeModels[uid];
529
+ }),
530
+ db: fakeDB,
531
+ };
532
+
533
+ instance = createEntityService({
534
+ strapi: global.strapi,
535
+ db: fakeDB,
536
+ eventHub: new EventEmitter(),
537
+ entityValidator,
538
+ });
539
+ });
540
+
541
+ test(`should fail if the entity doesn't exist`, async () => {
542
+ expect(
543
+ await instance.update(entityUID, Math.random() * (10000 - 100) + 100, {})
544
+ ).toBeNull();
545
+ });
546
+
547
+ test('should successfully update an existing relation', async () => {
548
+ const data = {
549
+ Name: 'TestEntry',
550
+ addresses: {
551
+ connect: [
552
+ {
553
+ id: 1,
554
+ },
555
+ ],
556
+ },
557
+ };
558
+ expect(await instance.update(entityUID, 0, { data })).toMatchObject({
559
+ ...fakeEntities[entityUID][0],
560
+ addresses: {
561
+ count: 1,
562
+ },
563
+ });
564
+ });
565
+
566
+ test('should throw an error when trying to associate a relation that does not exist', async () => {
567
+ const data = {
568
+ Name: 'TestEntry',
569
+ addresses: {
570
+ connect: [
571
+ {
572
+ id: 3,
573
+ },
574
+ ],
575
+ },
576
+ };
577
+
578
+ const res = instance.update(entityUID, 0, { data });
579
+ await expect(res).rejects.toThrowError(
580
+ new ValidationError(
581
+ `1 relation(s) of type api::relation.relation associated with this entity do not exist`
582
+ )
583
+ );
584
+ });
585
+ });
586
+ });
587
+ });