data-primals-engine 1.2.2 → 1.2.4

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 (54) hide show
  1. package/CONTRIBUTING.md +91 -0
  2. package/README.md +33 -17
  3. package/client/src/App.jsx +0 -5
  4. package/client/src/ConditionBuilder.scss +34 -1
  5. package/client/src/ConditionBuilder2.jsx +179 -53
  6. package/client/src/ContentView.jsx +0 -3
  7. package/client/src/CronBuilder.jsx +0 -1
  8. package/client/src/CronPartBuilder.jsx +0 -2
  9. package/client/src/DashboardView.jsx +0 -5
  10. package/client/src/DataEditor.jsx +8 -211
  11. package/client/src/DataLayout.jsx +0 -1
  12. package/client/src/DataTable.jsx +1 -3
  13. package/client/src/Field.jsx +0 -5
  14. package/client/src/FlexBuilder.jsx +1 -1
  15. package/client/src/ModelCreatorField.jsx +1 -5
  16. package/client/src/RTE.jsx +1 -6
  17. package/client/src/RTETrans.jsx +0 -2
  18. package/client/src/RelationField.jsx +1 -1
  19. package/client/src/RelationValue.jsx +1 -2
  20. package/client/src/TourSpotlight.jsx +0 -2
  21. package/client/src/constants.js +1 -1
  22. package/client/src/filter.js +87 -0
  23. package/client/src/hooks/data.js +1 -3
  24. package/client/src/hooks/useTutorials.jsx +0 -1
  25. package/package.json +3 -3
  26. package/server.js +2 -2
  27. package/src/constants.js +2 -2
  28. package/src/defaultModels.js +1 -14
  29. package/src/email.js +9 -7
  30. package/src/engine.js +60 -20
  31. package/src/events.js +1 -1
  32. package/src/filter.js +221 -0
  33. package/src/index.js +1 -1
  34. package/src/middlewares/middleware-mongodb.js +0 -1
  35. package/src/modules/assistant.js +1 -3
  36. package/src/modules/bucket.js +3 -4
  37. package/src/modules/{data.js → data/data.js} +42 -59
  38. package/src/modules/data/index.js +1 -0
  39. package/src/modules/file.js +1 -1
  40. package/src/modules/mongodb.js +0 -1
  41. package/src/modules/user.js +1 -1
  42. package/src/modules/workflow.js +299 -133
  43. package/src/packs.js +249 -8
  44. package/test/data.backup.integration.test.js +7 -5
  45. package/test/data.integration.test.js +8 -6
  46. package/test/events.test.js +1 -1
  47. package/test/file.test.js +11 -17
  48. package/test/import_export.integration.test.js +38 -27
  49. package/test/model.integration.test.js +20 -21
  50. package/test/user.test.js +32 -25
  51. package/test/vm.test.js +51 -0
  52. package/test/workflow.integration.test.js +22 -14
  53. package/test/workflow.robustness.test.js +19 -9
  54. package/src/modules/test +0 -147
@@ -1,34 +1,33 @@
1
1
  // __tests__/data.integration.test.js
2
2
  import { ObjectId } from 'mongodb';
3
- import {expect, describe, it, beforeEach, afterEach, beforeAll, afterAll, vi} from 'vitest';
3
+ import {expect, describe, it, beforeAll} from 'vitest';
4
4
  import { Config } from '../src/config.js';
5
5
 
6
6
  import {
7
- modelsCollection as getAppModelsCollection,
8
- datasCollection // Accès direct pour vérifications
9
- } from 'data-primals-engine/modules/mongodb';
10
- import {generateUniqueName, getUniquePort, initEngine} from "../src/setenv.js";
11
- import {editModel} from "../src/modules/data.js";
7
+ getCollection // Accès direct pour vérifications
8
+ } from '../src/modules/mongodb.js';
9
+ import {generateUniqueName, initEngine} from "../src/setenv.js";
10
+ import {editModel} from "../src/index.js";
12
11
  import {getCollectionForUser} from "../src/modules/mongodb.js";
13
12
 
14
13
  let testModelsColInstance;
