data-primals-engine 1.2.1 → 1.2.2
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 +69 -9
- package/client/src/DataLayout.jsx +4 -21
- package/client/src/DataTable.jsx +3 -1
- package/client/src/RestoreConfirmationModal.jsx +0 -137
- package/client/src/hooks/useTutorials.jsx +3 -3
- package/package.json +2 -2
- package/src/data.js +4 -4
- package/src/email.js +2 -2
- package/src/engine.js +10 -7
- package/src/events.js +25 -3
- package/src/modules/bucket.js +115 -5
- package/src/modules/data.js +166 -93
- package/src/modules/file.js +98 -57
- package/src/modules/test +147 -0
- package/test/data.backup.integration.test.js +1 -1
- package/test/events.test.js +202 -0
- package/test/file.test.js +196 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// __tests__/file.integration.test.js
|
|
2
|
+
import { expect, describe, it, beforeEach, afterEach, beforeAll, afterAll, vi } from 'vitest';
|
|
3
|
+
import { Config } from '../src/config.js';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { ObjectId } from 'mongodb';
|
|
8
|
+
import { initEngine, generateUniqueName } from "../src/setenv.js";
|
|
9
|
+
import { addFile, removeFile, getFile, onInit } from "../src/modules/file.js";
|
|
10
|
+
import { getCollection } from "../src/modules/mongodb.js";
|
|
11
|
+
import { Logger } from "../src/gameObject.js";
|
|
12
|
+
import {maxPrivateFileSize} from "../src/constants.js";
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
vi.stubEnv('ENCRYPTION_KEY', '12345678901234567890123456789012');
|
|
17
|
+
|
|
18
|
+
// Mock pour les fichiers uploadés
|
|
19
|
+
const createMockFile = (name, size, type = 'text/plain') => {
|
|
20
|
+
const tempPath = path.join(__dirname, 'temp', name);
|
|
21
|
+
fs.writeFileSync(tempPath, 'a'.repeat(size));
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
size,
|
|
25
|
+
type,
|
|
26
|
+
path: tempPath
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
let engine;
|
|
30
|
+
const testUploadDir = path.join(process.cwd(), "uploads", "private");
|
|
31
|
+
const testTempDir = path.join(__dirname, 'temp');
|
|
32
|
+
let filesCollection;
|
|
33
|
+
|
|
34
|
+
beforeAll(async () => {
|
|
35
|
+
Config.Set("modules", ["mongodb", "data", "file", "bucket", "user","file"]);
|
|
36
|
+
engine= await initEngine();
|
|
37
|
+
await onInit(engine);
|
|
38
|
+
|
|
39
|
+
// Créer les répertoires nécessaires
|
|
40
|
+
if (!fs.existsSync(testUploadDir)) {
|
|
41
|
+
fs.mkdirSync(testUploadDir, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
if (!fs.existsSync(testTempDir)) {
|
|
44
|
+
fs.mkdirSync(testTempDir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
filesCollection = await getCollection("files");
|
|
48
|
+
});
|
|
49
|
+
describe('File Module Integration Tests', () => {
|
|
50
|
+
let testUser;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
beforeEach(async () => {
|
|
54
|
+
// Créer un utilisateur de test
|
|
55
|
+
testUser = {
|
|
56
|
+
username: generateUniqueName('testuser'),
|
|
57
|
+
_user: generateUniqueName('testuser'),
|
|
58
|
+
userPlan: 'premium',
|
|
59
|
+
permissions: ['API_UPLOAD_FILE']
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Nettoyer les collections avant chaque test
|
|
63
|
+
await filesCollection.deleteMany({});
|
|
64
|
+
|
|
65
|
+
// Nettoyer le répertoire upload
|
|
66
|
+
const files = fs.readdirSync(testUploadDir);
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
fs.unlinkSync(path.join(testUploadDir, file));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterAll(async () => {
|
|
73
|
+
// Nettoyer après tous les tests
|
|
74
|
+
await filesCollection.deleteMany({});
|
|
75
|
+
if (fs.existsSync(testUploadDir)) {
|
|
76
|
+
fs.rmSync(testUploadDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
if (fs.existsSync(testTempDir)) {
|
|
79
|
+
fs.rmSync(testTempDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('addFile', () => {
|
|
84
|
+
it('should successfully add a file with local storage', async () => {
|
|
85
|
+
const mockFile = createMockFile('test.txt', 1024); // 1KB file
|
|
86
|
+
|
|
87
|
+
const guid = await addFile(mockFile, testUser);
|
|
88
|
+
|
|
89
|
+
// Vérifier le retour
|
|
90
|
+
expect(guid).toBeDefined();
|
|
91
|
+
expect(guid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
92
|
+
|
|
93
|
+
// Vérifier en base de données
|
|
94
|
+
const fileRecord = await filesCollection.findOne({ guid });
|
|
95
|
+
expect(fileRecord).toBeDefined();
|
|
96
|
+
expect(fileRecord.filename).toBeDefined();
|
|
97
|
+
expect(fileRecord.size).toBe(1024);
|
|
98
|
+
expect(fileRecord.user).toBe(testUser.username);
|
|
99
|
+
expect(fileRecord.storage).toBe('local');
|
|
100
|
+
|
|
101
|
+
// Vérifier que le fichier existe physiquement
|
|
102
|
+
const expectedPath = path.join(testUploadDir, `${guid}.txt`);
|
|
103
|
+
expect(fs.existsSync(expectedPath)).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should throw error when file size exceeds limit', async () => {
|
|
107
|
+
const mockFile = createMockFile('large.txt', maxPrivateFileSize+1); // 11MB (dépasse la limite de 10MB)
|
|
108
|
+
|
|
109
|
+
await expect(addFile(mockFile, testUser)).rejects.toThrow(/La taille du fichier dépasse la limite autorisée/);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should throw error when storage limit exceeded', async ({skip}) => {
|
|
113
|
+
skip();
|
|
114
|
+
// Mock la fonction de calcul d'usage pour simuler un dépassement
|
|
115
|
+
vi.spyOn(engine.userProvider, 'getUserStorageLimit').mockResolvedValue(85 * 1024 * 1024); // 5MB
|
|
116
|
+
|
|
117
|
+
const mockFile = createMockFile('test.txt', 1024 * 1024); // 1MB
|
|
118
|
+
|
|
119
|
+
await expect(addFile(mockFile, testUser)).rejects.toThrow("api.data.storageLimitExceeded");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should throw error when server capacity is insufficient', async ({skip}) => {
|
|
123
|
+
skip();
|
|
124
|
+
// Mock la vérification de capacité serveur
|
|
125
|
+
vi.spyOn(engine.userProvider, 'checkServerCapacity').mockResolvedValue({ isSufficient: false });
|
|
126
|
+
|
|
127
|
+
const mockFile = createMockFile('test.txt', 1024);
|
|
128
|
+
|
|
129
|
+
await expect(addFile(mockFile, testUser)).rejects.toThrow("api.data.serverStorageFull");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('removeFile', () => {
|
|
134
|
+
it('should successfully remove a locally stored file', async () => {
|
|
135
|
+
// D'abord ajouter un fichier
|
|
136
|
+
const mockFile = createMockFile('to-delete.txt', 1024);
|
|
137
|
+
const guid = await addFile(mockFile, testUser);
|
|
138
|
+
|
|
139
|
+
// Vérifier qu'il existe avant suppression
|
|
140
|
+
const beforeDelete = await getFile(guid);
|
|
141
|
+
expect(beforeDelete).toBeDefined();
|
|
142
|
+
|
|
143
|
+
// Supprimer le fichier
|
|
144
|
+
await removeFile(guid, testUser);
|
|
145
|
+
|
|
146
|
+
// Vérifier en base de données
|
|
147
|
+
const afterDelete = await getFile(guid);
|
|
148
|
+
expect(afterDelete).toBeNull();
|
|
149
|
+
|
|
150
|
+
// Vérifier que le fichier physique a été supprimé
|
|
151
|
+
const expectedPath = path.join(testUploadDir, `${guid}.txt`);
|
|
152
|
+
expect(fs.existsSync(expectedPath)).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should handle trying to remove non-existent file gracefully', async () => {
|
|
156
|
+
const nonExistentGuid = '123e4567-e89b-12d3-a456-426614174000';
|
|
157
|
+
|
|
158
|
+
vi.spyOn(engine.getComponent(Logger), "info");
|
|
159
|
+
|
|
160
|
+
// Cela ne devrait pas throw d'erreur
|
|
161
|
+
await removeFile(nonExistentGuid, testUser);
|
|
162
|
+
|
|
163
|
+
// Vérifier que le logger a bien enregistré l'info
|
|
164
|
+
expect(engine.getComponent(Logger).info).toHaveBeenCalledWith(
|
|
165
|
+
expect.stringContaining(`Tentative de suppression d'un fichier inexistant`),
|
|
166
|
+
'warn'
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should throw error when GUID is invalid', async () => {
|
|
171
|
+
await expect(removeFile('invalid-guid', testUser)).rejects.toThrow("Le GUID du fichier n'est pas valide.");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('getFile', () => {
|
|
176
|
+
it('should retrieve file metadata', async () => {
|
|
177
|
+
const mockFile = createMockFile('metadata-test.txt', 2048);
|
|
178
|
+
const guid = await addFile(mockFile, testUser);
|
|
179
|
+
|
|
180
|
+
const fileData = await getFile(guid);
|
|
181
|
+
|
|
182
|
+
expect(fileData).toBeDefined();
|
|
183
|
+
expect(fileData.guid).toBe(guid);
|
|
184
|
+
expect(fileData.filename).toContain('.txt');
|
|
185
|
+
expect(fileData.size).toBe(2048);
|
|
186
|
+
expect(fileData.user).toBe(testUser.username);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return null for non-existent file', async () => {
|
|
190
|
+
const nonExistentGuid = '123e4567-e89b-12d3-a456-426614174000';
|
|
191
|
+
const fileData = await getFile(nonExistentGuid);
|
|
192
|
+
|
|
193
|
+
expect(fileData).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|