@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/engine.spec.js
ADDED
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `engine.js` est l'orchestrateur du moteur. Il enchaîne de nombreux sous-modules
|
|
5
|
+
* de calcul (déperdition, apport/besoin, ecs, chauffage, conso, ...). Pour le
|
|
6
|
+
* tester **unitairement**, TOUS ces sous-modules sont mockés : ils renvoient des
|
|
7
|
+
* valeurs contrôlées, ce qui permet de piloter chacune des branches de
|
|
8
|
+
* l'orchestrateur sans exécuter la moindre logique métier.
|
|
9
|
+
*
|
|
10
|
+
* On vérifie que l'orchestrateur :
|
|
11
|
+
* - garde-fou sur le modèle / l'enveloppe (retours `null`) ;
|
|
12
|
+
* - choisit les bonnes surfaces (Sh / Sh immeuble) selon `th` et le map_id ;
|
|
13
|
+
* - construit et transmet les bons arguments aux sous-modules ;
|
|
14
|
+
* - agrège correctement les sorties (`sortie`, `ep_conso`, coefficients).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Drapeaux mutables pilotant les constantes de configuration importées depuis utils.
|
|
18
|
+
// Les getters garantissent une lecture « live » côté module testé.
|
|
19
|
+
let bugForBugCompat = false;
|
|
20
|
+
let useEnumAsStringFlag = false;
|
|
21
|
+
|
|
22
|
+
vi.mock('./utils.js', () => ({
|
|
23
|
+
add_references: vi.fn(),
|
|
24
|
+
get bug_for_bug_compat() {
|
|
25
|
+
return bugForBugCompat;
|
|
26
|
+
},
|
|
27
|
+
collectionCanBeEmpty: vi.fn(),
|
|
28
|
+
containsAnySubstring: vi.fn(),
|
|
29
|
+
isEffetJoule: vi.fn(),
|
|
30
|
+
get use_enum_as_string() {
|
|
31
|
+
return useEnumAsStringFlag;
|
|
32
|
+
},
|
|
33
|
+
useEnumAsString: vi.fn(),
|
|
34
|
+
xmlParser: { parse: vi.fn() }
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('./enums.js', () => ({
|
|
38
|
+
default: {
|
|
39
|
+
methode_application_dpe_log: {},
|
|
40
|
+
modele_dpe: {},
|
|
41
|
+
orientation: {},
|
|
42
|
+
type_vitrage: {},
|
|
43
|
+
type_baie: {},
|
|
44
|
+
type_materiaux_menuiserie: {},
|
|
45
|
+
type_generateur_ch: {},
|
|
46
|
+
type_generateur_ecs: {}
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
vi.mock('./conso.js', () => ({
|
|
51
|
+
default: vi.fn(),
|
|
52
|
+
classe_bilan_dpe: vi.fn(),
|
|
53
|
+
classe_emission_ges: vi.fn(),
|
|
54
|
+
coef_ep: { tag: 'coef_ep' },
|
|
55
|
+
coef_ep_1_7: { tag: 'coef_ep_1_7' },
|
|
56
|
+
coef_ep_2_3: { tag: 'coef_ep_2_3' },
|
|
57
|
+
COEFF_EP_1_7: 1.7,
|
|
58
|
+
COEFF_EP_2_3: 2.3
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
// Dépendances de calcul mockées : elles ne doivent pas s'exécuter réellement.
|
|
62
|
+
vi.mock('./3_deperdition.js', () => ({ default: vi.fn() }));
|
|
63
|
+
vi.mock('./apport_et_besoin.js', () => ({ default: vi.fn() }));
|
|
64
|
+
vi.mock('./10_clim.js', () => ({ default: vi.fn() }));
|
|
65
|
+
vi.mock('./11_ecs.js', () => ({ default: vi.fn() }));
|
|
66
|
+
vi.mock('./9_besoin_ch.js', () => ({ default: vi.fn() }));
|
|
67
|
+
vi.mock('./9_chauffage.js', () => ({ default: vi.fn(), tauxChargeForGenerator: vi.fn() }));
|
|
68
|
+
vi.mock('./2021_04_13_confort_ete.js', () => ({ default: vi.fn() }));
|
|
69
|
+
vi.mock('./2021_04_13_qualite_isolation.js', () => ({ default: vi.fn() }));
|
|
70
|
+
vi.mock('./ficheTechnique.js', () => ({ default: vi.fn() }));
|
|
71
|
+
|
|
72
|
+
// Les instances Inertie / ProductionENR sont créées au chargement du module.
|
|
73
|
+
// On expose des méthodes mockées récupérables dans les tests.
|
|
74
|
+
const calculateInertie = vi.fn();
|
|
75
|
+
const calculateEnr = vi.fn();
|
|
76
|
+
vi.mock('./7_inertie.js', () => ({ Inertie: vi.fn(() => ({ calculateInertie })) }));
|
|
77
|
+
vi.mock('./16.2_production_enr.js', () => ({ ProductionENR: vi.fn(() => ({ calculateEnr })) }));
|
|
78
|
+
|
|
79
|
+
// Le service de sanitisation est instancié au chargement puis appelé : `execute`
|
|
80
|
+
// renvoie l'entrée telle quelle.
|
|
81
|
+
const sanitizeExecute = vi.fn((dpe) => dpe);
|
|
82
|
+
vi.mock('./dpe-sanitizer.service.js', () => ({
|
|
83
|
+
default: vi.fn(() => ({ execute: sanitizeExecute }))
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
const { getVersion, get_conso_coeff_1_7_2027, get_classe_ges_dpe, calcul_3cl, calcul_3cl_xml } =
|
|
87
|
+
await import('./engine.js');
|
|
88
|
+
const { classe_bilan_dpe, classe_emission_ges } = await import('./conso.js');
|
|
89
|
+
const calc_conso = (await import('./conso.js')).default;
|
|
90
|
+
const {
|
|
91
|
+
add_references,
|
|
92
|
+
collectionCanBeEmpty,
|
|
93
|
+
containsAnySubstring,
|
|
94
|
+
isEffetJoule,
|
|
95
|
+
useEnumAsString,
|
|
96
|
+
xmlParser
|
|
97
|
+
} = await import('./utils.js');
|
|
98
|
+
const enums = (await import('./enums.js')).default;
|
|
99
|
+
const calc_deperdition = (await import('./3_deperdition.js')).default;
|
|
100
|
+
const calc_apport_et_besoin = (await import('./apport_et_besoin.js')).default;
|
|
101
|
+
const calc_clim = (await import('./10_clim.js')).default;
|
|
102
|
+
const calc_ecs = (await import('./11_ecs.js')).default;
|
|
103
|
+
const calc_besoin_ch = (await import('./9_besoin_ch.js')).default;
|
|
104
|
+
const { default: calc_chauffage, tauxChargeForGenerator } = await import('./9_chauffage.js');
|
|
105
|
+
const calc_confort_ete = (await import('./2021_04_13_confort_ete.js')).default;
|
|
106
|
+
const calc_qualite_isolation = (await import('./2021_04_13_qualite_isolation.js')).default;
|
|
107
|
+
const getFicheTechnique = (await import('./ficheTechnique.js')).default;
|
|
108
|
+
|
|
109
|
+
/** Objet conso renvoyé par le mock de `calc_conso` (mêmes réf pour les 3 appels). */
|
|
110
|
+
let consoResult;
|
|
111
|
+
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
vi.clearAllMocks();
|
|
114
|
+
bugForBugCompat = false;
|
|
115
|
+
useEnumAsStringFlag = false;
|
|
116
|
+
|
|
117
|
+
// Enums par défaut : maison, modèle supporté.
|
|
118
|
+
enums.methode_application_dpe_log = { 1: 'dpe maison individuelle' };
|
|
119
|
+
enums.modele_dpe = { 1: 'dpe 3cl 2021 méthode logement', 99: 'modèle non supporté' };
|
|
120
|
+
enums.orientation = { 1: 'sud' };
|
|
121
|
+
enums.type_vitrage = { 1: 'double' };
|
|
122
|
+
enums.type_baie = { 1: 'fenetre' };
|
|
123
|
+
enums.type_materiaux_menuiserie = { 1: 'pvc' };
|
|
124
|
+
enums.type_generateur_ch = {};
|
|
125
|
+
enums.type_generateur_ecs = {};
|
|
126
|
+
|
|
127
|
+
// Retours par défaut des sous-modules (chemin nominal).
|
|
128
|
+
vi.mocked(sanitizeExecute).mockImplementation((dpe) => dpe);
|
|
129
|
+
vi.mocked(collectionCanBeEmpty).mockReturnValue(true);
|
|
130
|
+
vi.mocked(isEffetJoule).mockReturnValue(false);
|
|
131
|
+
vi.mocked(getFicheTechnique).mockReturnValue(undefined);
|
|
132
|
+
vi.mocked(containsAnySubstring).mockReturnValue(false);
|
|
133
|
+
vi.mocked(calc_deperdition).mockReturnValue({ deperdition_enveloppe: 300 });
|
|
134
|
+
vi.mocked(calculateInertie).mockReturnValue({ enum_classe_inertie_id: '3' });
|
|
135
|
+
vi.mocked(calc_apport_et_besoin).mockReturnValue({
|
|
136
|
+
besoin_fr: 1,
|
|
137
|
+
besoin_fr_depensier: 2,
|
|
138
|
+
besoin_ecs: 100,
|
|
139
|
+
besoin_ecs_depensier: 120,
|
|
140
|
+
nadeq: 2
|
|
141
|
+
});
|
|
142
|
+
vi.mocked(calc_besoin_ch).mockReturnValue({
|
|
143
|
+
besoin_ch: 50,
|
|
144
|
+
besoin_ch_depensier: 60,
|
|
145
|
+
besoin_ch_mois: { m1: 10, m2: 20 }
|
|
146
|
+
});
|
|
147
|
+
consoResult = {
|
|
148
|
+
ep_conso: { classe_bilan_dpe: 'D', ep_conso_5_usages: 100, ep_conso_5_usages_m2: 5 },
|
|
149
|
+
ef_conso: {}
|
|
150
|
+
};
|
|
151
|
+
vi.mocked(calc_conso).mockReturnValue(consoResult);
|
|
152
|
+
vi.mocked(calculateEnr).mockReturnValue({ pv: 1 });
|
|
153
|
+
vi.mocked(calc_confort_ete).mockReturnValue('confort');
|
|
154
|
+
vi.mocked(calc_qualite_isolation).mockReturnValue('qualite');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** Construit un DPE complet exploitable par un run intégral de `calcul_3cl`. */
|
|
158
|
+
function makeDpe() {
|
|
159
|
+
return {
|
|
160
|
+
administratif: {
|
|
161
|
+
enum_modele_dpe_id: '1',
|
|
162
|
+
date_etablissement_dpe: '2024-01-01'
|
|
163
|
+
},
|
|
164
|
+
logement: {
|
|
165
|
+
meteo: {
|
|
166
|
+
enum_zone_climatique_id: 'zc1',
|
|
167
|
+
enum_classe_altitude_id: 'ca1',
|
|
168
|
+
batiment_materiaux_anciens: 0
|
|
169
|
+
},
|
|
170
|
+
caracteristique_generale: {
|
|
171
|
+
enum_methode_application_dpe_log_id: '1',
|
|
172
|
+
surface_habitable_logement: 80,
|
|
173
|
+
surface_habitable_immeuble: 200,
|
|
174
|
+
nombre_appartement: 4,
|
|
175
|
+
hsp: 2.5,
|
|
176
|
+
annee_construction: 1990
|
|
177
|
+
},
|
|
178
|
+
enveloppe: {
|
|
179
|
+
mur_collection: { mur: [{ donnee_entree: {} }] },
|
|
180
|
+
plancher_haut_collection: { plancher_haut: [{ donnee_entree: {} }] },
|
|
181
|
+
plancher_bas_collection: { plancher_bas: [{ donnee_entree: {} }] },
|
|
182
|
+
baie_vitree_collection: { baie_vitree: [{ donnee_entree: { presence_joint: 1 } }] },
|
|
183
|
+
ets_collection: { ets: [] },
|
|
184
|
+
inertie: { enum_classe_inertie_id: '3' }
|
|
185
|
+
},
|
|
186
|
+
installation_chauffage_collection: {
|
|
187
|
+
installation_chauffage: [
|
|
188
|
+
{ donnee_entree: {}, generateur_chauffage_collection: { generateur_chauffage: [] } }
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
installation_ecs_collection: {
|
|
192
|
+
installation_ecs: [
|
|
193
|
+
{
|
|
194
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
195
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
climatisation_collection: { climatisation: [{ donnee_entree: {} }] },
|
|
200
|
+
ventilation_collection: { ventilation: [{ donnee_entree: {} }] },
|
|
201
|
+
production_elec_enr: { some: 'pv' }
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Construit un générateur ECS pour les tests du bloc `bug_for_bug_compat`. */
|
|
207
|
+
function genEcs(overrides = {}) {
|
|
208
|
+
return { donnee_entree: { ...overrides } };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
describe('getVersion', () => {
|
|
212
|
+
test('retourne le jeton de version remplacé au build', () => {
|
|
213
|
+
// En source, le jeton n'est pas encore substitué.
|
|
214
|
+
expect(getVersion()).toBe('OPEN3CL_VERSION');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('get_classe_ges_dpe', () => {
|
|
219
|
+
test('délègue le classement DPE et GES aux fonctions de conso avec les bons arguments', () => {
|
|
220
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('D');
|
|
221
|
+
vi.mocked(classe_emission_ges).mockReturnValue('C');
|
|
222
|
+
|
|
223
|
+
const dpe = makeDpe();
|
|
224
|
+
dpe.logement.sortie = {
|
|
225
|
+
ep_conso: { ep_conso_5_usages_m2: 125 },
|
|
226
|
+
emission_ges: { emission_ges_5_usages_m2: 30 }
|
|
227
|
+
};
|
|
228
|
+
const res = get_classe_ges_dpe(dpe);
|
|
229
|
+
|
|
230
|
+
expect(res).toEqual({ dpeClass: 'D', gesClass: 'C' });
|
|
231
|
+
// Sh = surface_habitable_logement pour une maison
|
|
232
|
+
expect(classe_bilan_dpe).toHaveBeenCalledWith(125, 'zc1', 'ca1', 80);
|
|
233
|
+
expect(classe_emission_ges).toHaveBeenCalledWith(30, 'zc1', 'ca1', 80);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("utilise la surface de l'immeuble pour un modèle immeuble", () => {
|
|
237
|
+
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
238
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('E');
|
|
239
|
+
|
|
240
|
+
const dpe = makeDpe();
|
|
241
|
+
dpe.logement.sortie = {
|
|
242
|
+
ep_conso: { ep_conso_5_usages_m2: 125 },
|
|
243
|
+
emission_ges: { emission_ges_5_usages_m2: 30 }
|
|
244
|
+
};
|
|
245
|
+
get_classe_ges_dpe(dpe);
|
|
246
|
+
|
|
247
|
+
// Sh = surface_habitable_immeuble
|
|
248
|
+
expect(classe_bilan_dpe).toHaveBeenCalledWith(125, 'zc1', 'ca1', 200);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('méthode application inconnue : th null, surface indéterminée', () => {
|
|
252
|
+
// calc_th ne reconnaît ni maison ni appartement ni immeuble => null
|
|
253
|
+
enums.methode_application_dpe_log = { 1: 'méthode farfelue' };
|
|
254
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
255
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('G');
|
|
256
|
+
vi.mocked(classe_emission_ges).mockReturnValue('G');
|
|
257
|
+
|
|
258
|
+
const dpe = makeDpe();
|
|
259
|
+
dpe.logement.sortie = {
|
|
260
|
+
ep_conso: { ep_conso_5_usages_m2: 400 },
|
|
261
|
+
emission_ges: { emission_ges_5_usages_m2: 80 }
|
|
262
|
+
};
|
|
263
|
+
get_classe_ges_dpe(dpe);
|
|
264
|
+
|
|
265
|
+
// th null => Sh reste indéfini
|
|
266
|
+
expect(classe_bilan_dpe).toHaveBeenCalledWith(400, 'zc1', 'ca1', undefined);
|
|
267
|
+
expect(errSpy).toHaveBeenCalled();
|
|
268
|
+
errSpy.mockRestore();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('calcul_3cl - garde-fous précoces', () => {
|
|
273
|
+
test('retourne null pour un modèle de DPE non supporté', () => {
|
|
274
|
+
const dpe = { administratif: { enum_modele_dpe_id: '99' } };
|
|
275
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
276
|
+
|
|
277
|
+
const res = calcul_3cl(dpe, { sanitize: false });
|
|
278
|
+
|
|
279
|
+
expect(res).toBeNull();
|
|
280
|
+
errSpy.mockRestore();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("sanitise l'entrée par défaut avant de vérifier le modèle", () => {
|
|
284
|
+
const dpe = { administratif: { enum_modele_dpe_id: '99' } };
|
|
285
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
286
|
+
|
|
287
|
+
const res = calcul_3cl(dpe); // pas d'options => sanitize par défaut
|
|
288
|
+
|
|
289
|
+
expect(sanitizeExecute).toHaveBeenCalledWith(dpe);
|
|
290
|
+
expect(res).toBeNull();
|
|
291
|
+
errSpy.mockRestore();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("sans sanitisation, applique useEnumAsString si l'option globale est active", () => {
|
|
295
|
+
useEnumAsStringFlag = true;
|
|
296
|
+
const dpe = { administratif: { enum_modele_dpe_id: '99' } };
|
|
297
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
298
|
+
|
|
299
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
300
|
+
|
|
301
|
+
expect(sanitizeExecute).not.toHaveBeenCalled();
|
|
302
|
+
expect(useEnumAsString).toHaveBeenCalledWith(dpe);
|
|
303
|
+
errSpy.mockRestore();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('retourne null si enveloppe absente', () => {
|
|
307
|
+
const dpe = makeDpe();
|
|
308
|
+
delete dpe.logement.enveloppe;
|
|
309
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
310
|
+
|
|
311
|
+
expect(calcul_3cl(dpe, { sanitize: false })).toBeNull();
|
|
312
|
+
warnSpy.mockRestore();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('retourne null si mur_collection absent', () => {
|
|
316
|
+
const dpe = makeDpe();
|
|
317
|
+
dpe.logement.enveloppe.mur_collection = undefined;
|
|
318
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
319
|
+
|
|
320
|
+
expect(calcul_3cl(dpe, { sanitize: false })).toBeNull();
|
|
321
|
+
warnSpy.mockRestore();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test('plancher_haut vide + non-vidable => null', () => {
|
|
325
|
+
vi.mocked(collectionCanBeEmpty).mockReturnValue(false);
|
|
326
|
+
const dpe = makeDpe();
|
|
327
|
+
dpe.logement.enveloppe.plancher_haut_collection = { plancher_haut: [] };
|
|
328
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
329
|
+
|
|
330
|
+
expect(calcul_3cl(dpe, { sanitize: false })).toBeNull();
|
|
331
|
+
expect(collectionCanBeEmpty).toHaveBeenCalledWith(dpe.logement, 'plancher_haut', 3);
|
|
332
|
+
errSpy.mockRestore();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test('plancher_bas vide + non-vidable => null', () => {
|
|
336
|
+
vi.mocked(collectionCanBeEmpty).mockReturnValue(false);
|
|
337
|
+
const dpe = makeDpe();
|
|
338
|
+
// plancher_haut non vide pour atteindre le contrôle plancher_bas
|
|
339
|
+
dpe.logement.enveloppe.plancher_bas_collection = { plancher_bas: [] };
|
|
340
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
341
|
+
|
|
342
|
+
expect(calcul_3cl(dpe, { sanitize: false })).toBeNull();
|
|
343
|
+
expect(collectionCanBeEmpty).toHaveBeenCalledWith(dpe.logement, 'plancher_bas', 1);
|
|
344
|
+
errSpy.mockRestore();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('calcul_3cl_xml - garde sur le modèle de DPE', () => {
|
|
349
|
+
test('parse le XML puis retourne null pour un modèle non supporté', () => {
|
|
350
|
+
const dpe = { administratif: { enum_modele_dpe_id: '99' } };
|
|
351
|
+
vi.mocked(xmlParser.parse).mockReturnValue({ dpe });
|
|
352
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
353
|
+
|
|
354
|
+
const res = calcul_3cl_xml('<dpe/>', { sanitize: false });
|
|
355
|
+
|
|
356
|
+
expect(xmlParser.parse).toHaveBeenCalledWith('<dpe/>');
|
|
357
|
+
expect(res).toBeNull();
|
|
358
|
+
errSpy.mockRestore();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe('calcul_3cl - run intégral (maison)', () => {
|
|
363
|
+
test('orchestre les sous-modules et agrège la sortie', () => {
|
|
364
|
+
const dpe = makeDpe();
|
|
365
|
+
|
|
366
|
+
const res = calcul_3cl(dpe, { sanitize: false });
|
|
367
|
+
|
|
368
|
+
// Le DPE (muté) est retourné.
|
|
369
|
+
expect(res).toBe(dpe);
|
|
370
|
+
|
|
371
|
+
// Surfaces : maison => Sh = surface_habitable_logement ; map '1' hors liste
|
|
372
|
+
// immeuble => ShChauffageAndEcs = Sh.
|
|
373
|
+
expect(calc_deperdition).toHaveBeenCalledWith(
|
|
374
|
+
dpe.logement.caracteristique_generale,
|
|
375
|
+
'zc1',
|
|
376
|
+
'maison',
|
|
377
|
+
false,
|
|
378
|
+
dpe,
|
|
379
|
+
80
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// Références ajoutées à l'enveloppe.
|
|
383
|
+
expect(add_references).toHaveBeenCalledWith(dpe.logement.enveloppe);
|
|
384
|
+
|
|
385
|
+
// Version moteur injectée.
|
|
386
|
+
expect(dpe.administratif.diagnostiqueur).toEqual({
|
|
387
|
+
version_moteur_calcul: 'Open3CL OPEN3CL_VERSION'
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// besoin_ch fusionné, besoin_ch_mois retiré de apport_et_besoin.
|
|
391
|
+
expect(dpe.logement.sortie.apport_et_besoin.besoin_ch).toBe(50);
|
|
392
|
+
expect(dpe.logement.sortie.apport_et_besoin.besoin_ch_mois).toBeUndefined();
|
|
393
|
+
|
|
394
|
+
// Taux de charge appelé avant la 2ᵉ passe chauffage.
|
|
395
|
+
expect(tauxChargeForGenerator).toHaveBeenCalledWith(
|
|
396
|
+
dpe.logement.installation_chauffage_collection.installation_chauffage,
|
|
397
|
+
300,
|
|
398
|
+
'ca1',
|
|
399
|
+
'zc1',
|
|
400
|
+
'maison'
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// calc_chauffage appelé 2 fois par installation (1ʳᵉ et 2ᵉ passe).
|
|
404
|
+
expect(calc_chauffage).toHaveBeenCalledTimes(2);
|
|
405
|
+
// Première passe : besoins à 0.
|
|
406
|
+
expect(calc_chauffage.mock.calls[0][6]).toBe(0);
|
|
407
|
+
expect(calc_chauffage.mock.calls[0][7]).toBe(0);
|
|
408
|
+
// Deuxième passe : besoins réels + besoin_ch_mois.
|
|
409
|
+
expect(calc_chauffage.mock.calls[1][6]).toBe(50);
|
|
410
|
+
expect(calc_chauffage.mock.calls[1][7]).toBe(60);
|
|
411
|
+
expect(calc_chauffage.mock.calls[1][13]).toEqual({ m1: 10, m2: 20 });
|
|
412
|
+
// ilpa = '0' (materiaux anciens = 0).
|
|
413
|
+
expect(calc_chauffage.mock.calls[0][12]).toBe('0');
|
|
414
|
+
|
|
415
|
+
// 3 calculs de conso (coef_ep, coef_ep_1_7, coef_ep_2_3).
|
|
416
|
+
expect(calc_conso).toHaveBeenCalledTimes(3);
|
|
417
|
+
expect(calc_conso.mock.calls[0][10]).toEqual({ tag: 'coef_ep' });
|
|
418
|
+
expect(calc_conso.mock.calls[1][10]).toEqual({ tag: 'coef_ep_1_7' });
|
|
419
|
+
expect(calc_conso.mock.calls[2][10]).toEqual({ tag: 'coef_ep_2_3' });
|
|
420
|
+
// prorata ECS / chauffage neutres pour une maison.
|
|
421
|
+
expect(calc_conso.mock.calls[0][7]).toBe(1);
|
|
422
|
+
expect(calc_conso.mock.calls[0][8]).toBe(1);
|
|
423
|
+
|
|
424
|
+
// 3 calculs de production ENR ; le 1ᵉʳ sans coefficient.
|
|
425
|
+
expect(calculateEnr).toHaveBeenCalledTimes(3);
|
|
426
|
+
expect(calculateEnr.mock.calls[0]).toEqual([
|
|
427
|
+
dpe.logement.production_elec_enr,
|
|
428
|
+
consoResult,
|
|
429
|
+
80,
|
|
430
|
+
'maison',
|
|
431
|
+
'zc1'
|
|
432
|
+
]);
|
|
433
|
+
expect(calculateEnr.mock.calls[1][5]).toBe(1.7);
|
|
434
|
+
expect(calculateEnr.mock.calls[2][5]).toBe(2.3);
|
|
435
|
+
|
|
436
|
+
// Sorties agrégées.
|
|
437
|
+
expect(dpe.logement.sortie.deperdition).toEqual({ deperdition_enveloppe: 300 });
|
|
438
|
+
expect(dpe.logement.sortie.confort_ete).toBe('confort');
|
|
439
|
+
expect(dpe.logement.sortie.qualite_isolation).toBe('qualite');
|
|
440
|
+
expect(dpe.logement.sortie.production_electricite).toEqual({ pv: 1 });
|
|
441
|
+
expect(calc_confort_ete).toHaveBeenCalledWith(
|
|
442
|
+
'3',
|
|
443
|
+
dpe.logement.enveloppe.baie_vitree_collection.baie_vitree,
|
|
444
|
+
[dpe.logement.enveloppe.plancher_haut_collection.plancher_haut[0]]
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
// ep_conso enrichi des coefficients 1.7 / 2.3 et de la projection 2027.
|
|
448
|
+
expect(dpe.logement.sortie.ep_conso.coeff_2_3_classe_bilan_dpe).toBe('D');
|
|
449
|
+
expect(dpe.logement.sortie.ep_conso.coeff_1_7_ep_conso_5_usages).toBe(100);
|
|
450
|
+
expect(dpe.logement.sortie.ep_conso.classe_bilan_dpe_2027).toBe('D');
|
|
451
|
+
expect(dpe.logement.sortie.ep_conso.ep_conso_5_usages_2027).toBe(100);
|
|
452
|
+
|
|
453
|
+
// ECS individuelle unique : calc_ecs appelé une fois, sans division du besoin.
|
|
454
|
+
expect(calc_ecs).toHaveBeenCalledTimes(1);
|
|
455
|
+
expect(calc_ecs.mock.calls[0][2]).toBe(100);
|
|
456
|
+
expect(calc_ecs.mock.calls[0][11]).toBe(false);
|
|
457
|
+
|
|
458
|
+
// Climatisation présente : calc_clim appelé.
|
|
459
|
+
expect(calc_clim).toHaveBeenCalledTimes(1);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('bâtiment matériaux anciens => ilpa = 1 ; incohérence inertie => erreur loggée', () => {
|
|
463
|
+
const dpe = makeDpe();
|
|
464
|
+
dpe.logement.meteo.batiment_materiaux_anciens = 1;
|
|
465
|
+
vi.mocked(calculateInertie).mockReturnValue({ enum_classe_inertie_id: '2' }); // != '3'
|
|
466
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
467
|
+
|
|
468
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
469
|
+
|
|
470
|
+
expect(calc_chauffage.mock.calls[0][12]).toBe('1');
|
|
471
|
+
expect(errSpy).toHaveBeenCalled();
|
|
472
|
+
errSpy.mockRestore();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('collections ECS/clim absentes et nombre_appartement absent => valeurs par défaut', () => {
|
|
476
|
+
const dpe = makeDpe();
|
|
477
|
+
dpe.logement.installation_ecs_collection.installation_ecs = undefined;
|
|
478
|
+
dpe.logement.climatisation_collection.climatisation = undefined;
|
|
479
|
+
dpe.logement.caracteristique_generale.nombre_appartement = undefined;
|
|
480
|
+
|
|
481
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
482
|
+
|
|
483
|
+
// Nb_lgt par défaut = 1 (6ᵉ argument d'apport_et_besoin).
|
|
484
|
+
expect(calc_apport_et_besoin.mock.calls[0][5]).toBe(1);
|
|
485
|
+
// Pas d'ECS ni de clim à traiter.
|
|
486
|
+
expect(calc_ecs).not.toHaveBeenCalled();
|
|
487
|
+
expect(calc_clim).not.toHaveBeenCalled();
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe('calcul_3cl - collections vidables (poursuite du calcul)', () => {
|
|
492
|
+
test('plancher_haut vide mais vidable : réinitialisé et calcul poursuivi', () => {
|
|
493
|
+
const dpe = makeDpe();
|
|
494
|
+
dpe.logement.enveloppe.plancher_haut_collection = { plancher_haut: [] };
|
|
495
|
+
|
|
496
|
+
const res = calcul_3cl(dpe, { sanitize: false });
|
|
497
|
+
|
|
498
|
+
expect(res).toBe(dpe);
|
|
499
|
+
expect(collectionCanBeEmpty).toHaveBeenCalledWith(dpe.logement, 'plancher_haut', 3);
|
|
500
|
+
expect(dpe.logement.enveloppe.plancher_haut_collection).toEqual({ plancher_haut: [] });
|
|
501
|
+
// ph_list vide passé au confort d'été.
|
|
502
|
+
expect(calc_confort_ete.mock.calls[0][2]).toEqual([]);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test('plancher_bas vide mais vidable : réinitialisé et calcul poursuivi', () => {
|
|
506
|
+
const dpe = makeDpe();
|
|
507
|
+
dpe.logement.enveloppe.plancher_bas_collection = { plancher_bas: [] };
|
|
508
|
+
|
|
509
|
+
const res = calcul_3cl(dpe, { sanitize: false });
|
|
510
|
+
|
|
511
|
+
expect(res).toBe(dpe);
|
|
512
|
+
expect(collectionCanBeEmpty).toHaveBeenCalledWith(dpe.logement, 'plancher_bas', 1);
|
|
513
|
+
expect(dpe.logement.enveloppe.plancher_bas_collection).toEqual({ plancher_bas: [] });
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
describe('calcul_3cl - surfaces immeuble / appartement et prorata', () => {
|
|
518
|
+
test('immeuble : Sh = surface immeuble ; ECS collective individualisée non divisée', () => {
|
|
519
|
+
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
520
|
+
const dpe = makeDpe();
|
|
521
|
+
// Deux systèmes ECS individuels en immeuble => pas de division du besoin.
|
|
522
|
+
dpe.logement.installation_ecs_collection.installation_ecs = [
|
|
523
|
+
{
|
|
524
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
525
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
529
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
530
|
+
}
|
|
531
|
+
];
|
|
532
|
+
|
|
533
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
534
|
+
|
|
535
|
+
// th immeuble => Sh = surface_habitable_immeuble.
|
|
536
|
+
expect(calc_deperdition.mock.calls[0][5]).toBe(200);
|
|
537
|
+
expect(calc_deperdition.mock.calls[0][2]).toBe('immeuble');
|
|
538
|
+
// isImmeubleSystemEcsIndividuels = true, besoin non divisé.
|
|
539
|
+
expect(calc_ecs.mock.calls[0][11]).toBe(true);
|
|
540
|
+
expect(calc_ecs.mock.calls[0][2]).toBe(100);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test('immeuble : systèmes ECS non tous individuels => besoin divisé par deux', () => {
|
|
544
|
+
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
545
|
+
const dpe = makeDpe();
|
|
546
|
+
dpe.logement.installation_ecs_collection.installation_ecs = [
|
|
547
|
+
{
|
|
548
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
549
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
donnee_entree: { enum_type_installation_id: '2' },
|
|
553
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
554
|
+
}
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
558
|
+
|
|
559
|
+
expect(calc_ecs.mock.calls[0][11]).toBe(false);
|
|
560
|
+
expect(calc_ecs.mock.calls[0][2]).toBe(50); // 100 / 2
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test('maison avec deux systèmes ECS => besoin divisé par deux', () => {
|
|
564
|
+
const dpe = makeDpe();
|
|
565
|
+
dpe.logement.installation_ecs_collection.installation_ecs = [
|
|
566
|
+
{
|
|
567
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
568
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
572
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
573
|
+
}
|
|
574
|
+
];
|
|
575
|
+
|
|
576
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
577
|
+
|
|
578
|
+
// th != immeuble => isImmeubleSystemEcsIndividuels false, besoin divisé.
|
|
579
|
+
expect(calc_ecs.mock.calls[0][11]).toBe(false);
|
|
580
|
+
expect(calc_ecs.mock.calls[0][2]).toBe(50);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test('appartement issu immeuble (map 15) : Sh immeuble pour chauffage/ECS et prorata surfacique', () => {
|
|
584
|
+
enums.methode_application_dpe_log = { 15: 'dpe appartement collectif' };
|
|
585
|
+
const dpe = makeDpe();
|
|
586
|
+
dpe.logement.caracteristique_generale.enum_methode_application_dpe_log_id = '15';
|
|
587
|
+
|
|
588
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
589
|
+
|
|
590
|
+
// ShChauffageAndEcs = surface immeuble (200) pour la déperdition.
|
|
591
|
+
expect(calc_deperdition.mock.calls[0][5]).toBe(200);
|
|
592
|
+
// conso : Sh = surface logement (80) et prorata = 80/200 = 0.4.
|
|
593
|
+
expect(calc_conso.mock.calls[0][0]).toBe(80);
|
|
594
|
+
expect(calc_conso.mock.calls[0][7]).toBeCloseTo(0.4, 9); // prorataECS
|
|
595
|
+
expect(calc_conso.mock.calls[0][8]).toBeCloseTo(0.4, 9); // prorataChauffage
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
describe('calcul_3cl - présence de joint des baies vitrées', () => {
|
|
600
|
+
function dpeAvecBaieSansJoint() {
|
|
601
|
+
const dpe = makeDpe();
|
|
602
|
+
dpe.logement.enveloppe.baie_vitree_collection = {
|
|
603
|
+
baie_vitree: [
|
|
604
|
+
{
|
|
605
|
+
donnee_entree: {
|
|
606
|
+
description: 'baie 1',
|
|
607
|
+
enum_orientation_id: '1',
|
|
608
|
+
enum_type_vitrage_id: '1',
|
|
609
|
+
enum_type_baie_id: '1',
|
|
610
|
+
enum_type_materiaux_menuiserie_id: '1'
|
|
611
|
+
// presence_joint absent
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
]
|
|
615
|
+
};
|
|
616
|
+
return dpe;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
test('fiche technique "Oui" => présence de joint = 1', () => {
|
|
620
|
+
vi.mocked(getFicheTechnique).mockImplementation((d, id, key) =>
|
|
621
|
+
id === '4' && key === 'joint' ? { valeur: 'Oui' } : undefined
|
|
622
|
+
);
|
|
623
|
+
const dpe = dpeAvecBaieSansJoint();
|
|
624
|
+
|
|
625
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
626
|
+
|
|
627
|
+
expect(
|
|
628
|
+
dpe.logement.enveloppe.baie_vitree_collection.baie_vitree[0].donnee_entree.presence_joint
|
|
629
|
+
).toBe(1);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test('fiche technique "Non" => présence de joint = 0', () => {
|
|
633
|
+
vi.mocked(getFicheTechnique).mockImplementation((d, id, key) =>
|
|
634
|
+
id === '4' && key === 'joint' ? { valeur: 'Non' } : undefined
|
|
635
|
+
);
|
|
636
|
+
const dpe = dpeAvecBaieSansJoint();
|
|
637
|
+
|
|
638
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
639
|
+
|
|
640
|
+
expect(
|
|
641
|
+
dpe.logement.enveloppe.baie_vitree_collection.baie_vitree[0].donnee_entree.presence_joint
|
|
642
|
+
).toBe(0);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test('fiche technique de valeur inconnue => avertissement, présence de joint inchangée', () => {
|
|
646
|
+
vi.mocked(getFicheTechnique).mockImplementation((d, id, key) =>
|
|
647
|
+
id === '4' && key === 'joint' ? { valeur: 'Peut-être' } : undefined
|
|
648
|
+
);
|
|
649
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
650
|
+
const dpe = dpeAvecBaieSansJoint();
|
|
651
|
+
|
|
652
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
653
|
+
|
|
654
|
+
expect(
|
|
655
|
+
dpe.logement.enveloppe.baie_vitree_collection.baie_vitree[0].donnee_entree.presence_joint
|
|
656
|
+
).toBeUndefined();
|
|
657
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
658
|
+
warnSpy.mockRestore();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
test('absence de fiche technique => présence de joint reste indéterminée', () => {
|
|
662
|
+
// getFicheTechnique renvoie undefined (défaut)
|
|
663
|
+
const dpe = dpeAvecBaieSansJoint();
|
|
664
|
+
|
|
665
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
666
|
+
|
|
667
|
+
expect(
|
|
668
|
+
dpe.logement.enveloppe.baie_vitree_collection.baie_vitree[0].donnee_entree.presence_joint
|
|
669
|
+
).toBeUndefined();
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
describe('calcul_3cl - réalignements ECS (bug_for_bug_compat)', () => {
|
|
674
|
+
test('réaligne position_volume_chauffe et type de générateur ECS depuis le mixte de chauffage', () => {
|
|
675
|
+
bugForBugCompat = true;
|
|
676
|
+
enums.type_generateur_ch = { CH1: 'chaudiere gaz' };
|
|
677
|
+
enums.type_generateur_ecs = { ECS9: 'chaudiere gaz' };
|
|
678
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
679
|
+
|
|
680
|
+
// La fiche technique '8' impose "hors volume habitable" => position 0.
|
|
681
|
+
vi.mocked(getFicheTechnique).mockImplementation((d, id) =>
|
|
682
|
+
id === '8' ? { valeur: 'hors volume habitable', description: 'ft8' } : undefined
|
|
683
|
+
);
|
|
684
|
+
vi.mocked(containsAnySubstring).mockReturnValue(true); // => pvc = 0
|
|
685
|
+
|
|
686
|
+
const dpe = makeDpe();
|
|
687
|
+
dpe.logement.installation_chauffage_collection.installation_chauffage = [
|
|
688
|
+
{
|
|
689
|
+
donnee_entree: {},
|
|
690
|
+
generateur_chauffage_collection: {
|
|
691
|
+
generateur_chauffage: [
|
|
692
|
+
{
|
|
693
|
+
donnee_entree: {
|
|
694
|
+
reference_generateur_mixte: 'MX1',
|
|
695
|
+
enum_type_generateur_ch_id: 'CH1'
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
]
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
];
|
|
702
|
+
const generateur = genEcs({
|
|
703
|
+
description: 'gen1',
|
|
704
|
+
position_volume_chauffe: 1,
|
|
705
|
+
enum_usage_generateur_id: '3',
|
|
706
|
+
reference_generateur_mixte: 'MX1',
|
|
707
|
+
enum_type_generateur_ecs_id: 'OLD'
|
|
708
|
+
});
|
|
709
|
+
dpe.logement.installation_ecs_collection.installation_ecs = [
|
|
710
|
+
{
|
|
711
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
712
|
+
generateur_ecs_collection: { generateur_ecs: [generateur] }
|
|
713
|
+
}
|
|
714
|
+
];
|
|
715
|
+
|
|
716
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
717
|
+
|
|
718
|
+
// position réalignée à 0 et type ECS aligné sur le libellé du générateur mixte.
|
|
719
|
+
expect(generateur.donnee_entree.position_volume_chauffe).toBe(0);
|
|
720
|
+
expect(generateur.donnee_entree.enum_type_generateur_ecs_id).toBe('ECS9');
|
|
721
|
+
expect(errSpy).toHaveBeenCalled();
|
|
722
|
+
errSpy.mockRestore();
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
test('couvre les branches sans réalignement (fiche absente, mixte introuvable, libellés non résolus)', () => {
|
|
726
|
+
bugForBugCompat = true;
|
|
727
|
+
enums.type_generateur_ch = { CH1: 'chaudiere gaz', CHX: 'labelX' };
|
|
728
|
+
enums.type_generateur_ecs = { SAME: 'labelX' };
|
|
729
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
730
|
+
|
|
731
|
+
// Fiche '8' uniquement pour la description 'withFiche' ; valeur non "hors volume".
|
|
732
|
+
vi.mocked(getFicheTechnique).mockImplementation((d, id, key, arr) => {
|
|
733
|
+
if (id === '8' && arr?.[0] === 'withFiche') return { valeur: 'oui', description: 'ft8' };
|
|
734
|
+
return undefined;
|
|
735
|
+
});
|
|
736
|
+
// containsAnySubstring false => pvc = 1 (égal à la position courante => pas de réalignement).
|
|
737
|
+
vi.mocked(containsAnySubstring).mockReturnValue(false);
|
|
738
|
+
|
|
739
|
+
const dpe = makeDpe();
|
|
740
|
+
dpe.logement.installation_chauffage_collection.installation_chauffage = [
|
|
741
|
+
{
|
|
742
|
+
donnee_entree: {},
|
|
743
|
+
generateur_chauffage_collection: {
|
|
744
|
+
generateur_chauffage: [
|
|
745
|
+
{
|
|
746
|
+
donnee_entree: {
|
|
747
|
+
reference_generateur_mixte: 'MX_labelnull',
|
|
748
|
+
enum_type_generateur_ch_id: 'INCONNU'
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
donnee_entree: {
|
|
753
|
+
reference_generateur_mixte: 'MX_chgaz',
|
|
754
|
+
enum_type_generateur_ch_id: 'CH1'
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
donnee_entree: {
|
|
759
|
+
reference_generateur_mixte: 'MX_X',
|
|
760
|
+
enum_type_generateur_ch_id: 'CHX'
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
]
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
];
|
|
767
|
+
|
|
768
|
+
const generateurs = [
|
|
769
|
+
// Fiche présente, pvc calculé (=1) égal à la position => pas de réalignement ;
|
|
770
|
+
// usage '3' mais mixte introuvable (ref inconnue).
|
|
771
|
+
genEcs({
|
|
772
|
+
description: 'withFiche',
|
|
773
|
+
position_volume_chauffe: 1,
|
|
774
|
+
enum_usage_generateur_id: '3',
|
|
775
|
+
reference_generateur_mixte: 'AUCUN'
|
|
776
|
+
}),
|
|
777
|
+
// Pas de description (fiche ignorée) ; usage != 3.
|
|
778
|
+
genEcs({ enum_usage_generateur_id: '1' }),
|
|
779
|
+
// Description sans fiche ; usage '3' mais pas de référence mixte.
|
|
780
|
+
genEcs({ description: 'noFiche', enum_usage_generateur_id: '3' }),
|
|
781
|
+
// Mixte trouvé mais libellé de générateur de chauffage non résolu.
|
|
782
|
+
genEcs({
|
|
783
|
+
enum_usage_generateur_id: '3',
|
|
784
|
+
reference_generateur_mixte: 'MX_labelnull',
|
|
785
|
+
enum_type_generateur_ecs_id: 'X0'
|
|
786
|
+
}),
|
|
787
|
+
// Mixte + libellé résolus mais aucun type ECS correspondant => pas de réalignement.
|
|
788
|
+
genEcs({
|
|
789
|
+
enum_usage_generateur_id: '3',
|
|
790
|
+
reference_generateur_mixte: 'MX_chgaz',
|
|
791
|
+
enum_type_generateur_ecs_id: 'X1'
|
|
792
|
+
}),
|
|
793
|
+
// Mixte + libellé + type ECS correspondant identique à l'actuel => pas de réalignement.
|
|
794
|
+
genEcs({
|
|
795
|
+
enum_usage_generateur_id: '3',
|
|
796
|
+
reference_generateur_mixte: 'MX_X',
|
|
797
|
+
enum_type_generateur_ecs_id: 'SAME'
|
|
798
|
+
})
|
|
799
|
+
];
|
|
800
|
+
dpe.logement.installation_ecs_collection.installation_ecs = [
|
|
801
|
+
{
|
|
802
|
+
donnee_entree: { enum_type_installation_id: '1' },
|
|
803
|
+
generateur_ecs_collection: { generateur_ecs: generateurs }
|
|
804
|
+
}
|
|
805
|
+
];
|
|
806
|
+
|
|
807
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
808
|
+
|
|
809
|
+
// Aucun réalignement effectif.
|
|
810
|
+
expect(generateurs[0].donnee_entree.position_volume_chauffe).toBe(1);
|
|
811
|
+
expect(generateurs[3].donnee_entree.enum_type_generateur_ecs_id).toBe('X0');
|
|
812
|
+
expect(generateurs[4].donnee_entree.enum_type_generateur_ecs_id).toBe('X1');
|
|
813
|
+
expect(generateurs[5].donnee_entree.enum_type_generateur_ecs_id).toBe('SAME');
|
|
814
|
+
errSpy.mockRestore();
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
describe('get_conso_coeff_1_7_2027', () => {
|
|
819
|
+
test('projette la conso EP 2027 pour une maison', () => {
|
|
820
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('C');
|
|
821
|
+
const dpe = makeDpe();
|
|
822
|
+
dpe.logement.sortie = {
|
|
823
|
+
ep_conso: { ep_conso_5_usages: 10000 },
|
|
824
|
+
ef_conso: { conso_5_usages: 6000 }
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const res = get_conso_coeff_1_7_2027(dpe);
|
|
828
|
+
|
|
829
|
+
// ep = (0.7/0.9) * (10000 - 6000) + 6000 = 9111.11...
|
|
830
|
+
// Valeur de référence de régression.
|
|
831
|
+
expect(res.ep_conso_5_usages_2027).toBeCloseTo(9111.111111111, 6);
|
|
832
|
+
// m2 = floor(9111.11 / 80) = 113
|
|
833
|
+
expect(res.ep_conso_5_usages_m2_2027).toBe(113);
|
|
834
|
+
expect(res.classe_bilan_dpe_2027).toBe('C');
|
|
835
|
+
expect(classe_bilan_dpe).toHaveBeenCalledWith(113, 'zc1', 'ca1', 80);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
test("utilise la surface d'immeuble pour un modèle immeuble", () => {
|
|
839
|
+
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
840
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('E');
|
|
841
|
+
const dpe = makeDpe();
|
|
842
|
+
dpe.logement.sortie = {
|
|
843
|
+
ep_conso: { ep_conso_5_usages: 30000 },
|
|
844
|
+
ef_conso: { conso_5_usages: 12000 }
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
const res = get_conso_coeff_1_7_2027(dpe);
|
|
848
|
+
|
|
849
|
+
// Sh = 200 => m2 = floor(ep / 200).
|
|
850
|
+
const ep = (0.7 / 0.9) * (30000 - 12000) + 12000;
|
|
851
|
+
expect(res.ep_conso_5_usages_2027).toBeCloseTo(ep, 6);
|
|
852
|
+
expect(classe_bilan_dpe).toHaveBeenCalledWith(Math.floor(ep / 200), 'zc1', 'ca1', 200);
|
|
853
|
+
});
|
|
854
|
+
});
|