15
- let testDatasColInstance;
16
-
17
14
  let testModelId;
18
-
19
15
  // Cette fonction va remplacer la logique de votre beforeEach pour la création de contexte
20
16
  async function setupTestContext() {
21
17
 
22
18
  const currentTestModelName = generateUniqueName('relatedModel');
23
19
  const currentRelatedModelName = generateUniqueName('comprehensiveModel');
24
20
 
21
+
25
22
  // Créer un utilisateur unique pour ce test
26
23
  const currentTestUser = {
27
- username: generateUniqueName('testuserDataIntegration'),
24
+ username: generateUniqueName('testuserModelIntegration'),
28
25
  userPlan: 'free',
29
26
  email: generateUniqueName('test') + '@example.com'
30
27
  };
31
28
 
29
+ testModelsColInstance = getCollection("models");
30
+ const testDatasColInstance = await getCollectionForUser(currentTestUser);
32
31
 
33
32
  const relatedModelDefinition = {
34
33
  name: currentRelatedModelName,
@@ -105,43 +104,42 @@ async function setupTestContext() {
105
104
  await testDatasColInstance.deleteMany({ _user: currentTestUser.username });
106
105
  await testDatasColInstance.deleteMany({ _model: { $in: [comprehensiveTestModelDefinition.name, 'renamedTestModel'] } });
107
106
  // Retourner toutes les variables nécessaires pour un test
107
+
108
108
  return {
109
109
  currentTestUser,
110
+ coll:testDatasColInstance,
110
111
  comprehensiveTestModelDefinition,
111
112
  relatedModelDefinition
112
113
  };
113
114
  }
114
115
 
116
+
115
117
  describe('CRUD on model definitions and integrity tests', () => {
116
118
 
117
119
  beforeAll(async () =>{
118
120
  Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
119
121
  await initEngine();
120
-
121
- // Initialize collection instances after the engine is ready
122
- testModelsColInstance = getAppModelsCollection;
123
- testDatasColInstance = datasCollection;
124
122
  })
123
+
125
124
  describe('editModel unit tests', () => {
126
125
 
127
126
  it('should create and drop index when field.index is toggled (premium user)', async () => {
128
127
  // --- SETUP ---
129
- const { currentTestUser, comprehensiveTestModelDefinition } = await setupTestContext();
130
- const dataCollection = await getCollectionForUser(currentTestUser);
128
+ const { coll, currentTestUser, comprehensiveTestModelDefinition } = await setupTestContext();
131
129
  const fieldToIndex = 'stringUnique'; // Utiliser un champ qui existe vraiment dans le modèle
132
130
 
133
131
  // --- FIX: Ensure the collection exists before any operation ---
134
132
  // By inserting and deleting a dummy document, we force MongoDB to create the
135
133
  // collection and its default indexes. This prevents the "ns does not exist"
136
134
  // error in asynchronous listeners (like workflow triggers).
137
- const dummyDoc = await dataCollection.insertOne({ _model: 'dummy', _user: currentTestUser.username });
138
- await dataCollection.deleteOne({ _id: dummyDoc.insertedId });
135
+ const dummyDoc = await coll.insertOne({ _model: 'dummy', _user: currentTestUser.username });
136
+ await coll.deleteOne({ _id: dummyDoc.insertedId });
139
137
 
140
138
 
141
139
  // --- VERIFICATION INITIALE ---
142
140
  // S'assurer qu'aucun index n'existe au départ.
143
141
  // Cet appel ne plantera plus car la collection est maintenant créée.
144
- const initialIndexes = await dataCollection.indexes();
142
+ const initialIndexes = await coll.indexes();
145
143
  expect(initialIndexes.some(i => i.key[fieldToIndex] === 1)).toBe(false);
146
144
 
147
145
  // --- ACTION 1 : AJOUTER UN INDEX ---
@@ -155,7 +153,7 @@ describe('CRUD on model definitions and integrity tests', () => {
155
153
 
156
154
  // --- VERIFICATION 1 ---
157
155
  // Maintenant, la collection et l'index doivent exister.
158
- const indexesAfterCreation = await dataCollection.indexes();
156
+ const indexesAfterCreation = await coll.indexes();
159
157
  const newIndex = indexesAfterCreation.find(i => i.key[fieldToIndex] === 1);
160
158
 
161
159
  expect(newIndex).toBeDefined();
@@ -175,8 +173,9 @@ describe('CRUD on model definitions and integrity tests', () => {
175
173
  await editModel(currentTestUser, testModelId, modelWithoutIndex);
176
174
 
177
175
  // --- VERIFICATION 2 ---
178
- const indexesAfterDeletion = await dataCollection.indexes();
176
+ const indexesAfterDeletion = await coll.indexes();
179
177
  expect(indexesAfterDeletion.some(i => i.key[fieldToIndex] === 1)).toBe(false);
178
+
180
179
  }, 20000);
181
180
 
182
181
  it('should not save extra, non-defined fields in the model definition', async () => {
package/test/user.test.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { expect, describe, it, beforeEach, beforeAll, afterAll } from 'vitest';
2
2
  import { hasPermission, getUserActivePermissions } from '../src/modules/user.js';
3
- import {initEngine} from "data-primals-engine/setenv";
3
+ import {initEngine} from "../src/setenv";
4
4
  import {generateUniqueName} from "../src/setenv.js";
5
5
  import {
6
6
  getCollectionForUser as getAppUserCollection,
7
7
  modelsCollection as getAppModelsCollection
8
- } from "data-primals-engine/modules/mongodb";
8
+ } from "../src/modules/mongodb.js";
9
9
  import {Config} from "../src/index.js";
10
10
  let permRead, permWrite, permDelete, permManage;
11
11
  let roleEditor;
@@ -17,7 +17,7 @@ async function setupTestContext() {
17
17
 
18
18
 
19
19
  // Créer un utilisateur unique pour ce test
20
- const username = generateUniqueName('testuserDataIntegration');
20
+ const username = generateUniqueName('testuserUserIntegration');
21
21
  currentTestUser = {
22
22
  username,
23
23
  userPlan: 'free',
@@ -58,6 +58,8 @@ async function setupTestContext() {
58
58
  ]);
59
59
  testUser = {...currentTestUser, _id: users.insertedIds[0].toString(), roles: [roleEditor.toString()] };
60
60
  adminUser = {...currentTestUser, _id: users.insertedIds[1].toString(), roles: [roleEditor.toString()] };
61
+
62
+ return testDatasColInstance;
61
63
  };
62
64
  let engine;
63
65
  describe('User Permission Logic', () => {
@@ -72,26 +74,24 @@ describe('User Permission Logic', () => {
72
74
  // --- Tests pour la fonction principale de calcul ---
73
75
  describe('getUserActivePermissions()', () => {
74
76
 
75
-
76
- beforeEach(async () =>{
77
- await setupTestContext();
78
- })
79
77
  it('should return base permissions from the user\'s role', async () => {
78
+ const coll = await setupTestContext();
80
79
  const permissions = await getUserActivePermissions(testUser);
81
80
  expect(permissions).to.be.an.instanceOf(Set);
82
81
  expect(permissions.size).to.equal(2);
83
82
  expect(permissions.has('post:read')).to.be.true;
84
83
  expect(permissions.has('post:write')).to.be.true;
85
84
  expect(permissions.has('post:delete')).to.be.false;
85
+ await coll.drop();
86
86
  });
87
87
 
88
88
  it('should grant a temporary permission via an exception', async () => {
89
-
89
+ const coll = await setupTestContext();
90
90
  // Ajoute une exception qui donne la permission de supprimer, expirant demain
91
91
  const futureDate = new Date();
92
92
  futureDate.setDate(futureDate.getDate() + 1);
93
93
 
94
- await testDatasColInstance.insertOne({
94
+ await coll.insertOne({
95
95
  _model: 'userPermission',
96
96
  user: testUser._id,
97
97
  permission: permDelete,
@@ -104,15 +104,18 @@ describe('User Permission Logic', () => {
104
104
  expect(permissions.has('post:delete')).to.be.true;
105
105
 
106
106
  // Nettoyage de l'exception pour ne pas affecter les autres tests
107
- await testDatasColInstance.deleteOne({ _model: 'userPermission', user: testUser._id });
107
+ await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
108
+
109
+ await coll.drop();
108
110
  });
109
111
 
110
112
  it('should NOT grant an expired temporary permission', async () => {
113
+ const coll = await setupTestContext();
111
114
  // Ajoute une exception qui a expiré hier
112
115
  const pastDate = new Date();
113
116
  pastDate.setDate(pastDate.getDate() - 1);
114
117
 
115
- await testDatasColInstance.insertOne({
118
+ await coll.insertOne({
116
119
  _model: 'userPermission',
117
120
  user: testUser._id,
118
121
  permission: permDelete,
@@ -124,15 +127,17 @@ describe('User Permission Logic', () => {
124
127
  expect(permissions.size).to.equal(2); // Revient la normale
125
128
  expect(permissions.has('post:delete')).to.be.false;
126
129
 
127
- await testDatasColInstance.deleteOne({ _model: 'userPermission', user: testUser._id });
130
+ await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
131
+ await coll.drop();
128
132
  });
129
133
 
130
134
  it('should revoke a base permission via an exception', async () => {
135
+ const coll = await setupTestContext();
131
136
  // L'utilisateur est "Editor", mais on lui retire le droit d'écriture temporairement
132
137
  const futureDate = new Date();
133
138
  futureDate.setDate(futureDate.getDate() + 1);
134
139
 
135
- await testDatasColInstance.insertOne({
140
+ await coll.insertOne({
136
141
  _model: 'userPermission',
137
142
  user: testUser._id,
138
143
  permission: permWrite,
@@ -145,15 +150,17 @@ describe('User Permission Logic', () => {
145
150
  expect(permissions.has('post:read')).to.be.true;
146
151
  expect(permissions.has('post:write')).to.be.false; // La permission a été retirée
147
152
 
148
- await testDatasColInstance.deleteOne({ _model: 'userPermission', user: testUser._id });
153
+ await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
154
+ await coll.drop();
149
155
  });
150
156
 
151
157
  it('should restore a revoked permission if the revocation has expired', async () => {
158
+ const coll = await setupTestContext();
152
159
  // La révocation a expiré, l'utilisateur devrait retrouver son droit d'écriture
153
160
  const pastDate = new Date();
154
161
  pastDate.setDate(pastDate.getDate() - 1);
155
162
 
156
- await testDatasColInstance.insertOne({
163
+ await coll.insertOne({
157
164
  _model: 'userPermission',
158
165
  user: testUser._id,
159
166
  permission: permWrite,
@@ -165,39 +172,39 @@ describe('User Permission Logic', () => {
165
172
  expect(permissions.size).to.equal(2);
166
173
  expect(permissions.has('post:write')).to.be.true; // La permission est de retour
167
174
 
168
- await testDatasColInstance.deleteOne({ _model: 'userPermission', user: testUser._id });
175
+ await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
176
+ await coll.drop();
169
177
  });
170
178
  });
171
179
 
172
180
  // --- Tests pour la fonction publique `hasPermission` ---
173
181
  describe('hasPermission()', () => {
174
- beforeEach(async () => {
175
- await setupTestContext();
176
- });
177
182
  it('should return true for a permission the user has', async () => {
178
-
179
-
180
- console.log(await testDatasColInstance.find({ _model: 'permission' }).toArray());
181
- console.log(await testDatasColInstance.find({ _model: 'role' }).toArray());
182
- console.log(await testDatasColInstance.find({ _model: 'user' }).toArray());
183
- console.log(await testDatasColInstance.find({ _model: 'userPermission' }).toArray());
183
+ const coll = await setupTestContext();
184
184
  const result = await hasPermission('post:read', testUser);
185
185
  expect(result).to.be.true;
186
+ await coll.drop();
186
187
  });
187
188
 
188
189
  it('should return false for a permission the user does not have', async () => {
190
+ const coll = await setupTestContext();
189
191
  const result = await hasPermission('user:manage', testUser);
190
192
  expect(result).to.be.false;
193
+ await coll.drop();
191
194
  });
192
195
 
193
196
  it('should return true if user has at least one of the required permissions', async () => {
197
+ const coll = await setupTestContext();
194
198
  const result = await hasPermission(['user:manage', 'post:write'], testUser);
195
199
  expect(result).to.be.true;
200
+ await coll.drop();
196
201
  });
197
202
 
198
203
  it('should return false if user has none of the required permissions', async () => {
204
+ const coll = await setupTestContext();
199
205
  const result = await hasPermission(['user:manage', 'post:delete'], testUser);
200
206
  expect(result).to.be.false;
207
+ await coll.drop();
201
208
  });
202
209
  });
203
210
  });
@@ -0,0 +1,51 @@
1
+ import {expect, describe, it, beforeEach, afterEach, beforeAll, afterAll, vi, vitest} from 'vitest';
2
+ import ivm from "isolated-vm";
3
+ import {sleep} from "../src/core";
4
+
5
+ vi.stubEnv('ENCRYPTION_KEY', '12345678901234567890123456789012');
6
+
7
+ async function executeSafeJavascript() {
8
+ const isolate = new ivm.Isolate()
9
+
10
+ async function myAsyncFunction(str) {
11
+ await sleep(1000);
12
+ console.log("hello"+str)
13
+ }
14
+
15
+ const context = await isolate.createContext();
16
+ const jail = context.global;
17
+
18
+ await jail.set('myAsync', new ivm.Reference(myAsyncFunction));
19
+
20
+ const fn = await context.eval(`
21
+ const normalizeArgs = args => args.map(arg => {
22
+ if (typeof arg === 'object' && arg !== null) {
23
+ return JSON.stringify(arg); // Convert objects to strings
24
+ }
25
+ return arg;
26
+ });
27
+ const t = (...args) => {
28
+ myAsync.applySyncPromise(null, normalizeArgs(args));
29
+ }
30
+ (async function untrusted() {
31
+ let str = await t("ok", {"ok":true});
32
+ return str;
33
+ })
34
+ `, { reference: true })
35
+ const value = await fn.apply(undefined, [], { result: { promise: true } })
36
+ }
37
+
38
+
39
+ beforeAll(async () => {
40
+ });
41
+ describe('VM system ingration', () => {
42
+
43
+ it('should successfully add a file with local storage', async () => {
44
+ console.log(await executeSafeJavascript({
45
+ script: 'const t = await addSync();'
46
+ }, {}, { username:'test'}));
47
+
48
+
49
+ expect(true).toBe(true);
50
+ });
51
+ });
@@ -1,24 +1,21 @@
1
- // __tests__/workflow.integration.test.js
2
1
  import { expect, describe, it, beforeEach,afterEach, beforeAll, afterAll, vi } from 'vitest';
3
- import { Config } from "data-primals-engine/config";
4
-
5
- import {insertData, editData, deleteData, patchData} from 'data-primals-engine/modules/data';
6
- import { modelsCollection as getAppModelsCollection, getCollectionForUser } from 'data-primals-engine/modules/mongodb';
7
- import * as workflowModule from 'data-primals-engine/modules/workflow';
8
- import {getUniquePort, initEngine} from "../src/setenv.js";
9
- import process from "process";
2
+ import { Config } from "../src/config";
10
3
 
4
+ import {insertData, editData, deleteData, patchData} from '../src/index.js';
5
+ import { modelsCollection as getAppModelsCollection, getCollectionForUser } from '../src/modules/mongodb.js';
6
+ import * as workflowModule from '../src/modules/workflow.js';
7
+ import {initEngine} from "../src/setenv.js";
11
8
 
12
9
  beforeAll(async () =>{
13
10
  Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
14
11
  await initEngine();
15
12
  })
16
- vi.mock('data-primals-engine/modules/workflow', { spy: true })
13
+ vi.mock('../src/modules/workflow.js', { spy: true })
17
14
  // --- Données Mock pour les tests ---
18
15
  const mockUser = {
19
16
  username: 'testuserWorkflow',
20
17
  _user: 'testuserWorkflow',
21
- userPlan: 'free',
18
+ userPlan: 'premium',
22
19
  email: 'testWorkflow@example.com'
23
20
  };
24
21
 
@@ -138,8 +135,6 @@ const processWorkflowRunSpy = vi.spyOn(workflowModule, 'processWorkflowRun');
138
135
  beforeEach(async () => {
139
136
  testModelsColInstance = getAppModelsCollection;
140
137
  testDatasColInstance = await getCollectionForUser(mockUser);
141
- // Nettoyer les données avant chaque test
142
- await testDatasColInstance.deleteMany({_user: "testuserWorkflow"});
143
138
 
144
139
  // Réinitialiser l'espion
145
140
  processWorkflowRunSpy.mockClear();
@@ -152,9 +147,19 @@ beforeEach(async () => {
152
147
  ...workflowMetaModels.map(m => ({...m})) // Copie pour éviter les mutations
153
148
  ]);
154
149
  }
155
- console.log({mods})
156
- });
150
+ // tell vitest we use mocked time
151
+ vi.useFakeTimers({ shouldAdvanceTime: true })
152
+ })
157
153
 
154
+ afterEach(() => {
155
+ vi.runOnlyPendingTimers();
156
+ // restoring date after each test run
157
+ vi.useRealTimers()
158
+ })
159
+ afterAll(async () => {
160
+ const coll = await getCollectionForUser(mockUser);
161
+ await coll.drop();
162
+ })
158
163
  describe('Intégration des Workflows - triggerWorkflows', () => {
159
164
 
160
165
  let testWorkflow;
@@ -162,6 +167,9 @@ describe('Intégration des Workflows - triggerWorkflows', () => {
162
167
 
163
168
  // Avant chaque test de ce bloc, on crée un workflow et une étape de base
164
169
  const initTest = async () => {
170
+ // Nettoyer les données avant chaque test
171
+ await testDatasColInstance.deleteMany({_user: "testuserWorkflow"});
172
+
165
173
  const workflowInsertResult = await insertData('workflow', { name: 'Test Workflow' }, {}, mockUser, false);
166
174
  testWorkflow = { _id: workflowInsertResult.insertedIds[0] };
167
175
 
@@ -1,14 +1,14 @@
1
1
  import { ObjectId } from 'mongodb';
2
- import { expect, describe, it, beforeEach, beforeAll, vi } from 'vitest';
3
- import { Config } from "data-primals-engine/config";
4
- // --- Importations des modules de l'application ---
5
- import { insertData, editData } from 'data-primals-engine/modules/data';
6
- import { modelsCollection as getAppModelsCollection, getCollectionForUser, getCollection } from 'data-primals-engine/modules/mongodb';
7
- import * as workflowModule from 'data-primals-engine/modules/workflow';
2
+ import {expect, describe, it, beforeEach, beforeAll, vi, afterAll, afterEach} from 'vitest';
3
+ import { Config } from "../src/config";
4
+
5
+ import { insertData, editData } from '../src/index.js';
6
+ import { modelsCollection as getAppModelsCollection, getCollectionForUser, getCollection } from '../src/modules/mongodb.js';
7
+ import * as workflowModule from '../src/modules/workflow.js';
8
8
  import {initEngine} from "../src/setenv.js";
9
9
  import {maxExecutionsByStep} from "../src/constants.js";
10
10
 
11
- vi.mock('data-primals-engine/modules/workflow', { spy: true })
11
+ vi.mock('../src/modules/workflow.js', { spy: true })
12
12
 
13
13
  const mockUser = {
14
14
  username: 'robustnessUser',
@@ -41,12 +41,22 @@ beforeEach(async () => {
41
41
  await testDatasColInstance.deleteMany({ _user: mockUser.username });
42
42
  await getCollection('job_locks').deleteMany({}); // Nettoyer les verrous
43
43
  const mods = await testModelsColInstance.find({ $and: [{_user: mockUser.username}, {$or: [{name: targetDataModel.name}, ...workflowMetaModels.map(m =>({name: m.name}))] }]}).toArray();
44
- console.log({mods})
45
44
  if( mods.length === 0){
46
45
  await testModelsColInstance.insertMany([targetDataModel, ...workflowMetaModels]);
47
46
  }
48
- });
47
+ // tell vitest we use mocked time
48
+ vi.useFakeTimers({ shouldAdvanceTime: true })
49
+ })
49
50
 
51
+ afterEach(() => {
52
+ vi.runOnlyPendingTimers();
53
+ // restoring date after each test run
54
+ vi.useRealTimers()
55
+ })
56
+ afterAll(async () => {
57
+ const coll = await getCollectionForUser(mockUser);
58
+ await coll.drop();
59
+ })
50
60
  // ====================================================================================
51
61
  // =================== DÉBUT DES TESTS DE ROBUSTESSE ==================================
52
62
  // ====================================================================================
package/src/modules/test DELETED
@@ -1,147 +0,0 @@
1
- // src/modules/file.js
2
- import { getCollection } from "./mongodb.js";
3
- import { uuidv4, getFileExtension } from "../core.js";
4
- import { getUserS3Config, uploadToS3, deleteFromS3 } from "./bucket.js";
5
- import { maxFileSize } from "../constants.js";
6
- import path from "node:path";
7
- import fs from "node:fs";
8
- import { promises as fsPromises } from "node:fs";
9
- import { Logger } from "../gameObject.js";
10
-
11
- let logger;
12
-
13
- const getLogger = () => {
14
- if (!logger) {
15
- logger = new Logger();
16
- }
17
- return logger;
18
- };
19
-
20
- /**
21
- * Ajoute un fichier au système. Le téléverse sur S3 si configuré, sinon le sauvegarde localement.
22
- * @param {object} file - L'objet fichier provenant de express-formidable.
23
- * @param {object} user - L'objet utilisateur.
24
- * @returns {Promise<string>} Le GUID du fichier nouvellement ajouté.
25
- */
26
- export const addFile = async (file, user) => {
27
- if (!file) {
28
- throw new Error("Aucun fichier fourni.");
29
- }
30
- if (file.size > maxFileSize) {
31
- throw new Error(`La taille du fichier dépasse la limite de ${maxFileSize / 1024 / 1024} Mo.`);
32
- }
33
-
34
- const guid = uuidv4();
35
- const extension = getFileExtension(file.name);
36
- const newFilename = `${guid}.${extension}`;
37
- const s3Config = await getUserS3Config(user);
38
-
39
- const fileData = {
40
- _id: guid,
41
- guid: guid,
42
- originalFilename: file.name,
43
- size: file.size,
44
- mimetype: file.type,
45
- createdAt: new Date(),
46
- user: user.username // Bonne pratique : garder une trace de l'uploader
47
- };
48
-
49
- if (s3Config && s3Config.bucketName) { // Correction: bucketName au lieu de bucket
50
- try {
51
- // Correction: Appel manquant à la fonction de téléversement
52
- await uploadToS3(s3Config, file.path, newFilename);
53
-
54
- fileData.storage = 's3';
55
- fileData.filename = newFilename; // Le nom sur S3
56
- getLogger().log(`Fichier ${newFilename} téléversé sur le bucket S3 ${s3Config.bucketName}.`);
57
- } catch (error) {
58
- getLogger().log(`Le téléversement S3 a échoué pour ${file.name}: ${error.message}`, 'error');
59
- throw new Error("Le téléversement S3 a échoué.");
60
- } finally {
61
- // Nettoyer le fichier temporaire uploadé par express-formidable
62
- await fsPromises.unlink(file.path).catch(e => getLogger().log(`Échec de la suppression du fichier temporaire ${file.path}: ${e.message}`, 'error'));
63
- }
64
- } else {
65
- // Sauvegarde locale
66
- const uploadDir = path.join(process.cwd(), "uploads");
67
- if (!fs.existsSync(uploadDir)) {
68
- fs.mkdirSync(uploadDir, { recursive: true });
69
- }
70
- const newPath = path.join(uploadDir, newFilename);
71
-
72
- try {
73
- // express-formidable place déjà le fichier dans un répertoire temporaire. Nous n'avons qu'éplacer.
74
- await fsPromises.rename(file.path, newPath);
75
- fileData.storage = 'local';
76
- fileData.filename = newFilename; // Le nom dans le dossier uploads
77
- fileData.path = newPath; // Le chemin complet pour les fichiers locaux
78
- getLogger().log(`Fichier ${newFilename} sauvegardé localement dans ${newPath}.`);
79
- } catch (error) {
80
- getLogger().log(`Le déplacement du fichier local a échoué pour ${file.name}: ${error.message}`, 'error');
81
- // Essayer de nettoyer le fichier temporaire même si le renommage échoue
82
- await fsPromises.unlink(file.path).catch(e => getLogger().log(`Échec de la suppression du fichier temporaire ${file.path}: ${e.message}`, 'error'));
83
- throw new Error("Le stockage du fichier local a échoué.");
84
- }
85
- }
86
-
87
- const filesCollection = await getCollection("files");
88
- await filesCollection.insertOne(fileData);
89
-
90
- return guid;
91
- };
92
-
93
- /**
94
- * Récupère les métadonnées d'un fichier depuis la base de données.
95
- * @param {string} guid - Le GUID du fichier.
96
- * @returns {Promise<object|null>} L'objet de métadonnées du fichier ou null si non trouvé.
97
- */
98
- export const getFile = async (guid) => {
99
- const filesCollection = await getCollection("files");
100
- return await filesCollection.findOne({ guid });
101
- };
102
-
103
- /**
104
- * Supprime un fichier du système (S3 ou local) et son enregistrement en base de données.
105
- * @param {string} guid - Le GUID du fichier à supprimer.
106
- * @param {object} user - L'objet utilisateur.
107
- * @returns {Promise<void>}
108
- */
109
- export const deleteFile = async (guid, user) => {
110
- const fileData = await getFile(guid);
111
- if (!fileData) {
112
- getLogger().log(`Tentative de suppression d'un fichier inexistant avec le GUID : ${guid}`, 'warn');
113
- return;
114
- }
115
-
116
- if (fileData.storage === 's3') {
117
- const s3Config = await getUserS3Config(user);
118
- if (s3Config && s3Config.bucketName) { // Correction: bucketName au lieu de bucket
119
- try {
120
- await deleteFromS3(s3Config, fileData.filename);
121
- getLogger().log(`Fichier ${fileData.filename} supprimé du bucket S3 ${s3Config.bucketName}.`);
122
- } catch (error) {
123
- getLogger().log(`La suppression S3 a échoué pour ${fileData.filename}: ${error.message}`, 'error');
124
- throw new Error("La suppression S3 a échoué.");
125
- }
126
- } else {
127
- getLogger().log(`Configuration S3 non trouvée pour l'utilisateur, impossible de supprimer le fichier ${fileData.filename} de S3.`, 'error');
128
- throw new Error("Configuration S3 non trouvée, impossible de supprimer le fichier.");
129
- }
130
- } else if (fileData.storage === 'local') {
131
- try {
132
- if (fileData.path && fs.existsSync(fileData.path)) {
133
- await fsPromises.unlink(fileData.path);
134
- getLogger().log(`Fichier local ${fileData.path} supprimé.`);
135
- } else {
136
- getLogger().log(`Fichier local non trouvé au chemin ${fileData.path}, mais suppression de l'enregistrement en BDD.`, 'warn');
137
- }
138
- } catch (error) {
139
- getLogger().log(`La suppression du fichier local a échoué pour ${fileData.path}: ${error.message}`, 'error');
140
- throw new Error("La suppression du fichier local a échoué.");
141
- }
142
- }
143
-
144
- const filesCollection = await getCollection("files");
145
- await filesCollection.deleteOne({ guid });
146
- getLogger().log(`Enregistrement du fichier ${guid} supprimé de la base de données.`);
147
- };