data-primals-engine 1.2.6 → 1.3.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +440 -0
  2. package/README.md +782 -712
  3. package/client/src/App.jsx +13 -20
  4. package/client/src/App.scss +4 -0
  5. package/client/src/AssistantChat.jsx +5 -4
  6. package/client/src/DataEditor.jsx +1 -1
  7. package/client/src/DataLayout.jsx +2 -2
  8. package/client/src/DataTable.jsx +66 -5
  9. package/client/src/ExportDialog.jsx +2 -2
  10. package/client/src/Field.jsx +5 -4
  11. package/client/src/HistoryDialog.jsx +96 -0
  12. package/client/src/HistoryDialog.scss +73 -0
  13. package/client/src/KanbanCard.jsx +4 -2
  14. package/client/src/KanbanConfigModal.jsx +5 -7
  15. package/client/src/ModelCreator.jsx +36 -15
  16. package/client/src/ModelCreatorField.jsx +14 -15
  17. package/client/src/PackGallery.jsx +89 -9
  18. package/client/src/PackGallery.scss +58 -4
  19. package/client/src/RTETrans.jsx +11 -0
  20. package/client/src/RelationValue.jsx +3 -4
  21. package/client/src/constants.js +1 -1
  22. package/client/src/core/data.js +2 -1
  23. package/client/src/hooks/useTutorials.jsx +1 -1
  24. package/client/src/translations.js +217 -0
  25. package/package.json +9 -2
  26. package/server.js +4 -4
  27. package/src/HistoryDialog.jsx +86 -0
  28. package/src/HistoryDialog.scss +77 -0
  29. package/src/constants.js +6 -0
  30. package/src/defaultModels.js +23 -10
  31. package/src/email.js +1 -1
  32. package/src/engine.js +6 -6
  33. package/src/events.js +15 -5
  34. package/src/gameObject.js +0 -29
  35. package/src/i18n.js +1 -1
  36. package/src/modules/{assistant.js → assistant/assistant.js} +42 -27
  37. package/src/modules/assistant/constants.js +16 -0
  38. package/src/modules/data/data.history.js +304 -0
  39. package/src/modules/data/data.js +306 -238
  40. package/src/modules/data/data.routes.js +33 -7
  41. package/src/modules/mongodb.js +17 -1
  42. package/src/modules/user.js +21 -4
  43. package/src/modules/workflow.js +133 -48
  44. package/src/packs.js +1016 -9
  45. package/src/services/index.js +11 -0
  46. package/src/services/stripe.js +195 -0
  47. package/src/workers/crypto-worker.js +3 -3
  48. package/test/data.backup.integration.test.js +5 -0
  49. package/test/data.history.integration.test.js +193 -0
  50. package/test/data.integration.test.js +144 -41
  51. package/test/events.test.js +27 -27
  52. package/test/globalTeardown.js +1 -0
  53. package/test/model.integration.test.js +5 -0
  54. package/test/workflow.actions.integration.test.js +4 -2
  55. package/test/workflow.integration.test.js +3 -1
  56. package/test/workflow.robustness.test.js +2 -0
@@ -1,6 +1,6 @@
1
1
  // __tests__/data.integration.test.js
2
2
  import { ObjectId } from 'mongodb';
3
- import {vi, expect, describe, it, beforeEach, beforeAll, afterAll} from 'vitest';
3
+ import {vi, expect, describe, it, afterEach,beforeEach, beforeAll, afterAll} from 'vitest';
4
4
  import { Config } from '../src/config.js';
5
5
  import {
6
6
  insertData,
@@ -16,6 +16,7 @@ import {
16
16
  } from '../src/modules/mongodb.js';
17
17
  import {getRandom} from "../src/core.js";
18
18
  import {generateUniqueName, initEngine} from "../src/setenv.js";
19
+ import {purgeData} from "../src/modules/data/data.history.js";
19
20
 
20
21
  let testModelsColInstance;
21
22
  let testDatasColInstance;
@@ -29,7 +30,7 @@ async function setupTestContext() {
29
30
  // Créer un utilisateur unique pour ce test
30
31
  const currentTestUser = {
31
32
  username: generateUniqueName('testuserDataIntegration'),
32
- userPlan: 'free',
33
+ userPlan: 'premium',
33
34
  email: generateUniqueName('test') + '@example.com'
34
35
  };
35
36
 
@@ -111,11 +112,16 @@ async function setupTestContext() {
111
112
  }
112
113
  await testDatasColInstance.deleteMany({ _user: currentTestUser.username });
113
114
 
115
+ const purge = async ()=>{
116
+ await purgeData(currentTestUser);
117
+ }
118
+
114
119
  // Retourner toutes les variables nécessaires pour un test
115
120
  return {
116
121
  currentTestUser,
117
122
  comprehensiveTestModelDefinition,
118
- relatedModelDefinition
123
+ relatedModelDefinition,
124
+ purge
119
125
  };
120
126
  }
121
127
 
@@ -127,11 +133,10 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
127
133
  engine = await initEngine();
128
134
 
129
135
  })
