Coverage for tests/tests/test_vol_schemas.py: 0%
124 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-01-05 21:46 +0100
« prev ^ index » next coverage.py v7.11.3, created at 2026-01-05 21:46 +0100
1#!/usr/bin/env python3
2"""RAMSES RF - Test the configuration parsers."""
4from typing import Any
6import pytest
7import voluptuous as vol
8import yaml
10from ramses_rf.schemas import (
11 SCH_GATEWAY_DICT,
12 SCH_GLOBAL_SCHEMAS,
13 SCH_GLOBAL_SCHEMAS_DICT,
14 SCH_RESTORE_CACHE_DICT,
15)
16from ramses_tx.schemas import (
17 SCH_ENGINE_DICT,
18 SCH_GLOBAL_TRAITS_DICT,
19 sch_packet_log_dict_factory,
20 sch_serial_port_dict_factory,
21)
23SCH_PACKET_LOG_DICT = sch_packet_log_dict_factory(default_backups=7)
24SCH_SERIAL_PORT_DICT = sch_serial_port_dict_factory()
26SCH_GATEWAY = vol.Schema(SCH_GATEWAY_DICT | SCH_ENGINE_DICT, extra=vol.PREVENT_EXTRA)
27SCH_GLOBAL_TRAITS = vol.Schema(SCH_GLOBAL_TRAITS_DICT, extra=vol.PREVENT_EXTRA)
28SCH_PACKET_LOG = vol.Schema(SCH_PACKET_LOG_DICT, extra=vol.PREVENT_EXTRA)
29SCH_RESTORE_CACHE = vol.Schema(SCH_RESTORE_CACHE_DICT, extra=vol.PREVENT_EXTRA)
30SCH_SERIAL_PORT = vol.Schema(SCH_SERIAL_PORT_DICT, extra=vol.PREVENT_EXTRA)
33# TODO: These schema pass testing, but shouldn't
35_FAIL_BUT_VALID = (
36 """
37 01:333333: {is_vcs: true}
38 """,
39 """
40 02:333333: {is_vcs: true}
41 """,
42)
43_PASS_BUT_INVALID = (
44 """
45 main_tcs: 01:111111 # no TCS schema
46 """,
47 """
48 known_list:
49 01:111111: {}
50 block_list:
51 01:111111: {} # also in known_list
52 """,
53)
56def no_duplicates_constructor(
57 loader: yaml.Loader, node: yaml.Node, deep: bool = False
58) -> Any:
59 """Check for duplicate keys."""
60 mapping: dict[str, Any] = {}
61 for key_node, value_node in node.value:
62 key = loader.construct_object(key_node, deep=deep) # type: ignore[no-untyped-call]
63 if key in mapping:
64 raise yaml.constructor.ConstructorError(
65 f"Duplicate key: {key} ('{mapping[key]}' overwrites '{value_node}')"
66 )
67 value = loader.construct_object(value_node, deep=deep) # type: ignore[no-untyped-call]
68 mapping[key] = value
69 return loader.construct_mapping(node, deep)
72class CheckForDuplicatesLoader(yaml.Loader):
73 """Local class to prevent pollution of global yaml.Loader."""
75 pass
78CheckForDuplicatesLoader.add_constructor(
79 yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, no_duplicates_constructor
80)
83def _test_schema(validator: vol.Schema, config: str) -> dict:
84 return validator(yaml.load(config, CheckForDuplicatesLoader)) # type: ignore[no-any-return]
85 # return validator(yaml.safe_load(schema)) # PyYAML silently swallows duplicate keys!
88def _test_schema_bad(validator: vol.Schema, config: str) -> None:
89 global test_schemas_bad_failed
90 try:
91 _test_schema(validator, config)
92 except (vol.MultipleInvalid, yaml.YAMLError):
93 pass
94 else:
95 test_schemas_bad_failed = True
96 raise TypeError(f"should *not* be valid YAML, but parsed OK: {config}")
99def _test_schema_good(validator: vol.Schema, config: str) -> dict:
100 global test_schemas_good_failed
101 try:
102 return _test_schema(validator, config)
103 except vol.MultipleInvalid as err:
104 test_schemas_good_failed = True
105 raise TypeError(
106 f"should parse via voluptuous, but didn't: {config} ({err})"
107 ) from err
108 except yaml.YAMLError as err:
109 test_schemas_good_failed = True
110 raise TypeError(f"should be valid YAML, but isn't: {config} ({err})") from err
113GATEWAY_BAD = (
114 """
115 # expected a dictionary
116 """,
117 """
118 other_key: null # extra keys not allowed @ data['other_key']
119 """,
120 """
121 disable_discovery: null
122 """,
123 """
124 max_zones: 19
125 """,
126 """
127 use_regex:
128 """,
129 """
130 disable_discovery: null
131 enable_eavesdrop: null
132 max_zones: null
133 reduce_processing: null
134 use_aliases: null
135 use_native_ot: null
136 """,
137)
138GATEWAY_GOOD = (
139 """
140 {}
141 """,
142 """
143 disable_discovery: false
144 """,
145 """
146 max_zones: 16
147 """,
148 """
149 disable_discovery: false
150 enable_eavesdrop: false
151 max_zones: 12
152 reduce_processing: 0
153 use_aliases: false
154 use_native_ot: prefer
155 """,
156 """
157 disable_discovery: true
158 enable_eavesdrop: true
159 max_zones: 3
160 reduce_processing: 2
161 use_aliases: true
162 use_native_ot: never
163 use_regex: {}
164 """,
165 """
166 disable_sending: true
167 enforce_known_list: true
168 evofw_flag: "!V"
169 use_regex:
170 inbound: {}
171 outbound: {}
172 """,
173)
176@pytest.mark.parametrize("index", range(len(GATEWAY_BAD)))
177def test_gateway_bad(index: int, configs: tuple[str, ...] = GATEWAY_BAD) -> None:
178 _test_schema_bad(SCH_GATEWAY, configs[index])
181@pytest.mark.parametrize("index", range(len(GATEWAY_GOOD)))
182def test_gateway_good(index: int, configs: tuple[str, ...] = GATEWAY_GOOD) -> None:
183 _test_schema_good(SCH_GATEWAY, configs[index])
186KNOWN_LIST_BAD = (
187 """
188 # expected a dictionary
189 """,
190 """
191 other_key: null # extra keys not allowed @ data['other_key']
192 """,
193 """
194 known_list: [] # expected a dictionary for dictionary value @ data['known_list']
195 """,
196 """
197 known_list:
198 01:111111: {class: xxx}
199 """,
200 # """
201 # known_list:
202 # 01:111111: {class: CTL, notes: this is invalid _note}
203 # 02:111111: {class: UFC, extra: this is invalid too}
204 # """,
205 """
206 known_list:
207 01:111111: {class: CTL}
208 01:111111: {class: UFC} # Duplicate key: 01:111111
209 """,
210 # """
211 # known_list:
212 # 05:111111: {class: REM, scheme: xxxxxx}
213 # """,
214 # """
215 # known_list:
216 # 01:111111: {class: FAN}
217 # 02:111111: {class: RFS}
218 # 03:111111: {class: CO2, faked: true}
219 # 04:111111: {class: HUM, faked: true}
220 # 05:111111: {class: REM, faked: true, scheme: nuaire}
221 # 06:111111:
222 # class: DIS
223 # scheme: orcon
224 # _note: this is a note
225 # """,
226)
227KNOWN_LIST_GOOD = (
228 """
229 {}
230 """,
231 """
232 known_list: {}
233 """,
234 """
235 known_list: {}
236 block_list: {}
237 """,
238 """
239 known_list:
240 01:111111: {}
241 block_list:
242 01:222222: {}
243 """,
244 """
245 known_list:
246 01:111111: {class: CTL}
247 02:111111: {class: UFC}
248 03:111111: {class: THM, faked: true}
249 04:111111: {class: TRV}
250 07:111111: {class: DHW, faked: true}
251 10:111111: {class: OTB}
252 12:111111: {class: THM}
253 13:111111: {class: BDR}
254 17:111111: {class: OUT, faked: true}
255 18:111111: {class: HGI}
256 22:111111: {class: THM}
257 23:111111: {class: THM}
258 30:111111: {class: RFG}
259 34:111111: {class: THM, _note: this is a note}
260 """,
261 """
262 known_list:
263 01:111111: {class: FAN}
264 02:111111: {class: RFS}
265 03:111111: {class: CO2, faked: true}
266 04:111111: {class: HUM, faked: true}
267 05:111111: {class: REM, faked: true, scheme: nuaire}
268 06:111111:
269 class: DIS
270 _note: this is a note
271 """,
272)
275@pytest.mark.parametrize("index", range(len(KNOWN_LIST_BAD)))
276def test_known_list_bad(index: int, configs: tuple[str, ...] = KNOWN_LIST_BAD) -> None:
277 _test_schema_bad(SCH_GLOBAL_TRAITS, configs[index])
280@pytest.mark.parametrize("index", range(len(KNOWN_LIST_GOOD)))
281def test_known_list_good(
282 index: int, configs: tuple[str, ...] = KNOWN_LIST_GOOD
283) -> None:
284 _test_schema_good(SCH_GLOBAL_TRAITS, configs[index])
287PACKET_LOG_BAD = (
288 """
289 # expected a dictionary
290 """,
291 """
292 other_key: null # extra keys not allowed @ data['other_key']
293 """,
294 """
295 packet_log:
296 file_name: null # expected str for dictionary value @ data['packet_log']['file_name']
297 """,
298 """
299 packet_log: # required key not provided @ data['packet_log']['file_name']
300 rotate_backups: 7
301 rotate_bytes: 204800
302 """,
303)
304PACKET_LOG_GOOD = (
305 """
306 {}
307 """,
308 """
309 packet_log: packet.log
310 """,
311 """
312 packet_log: null # expected str for dictionary value @ data['packet_log']
313 """,
314 """
315 packet_log:
316 file_name: packet.log
317 """,
318 """
319 packet_log:
320 file_name: packet.log
321 rotate_backups: 7
322 """,
323 """
324 packet_log:
325 file_name: packet.log
326 rotate_bytes: 204800
327 """,
328 """
329 packet_log:
330 file_name: packet.log
331 rotate_backups: 7
332 rotate_bytes: 204800
333 """,
334)
337@pytest.mark.parametrize("index", range(len(PACKET_LOG_BAD)))
338def test_packet_log_bad(index: int, configs: tuple[str, ...] = PACKET_LOG_BAD) -> None:
339 _test_schema_bad(SCH_PACKET_LOG, configs[index])
342@pytest.mark.parametrize("index", range(len(PACKET_LOG_GOOD)))
343def test_packet_log_good(
344 index: int, configs: tuple[str, ...] = PACKET_LOG_GOOD
345) -> None:
346 _test_schema_good(SCH_PACKET_LOG, configs[index])
349RESTORE_CACHE_BAD = (
350 """
351 # expected a dictionary
352 """,
353 """
354 other_key: null # extra keys not allowed @ data['other_key']
355 """,
356 """
357 restore_cache: none # should be boolean
358 """,
359 """
360 restore_cache: true
361 restore_schema: true # yaml.scanner.ScannerError
362 """,
363 """
364 restore_schema: true # should be: restore_cache: restore_schema: true
365 """,
366 """
367 restore_state: false # should be: restore_cache: restore_state: true
368 """,
369)
370RESTORE_CACHE_GOOD = (
371 """
372 {}
373 """,
374 """
375 restore_cache: false
376 """,
377 """
378 restore_cache: true
379 """,
380 """
381 restore_cache:
382 restore_schema: true
383 """,
384 """
385 restore_cache:
386 restore_state: true
387 """,
388 """
389 restore_cache:
390 restore_schema: true
391 restore_state: false
392 """,
393 """
394 restore_cache:
395 restore_schema: false
396 restore_state: true
397 """,
398)
401@pytest.mark.parametrize("index", range(len(RESTORE_CACHE_BAD)))
402def test_restore_cache_bad(
403 index: int, configs: tuple[str, ...] = RESTORE_CACHE_BAD
404) -> None:
405 _test_schema_bad(SCH_RESTORE_CACHE, configs[index])
408@pytest.mark.parametrize("index", range(len(RESTORE_CACHE_GOOD)))
409def test_restore_cache_good(
410 index: int, configs: tuple[str, ...] = RESTORE_CACHE_GOOD
411) -> None:
412 _test_schema_good(SCH_RESTORE_CACHE, configs[index])
415SERIAL_PORT_BAD = (
416 """
417 # expected a dictionary
418 """,
419 """
420 {} # required key not provided @ data['serial_port']
421 """,
422 """
423 other_key: null # extra keys not allowed @ data['other_key']
424 """,
425 """
426 serial_name: /dev/ttyMOCK # should be: serial_port:
427 """,
428 """
429 serial_port: /dev/ttyMOCK # yaml.parser.ParserError
430 baudrate: 115200 # default
431 """,
432 """
433 serial_port:
434 port_name: /dev/ttyMOCK
435 baud_rate: 57600 # should be: baudrate:
436 """,
437 """
438 serial_port:
439 port_name: /dev/ttyMOCK
440 baudrate: 57600 # yaml.parser.ScannerError
441 """,
442)
443SERIAL_PORT_GOOD = (
444 """
445 serial_port: /dev/ttyMOCK
446 """,
447 """
448 serial_port:
449 port_name: /dev/ttyMOCK
450 """,
451 """
452 serial_port:
453 port_name: /dev/ttyMOCK
454 baudrate: 115200 # default
455 """,
456 """
457 serial_port:
458 port_name: /dev/ttyMOCK
459 baudrate: 57600
460 """,
461 """
462 serial_port:
463 port_name: /dev/ttyMOCK
464 baudrate: 57600
465 """,
466 """
467 serial_port:
468 port_name: /dev/ttyMOCK
469 baudrate: 57600
470 dsrdtr: false
471 rtscts: false
472 timeout: 0
473 xonxoff: true
474 """,
475)
478@pytest.mark.parametrize("index", range(len(SERIAL_PORT_BAD)))
479def test_serial_port_bad(
480 index: int, configs: tuple[str, ...] = SERIAL_PORT_BAD
481) -> None:
482 _test_schema_bad(SCH_SERIAL_PORT, configs[index])
485@pytest.mark.parametrize("index", range(len(SERIAL_PORT_GOOD)))
486def test_serial_port_good(
487 index: int, configs: tuple[str, ...] = SERIAL_PORT_GOOD
488) -> None:
489 _test_schema_good(SCH_SERIAL_PORT, configs[index])
492SCHEMAS_TCS_BAD = (
493 """
494 # expected a dictionary
495 """,
496 """
497 other_key: null # extra keys not allowed @ data['other_key']
498 """,
499 """
500 01:111111: # expected a dictionary for dictionary value @ data['01:111111']
501 """,
502 """
503 01:111111:
504 system: # expected a dictionary for dictionary value @ data['01:111111']['system']
505 """,
506 """
507 13:111111: # should be: 01:111111
508 system: {} # The ventilation control system schema must include at least one of [remotes, sensors] @ data['13:111111']
509 """,
510 """
511 01:111111:
512 system:
513 appliance_control: 10:111111
514 zones: # should be "00"
515 00: {} # extra keys not allowed @ data['01:111111']['zones'][0]
516 """,
517 """
518 01:111111:
519 system:
520 appliance_control: 10:111111
521 zones:
522 "00": # extra keys not allowed @ data['01:111111']['zones']['00']
523 """,
524 """
525 01:111111:
526 system:
527 appliance_control: 10:111111
528 zones:
529 "1C": {} # extra keys not allowed @ data['01:111111']['zones']['1C']
530 """,
531 """
532 01:111111:
533 system:
534 appliance_control: 10:111111
535 01:111111: {remotes: [29:111111, 29:222222]}
536 """,
537)
538SCHEMAS_TCS_GOOD = (
539 """
540 {}
541 """,
542 """
543 01:111111: {}
544 """,
545 """
546 01:111111: {is_tcs: true}
547 """,
548 """
549 01:111111:
550 system: {}
551 """,
552 """
553 01:111111:
554 system:
555 appliance_control:
556 """,
557 """
558 01:111111:
559 system:
560 appliance_control: null
561 """,
562 """
563 01:111111:
564 system:
565 appliance_control: 10:111111
566 """,
567 """
568 01:111111:
569 system:
570 appliance_control: 10:111111
571 01:222222:
572 system:
573 appliance_control: 13:222222
574 """,
575 """
576 main_tcs: null
577 01:111111:
578 system:
579 appliance_control: 10:111111
580 zones:
581 "0B":
582 sensor: 01:111111
583 """,
584 """
585 main_tcs: 01:222222
586 01:111111:
587 system:
588 appliance_control: 10:111111
589 zones:
590 "00": {}
591 "01": {sensor: 03:111111}
592 "02": {actuators: [04:111111, 04:222222]}
593 "03": {actuators: [13:111111, 13:222222]}
594 """,
595 """
596 main_tcs: 01:222222
597 01:111111:
598 system:
599 appliance_control: 10:111111
600 01:222222:
601 system:
602 appliance_control: 10:222222
603 """,
604)
607@pytest.mark.parametrize("index", range(len(SCHEMAS_TCS_BAD)))
608def test_schemas_tcs_bad(
609 index: int, configs: tuple[str, ...] = SCHEMAS_TCS_BAD
610) -> None:
611 _test_schema_bad(SCH_GLOBAL_SCHEMAS, configs[index])
614@pytest.mark.parametrize("index", range(len(SCHEMAS_TCS_GOOD)))
615def test_schemas_tcs_good(
616 index: int, configs: tuple[str, ...] = SCHEMAS_TCS_GOOD
617) -> None:
618 _test_schema_good(SCH_GLOBAL_SCHEMAS, configs[index])
621SCHEMAS_VCS_BAD = (
622 """
623 # expected a dictionary
624 """,
625 """
626 other_key: null # extra keys not allowed @ data['other_key']
627 """,
628 """
629 32:111111: # expected a dictionary for dictionary value @ data['01:111111']
630 """,
631 """
632 32:111111: {is_vcs: true}
633 """,
634 """
635 01:111111:
636 remotes: []
637 is_tcs: true
638 """,
639 """
640 32:111111: # should not duplicate device_id
641 remotes: [29:111111, 29:111111] # not a valid value for dictionary value @ data['32:111111']['remotes']
642 """,
643 """
644 32:111111: {remotes: [29:111111, 29:222222]}
645 32:111111: {remotes: [29:111111, 29:222222]} # has duplicate key
646 32:333333: {remotes: [29:111111, 29:222222]}
647 """,
648)
649SCHEMAS_VCS_GOOD = (
650 """
651 {}
652 """,
653 """
654 01:333333:
655 remotes: []
656 """,
657 """
658 32:111111:
659 remotes: []
660 """,
661 """
662 32:111111:
663 remotes: [29:111111]
664 """,
665 """
666 32:111111:
667 remotes: [29:111111, 29:222222]
668 sensors: [29:111111, 29:333333]
669 """,
670 """
671 32:111111: {remotes: [29:111111, 29:222222]}
672 32:222222: {remotes: [29:111111, 29:222222]}
673 32:333333: {remotes: [29:111111, 29:222222]}
674 """,
675)
678@pytest.mark.parametrize("index", range(len(SCHEMAS_VCS_BAD)))
679def test_schemas_vcs_bad(
680 index: int, configs: tuple[str, ...] = SCHEMAS_VCS_BAD
681) -> None:
682 _test_schema_bad(SCH_GLOBAL_SCHEMAS, configs[index])
685@pytest.mark.parametrize("index", range(len(SCHEMAS_VCS_GOOD)))
686def test_schemas_vcs_good(
687 index: int, configs: tuple[str, ...] = SCHEMAS_VCS_GOOD
688) -> None:
689 _test_schema_good(SCH_GLOBAL_SCHEMAS, configs[index])
692SCHEMAS_MIXED_BAD = tuple(x + y for x in SCHEMAS_TCS_GOOD for y in SCHEMAS_VCS_BAD[1:])
693SCHEMAS_MIXED_BAD += tuple(
694 x + y for x in SCHEMAS_TCS_BAD[1:] for y in SCHEMAS_VCS_GOOD[1:]
695)
696SCHEMAS_MIXED_GOOD = tuple(
697 x + y for x in SCHEMAS_TCS_GOOD[1:] for y in SCHEMAS_VCS_GOOD[1:]
698)
701test_schemas_bad_failed = False
702test_schemas_good_failed = False
705@pytest.mark.parametrize("index", range(len(SCHEMAS_MIXED_BAD)))
706def test_schemas_mixed_bad(
707 index: int, configs: tuple[str, ...] = SCHEMAS_MIXED_BAD
708) -> None:
709 global test_schemas_bad_failed
710 if not test_schemas_bad_failed:
711 _test_schema_bad(SCH_GLOBAL_SCHEMAS, configs[index])
714@pytest.mark.parametrize("index", range(len(SCHEMAS_MIXED_GOOD)))
715def test_schemas_mixed_good(
716 index: int, configs: tuple[str, ...] = SCHEMAS_MIXED_GOOD
717) -> None:
718 global test_schemas_good_failed
719 if not test_schemas_good_failed:
720 _test_schema_good(SCH_GLOBAL_SCHEMAS, configs[index])
723SCHEMAS_HASS_BAD = (
724 """
725 # expected a dictionary
726 """,
727 """
728 {}
729 """,
730 """
731 other_key: null # extra keys not allowed @ data['other_key']
732 """,
733 """
734ramses_rf:
735 serial_port: /dev/ttyUSB0
737 ramses_rf:
738 enforce_known_list: true
739 """,
740 """
741ramses_cc:
742 serial_port: /dev/ttyUSB0
744 ramses_cc:
745 enforce_known_list: true
746 """,
747 """
748ramses_cc:
749 serial_port: /dev/ttyUSB0
751 schema: # this not used
752 01:111111: # Temperature control system (e.g. evohome)
753 system:
754 appliance_control: 10:123446
755 zones:
756 "07": {sensor: 01:111111}
758 orphans_hvac: [30:111111, 32:333333, 32:555555, 32:666666]
759 """,
760 """
761ramses_cc:
762 serial_port: /dev/ttyACM0
763 01:123456: {}
764 01:123456: {} # Duplicate key: 01:123456...
765 """,
766)
767SCHEMAS_HASS_GOOD = (
768 """
769ramses_cc:
770 serial_port: /dev/ttyACM0
771 """,
772 """
773ramses_cc:
774 serial_port: /dev/ttyUSB0
775 restore_cache: false
776 known_list:
777 """,
778 """
779ramses_cc:
780 serial_port: rfc2217://localhost:5001
781 restore_cache:
782 restore_schema: true
783 known_list: {}
784 block_list:
785 """,
786 """
787ramses_cc:
788 serial_port: /dev/serial/by-id/usb-SHK_NANO_CUL_868-if00-port0
789 restore_cache:
790 restore_state: true
791 known_list:
792 30:111111: # becomes empty {}
793 32:333333: {} #
794 32:555555: {class: CO2} #
795 block_list: {}
796 """,
797 """
798ramses_cc:
799 serial_port: /dev/serial/by-id/usb-SHK_NANO_CUL_868-if00-port0
801 scan_interval: 300
803 restore_cache:
804 restore_schema: true
805 restore_state: true
807 packet_log:
808 file_name: packet.log
809 rotate_backups: 28
811 ramses_rf:
812 enforce_known_list: true
814 main_tcs: 01:111111
816 01:111111: # Temperature control system (e.g. evohome)
817 system:
818 appliance_control: 10:123446
819 zones:
820 "07": {sensor: 01:111111}
822 orphans_heat: [02:123456]
823 orphans_hvac: [30:111111, 32:333333, 32:555555, 32:666666]
825 known_list:
826 30:111111: {class: FAN} # Orcon HRU
827 32:333333: {class: REM, faked: true} # an impersonatable remote
828 32:555555: {class: CO2, faked: true} # a fully faked sensor
829 32:666666: {class: HUM}
831 block_list:
832 23:111111: {}
834 advanced_features:
835 send_packet: true
836 """,
837 """
838ramses_cc:
839 serial_port: /dev/ttyACM0
840 01:123456: {}
841 01:123457: {}
842 """,
843)
846SCH_DOMAIN_CONFIG = vol.All( # as per ramses_cc.schemas, TODO: add advanced_features
847 vol.Schema(
848 {
849 vol.Optional("ramses_rf", default={}): SCH_GATEWAY,
850 vol.Optional("scan_interval"): int,
851 vol.Optional("advanced_features", default={}): dict,
852 },
853 extra=vol.PREVENT_EXTRA,
854 )
855 .extend(SCH_GLOBAL_SCHEMAS_DICT)
856 .extend(SCH_GLOBAL_TRAITS_DICT)
857 .extend(SCH_PACKET_LOG_DICT)
858 .extend(SCH_RESTORE_CACHE_DICT)
859 .extend(SCH_SERIAL_PORT_DICT),
860)
862SCH_GLOBAL_HASS = vol.Schema(
863 {vol.Required("ramses_cc"): SCH_DOMAIN_CONFIG}, extra=vol.PREVENT_EXTRA
864)
867@pytest.mark.parametrize("index", range(len(SCHEMAS_HASS_BAD)))
868def test_schemas_hass_bad(
869 index: int, configs: tuple[str, ...] = SCHEMAS_HASS_BAD
870) -> None:
871 _test_schema_bad(SCH_GLOBAL_HASS, configs[index])
874@pytest.mark.parametrize("index", range(len(SCHEMAS_HASS_GOOD)))
875def test_schemas_hass_good(
876 index: int, configs: tuple[str, ...] = SCHEMAS_HASS_GOOD
877) -> None:
878 _test_schema_good(SCH_GLOBAL_HASS, configs[index])