Repository: aristanetworks/anta
Pull request #1053 (2025-02-21 07:41:23)

feat(anta.tests): Added negative testing in VerifyReachability test
# Description

Added Negative testing in VerifyReachability

Added `reachable: bool = True` to the input model, Indicates whether the destination should be reachable. Set to True if the network should be accessible, False otherwise.
Validated with the unit tests.

Fixes #1047 

# 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`)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=1053) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=1053&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=1053&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=aristanetworks_anta&pullRequest=1053&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=1053&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=1053&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=1053)


codspeed-hq[bot]: <!-- __CODSPEED_PERFORMANCE_REPORT_COMMENT__ -->
<!-- __CODSPEED_INSTRUMENTATION_PERFORMANCE_REPORT_COMMENT__ -->

## [CodSpeed Performance Report](https://codspeed.io/aristanetworks/anta/branches/vitthalmagadum%3Aissue_1047_reachability)

### Merging #1053 will **not alter performance**

<sub>Comparing <code>vitthalmagadum:issue_1047_reachability</code> (986944b) with <code>main</code> (7c9bef4)</sub>



### Summary

`✅ 22` untouched benchmarks  





Patch:
diff --git a/anta/input_models/connectivity.py b/anta/input_models/connectivity.py
index f967e775..464d22a5 100644
--- a/anta/input_models/connectivity.py
+++ b/anta/input_models/connectivity.py
@@ -23,13 +23,15 @@ class Host(BaseModel):
     source: IPv4Address | IPv6Address | Interface
     """Source address IP or egress interface to use."""
     vrf: str = "default"
-    """VRF context. Defaults to `default`."""
+    """VRF context."""
     repeat: int = 2
-    """Number of ping repetition. Defaults to 2."""
+    """Number of ping repetition."""
     size: int = 100
-    """Specify datagram size. Defaults to 100."""
+    """Specify datagram size."""
     df_bit: bool = False
-    """Enable do not fragment bit in IP header. Defaults to False."""
+    """Enable do not fragment bit in IP header."""
+    reachable: bool = True
+    """Indicates whether the destination should be reachable."""
 
     def __str__(self) -> str:
         """Return a human-readable string representation of the Host for reporting.
diff --git a/anta/input_models/system.py b/anta/input_models/system.py
index fd4a2709..1771c1a6 100644
--- a/anta/input_models/system.py
+++ b/anta/input_models/system.py
@@ -28,4 +28,4 @@ class NTPServer(BaseModel):
 
     def __str__(self) -> str:
         """Representation of the NTPServer model."""
-        return f"{self.server_address} (Preferred: {self.preferred}, Stratum: {self.stratum})"
+        return f"NTP Server: {self.server_address} Preferred: {self.preferred} Stratum: {self.stratum}"


Test patch:
diff --git a/anta/tests/connectivity.py b/anta/tests/connectivity.py
index 2fd58c1b..6568d842 100644
--- a/anta/tests/connectivity.py
+++ b/anta/tests/connectivity.py
@@ -37,6 +37,7 @@ class VerifyReachability(AntaTest):
               vrf: MGMT
               df_bit: True
               size: 100
+              reachable: true
             - source: Management0
               destination: 8.8.8.8
               vrf: MGMT
@@ -47,6 +48,7 @@ class VerifyReachability(AntaTest):
               vrf: default
               df_bit: True
               size: 100
+              reachable: false
     ```
     """
 
@@ -89,9 +91,14 @@ class VerifyReachability(AntaTest):
         self.result.is_success()
 
         for command, host in zip(self.instance_commands, self.inputs.hosts):
-            if f"{host.repeat} received" not in command.json_output["messages"][0]:
+            # Verifies the network is reachable
+            if host.reachable and f"{host.repeat} received" not in command.json_output["messages"][0]:
                 self.result.is_failure(f"{host} - Unreachable")
 