130
-
131
136
  describe('insertData avec comprehensiveTestModel', () => {
132
137
  it('devrait insérer des données valides pour tous les types de champs', async () => {
133
138
 
134
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
139
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
135
140
  // 1. Créer un suffixe unique pour cette exécution de test
136
141
  const testSuffix = getRandom(1000, 9999); // Génère un nombre aléatoire
137
142
 
@@ -197,125 +202,143 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
197
202
  expect(new Date(insertedDoc.datetimeField).getFullYear()).toBe(new Date().getFullYear());
198
203
  expect(insertedDoc.booleanDefault).toBe(true);
199
204
  expect(insertedDoc.relationSingle.toString()).toBe(relatedDoc1.insertedIds[0].toString());
205
+
206
+ await purge();
200
207
  }, 10000);
201
208
 
202
209
  describe('Validations des champs à l\'insertion', () => {
203
210
  // --- STRING VALIDATIONS ---
204
211
  it('devrait rejeter un string requis non fourni', async () => {
205
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
212
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
206
213
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringUnique: 'test' /* stringRequired manquant */ }, {}, currentTestUser, false);
207
214
  expect(result.success).toBe(false);
208
215
  expect(result.error).toContain('stringRequired');
216
+ await purge();
209
217
  });
210
218
 
211
219
  it('devrait rejeter un string trop long pour maxlength', async () => {
212
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
220
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
213
221
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', stringMaxLength: '123456' }, {}, currentTestUser, false);
214
222
  expect(result.success).toBe(false);
215
223
  expect(result.error).toContain('stringMaxLength');
224
+ await purge();
216
225
  });
217
226
 
218
227
  it('devrait rejeter une valeur dupliquée pour un champ unique (stringUnique)', async () => {
219
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
228
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
220
229
  await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req1', stringUnique: 'duplicateMe', 'passwordField': 'pass', number: 1, enumField: 'test' }, {}, currentTestUser, false);
221
230
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req2', stringUnique: 'duplicateMe', 'passwordField': 'pass', number: 1, enumField: 'test' }, {}, currentTestUser, false);
222
231
  expect(result.success).toBe(false);
232
+ await purge();
223
233
  });
224
234
 
225
235
  // --- NUMBER VALIDATIONS ---
226
236
  it('devrait rejeter un number non numérique', async () => {
227
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
237
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
228
238
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', number: 'not-a-number' }, {}, currentTestUser, false);
229
239
  expect(result.success).toBe(false);
230
240
  expect(result.error).toContain('number');
241
+ await purge();
231
242
  });
232
243
 
233
244
  it('devrait rejeter un number infur à min (numberMinMax)', async () => {
234
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
245
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
235
246
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', numberMinMax: 5 }, {}, currentTestUser, false);
236
247
  expect(result.success).toBe(false);
237
248
  expect(result.error).toContain('numberMinMax');
249
+ await purge();
238
250
  });
239
251
 
240
252
  it('devrait rejeter un number supérieur à max (numberMinMax)', async () => {
241
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
253
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
242
254
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', numberMinMax: 25 }, {}, currentTestUser, false);
243
255
  expect(result.success).toBe(false);
244
256
  expect(result.error).toContain('numberMinMax');
257
+ await purge();
245
258
  });
246
259
 
247
260
  // --- ARRAY VALIDATIONS ---
248
261
  it('devrait rejeter un array avec trop d\'items (arrayString maxItems: 2)', async () => {
249
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
262
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
250
263
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', arrayString: ['a', 'b', 'c'] }, {}, currentTestUser, false);
251
264
  expect(result.success).toBe(false);
252
265
  expect(result.error).toContain('arrayString');
266
+ await purge();
253
267
  });
254
268
 
255
269
  it('devrait rejeter un array avec pas assez d\'items (arrayNumber minItems: 1)', async () => {
256
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
270
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
257
271
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', arrayNumber: [] }, {}, currentTestUser, false);
258
272
  expect(result.success).toBe(false);
259
273
  expect(result.error).toContain('arrayNumber');
274
+ await purge();
260
275
  });
261
276
 
262
277
  it('devrait rejeter un array avec un item de type incorrect (arrayNumber)', async () => {
263
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
278
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
264
279
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', arrayNumber: [1, 'not-a-number', 3] }, {}, currentTestUser, false);
265
280
  expect(result.success).toBe(false);
266
281
  expect(result.error).toContain('arrayNumber');
282
+ await purge();
267
283
  });
268
284
 
269
285
  // --- FILE VALIDATIONS (metadata) ---
270
286
  it('devrait rejeter un file avec un mimeType non autorisé', async () => {
271
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
287
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
272
288
  const mockFile = { name: 'test.txt', type: 'text/plain', size: 1024, guid: 'dummy-txt' };
273
289
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', fileField: mockFile }, {}, currentTestUser, false);
274
290
  expect(result.success).toBe(false);
275
291
  expect(result.error).toContain('image/png'); // Ou un message plus spécifique sur mimeTypes
276
292
  expect(result.error).toContain('text/plain'); // Ou un message plus spécifique sur mimeTypes
293
+ await purge();
277
294
  });
278
295
 
279
296
  it('devrait rejeter un file avec une taille supérieure à maxSize', async () => {
280
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
297
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
281
298
  const mockFile = { name: 'large.png', type: 'image/png', size: 1024 * 15, guid: 'dummy-large' }; // 15KB > 10KB
282
299
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', fileField: mockFile }, {}, currentTestUser, false);
283
300
  expect(result.success).toBe(false);
284
301
  expect(result.error).toContain('fileField'); // Ou un message plus spécifique sur maxSize
302
+ await purge();
285
303
  });
286
304
 
287
305
  // --- EMAIL, URL, PHONE, COLOR (basic format) ---
288
306
  it('devrait rejeter un email invalide', async () => {
289
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
307
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
290
308
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', emailField: 'not-an-email' }, {}, currentTestUser, false);
291
309
  expect(result.success).toBe(false);
292
310
  expect(result.error).toContain('emailField');
311
+ await purge();
293
312
  });
294
313
  it('devrait rejeter une URL invalide', async () => {
295
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
314
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
296
315
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', urlField: 'not a url' }, {}, currentTestUser, false);
297
316
  expect(result.success).toBe(false);
298
317
  expect(result.error).toContain('urlField');
318
+ await purge();
299
319
  });
300
320
  it('devrait rejeter une couleur invalide', async () => {
301
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
321
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
302
322
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', colorField: '#GGHHII' }, {}, currentTestUser, false);
303
323
  expect(result.success).toBe(false);
304
324
  expect(result.error).toContain('colorField');
325
+ await purge();
305
326
  });
306
327
 
307
328
  // --- DATE / DATETIME VALIDATIONS ---
308
329
  it('devrait rejeter une date invalide (format)', async () => {
309
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
330
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
310
331
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', dateField: 'not-a-date' }, {}, currentTestUser, false);
311
332
  expect(result.success).toBe(false);
312
333
  expect(result.error).toContain('dateField');
334
+ await purge();
313
335
  });
314
336
  it('devrait rejeter une date en dehors de min/max (dateMinMax)', async () => {
315
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
337
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
316
338
  const result = await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'req', dateMinMax: '2024-01-01' }, {}, currentTestUser, false);
317
339
  expect(result.success).toBe(false);
318
340
  expect(result.error).toContain('dateMinMax');
341
+ await purge();
319
342
  });
