@open3cl/engine 1.7.1 → 1.7.3
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/13.2_generateur_combustion.js +2 -10
- package/3.1_b.js +45 -33
- package/3.2.1_mur.js +19 -10
- package/3.2.1_mur.spec.js +105 -0
- package/3.3_baie_vitree.js +32 -10
- package/3.4_pont_thermique.js +21 -13
- package/3.4_pont_thermique.spec.js +1 -1
- package/3_deperdition.js +6 -1
- package/3_deperdition.spec.js +47 -0
- package/README.md +40 -10
- package/engine.js +1 -1
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +22 -13
- package/ficheTechnique.js +133 -53
- package/ficheTechnique.spec.js +46 -14
- package/formula.js +251 -0
- package/formula.spec.js +99 -0
- package/package.json +5 -6
- package/utils.js +195 -44
- package/utils.spec.js +69 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open3cl/engine",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "Open Source 3CL-DPE engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -42,11 +42,8 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"dioma": "^0.4.6",
|
|
44
44
|
"fast-xml-parser": "^5.11.1",
|
|
45
|
-
"http-server": "^14.1.1",
|
|
46
45
|
"lodash-es": "^4.18.1",
|
|
47
|
-
"
|
|
48
|
-
"winston": "^3.19.0",
|
|
49
|
-
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
46
|
+
"winston": "^3.19.0"
|
|
50
47
|
},
|
|
51
48
|
"devDependencies": {
|
|
52
49
|
"@aws-sdk/client-s3": "^3.1013.0",
|
|
@@ -71,13 +68,15 @@
|
|
|
71
68
|
"eslint-plugin-n": "^16.6.2",
|
|
72
69
|
"eslint-plugin-promise": "^6.1.1",
|
|
73
70
|
"fast-csv": "^5.0.2",
|
|
71
|
+
"http-server": "^14.1.1",
|
|
74
72
|
"husky": "^9.0.11",
|
|
75
73
|
"jscpd": "^3.5.10",
|
|
76
74
|
"piscina": "^5.1.2",
|
|
77
75
|
"prettier": "^3.2.5",
|
|
78
76
|
"pretty-quick": "^4.0.0",
|
|
79
77
|
"semantic-release": "^25.0.2",
|
|
80
|
-
"vitest": "3.2.7"
|
|
78
|
+
"vitest": "3.2.7",
|
|
79
|
+
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
81
80
|
},
|
|
82
81
|
"author": "Open3Cl",
|
|
83
82
|
"license": "GPL-3.0",
|
package/utils.js
CHANGED
|
@@ -2,7 +2,7 @@ import enums from './enums.js';
|
|
|
2
2
|
import tvs from './tv.js';
|
|
3
3
|
import { set } from 'lodash-es';
|
|
4
4
|
import { XMLParser } from 'fast-xml-parser';
|
|
5
|
-
import {
|
|
5
|
+
import { evaluateFormula } from './formula.js';
|
|
6
6
|
|
|
7
7
|
export const xmlParser = new XMLParser({
|
|
8
8
|
// We want to make sure collections of length 1 are still parsed as arrays
|
|
@@ -57,13 +57,99 @@ export function set_use_enum_as_string() {
|
|
|
57
57
|
use_enum_as_string = true;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Implémentation de `tvMatch` réellement utilisée.
|
|
62
|
+
*
|
|
63
|
+
* Le choix est fait une seule fois, au moment du réglage, et non à chaque appel :
|
|
64
|
+
* `tv_match_new_version` est un *live binding* ESM, donc un `if (tv_match_new_version)`
|
|
65
|
+
* dans `tvMatch` force V8 à relire le slot de module à chaque appel et l'empêche
|
|
66
|
+
* d'inliner la délégation. Sur plusieurs millions d'appels par calcul, ce seul
|
|
67
|
+
* aiguillage représentait ~10% du temps CPU total.
|
|
68
|
+
*
|
|
69
|
+
* Les déclarations de fonction étant hoistées, la référence est valide ici.
|
|
70
|
+
*/
|
|
71
|
+
let tvMatchImpl = tvMatchLegacy;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cache mémoïsant `tv()`.
|
|
75
|
+
*
|
|
76
|
+
* Les tables de valeurs sont statiques et `tv()` est pure : pour un même couple
|
|
77
|
+
* (filePath, matcher) la ligne retournée est toujours la même. Le moteur rejoue
|
|
78
|
+
* les mêmes combinaisons des milliers de fois par DPE.
|
|
79
|
+
*
|
|
80
|
+
* Éviction FIFO : certains matchers contiennent des valeurs issues des données
|
|
81
|
+
* d'entrée (ug, aiu/aue…), la cardinalité n'est donc pas fermée sur un process
|
|
82
|
+
* qui tourne longtemps.
|
|
83
|
+
*/
|
|
84
|
+
let TV_CACHE_MAX = 20000;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Change la borne du cache de `tv()`.
|
|
88
|
+
*
|
|
89
|
+
* `Infinity` désactive l'éviction — utile pour les outils de diagnostic, qui ont besoin de
|
|
90
|
+
* l'union réelle des matchers et non d'une taille plafonnée. Réduire la borne n'évince pas
|
|
91
|
+
* immédiatement : les entrées en trop partent au fil des insertions suivantes.
|
|
92
|
+
*
|
|
93
|
+
* @param max {number}
|
|
94
|
+
*/
|
|
95
|
+
export function setTvCacheMax(max) {
|
|
96
|
+
TV_CACHE_MAX = max;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @returns {number} borne courante du cache de `tv()`. */
|
|
100
|
+
export function getTvCacheMax() {
|
|
101
|
+
return TV_CACHE_MAX;
|
|
102
|
+
}
|
|
103
|
+
const tvCache = new Map();
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Vide le cache de `tv()`.
|
|
107
|
+
* À appeler si les tables de valeurs ou la sémantique de matching changent.
|
|
108
|
+
*/
|
|
109
|
+
export function clearTvCache() {
|
|
110
|
+
tvCache.clear();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Nombre d'entrées actuellement mémoïsées, c'est-à-dire de matchers distincts rencontrés depuis
|
|
115
|
+
* le dernier `clearTvCache()`. Purement diagnostique (bancs de mesure, métriques) : aucun
|
|
116
|
+
* compteur n'est tenu sur le chemin chaud, la taille est lue à la demande.
|
|
117
|
+
*
|
|
118
|
+
* @returns {number}
|
|
119
|
+
*/
|
|
120
|
+
export function tvCacheSize() {
|
|
121
|
+
return tvCache.size;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Clés actuellement mémoïsées, au format interne : `table` puis, pour chaque entrée du matcher,
|
|
126
|
+
* `clévaleur`. Purement diagnostique — sert à analyser quelle table et quelle colonne
|
|
127
|
+
* génèrent de la cardinalité ouverte. Ne pas utiliser en production : la liste est copiée.
|
|
128
|
+
*
|
|
129
|
+
* @returns {string[]}
|
|
130
|
+
*/
|
|
131
|
+
export function tvCacheKeys() {
|
|
132
|
+
return [...tvCache.keys()];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Séparateurs utilisés dans les clés du cache, exposés pour que les outils de diagnostic
|
|
137
|
+
* n'aient pas à les redéclarer.
|
|
138
|
+
*/
|
|
139
|
+
export const TV_CACHE_KEY_SEPARATORS = { entry: '', value: '' };
|
|
140
|
+
|
|
60
141
|
export let tv_match_new_version = false;
|
|
61
142
|
export function set_tv_match_optimized_version() {
|
|
62
143
|
tv_match_new_version = true;
|
|
144
|
+
tvMatchImpl = tvMatchOptimized;
|
|
145
|
+
// La sémantique de matching change : les résultats mémoïsés ne sont plus valides.
|
|
146
|
+
clearTvCache();
|
|
63
147
|
}
|
|
64
148
|
|
|
65
149
|
export function unset_tv_match_optimized_version() {
|
|
66
150
|
tv_match_new_version = false;
|
|
151
|
+
tvMatchImpl = tvMatchLegacy;
|
|
152
|
+
clearTvCache();
|
|
67
153
|
}
|
|
68
154
|
|
|
69
155
|
export const Tbase = {
|
|
@@ -156,11 +242,29 @@ export function add_references(enveloppe) {
|
|
|
156
242
|
}
|
|
157
243
|
}
|
|
158
244
|
|
|
245
|
+
/**
|
|
246
|
+
* `Object.keys(enums[field])` mémoïsé.
|
|
247
|
+
*
|
|
248
|
+
* ATTENTION : le tableau retourné est partagé entre tous les appels et affecté tel
|
|
249
|
+
* quel à `du[enum_*_id]`. C'est sans risque tant que personne ne mute ces tableaux
|
|
250
|
+
* (aucun appelant ne le fait aujourd'hui) ; si un jour c'est le cas, remplacer par
|
|
251
|
+
* `enumKeys(field).slice()`.
|
|
252
|
+
*/
|
|
253
|
+
const enumKeysCache = new Map();
|
|
254
|
+
function enumKeys(field) {
|
|
255
|
+
let keys = enumKeysCache.get(field);
|
|
256
|
+
if (keys === undefined) {
|
|
257
|
+
keys = Object.keys(enums[field]);
|
|
258
|
+
enumKeysCache.set(field, keys);
|
|
259
|
+
}
|
|
260
|
+
return keys;
|
|
261
|
+
}
|
|
262
|
+
|
|
159
263
|
export function requestInputID(de, du, field, type) {
|
|
160
264
|
// enums
|
|
161
265
|
const enum_name = `enum_${field}_id`;
|
|
162
266
|
if (type) du[enum_name] = type;
|
|
163
|
-
else du[enum_name] =
|
|
267
|
+
else du[enum_name] = enumKeys(field);
|
|
164
268
|
return de[enum_name];
|
|
165
269
|
}
|
|
166
270
|
|
|
@@ -169,7 +273,7 @@ export function requestInput(de, du, field, type) {
|
|
|
169
273
|
// enums
|
|
170
274
|
const enum_name = `enum_${field}_id`;
|
|
171
275
|
if (type) du[enum_name] = type;
|
|
172
|
-
else du[enum_name] =
|
|
276
|
+
else du[enum_name] = enumKeys(field);
|
|
173
277
|
return enums[field][de[enum_name]];
|
|
174
278
|
} else {
|
|
175
279
|
// not enums
|
|
@@ -186,21 +290,38 @@ export function getKeyByValue(object, value) {
|
|
|
186
290
|
return Object.keys(object).find((key) => object[key] === value);
|
|
187
291
|
}
|
|
188
292
|
|
|
293
|
+
/**
|
|
294
|
+
* `tvColumnIDs` est une fonction pure de deux constantes (une table statique et un
|
|
295
|
+
* nom de colonne), mais elle était rappelée pour chaque générateur de chaque DPE.
|
|
296
|
+
* Le `unique_ids.includes(value)` en O(n²) est remplacé par un `Set`.
|
|
297
|
+
*
|
|
298
|
+
* Le tableau retourné est partagé : les appelants ne font que le lire (`includes`).
|
|
299
|
+
*/
|
|
300
|
+
const tvColumnIDsCache = new Map();
|
|
301
|
+
|
|
189
302
|
export function tvColumnIDs(filePath, field) {
|
|
303
|
+
const cacheKey = `${filePath}${field}`;
|
|
304
|
+
const cached = tvColumnIDsCache.get(cacheKey);
|
|
305
|
+
if (cached !== undefined) return cached;
|
|
306
|
+
|
|
190
307
|
const list = tvs[filePath];
|
|
191
308
|
const enum_name = `enum_${field}_id`;
|
|
192
|
-
let ids = list.map((row) => row[enum_name]);
|
|
193
|
-
// remove undefineds
|
|
194
|
-
ids = ids.filter((id) => id);
|
|
195
309
|
// split each by | and get uniques
|
|
310
|
+
const seen = new Set();
|
|
196
311
|
const unique_ids = [];
|
|
197
|
-
for (const
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
312
|
+
for (const row of list) {
|
|
313
|
+
const id = row[enum_name];
|
|
314
|
+
// remove undefineds
|
|
315
|
+
if (!id) continue;
|
|
316
|
+
for (const value of id.split('|')) {
|
|
317
|
+
if (!seen.has(value)) {
|
|
318
|
+
seen.add(value);
|
|
319
|
+
unique_ids.push(value);
|
|
320
|
+
}
|
|
201
321
|
}
|
|
202
322
|
}
|
|
203
|
-
|
|
323
|
+
|
|
324
|
+
tvColumnIDsCache.set(cacheKey, unique_ids);
|
|
204
325
|
return unique_ids;
|
|
205
326
|
}
|
|
206
327
|
|
|
@@ -211,7 +332,7 @@ export function tvColumnLines(filePath, column, matcher) {
|
|
|
211
332
|
for (const row of list) {
|
|
212
333
|
let match = true;
|
|
213
334
|
for (const key in matcher) {
|
|
214
|
-
if (
|
|
335
|
+
if (tvMatchImpl(row, key, matcher) === false) {
|
|
215
336
|
match = false;
|
|
216
337
|
break;
|
|
217
338
|
}
|
|
@@ -221,10 +342,7 @@ export function tvColumnLines(filePath, column, matcher) {
|
|
|
221
342
|
return lines;
|
|
222
343
|
}
|
|
223
344
|
|
|
224
|
-
function
|
|
225
|
-
if (tv_match_new_version) {
|
|
226
|
-
return tvMatchOptimized(row, key, matcher);
|
|
227
|
-
}
|
|
345
|
+
function tvMatchLegacy(row, key, matcher) {
|
|
228
346
|
if (!row.hasOwnProperty(key)) {
|
|
229
347
|
// for empty csv columns
|
|
230
348
|
// for q4pa_conv
|
|
@@ -291,7 +409,28 @@ function tvMatchOptimized(row, key, matcher) {
|
|
|
291
409
|
}
|
|
292
410
|
|
|
293
411
|
export function tv(filePath, matcher) {
|
|
412
|
+
let cacheKey = filePath;
|
|
413
|
+
for (const key in matcher) cacheKey += `${key}${matcher[key]}`;
|
|
414
|
+
|
|
415
|
+
const cached = tvCache.get(cacheKey);
|
|
416
|
+
// `tvLookup` ne retourne jamais `undefined` (une ligne ou `null`) : `undefined`
|
|
417
|
+
// distingue donc bien un défaut de cache d'un `null` mémoïsé.
|
|
418
|
+
if (cached !== undefined) return cached;
|
|
419
|
+
|
|
420
|
+
const result = tvLookup(filePath, matcher);
|
|
421
|
+
|
|
422
|
+
if (tvCache.size >= TV_CACHE_MAX) {
|
|
423
|
+
// Éviction FIFO : `Map` conserve l'ordre d'insertion.
|
|
424
|
+
tvCache.delete(tvCache.keys().next().value);
|
|
425
|
+
}
|
|
426
|
+
tvCache.set(cacheKey, result);
|
|
427
|
+
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function tvLookup(filePath, matcher) {
|
|
294
432
|
const list = tvs[filePath];
|
|
433
|
+
const matcher_size = Object.keys(matcher).length;
|
|
295
434
|
let match_count = 0;
|
|
296
435
|
let max_match_count = 0;
|
|
297
436
|
let match = null;
|
|
@@ -299,10 +438,10 @@ export function tv(filePath, matcher) {
|
|
|
299
438
|
for (const row of list) {
|
|
300
439
|
match_count = 0;
|
|
301
440
|
for (const key in matcher) {
|
|
302
|
-
if (
|
|
441
|
+
if (tvMatchImpl(row, key, matcher)) match_count += 1;
|
|
303
442
|
}
|
|
304
443
|
// if match_count is same as matcher, we are done
|
|
305
|
-
if (match_count ===
|
|
444
|
+
if (match_count === matcher_size) return row;
|
|
306
445
|
|
|
307
446
|
/* if (filePath === 'q4pa_conv') console.warn(match_count) */
|
|
308
447
|
if (match_count > max_match_count) {
|
|
@@ -385,22 +524,51 @@ export function getVolumeStockageFromDescription(description) {
|
|
|
385
524
|
return matching && matching.length > 1 ? parseInt(matching[1], 10) : 0;
|
|
386
525
|
}
|
|
387
526
|
|
|
527
|
+
/**
|
|
528
|
+
* Cache de `cleanReference`.
|
|
529
|
+
*
|
|
530
|
+
* Les r\u00e9f\u00e9rences proviennent des DPE en entr\u00e9e, leur cardinalit\u00e9 n'est donc pas ferm\u00e9e sur un
|
|
531
|
+
* process qui tourne longtemps (\u00e9coute NATS, worker) : le cache est born\u00e9 et \u00e9vinc\u00e9 en FIFO,
|
|
532
|
+
* comme celui de `tv()`.
|
|
533
|
+
*/
|
|
534
|
+
const CLEAN_REFERENCE_CACHE_MAX = 5000;
|
|
535
|
+
const cleanReferenceCache = new Map();
|
|
536
|
+
|
|
388
537
|
/**
|
|
389
538
|
* Remove space and accented characters
|
|
539
|
+
*
|
|
540
|
+
* M\u00e9mo\u00efs\u00e9e : `normalize('NFD')` suivi de deux remplacements par expression r\u00e9guli\u00e8re co\u00fbte cher,
|
|
541
|
+
* et les m\u00eames r\u00e9f\u00e9rences sont compar\u00e9es en boucle (chaque pont thermique est confront\u00e9 \u00e0 toutes
|
|
542
|
+
* les parois).
|
|
543
|
+
*
|
|
390
544
|
* @param reference {string}
|
|
391
545
|
* return {string}
|
|
392
546
|
*/
|
|
393
547
|
export function cleanReference(reference) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
548
|
+
// Valeurs falsy (undefined, null, '', 0) : retourn\u00e9es telles quelles, sans passer par le cache.
|
|
549
|
+
if (!reference) {
|
|
550
|
+
return reference;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const cached = cleanReferenceCache.get(reference);
|
|
554
|
+
if (cached !== undefined) {
|
|
555
|
+
return cached;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const cleaned = reference
|
|
559
|
+
.toString()
|
|
560
|
+
.toLowerCase()
|
|
561
|
+
.normalize('NFD')
|
|
562
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
563
|
+
.replace(/\s+/g, '');
|
|
564
|
+
|
|
565
|
+
if (cleanReferenceCache.size >= CLEAN_REFERENCE_CACHE_MAX) {
|
|
566
|
+
// \u00c9viction FIFO : `Map` conserve l'ordre d'insertion.
|
|
567
|
+
cleanReferenceCache.delete(cleanReferenceCache.keys().next().value);
|
|
401
568
|
}
|
|
569
|
+
cleanReferenceCache.set(reference, cleaned);
|
|
402
570
|
|
|
403
|
-
return
|
|
571
|
+
return cleaned;
|
|
404
572
|
}
|
|
405
573
|
|
|
406
574
|
/**
|
|
@@ -492,24 +660,7 @@ export function containsAnySubstring(mainString, substrings) {
|
|
|
492
660
|
* @returns {number} Résultat de l'évaluation.
|
|
493
661
|
*/
|
|
494
662
|
export function excel_to_js_exec(formulaOrValue, pn, E, F) {
|
|
495
|
-
|
|
496
|
-
// à convertir en point pour être évaluées.
|
|
497
|
-
const formula =
|
|
498
|
-
typeof formulaOrValue === 'string'
|
|
499
|
-
? formulaOrValue.replace(/(\d),(\d)/g, '$1.$2')
|
|
500
|
-
: formulaOrValue;
|
|
501
|
-
|
|
502
|
-
if (!isNaN(formula)) {
|
|
503
|
-
return Number(formula);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const Pn = pn / 1000;
|
|
507
|
-
return evaluate(formula, {
|
|
508
|
-
Pn,
|
|
509
|
-
logPn: Math.log10(Pn),
|
|
510
|
-
E,
|
|
511
|
-
F
|
|
512
|
-
});
|
|
663
|
+
return evaluateFormula(formulaOrValue, pn, E, F);
|
|
513
664
|
}
|
|
514
665
|
|
|
515
666
|
/**
|
package/utils.spec.js
CHANGED
|
@@ -41,6 +41,12 @@ const {
|
|
|
41
41
|
tvColumnIDs,
|
|
42
42
|
tvColumnLines,
|
|
43
43
|
tv,
|
|
44
|
+
clearTvCache,
|
|
45
|
+
setTvCacheMax,
|
|
46
|
+
getTvCacheMax,
|
|
47
|
+
tvCacheSize,
|
|
48
|
+
tvCacheKeys,
|
|
49
|
+
TV_CACHE_KEY_SEPARATORS,
|
|
44
50
|
removeKeyFromJSON,
|
|
45
51
|
clean_dpe,
|
|
46
52
|
getThicknessFromDescription,
|
|
@@ -308,6 +314,11 @@ describe('Utils unit tests', () => {
|
|
|
308
314
|
test('retourne les identifiants uniques éclatés', () => {
|
|
309
315
|
expect(tvColumnIDs('colonnes', 'x')).toStrictEqual(['1', '2', '3']);
|
|
310
316
|
});
|
|
317
|
+
|
|
318
|
+
test('met en cache le résultat : le même tableau est retourné au second appel', () => {
|
|
319
|
+
const first = tvColumnIDs('colonnes', 'x');
|
|
320
|
+
expect(tvColumnIDs('colonnes', 'x')).toBe(first);
|
|
321
|
+
});
|
|
311
322
|
});
|
|
312
323
|
|
|
313
324
|
// -------------------------------------------------------------------------------------------
|
|
@@ -420,6 +431,53 @@ describe('Utils unit tests', () => {
|
|
|
420
431
|
});
|
|
421
432
|
});
|
|
422
433
|
|
|
434
|
+
// -------------------------------------------------------------------------------------------
|
|
435
|
+
describe('tv / cache', () => {
|
|
436
|
+
// Règle : `tv()` mémoïse chaque couple (table, matcher), borné par une éviction FIFO.
|
|
437
|
+
let initialMax;
|
|
438
|
+
|
|
439
|
+
beforeEach(() => {
|
|
440
|
+
initialMax = getTvCacheMax();
|
|
441
|
+
clearTvCache();
|
|
442
|
+
return () => {
|
|
443
|
+
setTvCacheMax(initialMax);
|
|
444
|
+
clearTvCache();
|
|
445
|
+
};
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('mémoïse le résultat, y compris un résultat null', () => {
|
|
449
|
+
const row = tv('table_defaut', { enum_x_id: '1' });
|
|
450
|
+
expect(tv('table_defaut', { enum_x_id: '1' })).toBe(row);
|
|
451
|
+
expect(tv('table_defaut', { enum_x_id: 'absent' })).toBeNull();
|
|
452
|
+
expect(tv('table_defaut', { enum_x_id: 'absent' })).toBeNull();
|
|
453
|
+
expect(tvCacheSize()).toBe(2);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test('expose les clés du cache au format table + clé + valeur', () => {
|
|
457
|
+
tv('table_defaut', { enum_x_id: '1' });
|
|
458
|
+
const { entry, value } = TV_CACHE_KEY_SEPARATORS;
|
|
459
|
+
expect(tvCacheKeys()).toStrictEqual([`table_defaut${entry}enum_x_id${value}1`]);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('évince la plus ancienne entrée au-delà de la borne', () => {
|
|
463
|
+
setTvCacheMax(2);
|
|
464
|
+
expect(getTvCacheMax()).toBe(2);
|
|
465
|
+
|
|
466
|
+
tv('table_defaut', { enum_x_id: '1' });
|
|
467
|
+
tv('table_defaut', { enum_x_id: '2' });
|
|
468
|
+
tv('table_defaut', { enum_x_id: '3' });
|
|
469
|
+
|
|
470
|
+
expect(tvCacheSize()).toBe(2);
|
|
471
|
+
expect(tvCacheKeys().some((key) => key.endsWith('1'))).toBe(false);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test('clearTvCache vide le cache', () => {
|
|
475
|
+
tv('table_defaut', { enum_x_id: '1' });
|
|
476
|
+
clearTvCache();
|
|
477
|
+
expect(tvCacheSize()).toBe(0);
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
423
481
|
// -------------------------------------------------------------------------------------------
|
|
424
482
|
describe('removeKeyFromJSON', () => {
|
|
425
483
|
// Règle : supprime récursivement la clé cible, sauf sous les branches listées dans skipKeys.
|
|
@@ -513,6 +571,17 @@ describe('Utils unit tests', () => {
|
|
|
513
571
|
expect(cleanReference(input)).toBe(expected);
|
|
514
572
|
});
|
|
515
573
|
|
|
574
|
+
test('retourne le résultat mémoïsé au second appel', () => {
|
|
575
|
+
expect(cleanReference('Réf Mémo')).toBe('refmemo');
|
|
576
|
+
expect(cleanReference('Réf Mémo')).toBe('refmemo');
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test('reste correct une fois la borne du cache dépassée (éviction FIFO)', () => {
|
|
580
|
+
for (let i = 0; i <= 5000; i += 1) cleanReference(`Réf éviction ${i}`);
|
|
581
|
+
expect(cleanReference('Réf éviction 0')).toBe('refeviction0');
|
|
582
|
+
expect(cleanReference('Réf éviction 5000')).toBe('refeviction5000');
|
|
583
|
+
});
|
|
584
|
+
|
|
516
585
|
test('compareReferences ignore espaces et accents', () => {
|
|
517
586
|
expect(compareReferences('Réf 1', 'ref1')).toBe(true);
|
|
518
587
|
expect(compareReferences('a', 'b')).toBe(false);
|