+            # Verifies the network is unreachable.
+            if not host.reachable and f"{host.repeat} received" in command.json_output["messages"][0]:
+                self.result.is_failure(f"{host} - Destination is expected to be unreachable but found reachable.")
+
 
 class VerifyLLDPNeighbors(AntaTest):
     """Verifies the connection status of the specified LLDP (Link Layer Discovery Protocol) neighbors.
diff --git a/anta/tests/stp.py b/anta/tests/stp.py
index 87e3cd10..40f72c18 100644
--- a/anta/tests/stp.py
+++ b/anta/tests/stp.py
@@ -7,7 +7,7 @@
 # mypy: disable-error-code=attr-defined
 from __future__ import annotations
 
-from typing import Any, ClassVar, Literal
+from typing import ClassVar, Literal
 
 from pydantic import Field
 
@@ -54,8 +54,7 @@ class VerifySTPMode(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifySTPMode."""
-        not_configured = []
-        wrong_stp_mode = []
+        self.result.is_success()
         for command in self.instance_commands:
             vlan_id = command.params.vlan
             if not (
@@ -64,15 +63,9 @@ class VerifySTPMode(AntaTest):
                     f"spanningTreeVlanInstances.{vlan_id}.spanningTreeVlanInstance.protocol",
                 )
             ):
-                not_configured.append(vlan_id)
+                self.result.is_failure(f"VLAN {vlan_id} STP mode: {self.inputs.mode} - Not configured")
             elif stp_mode != self.inputs.mode:
-                wrong_stp_mode.append(vlan_id)
-        if not_configured:
-            self.result.is_failure(f"STP mode '{self.inputs.mode}' not configured for the following VLAN(s): {not_configured}")
-        if wrong_stp_mode:
-            self.result.is_failure(f"Wrong STP mode configured for the following VLAN(s): {wrong_stp_mode}")
-        if not not_configured and not wrong_stp_mode:
-            self.result.is_success()
+                self.result.is_failure(f"VLAN {vlan_id} - Incorrect STP mode - Expected: {self.inputs.mode} Actual: {stp_mode}")
 
 
 class VerifySTPBlockedPorts(AntaTest):
@@ -102,8 +95,8 @@ class VerifySTPBlockedPorts(AntaTest):
             self.result.is_success()
         else:
             for key, value in stp_instances.items():
-                stp_instances[key] = value.pop("spanningTreeBlockedPorts")
-            self.result.is_failure(f"The following ports are blocked by STP: {stp_instances}")
+                stp_block_ports = value.get("spanningTreeBlockedPorts")
+                self.result.is_failure(f"STP Instance: {key} - Blocked ports - {', '.join(stp_block_ports)}")
 
 
 class VerifySTPCounters(AntaTest):
