Repository: aristanetworks/anta (python)

Issue:
Add the test case to verify syslog logging is enabled on device.

Pull request:
feat(anta): Added the test case to verify syslog logging is enabled
# Description

Verifies if syslog logging is enabled.

    Expected Results
    ----------------
    * Success: The test will pass if syslog logging is enabled.
    * Failure: The test will fail if syslog logging is disabled.

Fixes #855 

# Checklist:

- [X] My code follows the style guidelines of this project
- [X] I have performed a self-review of my code
- [X] I have run pre-commit for code linting and typing (`pre-commit run`)
- [ ] I have made corresponding changes to the documentation
- [X] I have added tests that prove my fix is effective or that my feature works
- [X] New and existing unit tests pass locally with my changes (`tox -e testenv`)


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f12dda00..1961ec0c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -46,7 +46,7 @@ repos:
           - '<!--|  ~|  -->'
 
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.8.6
+    rev: v0.9.0
     hooks:
     - id: ruff
       name: Run Ruff linter
diff --git a/anta/catalog.py b/anta/catalog.py
index 65031ced..ca0eeb17 100644
--- a/anta/catalog.py
+++ b/anta/catalog.py
@@ -182,7 +182,7 @@ class AntaCatalogFile(RootModel[dict[ImportString[Any], list[AntaTestDefinition]
             except Exception as e:
                 # A test module is potentially user-defined code.
                 # We need to catch everything if we want to have meaningful logs
-                module_str = f"{module_name[1:] if module_name.startswith('.') else module_name}{f' from package {package}' if package else ''}"
+                module_str = f"{module_name.removeprefix('.')}{f' from package {package}' if package else ''}"
                 message = f"Module named {module_str} cannot be imported. Verify that the module exists and there is no Python syntax issues."
                 anta_log_exception(e, message, logger)
                 raise ValueError(message) from e
@@ -223,16 +223,14 @@ class AntaCatalogFile(RootModel[dict[ImportString[Any], list[AntaTestDefinition]
                         raise ValueError(msg)  # noqa: TRY004 pydantic catches ValueError or AssertionError, no TypeError
                     if len(test_definition) != 1:
                         msg = (
-                            f"Syntax error when parsing: {test_definition}\n"
-                            "It must be a dictionary with a single entry. Check the indentation in the test catalog."
+                            f"Syntax error when parsing: {test_definition}\nIt must be a dictionary with a single entry. Check the indentation in the test catalog."
                         )
                         raise ValueError(msg)
                     for test_name, test_inputs in test_definition.copy().items():
                         test: type[AntaTest] | None = getattr(module, test_name, None)
                         if test is None:
                             msg = (
-                                f"{test_name} is not defined in Python module {module.__name__}"
-                                f"{f' (from {module.__file__})' if module.__file__ is not None else ''}"
+                                f"{test_name} is not defined in Python module {module.__name__}{f' (from {module.__file__})' if module.__file__ is not None else ''}"
                             )
                             raise ValueError(msg)
                         test_definitions.append(AntaTestDefinition(test=test, inputs=test_inputs))
diff --git a/anta/cli/utils.py b/anta/cli/utils.py
index e740f8c5..508424dd 100644
--- a/anta/cli/utils.py
+++ b/anta/cli/utils.py
@@ -17,6 +17,7 @@ from yaml import YAMLError
 from anta.catalog import AntaCatalog
 from anta.inventory import AntaInventory
 from anta.inventory.exceptions import InventoryIncorrectSchemaError, InventoryRootKeyError
+from anta.logger import anta_log_exception
 
 if TYPE_CHECKING:
     from click import Option
@@ -242,7 +243,8 @@ def core_options(f: Callable[..., Any]) -> Callable[..., Any]:
                 insecure=insecure,
                 disable_cache=disable_cache,
             )
-        except (TypeError, ValueError, YAMLError, OSError, InventoryIncorrectSchemaError, InventoryRootKeyError):
+        except (TypeError, ValueError, YAMLError, OSError, InventoryIncorrectSchemaError, InventoryRootKeyError) as e:
+            anta_log_exception(e, f"Failed to parse the inventory: {inventory}", logger)
             ctx.exit(ExitCode.USAGE_ERROR)
         return f(*args, inventory=i, **kwargs)
 
@@ -319,7 +321,8 @@ def catalog_options(f: Callable[..., Any]) -> Callable[..., Any]:
         try:
             file_format = catalog_format.lower()
             c = AntaCatalog.parse(catalog, file_format=file_format)  # type: ignore[arg-type]
-        except (TypeError, ValueError, YAMLError, OSError):
+        except (TypeError, ValueError, YAMLError, OSError) as e:
+            anta_log_exception(e, f"Failed to parse the catalog: {catalog}", logger)
             ctx.exit(ExitCode.USAGE_ERROR)
         return f(*args, catalog=c, **kwargs)
 
diff --git a/anta/input_models/bfd.py b/anta/input_models/bfd.py
index 2d057d67..06838d0e 100644
--- a/anta/input_models/bfd.py
+++ b/anta/input_models/bfd.py
@@ -31,6 +31,10 @@ class BFDPeer(BaseModel):
     """Multiplier of BFD peer. Required field in the `VerifyBFDPeersIntervals` test."""
     protocols: list[BfdProtocol] | None = None
     """List of protocols to be verified. Required field in the `VerifyBFDPeersRegProtocols` test."""
+    detection_time: int | None = None
+    """Detection time of BFD peer in milliseconds. Defines how long to wait without receiving BFD packets before declaring the peer session as down.
+
+    Optional field in the `VerifyBFDPeersIntervals` test."""
 
     def __str__(self) -> str:
         """Return a human-readable string representation of the BFDPeer for reporting."""
diff --git a/anta/runner.py b/anta/runner.py
index 93c43372..2b3c8b70 100644
--- a/anta/runner.py
+++ b/anta/runner.py
@@ -115,7 +115,7 @@ async def setup_inventory(inventory: AntaInventory, tags: set[str] | None, devic
 
     # If there are no devices in the inventory after filtering, exit
     if not selected_inventory.devices:
-        msg = f'No reachable device {f"matching the tags {tags} " if tags else ""}was found.{f" Selected devices: {devices} " if devices is not None else ""}'
+        msg = f"No reachable device {f'matching the tags {tags} ' if tags else ''}was found.{f' Selected devices: {devices} ' if devices is not None else ''}"
         logger.warning(msg)
         return None
 
@@ -170,8 +170,7 @@ def prepare_tests(
 
     if total_test_count == 0:
         msg = (
-            f"There are no tests{f' matching the tags {tags} ' if tags else ' '}to run in the current "
-            "test catalog and device inventory, please verify your inputs."
+            f"There are no tests{f' matching the tags {tags} ' if tags else ' '}to run in the current test catalog and device inventory, please verify your inputs."
         )
         logger.warning(msg)
         return None
diff --git a/pyproject.toml b/pyproject.toml
index 1e85b01f..09fee8c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -75,7 +75,7 @@ dev = [
   "pytest-metadata>=3.0.0",
   "pytest>=7.4.0",
   "respx>=0.22.0",
-  "ruff>=0.5.4,<0.9.0",
+  "ruff>=0.5.4,<0.10.0",
   "tox>=4.10.0,<5.0.0",
   "types-PyYAML",
   "types-pyOpenSSL",
@@ -428,6 +428,9 @@ runtime-evaluated-base-classes = ["pydantic.BaseModel", "anta.models.AntaTest.In
   "C901",     # TODO: test function is too complex, needs a refactor
   "PLR0912"   # Too many branches (15/12) (too-many-branches), needs a refactor
 ]
+"anta/tests/logging.py" = [
+  "A005",  # TODO: Module `logging` shadows a Python standard-library module
+]
 "anta/decorators.py" = [
   "ANN401",  # Ok to use Any type hint in our decorators
 ]


Test patch:
diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py
index a27a786c..861a6a2e 100644
--- a/anta/tests/bfd.py
+++ b/anta/tests/bfd.py
@@ -99,15 +99,18 @@ class VerifyBFDPeersIntervals(AntaTest):
       1. Confirms that the specified VRF is configured.
       2. Verifies that the peer exists in the BFD configuration.
       3. Confirms that BFD peer is correctly configured with the `Transmit interval, Receive interval and Multiplier`.
+      4. Verifies that BFD peer is correctly configured with the `Detection time`, if provided.
 
     Expected Results
     ----------------
     * Success: If all of the following conditions are met:
         - All specified peers are found in the BFD configuration within the specified VRF.
         - All BFD peers are correctly configured with the `Transmit interval, Receive interval and Multiplier`.
+        - If provided, the `Detection time` is correctly configured.
     * Failure: If any of the following occur:
         - A specified peer is not found in the BFD configuration within the specified VRF.
         - Any BFD peer not correctly configured with the `Transmit interval, Receive interval and Multiplier`.
+        - Any BFD peer is not correctly configured with `Detection time`, if provided.
 
     Examples
     --------
@@ -125,6 +128,7 @@ class VerifyBFDPeersIntervals(AntaTest):
               tx_interval: 1200
               rx_interval: 1200
               multiplier: 3
+              detection_time: 3600
     ```
     """
 
@@ -151,6 +155,7 @@ class VerifyBFDPeersIntervals(AntaTest):
             tx_interval = bfd_peer.tx_interval
             rx_interval = bfd_peer.rx_interval
             multiplier = bfd_peer.multiplier
+            detect_time = bfd_peer.detection_time
 
             # Check if BFD peer configured
             bfd_output = get_value(
@@ -166,6 +171,7 @@ class VerifyBFDPeersIntervals(AntaTest):
             bfd_details = bfd_output.get("peerStatsDetail", {})
             op_tx_interval = bfd_details.get("operTxInterval") // 1000
             op_rx_interval = bfd_details.get("operRxInterval") // 1000
+            op_detection_time = bfd_details.get("detectTime") // 1000
             detect_multiplier = bfd_details.get("detectMult")
 
             if op_tx_interval != tx_interval:
@@ -177,6 +183,9 @@ class VerifyBFDPeersIntervals(AntaTest):
             if detect_multiplier != multiplier:
                 self.result.is_failure(f"{bfd_peer} - Incorrect Multiplier - Expected: {multiplier} Actual: {detect_multiplier}")
 
+            if detect_time and op_detection_time != detect_time:
+                self.result.is_failure(f"{bfd_peer} - Incorrect Detection Time - Expected: {detect_time} Actual: {op_detection_time}")
+
 
 class VerifyBFDPeersHealth(AntaTest):
     """Verifies the health of IPv4 BFD peers across all VRFs.
diff --git a/anta/tests/logging.py b/anta/tests/logging.py
index d3270183..4a1c594e 100644
--- a/anta/tests/logging.py
+++ b/anta/tests/logging.py
@@ -43,6 +43,35 @@ def _get_logging_states(logger: logging.Logger, command_output: str) -> str:
     return log_states
 
 
+class VerifySyslogLogging(AntaTest):
+    """Verifies if syslog logging is enabled.
+
+    Expected Results
+    ----------------
+    * Success: The test will pass if syslog logging is enabled.
+    * Failure: The test will fail if syslog logging is disabled.
+
+    Examples
+    --------
+    ```yaml
+    anta.tests.logging:
+      - VerifySyslogLogging:
+    ```
+    """
+
+    categories: ClassVar[list[str]] = ["logging"]
+    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show logging", ofmt="text")]
+
+    @AntaTest.anta_test
+    def test(self) -> None:
+        """Main test function for VerifySyslogLogging."""
+        self.result.is_success()
+        log_output = self.instance_commands[0].text_output
+
+        if "Syslog logging: enabled" not in _get_logging_states(self.logger, log_output):
+            self.result.is_failure("Syslog logging is disabled.")
+
+
 class VerifyLoggingPersistent(AntaTest):
     """Verifies if logging persistent is enabled and logs are saved in flash.
 
diff --git a/anta/tests/system.py b/anta/tests/system.py
index 85235f2f..11cf8398 100644
--- a/anta/tests/system.py
+++ b/anta/tests/system.py
@@ -224,7 +224,7 @@ class VerifyMemoryUtilization(AntaTest):
         if memory_usage > MEMORY_THRESHOLD:
             self.result.is_success()
         else:
-            self.result.is_failure(f"Device has reported a high memory usage: {(1 - memory_usage)*100:.2f}%")
+            self.result.is_failure(f"Device has reported a high memory usage: {(1 - memory_usage) * 100:.2f}%")
 
 
 class VerifyFileSystemUtilization(AntaTest):
diff --git a/examples/tests.yaml b/examples/tests.yaml
index a4bc1fab..0b573616 100644
--- a/examples/tests.yaml
+++ b/examples/tests.yaml
@@ -87,6 +87,7 @@ anta.tests.bfd:
           tx_interval: 1200
           rx_interval: 1200
           multiplier: 3
+          detection_time: 3600
   - VerifyBFDPeersRegProtocols:
       # Verifies the registered routing protocol of IPv4 BFD peer sessions.
       bfd_peers:
@@ -306,6 +307,8 @@ anta.tests.logging:
       vrf: default
   - VerifyLoggingTimestamp:
       # Verifies if logs are generated with the appropriate timestamp.
+  - VerifySyslogLogging:
+      # Verifies if syslog logging is enabled.
 anta.tests.mlag:
   - VerifyMlagConfigSanity:
       # Verifies there are no MLAG config-sanity inconsistencies.
diff --git a/tests/benchmark/test_anta.py b/tests/benchmark/test_anta.py
index 91d3b3ff..1daf7f36 100644
--- a/tests/benchmark/test_anta.py
+++ b/tests/benchmark/test_anta.py
@@ -47,7 +47,7 @@ def test_anta_dry_run(
 
     if len(results.results) != len(inventory) * len(catalog.tests):
         pytest.fail(f"Expected {len(inventory) * len(catalog.tests)} tests but got {len(results.results)}", pytrace=False)
-    bench_info = "\n--- ANTA NRFU Dry-Run Benchmark Information ---\n" f"Test count: {len(results.results)}\n" "-----------------------------------------------"
+    bench_info = f"\n--- ANTA NRFU Dry-Run Benchmark Information ---\nTest count: {len(results.results)}\n-----------------------------------------------"
     logger.info(bench_info)
 
 
diff --git a/tests/data/invalid_inventory.yml b/tests/data/invalid_inventory.yml
new file mode 100644
index 00000000..5199a15f
--- /dev/null
+++ b/tests/data/invalid_inventory.yml
@@ -0,0 +1,5 @@
+---
+anta_inventory:
+  - host: 172.20.20.101
+    name: DC1-SPINE1
+    tags: ["SPINE", "DC1"]
diff --git a/tests/units/anta_tests/test_bfd.py b/tests/units/anta_tests/test_bfd.py
index af1329f9..8b234222 100644
--- a/tests/units/anta_tests/test_bfd.py
+++ b/tests/units/anta_tests/test_bfd.py
@@ -27,6 +27,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 1200000,
                                             "operRxInterval": 1200000,
                                             "detectMult": 3,
+                                            "detectTime": 3600000,
                                         }
                                     }
                                 }
@@ -42,6 +43,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 1200000,
                                             "operRxInterval": 1200000,
                                             "detectMult": 3,
+                                            "detectTime": 3600000,
                                         }
                                     }
                                 }
@@ -59,6 +61,55 @@ DATA: list[dict[str, Any]] = [
         },
         "expected": {"result": "success"},
     },
+    {
+        "name": "success-detection-time",
+        "test": VerifyBFDPeersIntervals,
+        "eos_data": [
+            {
+                "vrfs": {
+                    "default": {
+                        "ipv4Neighbors": {
+                            "192.0.255.7": {
+                                "peerStats": {
+                                    "": {
+                                        "peerStatsDetail": {
+                                            "operTxInterval": 1200000,
+                                            "operRxInterval": 1200000,
+                                            "detectMult": 3,
+                                            "detectTime": 3600000,
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "MGMT": {
+                        "ipv4Neighbors": {
+                            "192.0.255.70": {
+                                "peerStats": {
+                                    "": {
+                                        "peerStatsDetail": {
+                                            "operTxInterval": 1200000,
+                                            "operRxInterval": 1200000,
+                                            "detectMult": 3,
+                                            "detectTime": 3600000,
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    },
+                }
+            }
+        ],
+        "inputs": {
+            "bfd_peers": [
+                {"peer_address": "192.0.255.7", "vrf": "default", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
+                {"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
+            ]
+        },
+        "expected": {"result": "success"},
+    },
     {
         "name": "failure-no-peer",
         "test": VerifyBFDPeersIntervals,
@@ -74,6 +125,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 1200000,
                                             "operRxInterval": 1200000,
                                             "detectMult": 3,
+                                            "detectTime": 3600000,
                                         }
                                     }
                                 }
@@ -89,6 +141,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 1200000,
                                             "operRxInterval": 1200000,
                                             "detectMult": 3,
+                                            "detectTime": 3600000,
                                         }
                                     }
                                 }
@@ -100,8 +153,8 @@ DATA: list[dict[str, Any]] = [
         ],
         "inputs": {
             "bfd_peers": [
-                {"peer_address": "192.0.255.7", "vrf": "CS", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3},
-                {"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3},
+                {"peer_address": "192.0.255.7", "vrf": "CS", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
+                {"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
             ]
         },
         "expected": {
@@ -127,6 +180,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 1300000,
                                             "operRxInterval": 1200000,
                                             "detectMult": 4,
+                                            "detectTime": 4000000,
                                         }
                                     }
                                 }
@@ -142,6 +196,7 @@ DATA: list[dict[str, Any]] = [
                                             "operTxInterval": 120000,
                                             "operRxInterval": 120000,
                                             "detectMult": 5,
+                                            "detectTime": 4000000,
                                         }
                                     }
                                 }
@@ -168,6 +223,66 @@ DATA: list[dict[str, Any]] = [
             ],
         },
     },
+    {
+        "name": "failure-incorrect-timers-with-detection-time",
+        "test": VerifyBFDPeersIntervals,
+        "eos_data": [
+            {
+                "vrfs": {
+                    "default": {
+                        "ipv4Neighbors": {
+                            "192.0.255.7": {
+                                "peerStats": {
+                                    "": {
+                                        "peerStatsDetail": {
+                                            "operTxInterval": 1300000,
+                                            "operRxInterval": 1200000,
+                                            "detectMult": 4,
+                                            "detectTime": 4000000,
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "MGMT": {
+                        "ipv4Neighbors": {
+                            "192.0.255.70": {
+                                "peerStats": {
+                                    "": {
+                                        "peerStatsDetail": {
+                                            "operTxInterval": 120000,
+                                            "operRxInterval": 120000,
+                                            "detectMult": 5,
+                                            "detectTime": 4000000,
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    },
+                }
+            }
+        ],
+        "inputs": {
+            "bfd_peers": [
+                {"peer_address": "192.0.255.7", "vrf": "default", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
+                {"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
+            ]
+        },
+        "expected": {
+            "result": "failure",
+            "messages": [
+                "Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval - Expected: 1200 Actual: 1300",
+                "Peer: 192.0.255.7 VRF: default - Incorrect Multiplier - Expected: 3 Actual: 4",
+                "Peer: 192.0.255.7 VRF: default - Incorrect Detection Time - Expected: 3600 Actual: 4000",
+                "Peer: 192.0.255.70 VRF: MGMT - Incorrect Transmit interval - Expected: 1200 Actual: 120",
+                "Peer: 192.0.255.70 VRF: MGMT - Incorrect Receive interval - Expected: 1200 Actual: 120",
+                "Peer: 192.0.255.70 VRF: MGMT - Incorrect Multiplier - Expected: 3 Actual: 5",
+                "Peer: 192.0.255.70 VRF: MGMT - Incorrect Detection Time - Expected: 3600 Actual: 4000",
+            ],
+        },
+    },
     {
         "name": "success",
         "test": VerifyBFDSpecificPeers,
diff --git a/tests/units/anta_tests/test_logging.py b/tests/units/anta_tests/test_logging.py
index 6aeac4a2..0c140881 100644
--- a/tests/units/anta_tests/test_logging.py
+++ b/tests/units/anta_tests/test_logging.py
@@ -16,6 +16,7 @@ from anta.tests.logging import (
     VerifyLoggingPersistent,
     VerifyLoggingSourceIntf,
     VerifyLoggingTimestamp,
+    VerifySyslogLogging,
 )
 from tests.units.anta_tests import test
 
@@ -277,4 +278,45 @@ DATA: list[dict[str, Any]] = [
         "inputs": None,
         "expected": {"result": "failure", "messages": ["Device has reported syslog messages with a severity of ERRORS or higher"]},
     },
+    {
+        "name": "success",
+        "test": VerifySyslogLogging,
+        "eos_data": [
+            """Syslog logging: enabled
+            Buffer logging: level debugging
+
+            External configuration:
+                active:
+                inactive:
+
+            Facility                   Severity            Effective Severity
+            --------------------       -------------       ------------------
+            aaa                        debugging           debugging
+            accounting                 debugging           debugging""",
+        ],
+        "inputs": None,
+        "expected": {"result": "success"},
+    },
+    {
+        "name": "failure",
+        "test": VerifySyslogLogging,
+        "eos_data": [
+            """Syslog logging: disabled
+            Buffer logging: level debugging
+            Console logging: level errors
+            Persistent logging: disabled
+            Monitor logging: level errors
+
+            External configuration:
+                active:
+                inactive:
+
+            Facility                   Severity            Effective Severity
+            --------------------       -------------       ------------------
+            aaa                        debugging           debugging
+            accounting                 debugging           debugging""",
+        ],
+        "inputs": None,
+        "expected": {"result": "failure", "messages": ["Syslog logging is disabled."]},
+    },
 ]
diff --git a/tests/units/cli/nrfu/test__init__.py b/tests/units/cli/nrfu/test__init__.py
index f81c67e9..bbc89283 100644
--- a/tests/units/cli/nrfu/test__init__.py
+++ b/tests/units/cli/nrfu/test__init__.py
@@ -5,14 +5,18 @@
 
 from __future__ import annotations
 
+from pathlib import Path
 from typing import TYPE_CHECKING
 
 from anta.cli import anta
 from anta.cli.utils import ExitCode
 
 if TYPE_CHECKING:
+    import pytest
     from click.testing import CliRunner
 
+DATA_DIR: Path = Path(__file__).parents[3].resolve() / "data"
+
 # TODO: write unit tests for ignore-status and ignore-error
 
 
@@ -123,3 +127,19 @@ def test_hide(click_runner: CliRunner) -> None:
     """Test the `--hide` option of the `anta nrfu` command."""
     result = click_runner.invoke(anta, ["nrfu", "--hide", "success", "text"])
     assert "SUCCESS" not in result.output
+
+
+def test_invalid_inventory(caplog: pytest.LogCaptureFixture, click_runner: CliRunner) -> None:
+    """Test invalid inventory."""
+    result = click_runner.invoke(anta, ["nrfu", "--inventory", str(DATA_DIR / "invalid_inventory.yml")])
+    assert "CRITICAL" in caplog.text
+    assert "Failed to parse the inventory" in caplog.text
+    assert result.exit_code == ExitCode.USAGE_ERROR
+
+
+def test_invalid_catalog(caplog: pytest.LogCaptureFixture, click_runner: CliRunner) -> None:
+    """Test invalid catalog."""
+    result = click_runner.invoke(anta, ["nrfu", "--catalog", str(DATA_DIR / "test_catalog_not_a_list.yml")])
+    assert "CRITICAL" in caplog.text
+    assert "Failed to parse the catalog" in caplog.text
+    assert result.exit_code == ExitCode.USAGE_ERROR
diff --git a/tests/units/test_runner.py b/tests/units/test_runner.py
index b3ac1b56..1b9c40c8 100644
--- a/tests/units/test_runner.py
+++ b/tests/units/test_runner.py
@@ -67,7 +67,7 @@ async def test_no_selected_device(caplog: pytest.LogCaptureFixture, inventory: A
     caplog.set_level(logging.WARNING)
     manager = ResultManager()
     await main(manager, inventory, FAKE_CATALOG, tags=tags, devices=devices)
-    msg = f'No reachable device {f"matching the tags {tags} " if tags else ""}was found.{f" Selected devices: {devices} " if devices is not None else ""}'
+    msg = f"No reachable device {f'matching the tags {tags} ' if tags else ''}was found.{f' Selected devices: {devices} ' if devices is not None else ''}"
     assert msg in caplog.messages