@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
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dépendances mockées pour isoler le module de ventilation :
|
|
5
|
+
* - `tv` : accès aux tables `debits_ventilation` / `q4pa_conv`, on contrôle les lignes ;
|
|
6
|
+
* - `requestInput` / `requestInputID` : passe-plats vers les données d'entrée ;
|
|
7
|
+
* - `bug_for_bug_compat` : désactivé pour isoler le comportement nominal ;
|
|
8
|
+
* - `calc_pvent` (5_conso_ventilation.js) : espion, la conso de ventilation est testée ailleurs.
|
|
9
|
+
*/
|
|
10
|
+
vi.mock('./utils.js', () => ({
|
|
11
|
+
tv: vi.fn(),
|
|
12
|
+
requestInput: (de, du, field) => de[field],
|
|
13
|
+
requestInputID: (de, du, field) => de[`enum_${field}_id`],
|
|
14
|
+
bug_for_bug_compat: false
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('./5_conso_ventilation.js', () => ({
|
|
18
|
+
default: vi.fn()
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const { default: calc_ventilation, calc_hperm } = await import('./4_ventilation.js');
|
|
22
|
+
const { tv } = await import('./utils.js');
|
|
23
|
+
const { default: calc_pvent } = await import('./5_conso_ventilation.js');
|
|
24
|
+
|
|
25
|
+
/** Ligne figée de la table `debits_ventilation`. */
|
|
26
|
+
const ROW_DEBITS = {
|
|
27
|
+
qvarep_conv: '1.2',
|
|
28
|
+
qvasouf_conv: '0.5',
|
|
29
|
+
smea_conv: '20',
|
|
30
|
+
tv_debits_ventilation_id: '3'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
tv.mockReset();
|
|
35
|
+
vi.mocked(calc_pvent).mockReset();
|
|
36
|
+
tv.mockImplementation((table) => {
|
|
37
|
+
if (table === 'debits_ventilation') return { ...ROW_DEBITS };
|
|
38
|
+
if (table === 'q4pa_conv') return { q4pa_conv: '1.3', tv_q4pa_conv_id: '9' };
|
|
39
|
+
return null;
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Déperdition par renouvellement d'air dû aux infiltrations (Hperm)
|
|
45
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §4
|
|
46
|
+
*/
|
|
47
|
+
describe('calc_hperm - déperdition par perméabilité', () => {
|
|
48
|
+
test('plusieurs façades exposées (pfe=1) : e=0,07 et f=15', () => {
|
|
49
|
+
const di = { q4pa_conv: 1.3, smea_conv: 20, qvasouf_conv: 0.5, qvarep_conv: 1.2 };
|
|
50
|
+
calc_hperm(di, 100, 2.5, 80, 1);
|
|
51
|
+
// valeur de référence de régression
|
|
52
|
+
expect(di.hperm).toBeCloseTo(124.24055432137072, 9);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('une seule façade exposée (pfe=0) : e=0,02 et f=20', () => {
|
|
56
|
+
const di = { q4pa_conv: 1.3, smea_conv: 20, qvasouf_conv: 0.5, qvarep_conv: 1.2 };
|
|
57
|
+
calc_hperm(di, 100, 2.5, 80, 0);
|
|
58
|
+
// valeur de référence de régression
|
|
59
|
+
expect(di.hperm).toBeCloseTo(31.494527138357977, 9);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('le nombre de façades exposées modifie le résultat', () => {
|
|
63
|
+
const diMulti = { q4pa_conv: 1.3, smea_conv: 20, qvasouf_conv: 0.5, qvarep_conv: 1.2 };
|
|
64
|
+
const diSingle = { q4pa_conv: 1.3, smea_conv: 20, qvasouf_conv: 0.5, qvarep_conv: 1.2 };
|
|
65
|
+
calc_hperm(diMulti, 100, 2.5, 80, 1);
|
|
66
|
+
calc_hperm(diSingle, 100, 2.5, 80, 0);
|
|
67
|
+
expect(diMulti.hperm).not.toBeCloseTo(diSingle.hperm, 5);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/** Fabrique un objet ventilation avec les données d'entrée utiles. */
|
|
72
|
+
function ventilation(de = {}) {
|
|
73
|
+
return { donnee_entree: { enum_type_ventilation_id: '1', ...de } };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const CG = {
|
|
77
|
+
hsp: 2.5,
|
|
78
|
+
enum_periode_construction_id: '1',
|
|
79
|
+
enum_methode_application_dpe_log_id: '1'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
describe('calc_ventilation - orchestration', () => {
|
|
83
|
+
test('surface ventilée absente : la surface habitable Sh est utilisée', () => {
|
|
84
|
+
const vt = ventilation({ q4pa_conv_saisi: 1.3 });
|
|
85
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
86
|
+
|
|
87
|
+
// Hvent = 0,34 * qvarep_conv * surface_ventile = 0,34 * 1,2 * 100
|
|
88
|
+
expect(vt.donnee_intermediaire.hvent).toBeCloseTo(40.8, 9);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('surface ventilée collective : divisée par la clé de répartition', () => {
|
|
92
|
+
const vt = ventilation({
|
|
93
|
+
surface_ventile: 200,
|
|
94
|
+
cle_repartition_ventilation: 2,
|
|
95
|
+
q4pa_conv_saisi: 1.3
|
|
96
|
+
});
|
|
97
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
98
|
+
|
|
99
|
+
// surface_ventile = 200 / 2 = 100 => Hvent = 0,34 * 1,2 * 100
|
|
100
|
+
expect(vt.donnee_intermediaire.hvent).toBeCloseTo(40.8, 9);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('Q4Pa mesuré saisi : utilisé directement, sans lecture de la table q4pa_conv', () => {
|
|
104
|
+
const vt = ventilation({ q4pa_conv_saisi: 0.6 });
|
|
105
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
106
|
+
|
|
107
|
+
expect(vt.donnee_intermediaire.q4pa_conv).toBe(0.6);
|
|
108
|
+
expect(tv).not.toHaveBeenCalledWith('q4pa_conv', expect.anything());
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Q4Pa non saisi : lu dans la table q4pa_conv', () => {
|
|
112
|
+
const vt = ventilation();
|
|
113
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
114
|
+
|
|
115
|
+
expect(tv).toHaveBeenCalledWith('q4pa_conv', expect.any(Object));
|
|
116
|
+
expect(vt.donnee_intermediaire.q4pa_conv).toBe(1.3);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('les débits conventionnels intermédiaires sont supprimés du résultat', () => {
|
|
120
|
+
const vt = ventilation({ q4pa_conv_saisi: 1.3 });
|
|
121
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
122
|
+
|
|
123
|
+
expect(vt.donnee_intermediaire.qvarep_conv).toBeUndefined();
|
|
124
|
+
expect(vt.donnee_intermediaire.qvasouf_conv).toBeUndefined();
|
|
125
|
+
expect(vt.donnee_intermediaire.smea_conv).toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('la consommation de ventilation est déléguée à calc_pvent', () => {
|
|
129
|
+
const vt = ventilation({ q4pa_conv_saisi: 1.3 });
|
|
130
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
131
|
+
|
|
132
|
+
expect(calc_pvent).toHaveBeenCalledWith(
|
|
133
|
+
vt.donnee_intermediaire,
|
|
134
|
+
vt.donnee_entree,
|
|
135
|
+
vt.donnee_utilisateur,
|
|
136
|
+
'th'
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('surface ventilée différente de Sh sans clé de répartition : divisée par 1', () => {
|
|
141
|
+
// Branche `de.cle_repartition_ventilation || 1` : surface_ventile conservée telle quelle
|
|
142
|
+
const vt = ventilation({ surface_ventile: 50, q4pa_conv_saisi: 1.3 });
|
|
143
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
144
|
+
|
|
145
|
+
// surface_ventile = 50 / 1 => Hvent = 0,34 * 1,2 * 50
|
|
146
|
+
expect(vt.donnee_intermediaire.hvent).toBeCloseTo(0.34 * 1.2 * 50, 9);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("débits de ventilation introuvables : message d'erreur, débits non renseignés", () => {
|
|
150
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
151
|
+
tv.mockImplementation((table) => (table === 'q4pa_conv' ? { q4pa_conv: '1.3' } : null));
|
|
152
|
+
|
|
153
|
+
const vt = ventilation({ q4pa_conv_saisi: 1.3 });
|
|
154
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
155
|
+
|
|
156
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('debits_ventilation'));
|
|
157
|
+
errorSpy.mockRestore();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("valeur forfaitaire q4pa_conv introuvable : message d'erreur", () => {
|
|
161
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
162
|
+
tv.mockImplementation((table) => (table === 'debits_ventilation' ? { ...ROW_DEBITS } : null));
|
|
163
|
+
|
|
164
|
+
const vt = ventilation();
|
|
165
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
166
|
+
|
|
167
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('q4pa_conv'));
|
|
168
|
+
errorSpy.mockRestore();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('classification des surfaces : majorité isolée et joints présents => "1"/"1"', () => {
|
|
172
|
+
const vt = ventilation();
|
|
173
|
+
const murs = [
|
|
174
|
+
// b === 0 => paroi ignorée dans les deux sommes
|
|
175
|
+
{
|
|
176
|
+
donnee_intermediaire: { b: 0 },
|
|
177
|
+
donnee_entree: { enum_type_isolation_id: '3', surface_paroi_opaque: 999 }
|
|
178
|
+
},
|
|
179
|
+
// type inconnu (1) mais période d'isolation >= 3 => considérée isolée
|
|
180
|
+
{
|
|
181
|
+
donnee_intermediaire: { b: 1 },
|
|
182
|
+
donnee_entree: {
|
|
183
|
+
enum_type_isolation_id: '1',
|
|
184
|
+
enum_periode_isolation_id: '3',
|
|
185
|
+
surface_paroi_opaque: 50
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
// type isolé connu (3) => isolée
|
|
189
|
+
{
|
|
190
|
+
donnee_intermediaire: { b: 1 },
|
|
191
|
+
donnee_entree: { enum_type_isolation_id: '3', surface_paroi_opaque: 40 }
|
|
192
|
+
}
|
|
193
|
+
];
|
|
194
|
+
const phs = [
|
|
195
|
+
// type non isolé (2) => non isolée
|
|
196
|
+
{
|
|
197
|
+
donnee_intermediaire: { b: 1 },
|
|
198
|
+
donnee_entree: { enum_type_isolation_id: '2', surface_paroi_opaque: 10 }
|
|
199
|
+
},
|
|
200
|
+
// type inconnu (1) période < 3 => non isolée
|
|
201
|
+
{
|
|
202
|
+
donnee_intermediaire: { b: 1 },
|
|
203
|
+
donnee_entree: {
|
|
204
|
+
enum_type_isolation_id: '1',
|
|
205
|
+
enum_periode_isolation_id: '1',
|
|
206
|
+
surface_paroi_opaque: 5
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
];
|
|
210
|
+
// surface_isolee = 90, surface_non_isolee = 15 => isolation_surfaces = '1'
|
|
211
|
+
const portes = [
|
|
212
|
+
{ donnee_entree: { presence_joint: 1, surface_porte: 2 } },
|
|
213
|
+
{ donnee_entree: { presence_joint: 0, surface_porte: 1 } }
|
|
214
|
+
];
|
|
215
|
+
const baies = [
|
|
216
|
+
{ donnee_entree: { presence_joint: 1, surface_totale_baie: 5 } },
|
|
217
|
+
{ donnee_entree: { presence_joint: 0, surface_totale_baie: 1 } }
|
|
218
|
+
];
|
|
219
|
+
// avec joint = 7, sans joint = 2 => ratio 7/9 > 0,5 => presence_joints_menuiserie = '1'
|
|
220
|
+
calc_ventilation(vt, CG, 'th', 80, 100, murs, phs, portes, baies);
|
|
221
|
+
|
|
222
|
+
expect(tv).toHaveBeenCalledWith('q4pa_conv', {
|
|
223
|
+
enum_periode_construction_id: '1',
|
|
224
|
+
enum_methode_application_dpe_log_id: '1',
|
|
225
|
+
isolation_surfaces: '1',
|
|
226
|
+
presence_joints_menuiserie: '1'
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('classification des surfaces : majorité non isolée et sans joints => "0"/"0"', () => {
|
|
231
|
+
const vt = ventilation();
|
|
232
|
+
const murs = [
|
|
233
|
+
{
|
|
234
|
+
donnee_intermediaire: { b: 1 },
|
|
235
|
+
donnee_entree: { enum_type_isolation_id: '2', surface_paroi_opaque: 100 }
|
|
236
|
+
}
|
|
237
|
+
];
|
|
238
|
+
const baies = [{ donnee_entree: { presence_joint: 0, surface_totale_baie: 10 } }];
|
|
239
|
+
|
|
240
|
+
calc_ventilation(vt, CG, 'th', 80, 100, murs, [], [], baies);
|
|
241
|
+
|
|
242
|
+
expect(tv).toHaveBeenCalledWith(
|
|
243
|
+
'q4pa_conv',
|
|
244
|
+
expect.objectContaining({ isolation_surfaces: '0', presence_joints_menuiserie: '0' })
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('fiche technique façades exposées incohérente : valeur de la fiche imposée', () => {
|
|
249
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
250
|
+
// Fiche "Non" => pfe = 0, alors que plusieurs_facade_exposee = 1 => incohérence signalée
|
|
251
|
+
const vt = ventilation({
|
|
252
|
+
q4pa_conv_saisi: 1.3,
|
|
253
|
+
plusieurs_facade_exposee: 1,
|
|
254
|
+
ficheTechniqueFacadesExposees: { valeur: 'Non', description: 'FT façades' }
|
|
255
|
+
});
|
|
256
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
257
|
+
|
|
258
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('plusieurs_facade_exposee'));
|
|
259
|
+
errorSpy.mockRestore();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('fiche technique façades exposées cohérente : aucun message', () => {
|
|
263
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
264
|
+
// Fiche "Deux" => pfe = 1, cohérent avec plusieurs_facade_exposee = 1
|
|
265
|
+
const vt = ventilation({
|
|
266
|
+
q4pa_conv_saisi: 1.3,
|
|
267
|
+
plusieurs_facade_exposee: 1,
|
|
268
|
+
ficheTechniqueFacadesExposees: { valeur: 'Deux', description: 'FT façades' }
|
|
269
|
+
});
|
|
270
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
271
|
+
|
|
272
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
273
|
+
errorSpy.mockRestore();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('fiche technique ventilation post 2012 incohérente : valeur forcée à 1', () => {
|
|
277
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
278
|
+
const vt = ventilation({
|
|
279
|
+
q4pa_conv_saisi: 1.3,
|
|
280
|
+
ventilation_post_2012: 0,
|
|
281
|
+
ficheTechniqueVentilationPost2012: { description: 'FT ventilation' }
|
|
282
|
+
});
|
|
283
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
284
|
+
|
|
285
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('ventilation_post_2012'));
|
|
286
|
+
expect(vt.donnee_entree.ventilation_post_2012).toBe(1);
|
|
287
|
+
errorSpy.mockRestore();
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Comportement historique `bug_for_bug_compat` activé : le seuil des joints devient `>= 0,5`
|
|
293
|
+
* et les incohérences avec la ligne `q4pa_conv` déjà saisie dans le DPE sont signalées.
|
|
294
|
+
*/
|
|
295
|
+
describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () => {
|
|
296
|
+
/**
|
|
297
|
+
* Recharge le module avec `bug_for_bug_compat = true`. Le double de `tv` renvoie une ligne
|
|
298
|
+
* `q4pa_conv` distincte selon que le matcher cible l'identifiant déjà saisi ou le matcher complet.
|
|
299
|
+
*/
|
|
300
|
+
async function chargerAvecBug(rowParId) {
|
|
301
|
+
vi.resetModules();
|
|
302
|
+
const tvBug = vi.fn((table, matcher) => {
|
|
303
|
+
if (table === 'debits_ventilation') return { ...ROW_DEBITS };
|
|
304
|
+
if (table === 'q4pa_conv') {
|
|
305
|
+
if (matcher.tv_q4pa_conv_id) return rowParId;
|
|
306
|
+
return { q4pa_conv: '1.3', tv_q4pa_conv_id: '9' };
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
});
|
|
310
|
+
vi.doMock('./utils.js', () => ({
|
|
311
|
+
tv: tvBug,
|
|
312
|
+
requestInput: (de, du, field) => de[field],
|
|
313
|
+
requestInputID: (de, du, field) => de[`enum_${field}_id`],
|
|
314
|
+
bug_for_bug_compat: true
|
|
315
|
+
}));
|
|
316
|
+
vi.doMock('./5_conso_ventilation.js', () => ({ default: vi.fn() }));
|
|
317
|
+
const { default: calc_ventilation_bug } = await import('./4_ventilation.js');
|
|
318
|
+
return { calc_ventilation_bug, tvBug };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
afterEach(() => {
|
|
322
|
+
vi.doUnmock('./utils.js');
|
|
323
|
+
vi.doUnmock('./5_conso_ventilation.js');
|
|
324
|
+
vi.resetModules();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
/** Surfaces produisant isolation_surfaces = '1' et joints = '1'. */
|
|
328
|
+
function surfacesIsolantes() {
|
|
329
|
+
return {
|
|
330
|
+
murs: [
|
|
331
|
+
{
|
|
332
|
+
donnee_intermediaire: { b: 1 },
|
|
333
|
+
donnee_entree: { enum_type_isolation_id: '3', surface_paroi_opaque: 100 }
|
|
334
|
+
}
|
|
335
|
+
],
|
|
336
|
+
baies: [{ donnee_entree: { presence_joint: 1, surface_totale_baie: 10 } }]
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
test('ligne DPE incohérente : les deux écarts (isolation et joints) sont signalés', async () => {
|
|
341
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
342
|
+
const { calc_ventilation_bug } = await chargerAvecBug({
|
|
343
|
+
q4pa_conv: '1.3',
|
|
344
|
+
tv_q4pa_conv_id: '9',
|
|
345
|
+
isolation_surfaces: '0',
|
|
346
|
+
presence_joints_menuiserie: '0',
|
|
347
|
+
description: 'ventilation'
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const { murs, baies } = surfacesIsolantes();
|
|
351
|
+
const vt = {
|
|
352
|
+
donnee_entree: {
|
|
353
|
+
enum_type_ventilation_id: '1',
|
|
354
|
+
tv_q4pa_conv_id: 9,
|
|
355
|
+
description: 'ventilation'
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
calc_ventilation_bug(vt, CG, 'th', 80, 100, murs, [], [], baies);
|
|
359
|
+
|
|
360
|
+
// Deux messages : isolation_surfaces et presence_joints_menuiserie
|
|
361
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('isolation_surfaces'));
|
|
362
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('presence_joints_menuiserie'));
|
|
363
|
+
errorSpy.mockRestore();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test('ligne DPE cohérente : aucun écart signalé', async () => {
|
|
367
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
368
|
+
const { calc_ventilation_bug } = await chargerAvecBug({
|
|
369
|
+
q4pa_conv: '1.3',
|
|
370
|
+
tv_q4pa_conv_id: '9',
|
|
371
|
+
isolation_surfaces: '1',
|
|
372
|
+
presence_joints_menuiserie: '1'
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const { murs, baies } = surfacesIsolantes();
|
|
376
|
+
const vt = {
|
|
377
|
+
donnee_entree: { enum_type_ventilation_id: '1', tv_q4pa_conv_id: 9 }
|
|
378
|
+
};
|
|
379
|
+
calc_ventilation_bug(vt, CG, 'th', 80, 100, murs, [], [], baies);
|
|
380
|
+
|
|
381
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
382
|
+
errorSpy.mockRestore();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('ligne DPE saisie introuvable : aucune comparaison effectuée', async () => {
|
|
386
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
387
|
+
// rowParId = null => la branche `if (rowQ4paConv)` est fausse
|
|
388
|
+
const { calc_ventilation_bug } = await chargerAvecBug(null);
|
|
389
|
+
|
|
390
|
+
// Baie sans joint => ratio < 0,5 => branche '0' du seuil `>= 0,5` (mode bug)
|
|
391
|
+
const murs = [
|
|
392
|
+
{
|
|
393
|
+
donnee_intermediaire: { b: 1 },
|
|
394
|
+
donnee_entree: { enum_type_isolation_id: '3', surface_paroi_opaque: 100 }
|
|
395
|
+
}
|
|
396
|
+
];
|
|
397
|
+
const baies = [{ donnee_entree: { presence_joint: 0, surface_totale_baie: 10 } }];
|
|
398
|
+
const vt = {
|
|
399
|
+
donnee_entree: { enum_type_ventilation_id: '1', tv_q4pa_conv_id: 9 }
|
|
400
|
+
};
|
|
401
|
+
calc_ventilation_bug(vt, CG, 'th', 80, 100, murs, [], [], baies);
|
|
402
|
+
|
|
403
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
404
|
+
errorSpy.mockRestore();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `requestInput` est mocké en simple passe-plat : il retourne la valeur portée par les données
|
|
5
|
+
* d'entrée `de` pour le champ demandé. Cela isole `calc_pvent` de la logique de `utils`.
|
|
6
|
+
*/
|
|
7
|
+
vi.mock('./utils.js', () => ({
|
|
8
|
+
requestInput: (de, du, field) => de[field]
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const { default: calc_pvent } = await import('./5_conso_ventilation.js');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 5. Consommation des auxiliaires de ventilation
|
|
15
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §5, pages 41-42
|
|
16
|
+
*/
|
|
17
|
+
describe('calc_pvent - consommation des auxiliaires de ventilation', () => {
|
|
18
|
+
let di;
|
|
19
|
+
let du;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
di = {};
|
|
23
|
+
du = {};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('ne calcule rien si le type de ventilation est indéfini', () => {
|
|
27
|
+
calc_pvent(di, {}, du, 'maison');
|
|
28
|
+
expect(di).toEqual({});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('ventilation naturelle : consommation nulle', () => {
|
|
32
|
+
const de = { type_ventilation: 'ventilation naturelle par conduit', ventilation_post_2012: 0 };
|
|
33
|
+
calc_pvent(di, de, du, 'maison');
|
|
34
|
+
expect(di.conso_auxiliaire_ventilation).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('maison, VMC simple flux auto après 2012 : Pvent = 35 W', () => {
|
|
38
|
+
const de = { type_ventilation: 'vmc sf auto réglable après 2012', ventilation_post_2012: 1 };
|
|
39
|
+
calc_pvent(di, de, du, 'maison');
|
|
40
|
+
// pvent_moy_maison['simple flux auto'][1] * coef(non hybride = 1) = 35
|
|
41
|
+
expect(di.pvent_moy).toBe(35);
|
|
42
|
+
expect(di.conso_auxiliaire_ventilation).toBeCloseTo(8.76 * 35, 10);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('maison, VMC double flux avant 2013 : Pvent = 80 W', () => {
|
|
46
|
+
const de = { type_ventilation: 'vmc df sans échangeur avant 2013', ventilation_post_2012: 0 };
|
|
47
|
+
calc_pvent(di, de, du, 'maison');
|
|
48
|
+
expect(di.pvent_moy).toBe(80);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("maison, ventilation hybride : ratio de temps d'utilisation appliqué (14/168)", () => {
|
|
52
|
+
const de = { type_ventilation: 'ventilation hybride après 2012', ventilation_post_2012: 1 };
|
|
53
|
+
calc_pvent(di, de, du, 'maison');
|
|
54
|
+
// hybride => type 'simple flux auto', post_2012 forcé à 0, coef = 14 / (24*7)
|
|
55
|
+
const coef = 14 / (24 * 7);
|
|
56
|
+
expect(di.pvent_moy).toBeCloseTo(65 * coef, 10);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('immeuble, VMC double flux : Pvent proratisé au débit et à la surface ventilée', () => {
|
|
60
|
+
di.qvarep_conv = 2;
|
|
61
|
+
const de = {
|
|
62
|
+
type_ventilation: 'vmc df collective avec échangeur avant 2013',
|
|
63
|
+
ventilation_post_2012: 0,
|
|
64
|
+
surface_ventile: 50
|
|
65
|
+
};
|
|
66
|
+
calc_pvent(di, de, du, 'immeuble');
|
|
67
|
+
// pvent_immeuble['double flux'][0] * qvarep_conv * surface_ventile * coef(1) = 1.1 * 2 * 50
|
|
68
|
+
expect(di.pvent_moy).toBeCloseTo(1.1 * 2 * 50, 10);
|
|
69
|
+
expect(di.conso_auxiliaire_ventilation).toBeCloseTo(8.76 * 110, 10);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("immeuble, ventilation hybride : ratio de temps d'utilisation immeuble (28/168)", () => {
|
|
73
|
+
// Branche `th !== 'maison'` du ratio hybride (28 au lieu de 14)
|
|
74
|
+
di.qvarep_conv = 2;
|
|
75
|
+
const de = {
|
|
76
|
+
type_ventilation: 'ventilation hybride de 2001 à 2012',
|
|
77
|
+
ventilation_post_2012: 1,
|
|
78
|
+
surface_ventile: 50
|
|
79
|
+
};
|
|
80
|
+
calc_pvent(di, de, du, 'immeuble');
|
|
81
|
+
// hybride => type 'simple flux auto', post_2012 forcé à 0 => pvent_immeuble = 0.46
|
|
82
|
+
const coef = 28 / (24 * 7);
|
|
83
|
+
expect(di.pvent_moy).toBeCloseTo(0.46 * 2 * 50 * coef, 10);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Couverture exhaustive des libellés de type de ventilation (branches du switch).
|
|
89
|
+
* Chaque libellé doit être routé vers le bon groupe (naturelle => conso nulle,
|
|
90
|
+
* sinon simple flux auto / hygro / double flux avec une consommation positive).
|
|
91
|
+
*/
|
|
92
|
+
describe('calc_pvent - routage de tous les types de ventilation', () => {
|
|
93
|
+
const naturelles = [
|
|
94
|
+
'ventilation par ouverture des fenêtres',
|
|
95
|
+
"ventilation par entrées d'air hautes et basses",
|
|
96
|
+
'ventilation naturelle par conduit',
|
|
97
|
+
"ventilation naturelle par conduit avec entrées d'air hygro",
|
|
98
|
+
'puits climatique sans échangeur avant 2013',
|
|
99
|
+
'puits climatique sans échangeur à partir de 2013',
|
|
100
|
+
'puits climatique avec échangeur avant 2013',
|
|
101
|
+
'puits climatique avec échangeur à partir de 2013'
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
test.each(naturelles)('ventilation naturelle "%s" : consommation nulle', (type_ventilation) => {
|
|
105
|
+
const di = {};
|
|
106
|
+
calc_pvent(di, { type_ventilation, ventilation_post_2012: 0 }, {}, 'maison');
|
|
107
|
+
expect(di.conso_auxiliaire_ventilation).toBe(0);
|
|
108
|
+
expect(di.pvent_moy).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const hybrides = [
|
|
112
|
+
'ventilation hybride avant 2001',
|
|
113
|
+
'ventilation hybride de 2001 à 2012',
|
|
114
|
+
'ventilation hybride après 2012',
|
|
115
|
+
"ventilation hybride avec entrées d'air hygro avant 2001",
|
|
116
|
+
"ventilation hybride avec entrées d'air hygro de 2001 à 2012",
|
|
117
|
+
"ventilation hybride avec entrées d'air hygro après 2012"
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
test.each(hybrides)(
|
|
121
|
+
'ventilation hybride "%s" : simple flux auto avec ratio 14/168 (maison)',
|
|
122
|
+
(type_ventilation) => {
|
|
123
|
+
const di = {};
|
|
124
|
+
// Même avec ventilation_post_2012 = 1, l'hybride force post_2012 = 0 (=> 65 W)
|
|
125
|
+
calc_pvent(di, { type_ventilation, ventilation_post_2012: 1 }, {}, 'maison');
|
|
126
|
+
expect(di.pvent_moy).toBeCloseTo(65 * (14 / (24 * 7)), 10);
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const simpleFluxAuto = [
|
|
131
|
+
'ventilation mécanique sur conduit existant avant 2013',
|
|
132
|
+
'ventilation mécanique sur conduit existant à partir de 2013',
|
|
133
|
+
'vmc sf auto réglable avant 1982',
|
|
134
|
+
'vmc sf auto réglable de 1982 à 2000',
|
|
135
|
+
'vmc sf auto réglable de 2001 à 2012',
|
|
136
|
+
'vmc sf auto réglable après 2012',
|
|
137
|
+
'vmc sf gaz avant 2001',
|
|
138
|
+
'vmc sf gaz de 2001 à 2012',
|
|
139
|
+
'vmc sf gaz après 2012'
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
test.each(simpleFluxAuto)(
|
|
143
|
+
'simple flux auto "%s" : 65 W (maison, avant 2012)',
|
|
144
|
+
(type_ventilation) => {
|
|
145
|
+
const di = {};
|
|
146
|
+
calc_pvent(di, { type_ventilation, ventilation_post_2012: 0 }, {}, 'maison');
|
|
147
|
+
expect(di.pvent_moy).toBe(65);
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const simpleFluxHygro = [
|
|
152
|
+
'vmc sf hygro b avant 2001',
|
|
153
|
+
'vmc sf hygro b de 2001 à 2012',
|
|
154
|
+
'vmc sf hygro b après 2012',
|
|
155
|
+
'vmc sf hygro a avant 2001',
|
|
156
|
+
'vmc sf hygro a de 2001 à 2012',
|
|
157
|
+
'vmc sf hygro a après 2012',
|
|
158
|
+
'vmc basse pression auto-réglable',
|
|
159
|
+
'vmc basse pression hygro a',
|
|
160
|
+
'vmc basse pression hygro b'
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
test.each(simpleFluxHygro)(
|
|
164
|
+
'simple flux hygro "%s" : 50 W (maison, avant 2012)',
|
|
165
|
+
(type_ventilation) => {
|
|
166
|
+
const di = {};
|
|
167
|
+
calc_pvent(di, { type_ventilation, ventilation_post_2012: 0 }, {}, 'maison');
|
|
168
|
+
expect(di.pvent_moy).toBe(50);
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const doubleFlux = [
|
|
173
|
+
'vmc df individuelle avec échangeur avant 2013',
|
|
174
|
+
'vmc df individuelle avec échangeur à partir de 2013',
|
|
175
|
+
'vmc df collective avec échangeur avant 2013',
|
|
176
|
+
'vmc df collective avec échangeur à partir de 2013',
|
|
177
|
+
'vmc df sans échangeur avant 2013',
|
|
178
|
+
'vmc df sans échangeur après 2012'
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
test.each(doubleFlux)('double flux "%s" : 80 W (maison, avant 2012)', (type_ventilation) => {
|
|
182
|
+
const di = {};
|
|
183
|
+
calc_pvent(di, { type_ventilation, ventilation_post_2012: 0 }, {}, 'maison');
|
|
184
|
+
expect(di.pvent_moy).toBe(80);
|
|
185
|
+
});
|
|
186
|
+
});
|