@@ -128,14 +121,14 @@ class VerifySTPCounters(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifySTPCounters."""
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
-        interfaces_with_errors = [
-            interface for interface, counters in command_output["interfaces"].items() if counters["bpduTaggedError"] or counters["bpduOtherError"] != 0
-        ]
-        if interfaces_with_errors:
-            self.result.is_failure(f"The following interfaces have STP BPDU packet errors: {interfaces_with_errors}")
-        else:
-            self.result.is_success()
+
+        for interface, counters in command_output["interfaces"].items():
+            if counters["bpduTaggedError"] != 0:
+                self.result.is_failure(f"Interface {interface} - STP BPDU packet tagged errors count mismatch - Expected: 0 Actual: {counters['bpduTaggedError']}")
+            if counters["bpduOtherError"] != 0:
+                self.result.is_failure(f"Interface {interface} - STP BPDU packet other errors count mismatch - Expected: 0 Actual: {counters['bpduOtherError']}")
 
 
 class VerifySTPForwardingPorts(AntaTest):
@@ -174,25 +167,22 @@ class VerifySTPForwardingPorts(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifySTPForwardingPorts."""
-        not_configured = []
-        not_forwarding = []
+        self.result.is_success()
+        interfaces_state = []
         for command in self.instance_commands:
             vlan_id = command.params.vlan
             if not (topologies := get_value(command.json_output, "topologies")):
-                not_configured.append(vlan_id)
-            else:
-                interfaces_not_forwarding = []
-                for value in topologies.values():
-                    if vlan_id and int(vlan_id) in value["vlans"]:
-                        interfaces_not_forwarding = [interface for interface, state in value["interfaces"].items() if state["state"] != "forwarding"]
-                if interfaces_not_forwarding:
-                    not_forwarding.append({f"VLAN {vlan_id}": interfaces_not_forwarding})
-        if not_configured:
-            self.result.is_failure(f"STP instance is not configured for the following VLAN(s): {not_configured}")
-        if not_forwarding:
-            self.result.is_failure(f"The following VLAN(s) have interface(s) that are not in a forwarding state: {not_forwarding}")
-        if not not_configured and not interfaces_not_forwarding:
-            self.result.is_success()
+                self.result.is_failure(f"VLAN {vlan_id} - STP instance is not configured")
+                continue
+            for value in topologies.values():
+                if vlan_id and int(vlan_id) in value["vlans"]:
+                    interfaces_state = [
+                        (interface, actual_state) for interface, state in value["interfaces"].items() if (actual_state := state["state"]) != "forwarding"
+                    ]
+
+            if interfaces_state:
+                for interface, state in interfaces_state:
+                    self.result.is_failure(f"VLAN {vlan_id} Interface: {interface} - Invalid state - Expected: forwarding Actual: {state}")
 
 
 class VerifySTPRootPriority(AntaTest):
@@ -229,6 +219,7 @@ class VerifySTPRootPriority(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifySTPRootPriority."""
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
         if not (stp_instances := command_output["instances"]):
             self.result.is_failure("No STP instances configured")
@@ -240,16 +231,15 @@ class VerifySTPRootPriority(AntaTest):
         elif first_name.startswith("VL"):
             prefix = "VL"
         else:
-            self.result.is_failure(f"Unsupported STP instance type: {first_name}")
+            self.result.is_failure(f"STP Instance: {first_name} - Unsupported STP instance type")
             return
         check_instances = [f"{prefix}{instance_id}" for instance_id in self.inputs.instances] if self.inputs.instances else command_output["instances"].keys()
-        wrong_priority_instances = [
-            instance for instance in check_instances if get_value(command_output, f"instances.{instance}.rootBridge.priority") != self.inputs.priority
-        ]
-        if wrong_priority_instances:
-            self.result.is_failure(f"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}")
-        else:
-            self.result.is_success()
+        for instance in check_instances:
+            if not (instance_details := get_value(command_output, f"instances.{instance}")):
+                self.result.is_failure(f"Instance: {instance} - Not configured")
+                continue
+            if (priority := get_value(instance_details, "rootBridge.priority")) != self.inputs.priority:
+                self.result.is_failure(f"STP Instance: {instance} - Incorrect root priority - Expected: {self.inputs.priority} Actual: {priority}")
 
 
 class VerifyStpTopologyChanges(AntaTest):
@@ -282,8 +272,7 @@ class VerifyStpTopologyChanges(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifyStpTopologyChanges."""
-        failures: dict[str, Any] = {"topologies": {}}
-
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
         stp_topologies = command_output.get("topologies", {})
 
@@ -297,18 +286,12 @@ class VerifyStpTopologyChanges(AntaTest):
 
         # Verifies the number of changes across all interfaces
         for topology, topology_details in stp_topologies.items():
-            interfaces = {
-                interface: {"Number of changes": num_of_changes}
-                for interface, details in topology_details.get("interfaces", {}).items()
-                if (num_of_changes := details.get("numChanges")) > self.inputs.threshold
-            }
-            if interfaces:
-                failures["topologies"][topology] = interfaces
-
-        if failures["topologies"]:
-            self.result.is_failure(f"The following STP topologies are not configured or number of changes not within the threshold:\n{failures}")
-        else:
-            self.result.is_success()
+            for interface, details in topology_details.get("interfaces", {}).items():
+                if (num_of_changes := details.get("numChanges")) > self.inputs.threshold:
+                    self.result.is_failure(
+                        f"Topology: {topology} Interface: {interface} - Number of changes not within the threshold - Expected: "
+                        f"{self.inputs.threshold} Actual: {num_of_changes}"
+                    )
 
 
 class VerifySTPDisabledVlans(AntaTest):
diff --git a/anta/tests/system.py b/anta/tests/system.py
index 11cf8398..9ec71918 100644
--- a/anta/tests/system.py
+++ b/anta/tests/system.py
@@ -24,7 +24,7 @@ DISK_SPACE_THRESHOLD = 75
 
 
 class VerifyUptime(AntaTest):
-    """Verifies if the device uptime is higher than the provided minimum uptime value.
+    """Verifies the device uptime.
 
     Expected Results
     ----------------
@@ -40,7 +40,6 @@ class VerifyUptime(AntaTest):
     ```
     """
 
-    description = "Verifies the device uptime."
     categories: ClassVar[list[str]] = ["system"]
     commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show uptime", revision=1)]
 
@@ -53,11 +52,10 @@ class VerifyUptime(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifyUptime."""
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
-        if command_output["upTime"] > self.inputs.minimum:
-            self.result.is_success()
-        else:
-            self.result.is_failure(f"Device uptime is {command_output['upTime']} seconds")
+        if command_output["upTime"] < self.inputs.minimum:
+            self.result.is_failure(f"Device uptime is incorrect - Expected: {self.inputs.minimum} Actual: {command_output['upTime']} seconds")
 
 
 class VerifyReloadCause(AntaTest):
@@ -96,11 +94,11 @@ class VerifyReloadCause(AntaTest):
         ]:
             self.result.is_success()
         else:
-            self.result.is_failure(f"Reload cause is: '{command_output_data}'")
+            self.result.is_failure(f"Reload cause is: {command_output_data}")
 
 
 class VerifyCoredump(AntaTest):
-    """Verifies if there are core dump files in the /var/core directory.
+    """Verifies there are no core dump files.
 
     Expected Results
     ----------------
@@ -119,7 +117,6 @@ class VerifyCoredump(AntaTest):
     ```
     """
 
-    description = "Verifies there are no core dump files."
     categories: ClassVar[list[str]] = ["system"]
     commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show system coredump", revision=1)]
 
@@ -133,7 +130,7 @@ class VerifyCoredump(AntaTest):
         if not core_files:
             self.result.is_success()
         else:
-            self.result.is_failure(f"Core dump(s) have been found: {core_files}")
+            self.result.is_failure(f"Core dump(s) have been found: {', '.join(core_files)}")
 
 
 class VerifyAgentLogs(AntaTest):
@@ -189,12 +186,11 @@ class VerifyCPUUtilization(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifyCPUUtilization."""
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
         command_output_data = command_output["cpuInfo"]["%Cpu(s)"]["idle"]
-        if command_output_data > CPU_IDLE_THRESHOLD:
-            self.result.is_success()
-        else:
-            self.result.is_failure(f"Device has reported a high CPU utilization: {100 - command_output_data}%")
+        if command_output_data < CPU_IDLE_THRESHOLD:
+            self.result.is_failure(f"Device has reported a high CPU utilization -  Expected: < 75% Actual: {100 - command_output_data}%")
 
 
 class VerifyMemoryUtilization(AntaTest):
@@ -219,12 +215,11 @@ class VerifyMemoryUtilization(AntaTest):
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifyMemoryUtilization."""
+        self.result.is_success()
         command_output = self.instance_commands[0].json_output
         memory_usage = command_output["memFree"] / command_output["memTotal"]
-        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}%")
+        if memory_usage < MEMORY_THRESHOLD:
+            self.result.is_failure(f"Device has reported a high memory usage - Expected: < 75% Actual: {(1 - memory_usage) * 100:.2f}%")
 
 
 class VerifyFileSystemUtilization(AntaTest):
@@ -253,7 +248,7 @@ class VerifyFileSystemUtilization(AntaTest):
         self.result.is_success()
         for line in command_output.split("\n")[1:]:
             if "loop" not in line and len(line) > 0 and (percentage := int(line.split()[4].replace("%", ""))) > DISK_SPACE_THRESHOLD:
-                self.result.is_failure(f"Mount point {line} is higher than 75%: reported {percentage}%")
+                self.result.is_failure(f"Mount point: {line} - Higher disk space utilization - Expected: {DISK_SPACE_THRESHOLD}% Actual: {percentage}%")
 
 
 class VerifyNTP(AntaTest):
@@ -272,7 +267,6 @@ class VerifyNTP(AntaTest):
     ```
     """
 
-    description = "Verifies if NTP is synchronised."
     categories: ClassVar[list[str]] = ["system"]
     commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ntp status", ofmt="text")]
 
@@ -284,7 +278,7 @@ class VerifyNTP(AntaTest):
             self.result.is_success()
         else:
             data = command_output.split("\n")[0]
-            self.result.is_failure(f"The device is not synchronized with the configured NTP server(s): '{data}'")
+            self.result.is_failure(f"NTP status mismatch - Expected: synchronised Actual: {data}")
 
 
 class VerifyNTPAssociations(AntaTest):
diff --git a/examples/tests.yaml b/examples/tests.yaml
index f5fd3ebd..6e5d88c6 100644
--- a/examples/tests.yaml
+++ b/examples/tests.yaml
@@ -130,6 +130,7 @@ anta.tests.connectivity:
           vrf: MGMT
           df_bit: True
           size: 100
+          reachable: true
         - source: Management0
           destination: 8.8.8.8
           vrf: MGMT
@@ -140,6 +141,7 @@ anta.tests.connectivity:
           vrf: default
           df_bit: True
           size: 100
+          reachable: false
 anta.tests.cvx:
   - VerifyActiveCVXConnections:
       # Verifies the number of active CVX Connections.
@@ -952,7 +954,7 @@ anta.tests.system:
   - VerifyMemoryUtilization:
       # Verifies whether the memory utilization is below 75%.
   - VerifyNTP:
-      # Verifies if NTP is synchronised.
+      # Verifies that the Network Time Protocol (NTP) is synchronized.
   - VerifyNTPAssociations:
       # Verifies the Network Time Protocol (NTP) associations.
       ntp_servers:
diff --git a/tests/units/anta_tests/test_connectivity.py b/tests/units/anta_tests/test_connectivity.py
index 86ada5de..1f5044b0 100644
--- a/tests/units/anta_tests/test_connectivity.py
+++ b/tests/units/anta_tests/test_connectivity.py
@@ -45,6 +45,23 @@ DATA: list[dict[str, Any]] = [
         ],
         "expected": {"result": "success"},
     },
+    {
+        "name": "success-expected-unreachable",
+        "test": VerifyReachability,
+        "eos_data": [
+            {
+                "messages": [
+                    """PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
+
+                --- 10.0.0.1 ping statistics ---
+                2 packets transmitted, 0 received, 100% packet loss, time 10ms
+                """,
+                ],
+            },
+        ],
+        "inputs": {"hosts": [{"destination": "10.0.0.1", "source": "10.0.0.5", "reachable": False}]},
+        "expected": {"result": "success"},
+    },
     {
         "name": "success-ipv6",
         "test": VerifyReachability,
@@ -268,6 +285,30 @@ DATA: list[dict[str, Any]] = [
         ],
         "expected": {"result": "failure", "messages": ["Host: 10.0.0.1 Source: Management0 VRF: default - Unreachable"]},
     },
+    {
+        "name": "failure-expected-unreachable",
+        "test": VerifyReachability,
+        "eos_data": [
+            {
+                "messages": [
+                    """PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
+                80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms
+                80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms
+
+                --- 10.0.0.1 ping statistics ---
+                2 packets transmitted, 2 received, 0% packet loss, time 0ms
+                rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
+
+                """,
+                ],
+            },
+        ],
+        "inputs": {"hosts": [{"destination": "10.0.0.1", "source": "10.0.0.5", "reachable": False}]},
+        "expected": {
+            "result": "failure",
+            "messages": ["Host: 10.0.0.1 Source: 10.0.0.5 VRF: default - Destination is expected to be unreachable but found reachable."],
+        },
+    },
     {
         "name": "success",
         "test": VerifyLLDPNeighbors,
diff --git a/tests/units/anta_tests/test_stp.py b/tests/units/anta_tests/test_stp.py
index 5de5df46..3dbd8c5d 100644
--- a/tests/units/anta_tests/test_stp.py
+++ b/tests/units/anta_tests/test_stp.py
@@ -37,7 +37,7 @@ DATA: list[dict[str, Any]] = [
             {"spanningTreeVlanInstances": {}},
         ],
         "inputs": {"mode": "rstp", "vlans": [10, 20]},
-        "expected": {"result": "failure", "messages": ["STP mode 'rstp' not configured for the following VLAN(s): [10, 20]"]},
+        "expected": {"result": "failure", "messages": ["VLAN 10 STP mode: rstp - Not configured", "VLAN 20 STP mode: rstp - Not configured"]},
     },
     {
         "name": "failure-wrong-mode",
@@ -47,7 +47,10 @@ DATA: list[dict[str, Any]] = [
             {"spanningTreeVlanInstances": {"20": {"spanningTreeVlanInstance": {"protocol": "mstp"}}}},
         ],
         "inputs": {"mode": "rstp", "vlans": [10, 20]},
-        "expected": {"result": "failure", "messages": ["Wrong STP mode configured for the following VLAN(s): [10, 20]"]},
+        "expected": {
+            "result": "failure",
+            "messages": ["VLAN 10 - Incorrect STP mode - Expected: rstp Actual: mstp", "VLAN 20 - Incorrect STP mode - Expected: rstp Actual: mstp"],
+        },
     },
     {
         "name": "failure-both",
@@ -59,7 +62,7 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"mode": "rstp", "vlans": [10, 20]},
         "expected": {
             "result": "failure",
-            "messages": ["STP mode 'rstp' not configured for the following VLAN(s): [10]", "Wrong STP mode configured for the following VLAN(s): [20]"],
+            "messages": ["VLAN 10 STP mode: rstp - Not configured", "VLAN 20 - Incorrect STP mode - Expected: rstp Actual: mstp"],
         },
     },
     {
@@ -74,7 +77,7 @@ DATA: list[dict[str, Any]] = [
         "test": VerifySTPBlockedPorts,
         "eos_data": [{"spanningTreeInstances": {"MST0": {"spanningTreeBlockedPorts": ["Ethernet10"]}, "MST10": {"spanningTreeBlockedPorts": ["Ethernet10"]}}}],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["The following ports are blocked by STP: {'MST0': ['Ethernet10'], 'MST10': ['Ethernet10']}"]},
+        "expected": {"result": "failure", "messages": ["STP Instance: MST0 - Blocked ports - Ethernet10", "STP Instance: MST10 - Blocked ports - Ethernet10"]},
     },
     {
         "name": "success",
@@ -84,18 +87,44 @@ DATA: list[dict[str, Any]] = [
         "expected": {"result": "success"},
     },
     {
-        "name": "failure",
+        "name": "failure-bpdu-tagged-error-mismatch",
         "test": VerifySTPCounters,
         "eos_data": [
             {
                 "interfaces": {
                     "Ethernet10": {"bpduSent": 201, "bpduReceived": 0, "bpduTaggedError": 3, "bpduOtherError": 0, "bpduRateLimitCount": 0},
+                    "Ethernet11": {"bpduSent": 99, "bpduReceived": 0, "bpduTaggedError": 3, "bpduOtherError": 0, "bpduRateLimitCount": 0},
+                },
+            },
+        ],
+        "inputs": None,
+        "expected": {
+            "result": "failure",
+            "messages": [
+                "Interface Ethernet10 - STP BPDU packet tagged errors count mismatch - Expected: 0 Actual: 3",
+                "Interface Ethernet11 - STP BPDU packet tagged errors count mismatch - Expected: 0 Actual: 3",
+            ],
+        },
+    },
+    {
+        "name": "failure-bpdu-other-error-mismatch",
+        "test": VerifySTPCounters,
+        "eos_data": [
+            {
+                "interfaces": {
+                    "Ethernet10": {"bpduSent": 201, "bpduReceived": 0, "bpduTaggedError": 0, "bpduOtherError": 3, "bpduRateLimitCount": 0},
                     "Ethernet11": {"bpduSent": 99, "bpduReceived": 0, "bpduTaggedError": 0, "bpduOtherError": 6, "bpduRateLimitCount": 0},
                 },
             },
         ],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["The following interfaces have STP BPDU packet errors: ['Ethernet10', 'Ethernet11']"]},
+        "expected": {
+            "result": "failure",
+            "messages": [
+                "Interface Ethernet10 - STP BPDU packet other errors count mismatch - Expected: 0 Actual: 3",
+                "Interface Ethernet11 - STP BPDU packet other errors count mismatch - Expected: 0 Actual: 6",
+            ],
+        },
     },
     {
         "name": "success",
@@ -134,7 +163,7 @@ DATA: list[dict[str, Any]] = [
         "test": VerifySTPForwardingPorts,
         "eos_data": [{"unmappedVlans": [], "topologies": {}}, {"unmappedVlans": [], "topologies": {}}],
         "inputs": {"vlans": [10, 20]},
-        "expected": {"result": "failure", "messages": ["STP instance is not configured for the following VLAN(s): [10, 20]"]},
+        "expected": {"result": "failure", "messages": ["VLAN 10 - STP instance is not configured", "VLAN 20 - STP instance is not configured"]},
     },
     {
         "name": "failure",
@@ -152,7 +181,10 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"vlans": [10, 20]},
         "expected": {
             "result": "failure",
-            "messages": ["The following VLAN(s) have interface(s) that are not in a forwarding state: [{'VLAN 10': ['Ethernet10']}, {'VLAN 20': ['Ethernet10']}]"],
+            "messages": [
+                "VLAN 10 Interface: Ethernet10 - Invalid state - Expected: forwarding Actual: discarding",
+                "VLAN 20 Interface: Ethernet10 - Invalid state - Expected: forwarding Actual: discarding",
+            ],
         },
     },
     {
@@ -261,6 +293,28 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"priority": 16384, "instances": [0]},
         "expected": {"result": "success"},
     },