320
343
 
321
344
  // TODO: Ajouter des tests pour les autres types et contraintes (password, richtext, code, object, model, modelField)
@@ -326,7 +349,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
326
349
  // --- NOUVEAU TEST POUR $find ---
327
350
  it('devrait insérer des données avec des relations référencées par $find', async () => {
328
351
  // 1. Insérer les documents liés qui seront trouvés par $find
329
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
352
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
330
353
  const relatedDocA = await insertData(relatedModelDefinition.name, { relatedName: 'FindMeA', relatedValue: 111 }, {}, currentTestUser, false);
331
354
  const relatedDocB = await insertData(relatedModelDefinition.name, { relatedName: 'FindMeB', relatedValue: 222 }, {}, currentTestUser, false);
332
355
  const relatedDocC = await insertData(relatedModelDefinition.name, { relatedName: 'FindMeC', relatedValue: 222 }, {}, currentTestUser, false);
@@ -389,6 +412,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
389
412
  expect(insertedDoc.stringRequired).toBe('DocWithFind');
390
413
  expect(insertedDoc.enumField).toBe('beta');
391
414
 
415
+ await purge();
392
416
  }, 10000); // Augmenter le timeout si nécessaire
393
417
  });
394
418
 
@@ -397,7 +421,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
397
421
  let relatedDocId;
