data-primals-engine 1.0.8 → 1.0.9
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/.github/workflows/node.js.yml +59 -0
- package/.github/workflows/npm-publish.yml +23 -0
- package/Dockerfile +15 -0
- package/DockerfileTest +15 -0
- package/package.json +4 -7
- package/src/engine.js +2 -2
- package/src/migrate.js +1 -1
- package/src/modules/data.js +12 -3
- package/src/modules/mongodb.js +0 -7
- package/src/setenv.js +39 -0
- package/test/data.backup.integration.test.js +141 -0
- package/test/data.integration.test.js +796 -0
- package/test/globalSetup.js +15 -0
- package/test/globalTeardown.js +8 -0
- package/test/import_export.integration.test.js +200 -0
- package/test/workflow.integration.test.js +314 -0
- package/test/workflow.robustness.test.js +195 -0
- package/vitest.config.js +16 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ "develop", "main" ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ "develop", "main" ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
container:
|
|
17
|
+
image: node:18 # Specify the Docker version you needx
|
|
18
|
+
env:
|
|
19
|
+
NODE_OPTIONS: "--max_old_space_size=4096"
|
|
20
|
+
NODE_ENV: development
|
|
21
|
+
ports:
|
|
22
|
+
- 7635
|
|
23
|
+
- 27017
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
node-version: [18.x]
|
|
27
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
28
|
+
services:
|
|
29
|
+
mongodb:
|
|
30
|
+
image: mongo:5.0
|
|
31
|
+
ports:
|
|
32
|
+
- 27017:27017
|
|
33
|
+
options: >-
|
|
34
|
+
--health-cmd "mongo --eval 'db.serverStatus()'"
|
|
35
|
+
--health-interval 10s
|
|
36
|
+
--health-timeout 5s
|
|
37
|
+
--health-retries 5
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
|
|
41
|
+
- name: Install MongoDB Tools
|
|
42
|
+
run: |
|
|
43
|
+
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | apt-key add -
|
|
44
|
+
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-5.0.list
|
|
45
|
+
apt-get update
|
|
46
|
+
apt-get install -y mongodb-org-tools
|
|
47
|
+
|
|
48
|
+
- name: Set up Node.js
|
|
49
|
+
uses: actions/setup-node@v2
|
|
50
|
+
with:
|
|
51
|
+
node-version: '18.18.2'
|
|
52
|
+
|
|
53
|
+
- name: Install dependencies
|
|
54
|
+
run: npm install
|
|
55
|
+
|
|
56
|
+
- name: Run tests
|
|
57
|
+
run: npm test
|
|
58
|
+
|
|
59
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish package to GitHub Packages
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
packages: write
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
# Setup .npmrc file to publish to GitHub Packages
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: '20.x'
|
|
17
|
+
registry-url: 'https://npm.pkg.github.com'
|
|
18
|
+
# Defaults to the user or organization that owns the workflow file
|
|
19
|
+
scope: '@octocat'
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm publish
|
|
22
|
+
env:
|
|
23
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Base image
|
|
2
|
+
FROM node:18.18.2
|
|
3
|
+
|
|
4
|
+
# Set working directory
|
|
5
|
+
WORKDIR /
|
|
6
|
+
|
|
7
|
+
# Copy package files and install dependencies
|
|
8
|
+
COPY package.json ./
|
|
9
|
+
RUN npm install
|
|
10
|
+
|
|
11
|
+
# Copy application code
|
|
12
|
+
COPY . .
|
|
13
|
+
|
|
14
|
+
# Expose the application port
|
|
15
|
+
EXPOSE 7633
|
package/DockerfileTest
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Base image
|
|
2
|
+
FROM node:18.18.2
|
|
3
|
+
|
|
4
|
+
# Set working directory
|
|
5
|
+
WORKDIR /
|
|
6
|
+
|
|
7
|
+
# Copy package files and install dependencies
|
|
8
|
+
COPY package.json ./
|
|
9
|
+
RUN npm install
|
|
10
|
+
|
|
11
|
+
# Copy application code
|
|
12
|
+
COPY . .
|
|
13
|
+
|
|
14
|
+
# Expose the application port
|
|
15
|
+
EXPOSE 7635
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "data-primals-engine is the package responsible from handling large amount of data in a practical and performant way. It can handle large amount of data in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation.",
|
|
5
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
+
"test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism",
|
|
8
9
|
"preinstall": "npx force-resolutions",
|
|
9
10
|
"devserver": "cross-env NODE_ENV=development node server.js",
|
|
10
11
|
"server": "cross-env NODE_ENV=production node server.js",
|
|
@@ -39,11 +40,9 @@
|
|
|
39
40
|
"chalk": "^5.4.1",
|
|
40
41
|
"check-disk-space": "^3.4.0",
|
|
41
42
|
"compression": "^1.8.0",
|
|
42
|
-
"connect-mongo": "^5.1.0",
|
|
43
43
|
"cookie-parser": "^1.4.7",
|
|
44
44
|
"cronstrue": "^3.1.0",
|
|
45
45
|
"csv-parse": "^5.6.0",
|
|
46
|
-
"csv-parser": "^3.2.0",
|
|
47
46
|
"date-fns": "^4.1.0",
|
|
48
47
|
"express": "^4.21.2",
|
|
49
48
|
"express-csrf-double-submit-cookie": "^1.2.1",
|
|
@@ -61,7 +60,6 @@
|
|
|
61
60
|
"nodemailer": "^6.10.0",
|
|
62
61
|
"openai": "^5.8.2",
|
|
63
62
|
"randomcolor": "^0.6.2",
|
|
64
|
-
"rate-limiter-flexible": "^5.0.5",
|
|
65
63
|
"react-helmet": "^6.1.0",
|
|
66
64
|
"react-i18next": "^15.4.1",
|
|
67
65
|
"react-markdown": "^10.1.0",
|
|
@@ -70,10 +68,9 @@
|
|
|
70
68
|
"sirv": "^3.0.1",
|
|
71
69
|
"swagger-ui-express": "^5.0.1",
|
|
72
70
|
"tar": "^7.4.3",
|
|
73
|
-
"tar-fs": "^3.0.8",
|
|
74
71
|
"uniqid": "^5.4.0",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
72
|
+
"vitest": "^3.2.3",
|
|
73
|
+
"yaml": "^2.8.0"
|
|
77
74
|
},
|
|
78
75
|
"devDependencies": {
|
|
79
76
|
"@eslint/js": "^9.17.0",
|
package/src/engine.js
CHANGED
|
@@ -17,7 +17,7 @@ import formidableMiddleware from 'express-formidable';
|
|
|
17
17
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
18
18
|
|
|
19
19
|
// Connection URL
|
|
20
|
-
export const dbUrl = process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017';
|
|
20
|
+
export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
|
|
21
21
|
export const MongoClient = new InternalMongoClient(dbUrl, { maxPoolSize: 20 });
|
|
22
22
|
|
|
23
23
|
// Database Name
|
|
@@ -87,7 +87,7 @@ export const Engine = {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
await Promise.all(Config.Get('modules').map(async module => {
|
|
90
|
+
await Promise.all(Config.Get('modules', []).map(async module => {
|
|
91
91
|
try {
|
|
92
92
|
if( fs.existsSync(module)){
|
|
93
93
|
return await importModule(module);
|
package/src/migrate.js
CHANGED
|
@@ -15,7 +15,7 @@ Config.Set("modules", ["mongodb"]);
|
|
|
15
15
|
const MIGRATIONS_DIR = path.resolve(process.cwd(), 'server', 'src', 'migrations');
|
|
16
16
|
const MIGRATIONS_COLLECTION = 'migrations_log';
|
|
17
17
|
|
|
18
|
-
const engine = Engine.Create();
|
|
18
|
+
const engine = await Engine.Create();
|
|
19
19
|
const port = process.env.MIGRATE_PORT || 7640;
|
|
20
20
|
|
|
21
21
|
engine.start(port, async () => {
|
package/src/modules/data.js
CHANGED
|
@@ -101,7 +101,7 @@ const sseConnections = new Map();
|
|
|
101
101
|
|
|
102
102
|
const delay = ms => new Promise(res => setTimeout(res, ms));
|
|
103
103
|
|
|
104
|
-
const
|
|
104
|
+
const getBackupDir = () => process.env.BACKUP_DIR || './backups'; // Répertoire de stockage des sauvegardes
|
|
105
105
|
const execAsync = promisify(exec);
|
|
106
106
|
|
|
107
107
|
let importJobs = {};
|
|
@@ -3003,10 +3003,14 @@ async function processRelations(docToProcess, model, collection, me, idMap) {
|
|
|
3003
3003
|
// Phase 3: Traitement des résultats
|
|
3004
3004
|
findResults.forEach((result, index) => {
|
|
3005
3005
|
const { field, multiple } = batchFinds[index];
|
|
3006
|
-
if (result.data?.length) {
|
|
3006
|
+
if (result.data?.length > 0) {
|
|
3007
|
+
// Cas où des documents sont trouvés
|
|
3007
3008
|
docToProcess[field] = multiple
|
|
3008
3009
|
? result.data.map(r => r._id.toString())
|
|
3009
3010
|
: result.data[0]._id.toString();
|
|
3011
|
+
} else {
|
|
3012
|
+
// Cas où AUCUN document n'est trouvé : il faut nettoyer le champ !
|
|
3013
|
+
docToProcess[field] = multiple ? [] : null;
|
|
3010
3014
|
}
|
|
3011
3015
|
});
|
|
3012
3016
|
|
|
@@ -4940,6 +4944,7 @@ export const loadFromDump = async (user, options = {}) => {
|
|
|
4940
4944
|
// ...
|
|
4941
4945
|
let backupFilePath; // Assurez-vous que cette variable est bien définie avec le chemin du fichier .tar.gz
|
|
4942
4946
|
// Exemple simplifié :
|
|
4947
|
+
const backupDir = getBackupDir();
|
|
4943
4948
|
const backupFilenameRegex = new RegExp(`^backup_${user.username}_(\\d+)\\.tar\\.gz$`);
|
|
4944
4949
|
const backupFiles = fs.readdirSync(backupDir).filter(filename => backupFilenameRegex.test(filename));
|
|
4945
4950
|
if (backupFiles.length === 0) throw new Error(`Aucun fichier de sauvegarde local trouvé pour l'utilisateur ${user.username}.`);
|
|
@@ -5016,6 +5021,7 @@ export const loadFromDump = async (user, options = {}) => {
|
|
|
5016
5021
|
|
|
5017
5022
|
// Fonction pour générer une clé aléatoire et la stocker dans un fichier
|
|
5018
5023
|
const generateAndStoreKey = (user) => {
|
|
5024
|
+
const backupDir = getBackupDir();
|
|
5019
5025
|
const keyFile = path.join(backupDir, getObjectHash({id:getUserId(user)})+'_encryption.key');
|
|
5020
5026
|
const key = crypto.randomBytes(16).toString('hex');
|
|
5021
5027
|
fs.writeFileSync(keyFile, key, { mode: 0o600 }); // Permissions strictes
|
|
@@ -5024,6 +5030,7 @@ const generateAndStoreKey = (user) => {
|
|
|
5024
5030
|
|
|
5025
5031
|
// Fonction pour lire la clé depuis le fichier
|
|
5026
5032
|
const readKeyFromFile = (user) => {
|
|
5033
|
+
const backupDir = getBackupDir();
|
|
5027
5034
|
const keyFile = path.join(backupDir, getObjectHash({id:getUserId(user)})+'_encryption.key');
|
|
5028
5035
|
if (fs.existsSync(keyFile)) {
|
|
5029
5036
|
return fs.readFileSync(keyFile, 'utf8');
|
|
@@ -5035,6 +5042,7 @@ export const dumpUserData = async (user) => {
|
|
|
5035
5042
|
// Déterminer la clé de chiffrement
|
|
5036
5043
|
// Pour cet exemple, on simule la config S3. Remplace par la vraie récupération.
|
|
5037
5044
|
const s3Config = user.configS3; // Supposons que l'objet 'user' passé contient déjà 'configS3'
|
|
5045
|
+
const backupDir = getBackupDir();
|
|
5038
5046
|
|
|
5039
5047
|
let encryptedKey = readKeyFromFile(user);
|
|
5040
5048
|
if (!encryptedKey) {
|
|
@@ -5080,7 +5088,7 @@ export const dumpUserData = async (user) => {
|
|
|
5080
5088
|
let col;
|
|
5081
5089
|
for (const collection of collections) {
|
|
5082
5090
|
|
|
5083
|
-
const colls = [
|
|
5091
|
+
const colls = [getUserCollectionName(user), 'models'];
|
|
5084
5092
|
if( colls.includes(collection.name) ){
|
|
5085
5093
|
|
|
5086
5094
|
// Exécuter mongodump avec les filtres appropriés
|
|
@@ -5166,6 +5174,7 @@ async function manageBackupRotation(user, backupFrequency, s3Config = null) { //
|
|
|
5166
5174
|
|
|
5167
5175
|
} else {
|
|
5168
5176
|
logger.info(`Gestion de la rotation des sauvegardes locales pour ${userId}.`);
|
|
5177
|
+
const backupDir = getBackupDir();
|
|
5169
5178
|
const localFiles = fs.readdirSync(backupDir);
|
|
5170
5179
|
filesToManage = localFiles
|
|
5171
5180
|
.filter(f => !fs.lstatSync(path.join(backupDir, f)).isDirectory() && f.startsWith(`backup_${userId}_`) && f.endsWith('.tar.gz'))
|
package/src/modules/mongodb.js
CHANGED
|
@@ -23,13 +23,6 @@ export async function onInit(defaultEngine) {
|
|
|
23
23
|
} catch (e) {
|
|
24
24
|
|
|
25
25
|
}
|
|
26
|
-
// Create a SecureContext object
|
|
27
|
-
// Connection URL
|
|
28
|
-
const dbUrl = process.env.MONGO_DB_URL || 'mongodb://localhost:27017';
|
|
29
|
-
const MongoClient = new InternalMongoClient(dbUrl, {
|
|
30
|
-
tls: false, maxPoolSize: 20
|
|
31
|
-
});
|
|
32
|
-
await MongoClient.connect();
|
|
33
26
|
|
|
34
27
|
modelsCollection = MongoDatabase.collection("models");
|
|
35
28
|
datasCollection = MongoDatabase.collection("datas");
|
package/src/setenv.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {getRandom} from "data-primals-engine/core";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import {Engine} from "./index.js";
|
|
4
|
+
|
|
5
|
+
let ports = [], engineInstance, mongod;
|
|
6
|
+
export const getUniquePort = () =>{
|
|
7
|
+
let d, it=0;
|
|
8
|
+
do{
|
|
9
|
+
d = getRandom(10000, 20000);
|
|
10
|
+
++it;
|
|
11
|
+
} while( ports.includes(d) && it < 10000);
|
|
12
|
+
return d;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// --- Utilitaires pour les tests ---
|
|
16
|
+
export const generateUniqueName = (baseName) => `${baseName}_${getRandom(1000, 9999)}_${Date.now()}`;
|
|
17
|
+
export const initEngine = async () => {
|
|
18
|
+
if( engineInstance )
|
|
19
|
+
return engineInstance;
|
|
20
|
+
|
|
21
|
+
process.env.OPENAI_API_KEY = "O000";
|
|
22
|
+
|
|
23
|
+
const port = process.env.PORT || getUniquePort(); // Different port for this test suite
|
|
24
|
+
engineInstance = await Engine.Create();
|
|
25
|
+
await engineInstance.start(port);
|
|
26
|
+
return engineInstance;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Stops the application engine and the in-memory MongoDB instance.
|
|
32
|
+
*/
|
|
33
|
+
export const stopEngine = async () => {
|
|
34
|
+
if (engineInstance) {
|
|
35
|
+
await engineInstance.stop();
|
|
36
|
+
engineInstance = null;
|
|
37
|
+
console.log("Test engine stopped.");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// test/data.backup.integration.test.js
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Config } from '../src/config.js';
|
|
5
|
+
|
|
6
|
+
import { ObjectId } from 'mongodb';
|
|
7
|
+
import {expect, describe, it, beforeAll, afterAll, beforeEach} from 'vitest';
|
|
8
|
+
import { vi } from 'vitest'
|
|
9
|
+
import { Buffer } from 'node:buffer'; // Explicitly import Buffer
|
|
10
|
+
import crypto from 'node:crypto'; //Explicitly import crypto
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
createModel,
|
|
14
|
+
getModel, insertData
|
|
15
|
+
} from 'data-primals-engine/modules/data';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
modelsCollection as getAppModelsCollection,
|
|
19
|
+
getCollectionForUser as getAppUserCollection,
|
|
20
|
+
} from 'data-primals-engine/modules/mongodb';
|
|
21
|
+
import { Engine } from "data-primals-engine/engine";
|
|
22
|
+
import process from "node:process";
|
|
23
|
+
|
|
24
|
+
import { dumpUserData, loadFromDump, getUserHash } from 'data-primals-engine/modules/data';
|
|
25
|
+
import fs from "node:fs";
|
|
26
|
+
import {getRandom} from "data-primals-engine/core";
|
|
27
|
+
import {getUniquePort, initEngine, stopEngine} from "../src/setenv.js";
|
|
28
|
+
|
|
29
|
+
vi.mock('data-primals-engine/engine', async(importOriginal) => {
|
|
30
|
+
const mod = await importOriginal() // type is inferred
|
|
31
|
+
return {
|
|
32
|
+
...mod
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Mock data and settings
|
|
37
|
+
const mockUser = {
|
|
38
|
+
username: 'testuserBackup',
|
|
39
|
+
_user: 'testuserBackup',
|
|
40
|
+
userPlan: 'premium',
|
|
41
|
+
email: 'testBackup@example.com',
|
|
42
|
+
configS3: {
|
|
43
|
+
bucketName: null
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const testDbName = 'testIntegrationDbHO_Backup';
|
|
47
|
+
const testModelDefinition = {
|
|
48
|
+
name: 'backupTestModel',
|
|
49
|
+
_user: mockUser.username,
|
|
50
|
+
description: 'Model for testing backup/restore',
|
|
51
|
+
fields: [
|
|
52
|
+
{ name: 'testField', type: 'string', required: true },
|
|
53
|
+
{ name: 'optionalField', type: 'number' },
|
|
54
|
+
],
|
|
55
|
+
maxRequestData: 10,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let testModelsColInstance;
|
|
59
|
+
let testDatasColInstance;
|
|
60
|
+
let engineInstance;
|
|
61
|
+
let testDatasApi;
|
|
62
|
+
|
|
63
|
+
const backupDir = path.resolve('./test-backups'); // Use an absolute path
|
|
64
|
+
|
|
65
|
+
beforeAll(async () => {
|
|
66
|
+
|
|
67
|
+
process.env.BACKUP_DIR = backupDir; // Set backup directory
|
|
68
|
+
|
|
69
|
+
// Create the backup directory if it doesn't exist
|
|
70
|
+
if (!fs.existsSync(backupDir)) {
|
|
71
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Delete any existing files in the backup directory
|
|
75
|
+
fs.readdirSync(backupDir).forEach(file => {
|
|
76
|
+
fs.unlinkSync(path.join(backupDir, file));
|
|
77
|
+
});
|
|
78
|
+
vi.stubEnv('S3_CONFIG_ENCRYPTION_KEY', '00000000000000000000000000000000');
|
|
79
|
+
vi.stubEnv('OPENAI_API_KEY', '00000000000000000000000000000000');
|
|
80
|
+
// You might need to create a model first if your dumpUserData requires it
|
|
81
|
+
await createModel(testModelDefinition);
|
|
82
|
+
}, 45000);
|
|
83
|
+
|
|
84
|
+
afterAll(async () => {
|
|
85
|
+
|
|
86
|
+
delete process.env.DB_URL;
|
|
87
|
+
delete process.env.DB_NAME;
|
|
88
|
+
|
|
89
|
+
// Clean up test backups
|
|
90
|
+
if (fs.existsSync(backupDir)) {
|
|
91
|
+
fs.readdirSync(backupDir).forEach(file => {
|
|
92
|
+
fs.unlinkSync(path.join(backupDir, file));
|
|
93
|
+
});
|
|
94
|
+
// Optional: fs.rmdirSync(backupDir); // Remove the directory itself
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
beforeAll(async () =>{
|
|
99
|
+
Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
|
|
100
|
+
await initEngine();
|
|
101
|
+
})
|
|
102
|
+
beforeEach(async () => {
|
|
103
|
+
testModelsColInstance = getAppModelsCollection;
|
|
104
|
+
testDatasColInstance = getAppUserCollection(mockUser);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Data Backup and Restore Integration', () => {
|
|
108
|
+
it('should dump and restore user data successfully, and verify data integrity', async () => { // Le nom du test est plus précis
|
|
109
|
+
// 1. Insérer des données à sauvegarder
|
|
110
|
+
const initialData = { testField: 'Initial Value', optionalField: 123 };
|
|
111
|
+
const insertResult = await insertData(testModelDefinition.name, initialData, {}, mockUser, false);
|
|
112
|
+
expect(insertResult.success).toBe(true);
|
|
113
|
+
const insertedId = insertResult.insertedIds[0];
|
|
114
|
+
|
|
115
|
+
// Vérifier que les données existent avant la sauvegarde
|
|
116
|
+
let docBeforeBackup = await testDatasColInstance.findOne({ _id: new ObjectId(insertedId) });
|
|
117
|
+
expect(docBeforeBackup).not.toBeNull();
|
|
118
|
+
expect(docBeforeBackup.testField).toBe('Initial Value');
|
|
119
|
+
|
|
120
|
+
// 2. Sauvegarder les données
|
|
121
|
+
await dumpUserData(mockUser);
|
|
122
|
+
|
|
123
|
+
// 3. Simuler une suppression totale des données
|
|
124
|
+
await testDatasColInstance.deleteMany({ _user: mockUser.username });
|
|
125
|
+
let docAfterDelete = await testDatasColInstance.findOne({ _id: new ObjectId(insertedId) });
|
|
126
|
+
expect(docAfterDelete).toBeNull();
|
|
127
|
+
|
|
128
|
+
// 4. Restaurer les données
|
|
129
|
+
await loadFromDump(mockUser);
|
|
130
|
+
|
|
131
|
+
// 5. **VÉRIFICATION FINALE** : S'assurer que les données sont restaurées correctement
|
|
132
|
+
const countAfterRestore = await testDatasColInstance.countDocuments({ _user: mockUser.username });
|
|
133
|
+
expect(countAfterRestore).toBeGreaterThan(0);
|
|
134
|
+
|
|
135
|
+
const docAfterRestore = await testDatasColInstance.findOne({ _user: mockUser.username, _model: testModelDefinition.name });
|
|
136
|
+
expect(docAfterRestore).not.toBeNull();
|
|
137
|
+
expect(docAfterRestore.testField).toBe('Initial Value');
|
|
138
|
+
expect(docAfterRestore.optionalField).toBe(123);
|
|
139
|
+
|
|
140
|
+
}, 15000); // Timeout augmenté pour les opérations de fichiers
|
|
141
|
+
});
|