+    {
+        "name": "success-input-instance-none",
+        "test": VerifySTPRootPriority,
+        "eos_data": [
+            {
+                "instances": {
+                    "MST0": {
+                        "rootBridge": {
+                            "priority": 16384,
+                            "systemIdExtension": 0,
+                            "macAddress": "02:1c:73:8b:93:ac",
+                            "helloTime": 2.0,
+                            "maxAge": 20,
+                            "forwardDelay": 15,
+                        },
+                    },
+                },
+            },
+        ],
+        "inputs": {"priority": 16384},
+        "expected": {"result": "success"},
+    },
     {
         "name": "failure-no-instances",
         "test": VerifySTPRootPriority,
@@ -281,7 +335,7 @@ DATA: list[dict[str, Any]] = [
             },
         ],
         "inputs": {"priority": 32768, "instances": [0]},
-        "expected": {"result": "failure", "messages": ["Unsupported STP instance type: WRONG0"]},
+        "expected": {"result": "failure", "messages": ["STP Instance: WRONG0 - Unsupported STP instance type"]},
     },
     {
         "name": "failure-wrong-instance-type",
@@ -290,6 +344,28 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"priority": 32768, "instances": [10, 20]},
         "expected": {"result": "failure", "messages": ["No STP instances configured"]},
     },
