@open3cl/engine 1.6.1 → 1.7.0

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 (109) hide show
  1. package/10_besoin_fr.spec.js +122 -0
  2. package/10_clim.spec.js +116 -0
  3. package/11_besoin_ecs.spec.js +69 -0
  4. package/11_ecs.spec.js +282 -0
  5. package/11_nadeq.spec.js +37 -0
  6. package/12.4_pac.spec.js +131 -0
  7. package/13.2_generateur_combustion.js +2 -0
  8. package/13.2_generateur_combustion.spec.js +537 -0
  9. package/13.2_generateur_combustion_bouilleur.js +4 -1
  10. package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
  11. package/13.2_generateur_combustion_ch.spec.js +323 -0
  12. package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
  13. package/13.2_generateur_pac.spec.js +64 -0
  14. package/13_rendement_distribution_ecs.spec.js +38 -0
  15. package/14_generateur_ecs.js +4 -4
  16. package/14_generateur_ecs.spec.js +745 -0
  17. package/15_conso_aux.js +121 -1
  18. package/15_conso_aux.spec.js +550 -0
  19. package/16.2_production_enr.spec.js +188 -0
  20. package/16_conso_eclairage.spec.js +62 -0
  21. package/2021_04_13_confort_ete.spec.js +159 -0
  22. package/2021_04_13_qualite_isolation.js +1 -1
  23. package/2021_04_13_qualite_isolation.spec.js +286 -0
  24. package/3.1_b.js +1 -1
  25. package/3.1_b.spec.js +367 -0
  26. package/3.2.1_mur.spec.js +812 -39
  27. package/3.2.2_plancher_bas.spec.js +643 -78
  28. package/3.2.3_plancher_haut.spec.js +452 -0
  29. package/3.3.1.4_porte.spec.js +114 -0
  30. package/3.3_baie_vitree.spec.js +449 -295
  31. package/3.4_pont_thermique.spec.js +890 -0
  32. package/3_deperdition.spec.js +397 -0
  33. package/4_ventilation.js +1 -1
  34. package/4_ventilation.spec.js +406 -0
  35. package/5_conso_ventilation.spec.js +186 -0
  36. package/6.2_surface_sud_equivalente.js +2 -1
  37. package/6.2_surface_sud_equivalente.spec.js +138 -0
  38. package/8_intermittence.spec.js +33 -0
  39. package/9_besoin_ch.js +2 -2
  40. package/9_besoin_ch.spec.js +381 -0
  41. package/9_chauffage.js +4 -0
  42. package/9_chauffage.spec.js +478 -92
  43. package/9_conso_ch.spec.js +502 -248
  44. package/9_emetteur_ch.spec.js +339 -0
  45. package/9_generateur_ch.spec.js +467 -81
  46. package/apport_et_besoin.spec.js +126 -0
  47. package/conso.js +21 -7
  48. package/conso.spec.js +491 -82
  49. package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
  50. package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
  51. package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
  52. package/core/conf/infrastructure/application.config.spec.js +35 -0
  53. package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
  54. package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
  55. package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
  56. package/core/util/infrastructure/object-util.spec.js +20 -0
  57. package/core/util/logger/log-service.js +16 -7
  58. package/core/util/logger/log-service.spec.js +101 -0
  59. package/dpe-sanitizer.service.spec.js +138 -0
  60. package/engine.js +20 -1
  61. package/engine.spec.js +949 -0
  62. package/enums.spec.js +46 -0
  63. package/features/dpe/domain/models/type-generateur.model.js +1 -2
  64. package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
  65. package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
  66. package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
  67. package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
  68. package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
  69. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
  70. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
  71. package/features/dpe/infrastructure/tv.store.js +28 -9
  72. package/features/dpe/infrastructure/tv.store.spec.js +44 -0
  73. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
  74. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
  75. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
  76. package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
  77. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
  78. package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
  79. package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
  80. package/features/engine/domain/ch/generateur-ch.service.js +2 -0
  81. package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
  82. package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
  83. package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
  84. package/features/engine/domain/constants.spec.js +14 -0
  85. package/features/engine/domain/contexte.builder.spec.js +21 -0
  86. package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
  87. package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
  88. package/features/engine/domain/engine.service.spec.js +227 -4
  89. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
  90. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
  91. package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
  92. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
  93. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
  94. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
  95. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
  96. package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
  97. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
  98. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
  99. package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
  100. package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
  101. package/ficheTechnique.spec.js +26 -0
  102. package/index.spec.js +54 -0
  103. package/package.json +5 -2
  104. package/preprocessor/index.js +20 -0
  105. package/preprocessor/index.spec.js +15 -0
  106. package/tv.spec.js +38 -0
  107. package/utils.js +43 -1
  108. package/utils.spec.js +518 -2
  109. package/output.js +0 -1