398
422
 
399
423
  const initTest = async () => {
400
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
424
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
401
425
  // Insert a base document and a related document to be used in edit tests
402
426
  const relatedInsert = await insertData(relatedModelDefinition.name, { relatedName: 'RelForEdit', relatedValue: 50 }, {}, currentTestUser, false);
403
427
  relatedDocId = relatedInsert.insertedIds[0];
@@ -412,10 +436,10 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
412
436
  const insertResult = await insertData(comprehensiveTestModelDefinition.name, initialData, {}, currentTestUser, false);
413
437
  docToEditId = insertResult.insertedIds[0];
414
438
 
415
- return { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition };
439
+ return { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge };
416
440
  };
417
441
  it('devrait modifier des données valides', async () => {
418
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
442
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
419
443
  const dataToEdit = { stringRequired: 'Edited String', numberMinMax: 12 };
420
444
  const result = await editData(comprehensiveTestModelDefinition.name, docToEditId, dataToEdit, {}, currentTestUser);
421
445
 
@@ -425,24 +449,29 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
425
449
  const editedDoc = await testDatasColInstance.findOne({ _id: new ObjectId(docToEditId) });
426
450
  expect(editedDoc.stringRequired).toBe('Edited String');
427
451
  expect(editedDoc.numberMinMax).toBe(12);
452
+
453
+ await purge();
428
454
  });
429
455
 
430
456
  it('devrait rejeter une modification avec un string requis vide', async () => {
431
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
457
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
432
458
  const result = await editData(comprehensiveTestModelDefinition.name, docToEditId, { stringRequired: '' }, {}, currentTestUser);
433
459
  expect(result.success).toBe(false);
434
460
  expect(result.error).toContain('stringRequired');
461
+
462
+ await purge();
435
463
  });
436
464
 
437
465
  it('devrait rejeter une modification avec un number hors des bornes (numberMinMax)', async () => {
438
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
466
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
439
467
  const result = await editData(comprehensiveTestModelDefinition.name, docToEditId, { stringRequired: 'test', numberMinMax: 50 }, {}, currentTestUser);
440
468
  expect(result.success).toBe(false);
441
469
  expect(result.error).toContain('numberMinMax');
470
+ await purge();
442
471
  });
443
472
 
444
473
  it('devrait rejeter une modification qui viole une contrainte unique (stringUnique)', async ({skip}) => {
445
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
474
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
446
475
  // Insert another doc to create the unique conflict
447
476
 
448
477
  await insertData(comprehensiveTestModelDefinition.name, { stringRequired: 'Other', stringUnique: 'UniqueValue', enumField: 'beta', passwordField: 'pass' }, {}, currentTestUser, false);
@@ -453,6 +482,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
453
482
  expect(result.success).toBe(false);
454
483
  expect(result.error).toContain('UniqueValue');
455
484
  expect(result.error).toContain('stringUnique');
485
+ await purge();
456
486
  });
457
487
 
458
488
  // TODO: Ajouter plus de tests de validation pour editData, similaires à ceux de insertData
@@ -461,7 +491,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
461
491
  // --- Tests deleteData (repris de votre exemple, peuvent être gardés tels quels ou adaptés) ---
462
492
  describe('deleteData (tests existants)', () => {
463
493
  it('supprimer des données par ID (deleteData)', async () => {
464
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
494
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
465
495
  const dataToDelete = { name: 'Objet à supprimer', value: 300 };
466
496
  // Utiliser le modèle simple pour ces tests delete pour ne pas avoir à remplir tous les champs requis de comprehensive
467
497
  const simpleModel = { name: 'simpleDeleteModel', _user: currentTestUser.username, description: '', fields: [{ name: 'name', type: 'string' }, { name: 'value', type: 'number' }] };
@@ -477,10 +507,12 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
477
507
 
478
508
  const deletedDoc = await testDatasColInstance.findOne({ _id: new ObjectId(docId) });
479
509
  expect(deletedDoc).toBeNull();
510
+ await purge();
511
+ await purge();
480
512
  });
481
513
 
482
514
  it('supprimer des données par filtre (deleteData)', async () => {
483
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
515
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
484
516
  const simpleModel = { name: 'deleteModelTest', _user: currentTestUser.username, description: '',fields: [{ name: 'name', type: 'string' }, { name: 'value', type: 'number' }] };
485
517
  await testModelsColInstance.insertOne(simpleModel);
486
518
 
@@ -501,6 +533,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
501
533
  expect(remainingCount).toBe(0);
502
534
  const totalRemaining = await testDatasColInstance.countDocuments({ _user: currentTestUser.username, _model: simpleModel.name });
503
535
  expect(totalRemaining).toBe(1);
536
+ await purge();
504
537
  });
505
538
  });
