@open3cl/engine 1.6.1 → 1.6.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/10_besoin_fr.spec.js +122 -0
- package/10_clim.spec.js +116 -0
- package/11_besoin_ecs.spec.js +69 -0
- package/11_ecs.spec.js +282 -0
- package/11_nadeq.spec.js +37 -0
- package/12.4_pac.spec.js +131 -0
- package/13.2_generateur_combustion.js +2 -0
- package/13.2_generateur_combustion.spec.js +537 -0
- package/13.2_generateur_combustion_bouilleur.js +4 -1
- package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
- package/13.2_generateur_combustion_ch.spec.js +323 -0
- package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
- package/13.2_generateur_pac.spec.js +64 -0
- package/13_rendement_distribution_ecs.spec.js +38 -0
- package/14_generateur_ecs.js +4 -4
- package/14_generateur_ecs.spec.js +745 -0
- package/15_conso_aux.spec.js +225 -0
- package/16.2_production_enr.spec.js +188 -0
- package/16_conso_eclairage.spec.js +62 -0
- package/2021_04_13_confort_ete.spec.js +159 -0
- package/2021_04_13_qualite_isolation.js +1 -1
- package/2021_04_13_qualite_isolation.spec.js +286 -0
- package/3.1_b.js +1 -1
- package/3.1_b.spec.js +367 -0
- package/3.2.1_mur.spec.js +812 -39
- package/3.2.2_plancher_bas.spec.js +643 -78
- package/3.2.3_plancher_haut.spec.js +452 -0
- package/3.3.1.4_porte.spec.js +114 -0
- package/3.3_baie_vitree.spec.js +449 -295
- package/3.4_pont_thermique.spec.js +890 -0
- package/3_deperdition.spec.js +397 -0
- package/4_ventilation.js +1 -1
- package/4_ventilation.spec.js +406 -0
- package/5_conso_ventilation.spec.js +186 -0
- package/6.2_surface_sud_equivalente.spec.js +138 -0
- package/8_intermittence.spec.js +33 -0
- package/9_besoin_ch.js +2 -2
- package/9_besoin_ch.spec.js +381 -0
- package/9_chauffage.js +4 -0
- package/9_chauffage.spec.js +478 -92
- package/9_conso_ch.spec.js +502 -248
- package/9_emetteur_ch.spec.js +339 -0
- package/9_generateur_ch.spec.js +467 -81
- package/apport_et_besoin.spec.js +126 -0
- package/conso.js +1 -1
- package/conso.spec.js +471 -82
- package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
- package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
- package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
- package/core/conf/infrastructure/application.config.spec.js +35 -0
- package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
- package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
- package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
- package/core/util/infrastructure/object-util.spec.js +20 -0
- package/core/util/logger/log-service.js +16 -7
- package/core/util/logger/log-service.spec.js +101 -0
- package/dpe-sanitizer.service.spec.js +138 -0
- package/engine.js +4 -1
- package/engine.spec.js +854 -0
- package/enums.spec.js +46 -0
- package/features/dpe/domain/models/type-generateur.model.js +1 -2
- package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
- package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
- package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
- package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
- package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
- package/features/dpe/infrastructure/tv.store.js +28 -9
- package/features/dpe/infrastructure/tv.store.spec.js +44 -0
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
- package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
- package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
- package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
- package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
- package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
- package/features/engine/domain/ch/generateur-ch.service.js +2 -0
- package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
- package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
- package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
- package/features/engine/domain/constants.spec.js +14 -0
- package/features/engine/domain/contexte.builder.spec.js +21 -0
- package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
- package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
- package/features/engine/domain/engine.service.spec.js +227 -4
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
- package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
- package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
- package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
- package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
- package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
- package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
- package/ficheTechnique.spec.js +26 -0
- package/index.spec.js +54 -0
- package/package.json +5 -2
- package/preprocessor/index.js +20 -0
- package/preprocessor/index.spec.js +15 -0
- package/tv.spec.js +38 -0
- package/utils.js +43 -1
- package/utils.spec.js +518 -2
- package/output.js +0 -1
package/enums.spec.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import enums from './enums.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `enums.js` est un gros mapping figé (identifiant -> libellé) issu du modèle
|
|
6
|
+
* de données ADEME. On ne fige pas l'intégralité du contenu : on vérifie
|
|
7
|
+
* uniquement des invariants structurels et la présence de quelques enums clés.
|
|
8
|
+
*/
|
|
9
|
+
describe('Table des enums (enums.js)', () => {
|
|
10
|
+
test('exporte un objet non vide par défaut', () => {
|
|
11
|
+
expect(enums).toBeTypeOf('object');
|
|
12
|
+
expect(enums).not.toBeNull();
|
|
13
|
+
expect(Object.keys(enums).length).toBeGreaterThan(0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('quelques enums clés sont présents et non vides', () => {
|
|
17
|
+
['version', 'modele_dpe', 'usage_fonctionnel_batiment', 'consentement_formulaire'].forEach(
|
|
18
|
+
(cle) => {
|
|
19
|
+
expect(enums).toHaveProperty(cle);
|
|
20
|
+
expect(Object.keys(enums[cle]).length).toBeGreaterThan(0);
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('chaque enum est un objet dont les libellés sont des chaînes non vides', () => {
|
|
26
|
+
Object.entries(enums).forEach(([nomEnum, valeurs]) => {
|
|
27
|
+
expect(valeurs, `enum ${nomEnum}`).toBeTypeOf('object');
|
|
28
|
+
Object.values(valeurs).forEach((libelle) => {
|
|
29
|
+
expect(typeof libelle).toBe('string');
|
|
30
|
+
expect(libelle.length).toBeGreaterThan(0);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('les identifiants (clés) sont uniques au sein de chaque enum', () => {
|
|
36
|
+
Object.entries(enums).forEach(([nomEnum, valeurs]) => {
|
|
37
|
+
const cles = Object.keys(valeurs);
|
|
38
|
+
expect(new Set(cles).size, `enum ${nomEnum}`).toBe(cles.length);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('contenu attendu pour l’enum modele_dpe', () => {
|
|
43
|
+
expect(enums.modele_dpe['1']).toBe('dpe 3cl 2021 méthode logement');
|
|
44
|
+
expect(enums.consentement_formulaire['0']).toBe('absence de consentement');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { TypeGenerateur } from './type-generateur.model.js';
|
|
3
|
+
|
|
4
|
+
describe('Modèle TypeGenerateur', () => {
|
|
5
|
+
test('expose les types de générateurs attendus', () => {
|
|
6
|
+
expect(TypeGenerateur).toEqual({
|
|
7
|
+
CHAUDIERE: 'CHAUDIERE',
|
|
8
|
+
OTHER: 'OTHER'
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('chaque clé correspond à une valeur chaîne identique (auto-référence)', () => {
|
|
13
|
+
Object.entries(TypeGenerateur).forEach(([cle, valeur]) => {
|
|
14
|
+
expect(typeof valeur).toBe('string');
|
|
15
|
+
expect(valeur).toBe(cle);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('les valeurs sont uniques et non vides', () => {
|
|
20
|
+
const valeurs = Object.values(TypeGenerateur);
|
|
21
|
+
expect(valeurs.length).toBeGreaterThan(0);
|
|
22
|
+
expect(new Set(valeurs).size).toBe(valeurs.length);
|
|
23
|
+
valeurs.forEach((valeur) => expect(valeur.length).toBeGreaterThan(0));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { TypeHabitation, TypeDpe } from './type-habitation.model.js';
|
|
3
|
+
|
|
4
|
+
describe('Modèle TypeHabitation', () => {
|
|
5
|
+
test("expose les types d'habitation attendus", () => {
|
|
6
|
+
expect(TypeHabitation).toEqual({
|
|
7
|
+
MAISON: 'MAISON',
|
|
8
|
+
APPARTEMENT: 'APPARTEMENT',
|
|
9
|
+
IMMEUBLE: 'IMMEUBLE'
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('chaque clé est auto-référencée par sa valeur', () => {
|
|
14
|
+
Object.entries(TypeHabitation).forEach(([cle, valeur]) => {
|
|
15
|
+
expect(valeur).toBe(cle);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Modèle TypeDpe', () => {
|
|
21
|
+
test('expose les types de DPE attendus', () => {
|
|
22
|
+
expect(TypeDpe).toEqual({
|
|
23
|
+
MAISON: 'MAISON',
|
|
24
|
+
APPARTEMENT: 'APPARTEMENT',
|
|
25
|
+
APPARTEMENT_A_PARTIR_IMMEUBLE: 'APPARTEMENT_A_PARTIR_IMMEUBLE',
|
|
26
|
+
IMMEUBLE: 'IMMEUBLE'
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('les valeurs sont uniques et non vides', () => {
|
|
31
|
+
const valeurs = Object.values(TypeDpe);
|
|
32
|
+
expect(new Set(valeurs).size).toBe(valeurs.length);
|
|
33
|
+
valeurs.forEach((valeur) => expect(valeur.length).toBeGreaterThan(0));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('reprend les types communs avec TypeHabitation', () => {
|
|
37
|
+
expect(TypeDpe.MAISON).toBe(TypeHabitation.MAISON);
|
|
38
|
+
expect(TypeDpe.APPARTEMENT).toBe(TypeHabitation.APPARTEMENT);
|
|
39
|
+
expect(TypeDpe.IMMEUBLE).toBe(TypeHabitation.IMMEUBLE);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { TypeStockage } from './type-stockage.model.js';
|
|
3
|
+
|
|
4
|
+
describe('Modèle TypeStockage', () => {
|
|
5
|
+
test('expose les types de stockage attendus', () => {
|
|
6
|
+
expect(TypeStockage).toEqual({
|
|
7
|
+
INSTANTANE: 'INSTANTANE',
|
|
8
|
+
INTEGRE: 'INTEGRE',
|
|
9
|
+
INDEPENDANT: 'INDEPENDANT'
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('chaque clé est auto-référencée et les valeurs sont uniques', () => {
|
|
14
|
+
const valeurs = Object.values(TypeStockage);
|
|
15
|
+
Object.entries(TypeStockage).forEach(([cle, valeur]) => expect(valeur).toBe(cle));
|
|
16
|
+
expect(new Set(valeurs).size).toBe(valeurs.length);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { TypeVentilation } from './type-ventilation.model.js';
|
|
3
|
+
|
|
4
|
+
describe('Modèle TypeVentilation', () => {
|
|
5
|
+
test('expose les types de ventilation attendus', () => {
|
|
6
|
+
expect(TypeVentilation).toEqual({
|
|
7
|
+
SIMPLE_FLUX_AUTO: 'SIMPLE_FLUX_AUTO',
|
|
8
|
+
SIMPLE_FLUX_HYGRO: 'SIMPLE_FLUX_HYGRO',
|
|
9
|
+
DOUBLE_FLUX: 'DOUBLE_FLUX'
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('chaque clé est auto-référencée et les valeurs sont uniques', () => {
|
|
14
|
+
const valeurs = Object.values(TypeVentilation);
|
|
15
|
+
Object.entries(TypeVentilation).forEach(([cle, valeur]) => expect(valeur).toBe(cle));
|
|
16
|
+
expect(new Set(valeurs).size).toBe(valeurs.length);
|
|
17
|
+
valeurs.forEach((valeur) => expect(valeur.length).toBeGreaterThan(0));
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -33,8 +33,8 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
33
33
|
enum_type_generateur_ecs_id: '46|93',
|
|
34
34
|
type_generateur: 'Chaudière gaz classique 1981-1985',
|
|
35
35
|
pn: 'Pn',
|
|
36
|
-
rpn: '84
|
|
37
|
-
rpint: '80
|
|
36
|
+
rpn: '84+2*log10(Pn)',
|
|
37
|
+
rpint: '80+3*log10(Pn)',
|
|
38
38
|
qp0_perc: '2%',
|
|
39
39
|
pveil: '150'
|
|
40
40
|
});
|
|
@@ -46,8 +46,8 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
46
46
|
type_generateur: 'Chaudière fioul à condensation après 2015',
|
|
47
47
|
critere_pn: '70<Pn≤400',
|
|
48
48
|
pn: 'Pn',
|
|
49
|
-
rpn: '94
|
|
50
|
-
rpint: '100
|
|
49
|
+
rpn: '94+log10(Pn)',
|
|
50
|
+
rpint: '100+log10(Pn)',
|
|
51
51
|
qp0_perc: '0.60%'
|
|
52
52
|
});
|
|
53
53
|
});
|
|
@@ -119,15 +119,19 @@ export class BaieVitreeTvStore extends TvStore {
|
|
|
119
119
|
(v) =>
|
|
120
120
|
v.enum_type_baie_id.split('|').includes(enumTypeBaieId) &&
|
|
121
121
|
v.enum_type_materiaux_menuiserie_id.split('|').includes(enumTypeMateriauxMenuiserieId)
|
|
122
|
-
) ||
|
|
122
|
+
) /* c8 ignore next -- .filter renvoie toujours un tableau : fallback défensif inatteignable */ ||
|
|
123
|
+
[];
|
|
123
124
|
|
|
124
125
|
let ug1, ug2;
|
|
125
126
|
|
|
126
127
|
if (ugValues.length) {
|
|
127
128
|
[ug1, ug2] = getRange(ug, [...new Set(ugValues.map((value) => value.ug).sort())]);
|
|
128
129
|
|
|
130
|
+
/* c8 ignore start -- getRange renvoie des bornes issues de ugValues : le find aboutit
|
|
131
|
+
toujours et le fallback `|| 0` reste défensif (inatteignable avec les données réelles). */
|
|
129
132
|
const uw1 = ugValues.find((value) => value.ug === ug1)?.uw || 0;
|
|
130
133
|
const uw2 = ugValues.find((value) => value.ug === ug2)?.uw || 0;
|
|
134
|
+
/* c8 ignore stop */
|
|
131
135
|
|
|
132
136
|
const delta_uw = Number(uw2) - Number(uw1);
|
|
133
137
|
const delta_ug = ug2 - ug1 || 0;
|
|
@@ -191,7 +195,9 @@ export class BaieVitreeTvStore extends TvStore {
|
|
|
191
195
|
*/
|
|
192
196
|
const deltar = this.getDeltar(enumTypeFermetureId);
|
|
193
197
|
const uwValues =
|
|
194
|
-
tv['ujn'].filter(
|
|
198
|
+
tv['ujn'].filter(
|
|
199
|
+
(v) => Number(v.deltar).toPrecision(2) === Number(deltar).toPrecision(2)
|
|
200
|
+
) /* c8 ignore next -- .filter renvoie toujours un tableau : fallback défensif inatteignable */ ||
|
|
195
201
|
[];
|
|
196
202
|
|
|
197
203
|
let uw1, uw2;
|
|
@@ -199,12 +205,15 @@ export class BaieVitreeTvStore extends TvStore {
|
|
|
199
205
|
if (uwValues.length) {
|
|
200
206
|
[uw1, uw2] = getRange(uw, [...new Set(uwValues.map((value) => value.uw).sort())]);
|
|
201
207
|
|
|
208
|
+
/* c8 ignore start -- getRange renvoie des bornes issues de uwValues : le find aboutit
|
|
209
|
+
toujours et le fallback `|| 0` reste défensif (inatteignable avec les données réelles). */
|
|
202
210
|
const ujn1 =
|
|
203
211
|
uwValues.find((value) => Number(value.uw).toPrecision(2) === Number(uw1).toPrecision(2))
|
|
204
212
|
?.ujn || 0;
|
|
205
213
|
const ujn2 =
|
|
206
214
|
uwValues.find((value) => Number(value.uw).toPrecision(2) === Number(uw2).toPrecision(2))
|
|
207
215
|
?.ujn || 0;
|
|
216
|
+
/* c8 ignore stop */
|
|
208
217
|
|
|
209
218
|
const delta_ujn = Number(ujn2) - Number(ujn1);
|
|
210
219
|
const delta_uw = uw2 - uw1 || 0;
|
|
@@ -216,12 +225,15 @@ export class BaieVitreeTvStore extends TvStore {
|
|
|
216
225
|
ujn = Number(ujn1) + (delta_ujn * (uw - uw1)) / delta_uw;
|
|
217
226
|
}
|
|
218
227
|
|
|
228
|
+
/* c8 ignore start -- les valeurs ujn tabulées sont toujours strictement positives :
|
|
229
|
+
ce garde-fou `!ujn` est défensif et inatteignable avec les données réelles. */
|
|
219
230
|
if (!ujn) {
|
|
220
231
|
logger.error(
|
|
221
232
|
`Pas de valeur forfaitaire ujn pour enumTypeFermetureId:${enumTypeFermetureId}, uw:${uw}`
|
|
222
233
|
);
|
|
223
234
|
return;
|
|
224
235
|
}
|
|
236
|
+
/* c8 ignore stop */
|
|
225
237
|
|
|
226
238
|
return parseFloat(ujn);
|
|
227
239
|
}
|
|
@@ -222,6 +222,16 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
222
222
|
const ug = tvStore.getSw('0', '0', 0);
|
|
223
223
|
expect(ug).toBeUndefined();
|
|
224
224
|
});
|
|
225
|
+
|
|
226
|
+
test('uw non interpolé lorsque le ug fourni correspond exactement à une valeur tabulée', () => {
|
|
227
|
+
// ug passé sous forme de chaîne présente dans la table => getRange renvoie deux
|
|
228
|
+
// bornes égales, delta_ug vaut 0 et uw prend directement la valeur tabulée (uw1).
|
|
229
|
+
expect(tvStore.getUw('4', '6', '0.5')).toBeCloseTo(1.3, 2);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("uw indéfini lorsqu'aucune ligne ne correspond au type de baie / matériau", () => {
|
|
233
|
+
expect(tvStore.getUw('9', '99', 2)).toBeUndefined();
|
|
234
|
+
});
|
|
225
235
|
});
|
|
226
236
|
|
|
227
237
|
describe('lecture des valeurs de ujn', () => {
|
|
@@ -252,6 +262,12 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
252
262
|
const ug = tvStore.getUjn('0', 0);
|
|
253
263
|
expect(ug).toBeUndefined();
|
|
254
264
|
});
|
|
265
|
+
|
|
266
|
+
test('ujn non interpolé lorsque le uw fourni correspond exactement à une valeur tabulée', () => {
|
|
267
|
+
// uw passé sous forme de chaîne présente dans la table => getRange renvoie deux
|
|
268
|
+
// bornes égales, delta_uw vaut 0 et ujn prend directement la valeur tabulée (ujn1).
|
|
269
|
+
expect(tvStore.getUjn('2', '0.8')).toBeCloseTo(0.8, 2);
|
|
270
|
+
});
|
|
255
271
|
});
|
|
256
272
|
|
|
257
273
|
describe('lecture des valeurs de deltar', () => {
|
|
@@ -3,6 +3,23 @@ import { getRange } from '../../../utils.js';
|
|
|
3
3
|
import { logger } from '../../../core/util/logger/log-service.js';
|
|
4
4
|
import { TypeHabitation } from '../domain/models/type-habitation.model.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Extrait la borne numérique d'un libellé de plage aiu/aue de la table
|
|
8
|
+
* `coef_reduction_deperdition`. Les bornes y sont stockées sous forme de libellés
|
|
9
|
+
* formatés à la française (ex: `'≤ 6,00'`, `'4,00 <'`) et doivent être converties
|
|
10
|
+
* en nombre pour être comparées au ratio surface_aiu / surface_aue.
|
|
11
|
+
*
|
|
12
|
+
* @param bound {string} Libellé de borne (ex: `'≤ 0,25'`, `'0,25 <'`)
|
|
13
|
+
* @return {number} Valeur numérique de la borne
|
|
14
|
+
*/
|
|
15
|
+
export function parseAiuAueBound(bound) {
|
|
16
|
+
return parseFloat(
|
|
17
|
+
String(bound)
|
|
18
|
+
.replace(/,/g, '.')
|
|
19
|
+
.replace(/[^\d.]/g, '')
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
/**
|
|
7
24
|
* Accès aux données des tables de valeurs
|
|
8
25
|
*
|
|
@@ -60,8 +77,8 @@ export class TvStore {
|
|
|
60
77
|
!v.zone_climatique ||
|
|
61
78
|
zc.toLowerCase().startsWith(v.zone_climatique.toLowerCase())) &&
|
|
62
79
|
(!rAiuAue ||
|
|
63
|
-
((!v.aiu_aue_min || v.aiu_aue_min < rAiuAue) &&
|
|
64
|
-
(!v.aiu_aue_max || v.aiu_aue_max >= rAiuAue)))
|
|
80
|
+
((!v.aiu_aue_min || parseAiuAueBound(v.aiu_aue_min) < rAiuAue) &&
|
|
81
|
+
(!v.aiu_aue_max || parseAiuAueBound(v.aiu_aue_max) >= rAiuAue)))
|
|
65
82
|
)?.b;
|
|
66
83
|
|
|
67
84
|
if (!b) {
|
|
@@ -208,13 +225,14 @@ export class TvStore {
|
|
|
208
225
|
* @return {number|undefined}
|
|
209
226
|
*/
|
|
210
227
|
getUeByUpd(enumTypeAdjacenceId, enumPeriodeConstructionId, dsp, upb) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
(v)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
228
|
+
const ueFiltered = tv['ue'].filter(
|
|
229
|
+
(v) =>
|
|
230
|
+
parseInt(v['2s_p']) === dsp &&
|
|
231
|
+
v.enum_type_adjacence_id.split('|').includes(enumTypeAdjacenceId) &&
|
|
232
|
+
v.enum_periode_construction_id.split('|').includes(enumPeriodeConstructionId)
|
|
233
|
+
);
|
|
234
|
+
/* c8 ignore next -- Array.filter renvoie toujours un tableau : repli || [] défensif inatteignable */
|
|
235
|
+
const ueValues = ueFiltered || [];
|
|
218
236
|
|
|
219
237
|
const ueRange = [...new Set(ueValues.map((value) => value.upb).sort())];
|
|
220
238
|
let ue;
|
|
@@ -223,6 +241,7 @@ export class TvStore {
|
|
|
223
241
|
if (ueValues.length) {
|
|
224
242
|
[upb1, upb2] = getRange(upb, ueRange);
|
|
225
243
|
|
|
244
|
+
/* c8 ignore next 2 -- upb1/upb2 sont toujours des bornes tabulées de ueValues : find() aboutit et ?.ue n'est jamais falsy (replis inatteignables) */
|
|
226
245
|
const ue1 = ueValues.find((value) => value.upb === upb1)?.ue || 0;
|
|
227
246
|
const ue2 = ueValues.find((value) => value.upb === upb2)?.ue || 0;
|
|
228
247
|
|
|
@@ -11,6 +11,23 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
11
11
|
tvStore = new TvStore();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
describe('lecture des valeurs de uPorte', () => {
|
|
15
|
+
test.each([
|
|
16
|
+
{
|
|
17
|
+
label: 'porte simple bois/PVC opaque pleine (id partagé 1|5)',
|
|
18
|
+
enumTypePorteId: '1',
|
|
19
|
+
expected: 3.5
|
|
20
|
+
},
|
|
21
|
+
{ label: 'porte simple métal opaque pleine', enumTypePorteId: '9', expected: 5.8 }
|
|
22
|
+
])('uPorte pour $label', ({ enumTypePorteId, expected }) => {
|
|
23
|
+
expect(tvStore.getUPorte(enumTypePorteId)).toBe(expected);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('pas de valeur de uPorte', () => {
|
|
27
|
+
expect(tvStore.getUPorte('0')).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
14
31
|
describe('lecture des valeurs de b', () => {
|
|
15
32
|
test.each([
|
|
16
33
|
{ enumTypeAdjacenceId: '1', label: 'extérieur', bExpected: 1 },
|
|
@@ -56,6 +73,26 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
56
73
|
expect(b).toBe(bExpected);
|
|
57
74
|
}
|
|
58
75
|
);
|
|
76
|
+
|
|
77
|
+
test('pas de valeur de b', () => {
|
|
78
|
+
// Aucune adjacence '0' dans la table : b introuvable -> undefined.
|
|
79
|
+
expect(tvStore.getB('0')).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('b filtré par zone climatique (espace tampon solarisé)', () => {
|
|
83
|
+
// Adjacence 10 : les lignes portent une zone_climatique -> exerce le filtre
|
|
84
|
+
// zc.toLowerCase().startsWith(v.zone_climatique.toLowerCase()) (H1 <- h1a).
|
|
85
|
+
expect(tvStore.getB('10', undefined, '6', undefined, 'h1a')).toBe(0.95);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test.each([
|
|
89
|
+
// rAiuAue au-dessus d'une borne min : exerce parseAiuAueBound(aiu_aue_min) < rAiuAue.
|
|
90
|
+
{ label: 'ratio dans un intervalle borné (min et max)', rAiuAue: 0.3, expected: 0.95 },
|
|
91
|
+
// rAiuAue très élevé : dernière ligne sans aiu_aue_max -> exerce la branche !v.aiu_aue_max.
|
|
92
|
+
{ label: 'ratio au-dessus de la dernière borne (sans max)', rAiuAue: 60, expected: 0.2 }
|
|
93
|
+
])('b filtré par le ratio aiu/aue pour $label', ({ rAiuAue, expected }) => {
|
|
94
|
+
expect(tvStore.getB('8', 3, '4', rAiuAue)).toBe(expected);
|
|
95
|
+
});
|
|
59
96
|
});
|
|
60
97
|
|
|
61
98
|
describe('lecture des valeurs de uVue', () => {
|
|
@@ -375,6 +412,13 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
375
412
|
}
|
|
376
413
|
);
|
|
377
414
|
|
|
415
|
+
test('ue sans interpolation quand upb correspond exactement à une borne tabulée', () => {
|
|
416
|
+
// upb fourni comme borne exacte de la table ('0.83') : getRange renvoie deux bornes
|
|
417
|
+
// identiques, delta_upb vaut 0 et la valeur tabulée est retournée sans interpolation.
|
|
418
|
+
const ue = tvStore.getUeByUpd('3', '1', 4, '0.83');
|
|
419
|
+
expect(ue).toBe(0.37);
|
|
420
|
+
});
|
|
421
|
+
|
|
378
422
|
test('pas de valeur de ue', () => {
|
|
379
423
|
const ue = tvStore.getUeByUpd('0', '1', 0, 0);
|
|
380
424
|
expect(ue).toBeUndefined();
|
|
@@ -15,6 +15,7 @@ import { PRECISION_PERCENT } from '../../../../../test/constant.js';
|
|
|
15
15
|
import { DpeNormalizerService } from '../../../normalizer/domain/dpe-normalizer.service.js';
|
|
16
16
|
import { ContexteBuilder } from '../contexte.builder.js';
|
|
17
17
|
import { InstallationChService } from '../ch/installation-ch.service.js';
|
|
18
|
+
import { describeIntegration } from '../../../../../test/helpers/integration-test.js';
|
|
18
19
|
|
|
19
20
|
/** @type {SurfaceSudEquivalenteService} **/
|
|
20
21
|
let surfaceSudEquivalenteService;
|
|
@@ -157,7 +158,7 @@ describe('Calcul des apports et besoin du logement', () => {
|
|
|
157
158
|
expect(installationChService.execute).toHaveBeenCalledWith(ctx, logement);
|
|
158
159
|
});
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
describeIntegration("Test d'intégration pour les besoins en chauffage", () => {
|
|
161
162
|
test.each(corpus)(
|
|
162
163
|
'Vérification de la DI besoin_ch des besoins en chauffage pour dpe %s',
|
|
163
164
|
(ademeId) => {
|
|
@@ -50,6 +50,24 @@ describe('Calcul des besoins en chauffage du logement', () => {
|
|
|
50
50
|
}
|
|
51
51
|
);
|
|
52
52
|
|
|
53
|
+
test('Fraction des apports gratuits nulle quand les degrés-heures de chauffage sont nuls', () => {
|
|
54
|
+
// dh === 0 : aucun besoin de chauffage sur le mois, la fraction est court-circuitée à 0
|
|
55
|
+
// (évite aussi la division par zéro dans le calcul de Xj).
|
|
56
|
+
/** @type {Contexte} */
|
|
57
|
+
const ctx = { inertie: { id: 1 } };
|
|
58
|
+
|
|
59
|
+
/** @type { Logement } **/
|
|
60
|
+
const logement = {
|
|
61
|
+
donnees_de_calcul: {
|
|
62
|
+
apportsInterneCh: { Janvier: 102.5 },
|
|
63
|
+
apportsSolaire: { Janvier: 12.5 }
|
|
64
|
+
},
|
|
65
|
+
sortie: { deperdition: { deperdition_enveloppe: 25.9 } }
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
expect(service.fractionBesoinCh(ctx, logement, 'Janvier', 0)).toBe(0);
|
|
69
|
+
});
|
|
70
|
+
|
|
53
71
|
test('Besoin de chauffage hors pertes récupérées et fraction des apports gratuits pour un mois', () => {
|
|
54
72
|
/** @type {Contexte} */
|
|
55
73
|
const ctx = { inertie: { id: 1 } };
|
|
@@ -9,6 +9,7 @@ import { PerteChRecupService } from './perte-ch-recup.service.js';
|
|
|
9
9
|
import { BesoinChService } from './besoin-ch.service.js';
|
|
10
10
|
import { ApportGratuitService } from '../apport_gratuit/apport-gratuit.service.js';
|
|
11
11
|
import { PRECISION_PERCENT } from '../../../../../../test/constant.js';
|
|
12
|
+
import { describeIntegration } from '../../../../../../test/helpers/integration-test.js';
|
|
12
13
|
|
|
13
14
|
/** @type {PerteChRecupService} **/
|
|
14
15
|
let service;
|
|
@@ -239,7 +240,38 @@ describe('Calcul des pertes de génération de chauffage récupérées', () => {
|
|
|
239
240
|
}
|
|
240
241
|
});
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
test('Qrec vaut 0 pour un usage de générateur inconnu (aucune durée de récupération)', () => {
|
|
244
|
+
// enum_usage_generateur_id hors {1, 2, 3} : le switch ne définit pas Dperj (undefined),
|
|
245
|
+
// le produit devient NaN et la garde `|| 0` ramène la contribution à 0.
|
|
246
|
+
/** @type {GenerateurChauffage[]} */
|
|
247
|
+
const generateurs = [
|
|
248
|
+
{
|
|
249
|
+
donnee_entree: { presence_ventouse: 0, enum_usage_generateur_id: 99 },
|
|
250
|
+
donnee_intermediaire: { pn: 25000, qp0: 125 }
|
|
251
|
+
}
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
expect(service.Qrec(generateurs, 12.5, 1532.9)).toBe(0);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('Conversion en kWh des pertes de génération récupérées (execute)', () => {
|
|
258
|
+
// On isole execute de la formule mensuelle : pertesGenerateurChRecup est doublée pour
|
|
259
|
+
// ne vérifier que la conversion Wh -> kWh (division par 1000) et le mapping des 2 champs.
|
|
260
|
+
vi.spyOn(service, 'pertesGenerateurChRecup').mockImplementation((ctx, logement, depensier) =>
|
|
261
|
+
depensier ? 6000 : 3000
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const res = service.execute({}, {});
|
|
265
|
+
|
|
266
|
+
expect(res).toStrictEqual({
|
|
267
|
+
pertes_generateur_ch_recup: 3,
|
|
268
|
+
pertes_generateur_ch_recup_depensier: 6
|
|
269
|
+
});
|
|
270
|
+
expect(service.pertesGenerateurChRecup).toHaveBeenCalledWith({}, {}, false);
|
|
271
|
+
expect(service.pertesGenerateurChRecup).toHaveBeenCalledWith({}, {}, true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describeIntegration("Test d'intégration des installations CH", () => {
|
|
243
275
|
test.each(corpus)('vérification des pertes de génération ch pour dpe %s', (ademeId) => {
|
|
244
276
|
let dpeRequest = getAdemeFileJson(ademeId);
|
|
245
277
|
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
@@ -5,6 +5,7 @@ import { ContexteBuilder } from '../../contexte.builder.js';
|
|
|
5
5
|
import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
6
6
|
import { BesoinEcsService } from './besoin-ecs.service.js';
|
|
7
7
|
import { EcsTvStore } from '../../../../dpe/infrastructure/ecs/ecsTv.store.js';
|
|
8
|
+
import { describeIntegration } from '../../../../../../test/helpers/integration-test.js';
|
|
8
9
|
|
|
9
10
|
/** @type {BesoinEcsService} **/
|
|
10
11
|
let service;
|
|
@@ -45,7 +46,26 @@ describe('Calcul du besoin en eau chaude sanitaire', () => {
|
|
|
45
46
|
expect(tvStore.getTefs).toHaveBeenCalledWith('400-800m', 'h1a', 'Janvier');
|
|
46
47
|
});
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
test("Sommation du besoin ECS sur les douze mois de l'année", () => {
|
|
50
|
+
// On isole execute de la formule mensuelle : besoinEcsMois est doublé pour ne mesurer
|
|
51
|
+
// que la logique d'agrégation (une valeur conventionnelle, une valeur dépensière par mois).
|
|
52
|
+
vi.spyOn(service, 'besoinEcsMois').mockImplementation((ctx, mois, depensier) =>
|
|
53
|
+
depensier ? 10 : 1
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/** @type {Contexte} */
|
|
57
|
+
const ctx = { altitude: { value: '400-800m' }, zoneClimatique: { value: 'h1a' }, nadeq: 2 };
|
|
58
|
+
|
|
59
|
+
const res = service.execute(ctx);
|
|
60
|
+
|
|
61
|
+
// 12 mois : conventionnel = 12 * 1, dépensier = 12 * 10
|
|
62
|
+
expect(res).toStrictEqual({ besoin_ecs: 12, besoin_ecs_depensier: 120 });
|
|
63
|
+
expect(service.besoinEcsMois).toHaveBeenCalledTimes(24);
|
|
64
|
+
expect(service.besoinEcsMois).toHaveBeenCalledWith(ctx, 'Janvier', false);
|
|
65
|
+
expect(service.besoinEcsMois).toHaveBeenCalledWith(ctx, 'Janvier', true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describeIntegration("Test d'intégration pour le besoin en eau chaude sanitaire", () => {
|
|
49
69
|
test.each(corpus)('vérification des sorties besoin_ecs pour dpe %s', (ademeId) => {
|
|
50
70
|
/**
|
|
51
71
|
* @type {Dpe}
|
|
@@ -9,6 +9,7 @@ import { mois_liste } from '../../../../../utils.js';
|
|
|
9
9
|
import { EcsTvStore } from '../../../../dpe/infrastructure/ecs/ecsTv.store.js';
|
|
10
10
|
import { GenerateurEcsService } from '../../ecs/generateur-ecs.service.js';
|
|
11
11
|
import { InstallationEcsService } from '../../ecs/installation-ecs.service.js';
|
|
12
|
+
import { describeIntegration } from '../../../../../../test/helpers/integration-test.js';
|
|
12
13
|
|
|
13
14
|
/** @type {PerteEcsRecupService} **/
|
|
14
15
|
let service;
|
|
@@ -130,7 +131,46 @@ describe('Calcul des pertes de distribution et stockage récupérées', () => {
|
|
|
130
131
|
}
|
|
131
132
|
});
|
|
132
133
|
|
|
133
|
-
|
|
134
|
+
test('Agrégation et conversion en kWh des pertes ECS récupérées (execute)', () => {
|
|
135
|
+
// On isole execute des formules mensuelles : les deux méthodes de calcul sont doublées
|
|
136
|
+
// pour ne vérifier que la conversion Wh -> kWh (division par 1000) et le mapping des 4 champs.
|
|
137
|
+
vi.spyOn(service, 'pertesDistributionEcsRecup').mockImplementation(
|
|
138
|
+
(ctx, logement, depensier) => (depensier ? 2000 : 1000)
|
|
139
|
+
);
|
|
140
|
+
vi.spyOn(service, 'pertesStockageEcsRecup').mockImplementation((ctx, logement, depensier) =>
|
|
141
|
+
depensier ? 4000 : 3000
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const res = service.execute({}, {});
|
|
145
|
+
|
|
146
|
+
expect(res).toStrictEqual({
|
|
147
|
+
pertes_distribution_ecs_recup: 1,
|
|
148
|
+
pertes_distribution_ecs_recup_depensier: 2,
|
|
149
|
+
pertes_stockage_ecs_recup: 3,
|
|
150
|
+
pertes_stockage_ecs_recup_depensier: 4
|
|
151
|
+
});
|
|
152
|
+
expect(service.pertesDistributionEcsRecup).toHaveBeenCalledWith({}, {}, false);
|
|
153
|
+
expect(service.pertesDistributionEcsRecup).toHaveBeenCalledWith({}, {}, true);
|
|
154
|
+
expect(service.pertesStockageEcsRecup).toHaveBeenCalledWith({}, {}, false);
|
|
155
|
+
expect(service.pertesStockageEcsRecup).toHaveBeenCalledWith({}, {}, true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("Pertes nulles en l'absence d'installation ECS", () => {
|
|
159
|
+
// Valeur de repli [] quand installation_ecs_collection est absente : aucune installation à sommer.
|
|
160
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(1);
|
|
161
|
+
|
|
162
|
+
/** @type {Contexte} */
|
|
163
|
+
const ctx = {
|
|
164
|
+
altitude: { value: '400-800m' },
|
|
165
|
+
zoneClimatique: { value: 'h1a' },
|
|
166
|
+
inertie: { ilpa: 0 }
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
expect(service.pertesDistributionEcsRecup(ctx, {}, false)).toBe(0);
|
|
170
|
+
expect(service.pertesStockageEcsRecup(ctx, {}, false)).toBe(0);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describeIntegration("Test d'intégration des installations ECS", () => {
|
|
134
174
|
test.each(corpus)(
|
|
135
175
|
'vérification des pertes de distribution ecs recup des installations ECS pour dpe %s',
|
|
136
176
|
(ademeId) => {
|
|
@@ -6,6 +6,7 @@ import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
|
6
6
|
import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
|
|
7
7
|
import { ApportGratuitService } from '../apport_gratuit/apport-gratuit.service.js';
|
|
8
8
|
import { BesoinFroidService } from './besoin-froid.service.js';
|
|
9
|
+
import { describeIntegration } from '../../../../../../test/helpers/integration-test.js';
|
|
9
10
|
|
|
10
11
|
/** @type {ApportGratuitService} **/
|
|
11
12
|
let apportGratuitService;
|
|
@@ -207,7 +208,49 @@ describe('Calcul des besoins en froid du logement', () => {
|
|
|
207
208
|
);
|
|
208
209
|
});
|
|
209
210
|
|
|
210
|
-
|
|
211
|
+
test('Sommation du besoin en froid sur les douze mois en présence de climatisation', () => {
|
|
212
|
+
// Climatisation présente : execute doit agréger besoinFrMois sur les 12 mois.
|
|
213
|
+
// besoinFrMois est doublé pour n'évaluer que la logique d'agrégation.
|
|
214
|
+
vi.spyOn(service, 'besoinFrMois').mockImplementation((ctx, logement, mois, depensier) =>
|
|
215
|
+
depensier ? 5 : 2
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
/** @type {Contexte} */
|
|
219
|
+
const ctx = { zoneClimatique: { id: 1 } };
|
|
220
|
+
|
|
221
|
+
/** @type {Logement} */
|
|
222
|
+
const logement = { climatisation_collection: { climatisation: [{}] } };
|
|
223
|
+
|
|
224
|
+
const res = service.execute(ctx, logement);
|
|
225
|
+
|
|
226
|
+
expect(res).toStrictEqual({ besoin_fr: 24, besoin_fr_depensier: 60 });
|
|
227
|
+
expect(service.besoinFrMois).toHaveBeenCalledTimes(24);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('Facteur d’utilisation lorsque le ratio de bilan thermique Rbth vaut exactement 1', () => {
|
|
231
|
+
// Branche fut = a / (a + 1) : atteinte uniquement quand Rbth === 1.
|
|
232
|
+
// Rbth = (aijFr + asjFr) / (GV * (tempExtMoyClim - 28) * nref)
|
|
233
|
+
// = 6000 / (100 * (30 - 28) * 30) = 1
|
|
234
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(30);
|
|
235
|
+
vi.spyOn(apportGratuitService, 'apportInterneMois').mockReturnValue(6000);
|
|
236
|
+
vi.spyOn(apportGratuitService, 'apportSolaireMois').mockReturnValue(0);
|
|
237
|
+
|
|
238
|
+
/** @type {Logement} **/
|
|
239
|
+
const logement = { enveloppe: {}, sortie: { deperdition: { deperdition_enveloppe: 100 } } };
|
|
240
|
+
|
|
241
|
+
/** @type {Contexte} */
|
|
242
|
+
const ctx = {
|
|
243
|
+
altitude: { value: '400-800m' },
|
|
244
|
+
zoneClimatique: { value: 'h1a' },
|
|
245
|
+
inertie: { id: 4 },
|
|
246
|
+
surfaceHabitable: 25
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Valeur de référence de régression (module réel exécuté avec ces entrées)
|
|
250
|
+
expect(service.besoinFrMois(ctx, logement, 'Janvier', false)).toBeCloseTo(9.608856088560886, 9);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describeIntegration("Test d'intégration pour le besoin en froid", () => {
|
|
211
254
|
test.each(corpus)('vérification des sorties besoin_fr pour dpe %s', (ademeId) => {
|
|
212
255
|
/**
|
|
213
256
|
* @type {Dpe}
|