+    {
+        "name": "failure-instance-not-found",
+        "test": VerifySTPRootPriority,
+        "eos_data": [
+            {
+                "instances": {
+                    "VL10": {
+                        "rootBridge": {
+                            "priority": 32768,
+                            "systemIdExtension": 10,
+                            "macAddress": "00:1c:73:27:95:a2",
+                            "helloTime": 2.0,
+                            "maxAge": 20,
+                            "forwardDelay": 15,
+                        },
+                    }
+                }
+            }
+        ],
+        "inputs": {"priority": 32768, "instances": [11, 20]},
+        "expected": {"result": "failure", "messages": ["Instance: VL11 - Not configured", "Instance: VL20 - Not configured"]},
+    },
     {
         "name": "failure-wrong-priority",
         "test": VerifySTPRootPriority,
@@ -330,7 +406,13 @@ DATA: list[dict[str, Any]] = [
             },
         ],
         "inputs": {"priority": 32768, "instances": [10, 20, 30]},
-        "expected": {"result": "failure", "messages": ["The following instance(s) have the wrong STP root priority configured: ['VL20', 'VL30']"]},
+        "expected": {
+            "result": "failure",
+            "messages": [
+                "STP Instance: VL20 - Incorrect root priority - Expected: 32768 Actual: 8196",
+                "STP Instance: VL30 - Incorrect root priority - Expected: 32768 Actual: 8196",
+            ],
+        },
     },
     {
         "name": "success-mstp",
@@ -470,8 +552,8 @@ DATA: list[dict[str, Any]] = [
         "expected": {
             "result": "failure",
             "messages": [
-                "The following STP topologies are not configured or number of changes not within the threshold:\n"
-                "{'topologies': {'Cist': {'Cpu': {'Number of changes': 15}, 'Port-Channel5': {'Number of changes': 15}}}}"
+                "Topology: Cist Interface: Cpu - Number of changes not within the threshold - Expected: 10 Actual: 15",
+                "Topology: Cist Interface: Port-Channel5 - Number of changes not within the threshold - Expected: 10 Actual: 15",
             ],
         },
     },
diff --git a/tests/units/anta_tests/test_system.py b/tests/units/anta_tests/test_system.py
index 858b793d..5fe0fbad 100644
--- a/tests/units/anta_tests/test_system.py
+++ b/tests/units/anta_tests/test_system.py
@@ -33,7 +33,7 @@ DATA: list[dict[str, Any]] = [
         "test": VerifyUptime,
         "eos_data": [{"upTime": 665.15, "loadAvg": [0.13, 0.12, 0.09], "users": 1, "currentTime": 1683186659.139859}],
         "inputs": {"minimum": 666},
-        "expected": {"result": "failure", "messages": ["Device uptime is 665.15 seconds"]},
+        "expected": {"result": "failure", "messages": ["Device uptime is incorrect - Expected: 666 Actual: 665.15 seconds"]},
     },
     {
         "name": "success-no-reload",
@@ -74,7 +74,7 @@ DATA: list[dict[str, Any]] = [
             },
         ],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["Reload cause is: 'Reload after crash.'"]},