506
539
 
@@ -509,7 +542,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
509
542
  let relatedDocId_A1, relatedDocId_A2, relatedDocId_B1;
510
543
 
511
544
  const initTest =async () => {
512
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
545
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
513
546
 
514
547
  // Insérer des documents liés
515
548
  const relA1 = await insertData(relatedModelDefinition.name, { relatedName: 'Rel_Search_A1', relatedValue: 101 }, {}, currentTestUser, false);
@@ -544,11 +577,11 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
544
577
  const insertResult2 = await insertData(comprehensiveTestModelDefinition.name, data2, {}, currentTestUser, false);
545
578
  docId2 = insertResult2.insertedIds[0].toString();
546
579
 
547
- return { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition };
580
+ return { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge };
548
581
  };
549
582
 
550
583
  it('devrait trouver un document par une condition sur une relation simple ($find)', async () => {
551
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
584
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
552
585
  const searchParams = {
553
586
  model: comprehensiveTestModelDefinition.name,
554
587
  filter: {
@@ -566,10 +599,11 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
566
599
  // Vérifier que la relation est bien populée si depth > 0
567
600
  expect(data[0].relationSingle).toBeInstanceOf(Object);
568
601
  expect(data[0].relationSingle.relatedName).toBe('Rel_Search_A1');
602
+ await purge();
569
603
  });
570
604
 
571
605
  it('ne devrait pas trouver de document si la condition $find sur relation simple ne correspond pas', async () => {
572
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
606
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
573
607
  const searchParams = {
574
608
  model: comprehensiveTestModelDefinition.name,
575
609
  filter: {
@@ -582,10 +616,11 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
582
616
  const { data, count } = await searchData(searchParams, currentTestUser);
583
617
  expect(count).toBe(0);
584
618
  expect(data).toHaveLength(0);
619
+ await purge();
585
620
  });
586
621
 
587
622
  it('devrait trouver des documents par une condition sur une relation multiple ($find)', async () => {
588
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
623
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
589
624
  const searchParams = {
590
625
  model: comprehensiveTestModelDefinition.name,
591
626
  filter: {
@@ -604,11 +639,12 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
604
639
  // Note: searchData with depth > 0 will populate the relationMultiple array
605
640
  expect(data[0].relationMultiple).toBeInstanceOf(Array);
606
641
  expect(data[0].relationMultiple.some(rel => rel.relatedValue === 102)).toBe(true);
642
+ await purge();
607
643
  });
608
644
 
609
645
 
610
646
  it('devrait trouver des documents en combinant un filtre normal et un filtre $find', async () => {
611
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
647
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
612
648
  const searchParams = {
613
649
  model: comprehensiveTestModelDefinition.name,
614
650
  filter: {
@@ -623,10 +659,11 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
623
659
  expect(count).toBe(1);
624
660
  expect(data).toHaveLength(1);
625
661
  expect(data[0]._id.toString()).toBe(docId1.toString());
662
+ await purge();
626
663
  });
627
664
 
628
665
  it('devrait retourner un tableau vide si $find ne correspond à aucun document lié', async () => {
629
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await initTest();
666
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await initTest();
630
667
  const searchParams = {
631
668
  model: comprehensiveTestModelDefinition.name,
632
669
  filter: {
@@ -639,6 +676,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
639
676
  const { data, count } = await searchData(searchParams,currentTestUser);
640
677
  expect(count).toBe(0);
641
678
  expect(data).toHaveLength(0);
679
+ await purge();
642
680
  });
643
681
 
644
682
  });
@@ -658,7 +696,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
658
696
  });
659
697
 
660
698
  it('devrait installer un pack, créer les modèles et insérer les données avec relations via $link', async () => {
661
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
699
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
662
700
  // 1. Définir le pack de données mock
663
701
  const mockPack = {
664
702
  name: 'test-pack-for-install',
@@ -697,7 +735,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
697
735
 
698
736
  // 2. Insérer le pack dans la collection pour le rendre disponible au test
699
737
  const packInsertResult = await testPacksColInstance.insertOne(mockPack);
700
- const packId = packInsertResult.insertedId;
738
+ const packId = packInsertResult.insertedId.toString();
701
739
 
702
740
  // 3. Appeler la fonction à tester avec la nouvelle signature
703
741
  const result = await installPack(packId, currentTestUser, 'en');
@@ -729,10 +767,11 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
729
767
  const playerTwo = insertedUsers.find(u => u.username === 'playerTwo');
730
768
  const shield = insertedItems.find(i => i.itemName === 'Shield');
731
769
  expect(shield.owner.toString()).toBe(playerTwo._id.toString());
770
+ await purge();
732
771
  }, 10000);
733
772
 
734
773
  it("ne devrait installer que les modèles valides d'un pack et rapporter les erreurs", async () => {
735
- const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition } = await setupTestContext();
774
+ const { currentTestUser, comprehensiveTestModelDefinition, relatedModelDefinition, purge } = await setupTestContext();
736
775
  const mockPackWithInvalidModel = {
737
776
  name: 'invalid-model-pack',
738
777
  models: [
@@ -751,7 +790,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
751
790
  };
752
791
 
753
792
  const packInsertResult = await testPacksColInstance.insertOne(mockPackWithInvalidModel);
754
- const packId = packInsertResult.insertedId;
793
+ const packId = packInsertResult.insertedId.toString();
755
794
 
756
795
  const result = await installPack(packId, currentTestUser, 'en');
757
796
 
@@ -769,6 +808,70 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
769
808
  // Vérifier en BDD que le modèle valide a bien été créé
770
809
  const validModel = await testModelsColInstance.findOne({ name: 'validPackModel', _user: currentTestUser.username });
771
810
  expect(validModel).not.toBeNull();
811
+ await purge();
812
+ });
813
+
814
+ it('devrait correctement insérer des données JSON depuis un pack', async () => {
815
+ const { currentTestUser } = await setupTestContext();
816
+ // 1. Définir le pack avec des données JSON
817
+ const mockPackWithJson = {
818
+ name: 'test-pack-with-json',
819
+ models: [
820
+ {
821
+ name: 'packConfig',
822
+ description: 'Modèle avec un champ JSON',
823
+ fields: [
824
+ { name: 'configName', type: 'string', required: true },
825
+ { name: 'settings', type: 'code', language: 'json' }
826
+ ]
827
+ }
828
+ ],
829
+ data: {
830
+ all: {
831
+ packConfig: [
832
+ {
833
+ configName: 'mainConfig',
834
+ settings: {
835
+ theme: 'dark',
836
+ notifications: {
837
+ enabled: true,
838
+ level: 'important'
839
+ }
840
+ }
841
+ }
842
+ ]
843
+ }
844
+ }
845
+ };
846
+
847
+ // 2. Insérer le pack
848
+ const packInsertResult = await testPacksColInstance.insertOne(mockPackWithJson);
849
+ const packId = packInsertResult.insertedId.toString();
850
+
851
+ // 3. Appeler la fonction à tester
852
+ const result = await installPack(packId, currentTestUser, 'en');
853
+
854
+ // 4. Assertions
855
+ expect(result.success, `L'installation du pack JSON a échoué: ${result.errors?.join('; ')}`).toBe(true);
856
+ expect(result.summary.models.installed).toHaveLength(1);
857
+ expect(result.summary.datas.inserted).toBe(1);
858
+
859
+ // 5. Vérifier les données insérées en BDD
860
+ const datasCol = await getCollectionForUser(currentTestUser);
861
+ const insertedConfig = await datasCol.findOne({ _model: 'packConfig', _user: currentTestUser.username });
862
+
863
+ expect(insertedConfig).not.toBeNull();
864
+ expect(insertedConfig.configName).toBe('mainConfig');
865
+ expect(typeof insertedConfig.settings).toBe('object');
866
+ expect(insertedConfig.settings).toEqual({
867
+ theme: 'dark',
868
+ notifications: {
869
+ enabled: true,
870
+ level: 'important'
871
+ }
872
+ });
873
+
874
+ await getCollection('packs').deleteMany({ _id: packId});
772
875
  });
773
876
  });
774
877
  // In test/data.integration.test.js