@@ -249,4 +249,192 @@ describe('production ENR unit tests', () => {
249
249
  }
250
250
  });
251
251
  });
252
+
253
+ test('should reuse an existing donnee_intermediaire when computing conso elec ac', () => {
254
+ // Le noeud donnee_intermediaire préexiste : il doit être conservé (et non recréé)
255
+ const productionElecEnr = {
256
+ donnee_intermediaire: { valeur_existante: 42 },
257
+ panneaux_pv_collection: {
258
+ panneaux_pv: [{ nombre_module: 8, enum_orientation_pv_id: 1, enum_inclinaison_pv_id: 2 }]
259
+ }
260
+ };
261
+
262
+ const productionElectricite = {};
263
+ const conso = {
264
+ ef_conso: {},
265
+ sortie_par_energie_collection: {
266
+ sortie_par_energie: [
267
+ { enum_type_energie_id: '1', conso_5_usages: 1500, conso_ch: 1000, conso_ecs: 1500 }
268
+ ]
269
+ }
270
+ };
271
+
272
+ productionENR.calculateConsoElecAc(
273
+ productionElectricite,
274
+ productionElecEnr,
275
+ conso,
276
+ 1,
277
+ 'maison',
278
+ 10
279
+ );
280
+
281
+ // la clé préexistante est préservée, les champs calculés sont ajoutés
282
+ expect(productionElecEnr.donnee_intermediaire.valeur_existante).toBe(42);
283
+ expect(productionElecEnr.donnee_intermediaire.production_pv).toBe(2826.8015616000007);
284
+ expect(productionElecEnr.donnee_intermediaire.conso_elec_ac).toBeGreaterThan(0);
285
+ });
286
+
287
+ test('should get ppv when panneaux_pv is a single object (not an array)', () => {
288
+ const productionElecEnr = {
289
+ panneaux_pv_collection: {
290
+ panneaux_pv: {
291
+ nombre_module: 8,
292
+ enum_orientation_pv_id: 1,
293
+ enum_inclinaison_pv_id: 2
294
+ }
295
+ }
296
+ };
297
+ // même résultat qu'avec un tableau à un élément
298
+ expect(productionENR.getPpv(productionElecEnr, 1)).toBe(2826.8015616000007);
299
+ });
300
+
301
+ test('should update ep conso with a coeff ep override', () => {
302
+ const productionElectricite = {
303
+ conso_elec_ac_ecs: 200,
304
+ conso_elec_ac_ch: 150,
305
+ conso_elec_ac_fr: 100,
306
+ conso_elec_ac_eclairage: 250,
307
+ conso_elec_ac_auxiliaire: 300
308
+ };
309
+ const conso = {
310
+ ep_conso: {
311
+ ep_conso_ecs: 1000,
312
+ ep_conso_ch: 500,
313
+ ep_conso_fr: 800,
314
+ ep_conso_eclairage: 900,
315
+ ep_conso_totale_auxiliaire: 1250,
316
+ ep_conso_5_usages: 5000,
317
+ ep_conso_5_usages_m2: 100
318
+ }
319
+ };
320
+
321
+ // coeff_ep_override = 2 : chaque poste est minoré de 2 * autoconso
322
+ productionENR.updateEPConso(productionElectricite, conso, 10, 2);
323
+
324
+ expect(conso.ep_conso).toStrictEqual({
325
+ ep_conso_ecs: 600, // 1000 - 2*200
326
+ ep_conso_ch: 200, // 500 - 2*150
327
+ ep_conso_fr: 600, // 800 - 2*100
328
+ ep_conso_eclairage: 400, // 900 - 2*250
329
+ ep_conso_totale_auxiliaire: 650, // 1250 - 2*300
330
+ ep_conso_5_usages: 3000, // 5000 - 2*1000
331
+ ep_conso_5_usages_m2: 300 // floor(3000 / 10)
332
+ });
333
+ });
334
+
335
+ test('should update ep conso with the default coeff ep (1.9) when no override', () => {
336
+ const productionElectricite = {
337
+ conso_elec_ac_ecs: 200,
338
+ conso_elec_ac_ch: 150,
339
+ conso_elec_ac_fr: 100,
340
+ conso_elec_ac_eclairage: 250,
341
+ conso_elec_ac_auxiliaire: 300
342
+ };
343
+ const conso = {
344
+ ep_conso: {
345
+ ep_conso_ecs: 1000,
346
+ ep_conso_ch: 500,
347
+ ep_conso_fr: 800,
348
+ ep_conso_eclairage: 900,
349
+ ep_conso_totale_auxiliaire: 1250,
350
+ ep_conso_5_usages: 5000,
351
+ ep_conso_5_usages_m2: 100
352
+ }
353
+ };
354
+
355
+ // pas d'override => coefficient par défaut 1.9
356
+ productionENR.updateEPConso(productionElectricite, conso, 10);
357
+
358
+ expect(conso.ep_conso).toStrictEqual({
359
+ ep_conso_ecs: 620, // 1000 - 1.9*200
360
+ ep_conso_ch: 215, // 500 - 1.9*150
361
+ ep_conso_fr: 610, // 800 - 1.9*100
362
+ ep_conso_eclairage: 425, // 900 - 1.9*250
363
+ ep_conso_totale_auxiliaire: 680, // 1250 - 1.9*300
364
+ ep_conso_5_usages: 3100, // 5000 - 1.9*1000
365
+ ep_conso_5_usages_m2: 310 // floor(3100 / 10)
366
+ });
367
+ });
368
+
369
+ test.each([
370
+ ['une installation absente', null],
371
+ ["l'absence de production photovoltaïque", { donnee_entree: { presence_production_pv: 0 } }]
372
+ ])('calculateEnr retourne des autoconsommations nulles pour %s', (_label, productionElecEnr) => {
373
+ const conso = { ef_conso: {}, ep_conso: {} };
374
+
375
+ const ret = productionENR.calculateEnr(productionElecEnr, conso, 10, 'maison', 1, 100);
376
+
377
+ expect(ret).toStrictEqual({
378
+ production_pv: 0,
379
+ conso_elec_ac: 0,
380
+ conso_elec_ac_ch: 0,
381
+ conso_elec_ac_ecs: 0,
382
+ conso_elec_ac_fr: 0,
383
+ conso_elec_ac_eclairage: 0,
384
+ conso_elec_ac_auxiliaire: 0,
385
+ conso_elec_ac_autre_usage: 0
386
+ });
387
+ });
388
+
389
+ test('calculateEnr calcule et applique les autoconsommations en présence de PV', () => {
390
+ const productionElecEnr = {
391
+ donnee_entree: { presence_production_pv: 1 },
392
+ panneaux_pv_collection: {
393
+ panneaux_pv: [{ nombre_module: 8, enum_orientation_pv_id: 1, enum_inclinaison_pv_id: 2 }]
394
+ }
395
+ };
396
+ const conso = {
397
+ ef_conso: {
398
+ conso_ecs: 1000,
399
+ conso_ch: 500,
400
+ conso_fr: 800,
401
+ conso_eclairage: 900,
402
+ conso_totale_auxiliaire: 1250,
403
+ conso_5_usages: 1500,
404
+ conso_5_usages_m2: 100
405
+ },
406
+ ep_conso: {
407
+ ep_conso_ecs: 3000,
408
+ ep_conso_ch: 1500,
409
+ ep_conso_fr: 2000,
410
+ ep_conso_eclairage: 1800,
411
+ ep_conso_totale_auxiliaire: 2500,
412
+ ep_conso_5_usages: 10000,
413
+ ep_conso_5_usages_m2: 100
414
+ },
415
+ sortie_par_energie_collection: {
416
+ sortie_par_energie: [
417
+ { enum_type_energie_id: '1', conso_5_usages: 1500, conso_ch: 1000, conso_ecs: 1500 }
418
+ ]
419
+ }
420
+ };
421
+
422
+ const ret = productionENR.calculateEnr(productionElecEnr, conso, 10, 'maison', 1, 100);
423
+
424
+ // valeurs de référence de régression
425
+ expect(ret).toStrictEqual({
426
+ production_pv: 2826.8015616000007,
427
+ conso_elec_ac: 403.3632077277818,
428
+ conso_elec_ac_ch: 17.146151231786686,
429
+ conso_elec_ac_ecs: 64.29806711920007,
430
+ conso_elec_ac_fr: 171.46151231786683,
431
+ conso_elec_ac_eclairage: 38.57884027152004,
432
+ conso_elec_ac_auxiliaire: 0,
433
+ conso_elec_ac_autre_usage: 21.14245810055866
434
+ });
435
+
436
+ // les consommations d'énergie finale sont bien minorées par l'autoconsommation
437
+ expect(conso.ef_conso.conso_ecs).toBeLessThan(1000);
438
+ expect(conso.ef_conso.conso_5_usages).toBeLessThan(1500);
439
+ });
252
440
  });