+        "expected": {"result": "failure", "messages": ["Reload cause is: Reload after crash."]},
     },
     {
         "name": "success-without-minidump",
@@ -95,14 +95,14 @@ DATA: list[dict[str, Any]] = [
         "test": VerifyCoredump,
         "eos_data": [{"mode": "compressedDeferred", "coreFiles": ["core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["Core dump(s) have been found: ['core.2344.1584483862.Mlag.gz', 'core.23101.1584483867.Mlag.gz']"]},
+        "expected": {"result": "failure", "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
     },
     {
         "name": "failure-with-minidump",
         "test": VerifyCoredump,
         "eos_data": [{"mode": "compressedDeferred", "coreFiles": ["minidump", "core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["Core dump(s) have been found: ['core.2344.1584483862.Mlag.gz', 'core.23101.1584483867.Mlag.gz']"]},
+        "expected": {"result": "failure", "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
     },
     {
         "name": "success",
@@ -190,7 +190,7 @@ EntityManager::doBackoff waiting for remote sysdb version ...................ok
             },
         ],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["Device has reported a high CPU utilization: 75.2%"]},
+        "expected": {"result": "failure", "messages": ["Device has reported a high CPU utilization -  Expected: < 75% Actual: 75.2%"]},
     },
     {
         "name": "success",
@@ -222,7 +222,7 @@ EntityManager::doBackoff waiting for remote sysdb version ...................ok
             },
         ],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["Device has reported a high memory usage: 95.56%"]},
+        "expected": {"result": "failure", "messages": ["Device has reported a high memory usage - Expected: < 75% Actual: 95.56%"]},
     },
     {
         "name": "success",
@@ -253,8 +253,8 @@ none            294M   78M  217M  84% /.overlay
         "expected": {
             "result": "failure",
             "messages": [
-                "Mount point /dev/sda2       3.9G  988M  2.9G  84% /mnt/flash is higher than 75%: reported 84%",
-                "Mount point none            294M   78M  217M  84% /.overlay is higher than 75%: reported 84%",
+                "Mount point: /dev/sda2       3.9G  988M  2.9G  84% /mnt/flash - Higher disk space utilization - Expected: 75% Actual: 84%",
+                "Mount point: none            294M   78M  217M  84% /.overlay - Higher disk space utilization - Expected: 75% Actual: 84%",
             ],
         },
     },
@@ -278,7 +278,7 @@ poll interval unknown
 """,
         ],
         "inputs": None,
-        "expected": {"result": "failure", "messages": ["The device is not synchronized with the configured NTP server(s): 'unsynchronised'"]},
+        "expected": {"result": "failure", "messages": ["NTP status mismatch - Expected: synchronised Actual: unsynchronised"]},
     },
     {
         "name": "success",
@@ -413,9 +413,9 @@ poll interval unknown
         "expected": {
             "result": "failure",
             "messages": [
-                "1.1.1.1 (Preferred: True, Stratum: 1) - Bad association - Condition: candidate, Stratum: 2",
-                "2.2.2.2 (Preferred: False, Stratum: 2) - Bad association - Condition: sys.peer, Stratum: 2",
-                "3.3.3.3 (Preferred: False, Stratum: 2) - Bad association - Condition: sys.peer, Stratum: 3",
+                "NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Bad association - Condition: candidate, Stratum: 2",
+                "NTP Server: 2.2.2.2 Preferred: False Stratum: 2 - Bad association - Condition: sys.peer, Stratum: 2",
+                "NTP Server: 3.3.3.3 Preferred: False Stratum: 2 - Bad association - Condition: sys.peer, Stratum: 3",
             ],
         },
     },
@@ -463,7 +463,7 @@ poll interval unknown
         },
         "expected": {
             "result": "failure",
-            "messages": ["3.3.3.3 (Preferred: False, Stratum: 1) - Not configured"],
+            "messages": ["NTP Server: 3.3.3.3 Preferred: False Stratum: 1 - Not configured"],
         },
     },
     {
@@ -490,9 +490,9 @@ poll interval unknown
         "expected": {
             "result": "failure",
             "messages": [
-                "1.1.1.1 (Preferred: True, Stratum: 1) - Bad association - Condition: candidate, Stratum: 1",
-                "2.2.2.2 (Preferred: False, Stratum: 1) - Not configured",
-                "3.3.3.3 (Preferred: False, Stratum: 1) - Not configured",
+                "NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Bad association - Condition: candidate, Stratum: 1",
+                "NTP Server: 2.2.2.2 Preferred: False Stratum: 1 - Not configured",
+                "NTP Server: 3.3.3.3 Preferred: False Stratum: 1 - Not configured",
             ],
         },
     },