@@ -0,0 +1,62 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Le module ne dépend que du mapping enum_zone_climatique_id -> libellé de zone.
5
+ * On le mocke afin d'isoler le test du contenu réel de `enums`.
6
+ */
7
+ vi.mock('./enums.js', () => ({
8
+ default: {
9
+ zone_climatique: {
10
+ 1: 'h1a',
11
+ 2: 'h1b',
12
+ 3: 'h1c',
13
+ 4: 'h2a',
14
+ 5: 'h2b',
15
+ 6: 'h2c',
16
+ 7: 'h2d',
17
+ 8: 'h3'
18
+ }
19
+ }
20
+ }));
21
+
22
+ const { default: calc_conso_eclairage } = await import('./16_conso_eclairage.js');
23
+
24
+ /**
25
+ * 16.1 Consommation d'éclairage (Cecl)
26
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §16.1
27
+ *
28
+ * Cecl = (C * Pecl * Nh) / 1000 avec C = 0.9 (interrupteur) et Pecl = 1.4 W/m²
29
+ * Nh = nombre d'heures de fonctionnement de l'éclairage, fonction de la zone climatique.
30
+ */
31
+ describe("Calcul de la consommation d'éclairage (Cecl)", () => {
32
+ // [enum_zone_climatique_id, libellé, Nh, Cecl attendu]
33
+ test.each([
34
+ ['1', 'h1a', 1500, (0.9 * 1.4 * 1500) / 1000],
35
+ ['2', 'h1b', 1445, (0.9 * 1.4 * 1445) / 1000],
36
+ ['3', 'h1c', 1476, (0.9 * 1.4 * 1476) / 1000],
37
+ ['4', 'h2a', 1500, (0.9 * 1.4 * 1500) / 1000],
38
+ ['5', 'h2b', 1531, (0.9 * 1.4 * 1531) / 1000],
39
+ ['6', 'h2c', 1566, (0.9 * 1.4 * 1566) / 1000],
40
+ ['7', 'h2d', 1566, (0.9 * 1.4 * 1566) / 1000],
41
+ ['8', 'h3', 1506, (0.9 * 1.4 * 1506) / 1000]
42
+ ])(
43
+ 'zone climatique %s (%s) : Nh=%d heures => Cecl attendu',
44
+ (zc_id, _libelle, _nh, cecl_attendu) => {
45
+ expect(calc_conso_eclairage(zc_id)).toBeCloseTo(cecl_attendu, 10);
46
+ }
47
+ );
48
+
49
+ test('la consommation est identique pour deux zones ayant le même Nh (h1a et h2a)', () => {
50
+ expect(calc_conso_eclairage('1')).toBeCloseTo(calc_conso_eclairage('4'), 10);
51
+ });
52
+
53
+ test("la consommation croît avec le nombre d'heures (h1b < h3 < h2c)", () => {
54
+ expect(calc_conso_eclairage('2')).toBeLessThan(calc_conso_eclairage('8'));
55
+ expect(calc_conso_eclairage('8')).toBeLessThan(calc_conso_eclairage('6'));
56
+ });
57
+
58
+ test('retourne NaN pour un identifiant de zone climatique inconnu', () => {
59
+ // enums.zone_climatique[zc_id] est undefined => Nh[undefined] est undefined => NaN
60
+ expect(calc_conso_eclairage('99')).toBeNaN();
61
+ });
62
+ });
@@ -0,0 +1,159 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendances mockées pour isoler `calc_confort_ete` :
5
+ * - `enums.classe_inertie` : détermine le caractère « lourd » de l'inertie ;
6
+ * - `getKeyByValue` : réduit à un passe-plat renvoyant directement le libellé
7
+ * de confort (on assertera donc `enum_indicateur_confort_ete_id` = libellé) ;
8
+ * - `requestInput` : passe-plat lisant le champ demandé dans les données d'entrée.
9
+ */
10
+ vi.mock('./enums.js', () => ({
11
+ default: {
12
+ classe_inertie: { 1: 'légère', 2: 'moyenne', 3: 'lourde', 4: 'très lourde' },
13
+ indicateur_confort_ete: { 1: 'insuffisant', 2: 'moyen', 3: 'bon' }
14
+ }
15
+ }));
16
+
17
+ vi.mock('./utils.js', () => ({
18
+ getKeyByValue: (object, value) => value,
19
+ requestInput: (de, du, field) => de[field]
20
+ }));
21
+
22
+ const { default: calc_confort_ete } = await import('./2021_04_13_confort_ete.js');
23
+
24
+ /** Fabrique une baie vitrée : `enum_orientation_id` sert au caractère traversant,
25
+ * `orientation` et `type_fermeture` à la protection solaire. */
26
+ function baie({ orientationId, orientation = 'sud', typeFermeture = 'volet' }) {
27
+ return {
28
+ donnee_entree: {
29
+ enum_orientation_id: orientationId,
30
+ orientation,
31
+ type_fermeture: typeFermeture
32
+ },
33
+ donnee_utilisateur: {}
34
+ };
35
+ }
36
+
37
+ /** Fabrique un plancher haut avec son type d'adjacence et d'isolation. */
38
+ function plancherHaut({ typeAdjacence = 'extérieur', typeIsolation = 'isolé' }) {
39
+ return {
40
+ donnee_entree: { type_adjacence: typeAdjacence, type_isolation: typeIsolation },
41
+ donnee_utilisateur: {}
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Indicateur de confort d'été
47
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - annexe confort d'été
48
+ */
49
+ describe("calc_confort_ete - indicateur de confort d'été", () => {
50
+ test('inertie lourde + aspect traversant : niveau « bon »', () => {
51
+ // deux orientations distinctes => aspect_traversant = 1 ; inertie lourde = 1 => nv_bon = 2
52
+ const bv = [baie({ orientationId: '1' }), baie({ orientationId: '2' })];
53
+ const ph = [plancherHaut({ typeIsolation: 'isolé' })];
54
+
55
+ const ret = calc_confort_ete('3', bv, ph);
56
+
57
+ expect(ret.inertie_lourde).toBe(1);
58
+ expect(ret.aspect_traversant).toBe(1);
59
+ expect(ret.enum_indicateur_confort_ete_id).toBe('bon');
60
+ });
61
+
62
+ test('inertie « très lourde » également considérée comme lourde', () => {
63
+ const bv = [baie({ orientationId: '1' }), baie({ orientationId: '2' })];
64
+ const ph = [plancherHaut({})];
65
+
66
+ const ret = calc_confort_ete('4', bv, ph);
67
+
68
+ expect(ret.inertie_lourde).toBe(1);
69
+ });
70
+
71
+ test('une seule orientation : aspect non traversant', () => {
72
+ const bv = [baie({ orientationId: '1' }), baie({ orientationId: '1' })];
73
+ const ph = [plancherHaut({})];
74
+
75
+ const ret = calc_confort_ete('2', bv, ph);
76
+
77
+ expect(ret.aspect_traversant).toBe(0);
78
+ });
79
+
80
+ test('inertie légère sans aspect traversant : niveau « moyen »', () => {
81
+ // nv_bon = 0 (< 2) mais protections OK => moyen
82
+ const bv = [baie({ orientationId: '1' })];
83
+ const ph = [plancherHaut({})];
84
+
85
+ const ret = calc_confort_ete('1', bv, ph);
86
+
87
+ expect(ret.inertie_lourde).toBe(0);
88
+ expect(ret.enum_indicateur_confort_ete_id).toBe('moyen');
89
+ });
90
+
91
+ test("toiture non isolée donnant sur l'extérieur : niveau « insuffisant »", () => {
92
+ const bv = [baie({ orientationId: '1' }), baie({ orientationId: '2' })];
93
+ const ph = [plancherHaut({ typeAdjacence: 'extérieur', typeIsolation: 'non isolé' })];
94
+
95
+ const ret = calc_confort_ete('3', bv, ph);
96
+
97
+ expect(ret.isolation_toiture).toBe(0);
98
+ expect(ret.enum_indicateur_confort_ete_id).toBe('insuffisant');
99
+ });
100
+
101
+ test("toiture d'isolation inconnue donnant sur l'extérieur : isolation nulle", () => {
102
+ const bv = [baie({ orientationId: '1' })];
103
+ const ph = [plancherHaut({ typeAdjacence: 'extérieur', typeIsolation: 'inconnu' })];
104
+
105
+ const ret = calc_confort_ete('3', bv, ph);
106
+
107
+ expect(ret.isolation_toiture).toBe(0);
108
+ });
109
+
110
+ test('toiture non isolée mais non extérieure : isolation préservée', () => {
111
+ const bv = [baie({ orientationId: '1' }), baie({ orientationId: '2' })];
112
+ const ph = [plancherHaut({ typeAdjacence: 'local non chauffé', typeIsolation: 'non isolé' })];
113
+
114
+ const ret = calc_confort_ete('3', bv, ph);
115
+
116
+ expect(ret.isolation_toiture).toBe(1);
117
+ });
118
+
119
+ test('baie non nord sans fermeture : protection solaire nulle => « insuffisant »', () => {
120
+ const bv = [
121
+ baie({
122
+ orientationId: '1',
123
+ orientation: 'sud',
124
+ typeFermeture: 'abscence de fermeture pour la baie vitrée'
125
+ })
126
+ ];
127
+ const ph = [plancherHaut({})];
128
+
129
+ const ret = calc_confort_ete('3', bv, ph);
130
+
131
+ expect(ret.protection_solaire_exterieure).toBe(0);
132
+ expect(ret.enum_indicateur_confort_ete_id).toBe('insuffisant');
133
+ });
134
+
135
+ test('baie nord sans fermeture : ignorée, protection solaire préservée', () => {
136
+ const bv = [
137
+ baie({
138
+ orientationId: '1',
139
+ orientation: 'nord',
140
+ typeFermeture: 'abscence de fermeture pour la baie vitrée'
141
+ }),
142
+ baie({
143
+ orientationId: '2',
144
+ orientation: 'nord',
145
+ typeFermeture: 'abscence de fermeture pour la baie vitrée'
146
+ })
147
+ ];
148
+ const ph = [plancherHaut({})];
149
+
150
+ const ret = calc_confort_ete('3', bv, ph);
151
+
152
+ expect(ret.protection_solaire_exterieure).toBe(1);
153
+ });
154
+
155
+ test("le brasseur d'air est toujours absent (0) dans ce calcul", () => {
156
+ const ret = calc_confort_ete('3', [baie({ orientationId: '1' })], [plancherHaut({})]);
157
+ expect(ret.brasseur_air).toBe(0);
158
+ });
159
+ });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @returns {number}
3
3
  */
4
- function qualite_isol(dep, surface, seuil1, seuil2, seuil3) {
4
+ export function qualite_isol(dep, surface, seuil1, seuil2, seuil3) {
5
5
  /**
6
6
  * 1 - Très bonne
7
7
  * 2 - Bonne