Repository: aristanetworks/anta
Pull request #955 (2024-12-09 06:15:07)

fix(anta.tests): Cleaning up Services tests module (VerifyErrdisableRecovery) 
# Description

Refactoring Services tests module to address the following issues:

- https://github.com/aristanetworks/anta/issues/809
- https://github.com/aristanetworks/anta/issues/587
- https://github.com/aristanetworks/anta/issues/388

# 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`)
- [X] 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`)


sonarcloud[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=955) **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=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/aristanetworks/anta/branches/vitthalmagadum%3Afeat%2Fservices)

### Merging #955 will **not alter performance**

<sub>Comparing <code>vitthalmagadum:feat/services</code> (877cd17) with <code>main</code> (0c32b83)</sub>



### Summary

`✅ 8` untouched benchmarks  




sonarcloud[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=955) **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=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **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=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **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=955&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=955&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=955&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [93.2% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **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=955&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=955&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=955&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [93.2% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **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=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)


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=955) **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=955&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=955&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=955&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=955&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=955&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=955)



Patch:
diff --git a/anta/custom_types.py b/anta/custom_types.py
index f3877459..3af27db9 100644
--- a/anta/custom_types.py
+++ b/anta/custom_types.py
@@ -154,16 +154,38 @@ BfdMultiplier = Annotated[int, Field(ge=3, le=50)]
 ErrDisableReasons = Literal[
     "acl",
     "arp-inspection",
+    "bgp-session-tracking",
     "bpduguard",
+    "dot1x",
+    "dot1x-coa",
     "dot1x-session-replace",
+    "evpn-sa-mh",
+    "fabric-link-failure",
+    "fabric-link-flap",
     "hitless-reload-down",
+    "lacp-no-portid",
     "lacp-rate-limit",
+    "license-enforce",
     "link-flap",
+    "mlagasu",
+    "mlagdualprimary",
+    "mlagissu",
+    "mlagmaintdown",
     "no-internal-vlan",
+    "out-of-voqs",
     "portchannelguard",
+    "portgroup-disabled",
     "portsec",
+    "speed-misconfigured",
+    "storm-control",
+    "stp-no-portid",
+    "stuck-queue",
     "tapagg",
     "uplink-failure-detection",
+    "xcvr-misconfigured",
+    "xcvr-overheat",
+    "xcvr-power-unsupported",
+    "xcvr-unsupported",
 ]
 ErrDisableInterval = Annotated[int, Field(ge=30, le=86400)]
 Percent = Annotated[float, Field(ge=0.0, le=100.0)]
diff --git a/anta/input_models/services.py b/anta/input_models/services.py
index 9989dae1..25d772e4 100644
--- a/anta/input_models/services.py
+++ b/anta/input_models/services.py
@@ -6,9 +6,13 @@
 from __future__ import annotations
 
 from ipaddress import IPv4Address, IPv6Address
+from typing import Any, Literal
+from warnings import warn
 
 from pydantic import BaseModel, ConfigDict, Field
 
+from anta.custom_types import ErrDisableReasons
+
 
 class DnsServer(BaseModel):
     """Model for a DNS server configuration."""
@@ -29,3 +33,42 @@ class DnsServer(BaseModel):
         Server 10.0.0.1 (VRF: default, Priority: 1)
         """
         return f"Server {self.server_address} (VRF: {self.vrf}, Priority: {self.priority})"
+
+
+class ErrdisableRecovery(BaseModel):
+    """Model for the error disable recovery functionality."""
+
+    model_config = ConfigDict(extra="forbid")
+    reason: ErrDisableReasons
+    """Name of the error disable reason."""
+    status: Literal["Enabled", "Disabled"] = "Enabled"
+    """Operational status of the reason. Defaults to 'Enabled'."""
+    interval: int = Field(ge=30, le=86400)
+    """Timer interval of the reason in seconds."""
+
+    def __str__(self) -> str:
+        """Return a human-readable string representation of the ErrdisableRecovery for reporting.
+
+        Examples
+        --------
+        Reason: acl Status: Enabled Interval: 300
+        """
+        return f"Reason: {self.reason} Status: {self.status} Interval: {self.interval}"
+
+
+class ErrDisableReason(ErrdisableRecovery):  # pragma: no cover
+    """Alias for the ErrdisableRecovery model to maintain backward compatibility.
+
+    When initialised, it will emit a deprecation warning and call the ErrdisableRecovery model.
+
+    TODO: Remove this class in ANTA v2.0.0.
+    """
+
+    def __init__(self, **data: Any) -> None:  # noqa: ANN401
+        """Initialize the ErrdisableRecovery class, emitting a depreciation warning."""
+        warn(
+            message="ErrDisableReason model is deprecated and will be removed in ANTA v2.0.0. Use the ErrdisableRecovery model instead.",
+            category=DeprecationWarning,
+            stacklevel=2,
+        )
+        super().__init__(**data)


Test patch:
diff --git a/anta/tests/services.py b/anta/tests/services.py
index 89a7f263..8d894216 100644
--- a/anta/tests/services.py
+++ b/anta/tests/services.py
@@ -9,12 +9,9 @@ from __future__ import annotations
 # mypy: disable-error-code=attr-defined
 from typing import ClassVar
 
-from pydantic import BaseModel
-
-from anta.custom_types import ErrDisableInterval, ErrDisableReasons
-from anta.input_models.services import DnsServer
+from anta.input_models.services import DnsServer, ErrDisableReason, ErrdisableRecovery
 from anta.models import AntaCommand, AntaTemplate, AntaTest
-from anta.tools import get_dict_superset, get_failed_logs
+from anta.tools import get_dict_superset, get_item
 
 
 class VerifyHostname(AntaTest):
@@ -166,12 +163,24 @@ class VerifyDNSServers(AntaTest):
 
 
 class VerifyErrdisableRecovery(AntaTest):
-    """Verifies the errdisable recovery reason, status, and interval.
+    """Verifies the error disable recovery functionality.
+
+    This test performs the following checks for each specified error disable reason:
+
+      1. Verifying if the specified error disable reason exists.
+      2. Checking if the recovery timer status matches the expected enabled/disabled state.
+      3. Validating that the timer interval matches the configured value.
 
     Expected Results
     ----------------
-    * Success: The test will pass if the errdisable recovery reason status is enabled and the interval matches the input.
-    * Failure: The test will fail if the errdisable recovery reason is not found, the status is not enabled, or the interval does not match the input.
+    * Success: The test will pass if:
+        - The specified error disable reason exists.
+        - The recovery timer status matches the expected state.
+        - The timer interval matches the configured value.
+    * Failure: The test will fail if:
+        - The specified error disable reason does not exist.
+        - The recovery timer status does not match the expected state.
+        - The timer interval does not match the configured value.
 
     Examples
     --------
@@ -181,8 +190,10 @@ class VerifyErrdisableRecovery(AntaTest):
           reasons:
             - reason: acl
               interval: 30
+              status: Enabled
             - reason: bpduguard
               interval: 30
+              status: Enabled
     ```
     """
 
@@ -193,44 +204,35 @@ class VerifyErrdisableRecovery(AntaTest):
     class Input(AntaTest.Input):
         """Input model for the VerifyErrdisableRecovery test."""
 
-        reasons: list[ErrDisableReason]
+        reasons: list[ErrdisableRecovery]
         """List of errdisable reasons."""
-
-        class ErrDisableReason(BaseModel):
-            """Model for an errdisable reason."""
-
-            reason: ErrDisableReasons
-            """Type or name of the errdisable reason."""
-            interval: ErrDisableInterval
-            """Interval of the reason in seconds."""
+        ErrDisableReason: ClassVar[type[ErrdisableRecovery]] = ErrDisableReason
 
     @AntaTest.anta_test
     def test(self) -> None:
         """Main test function for VerifyErrdisableRecovery."""
-        command_output = self.instance_commands[0].text_output
         self.result.is_success()
+
+        # Skip header and last empty line
+        command_output = self.instance_commands[0].text_output.split("\n")[2:-1]
+
+        # Collecting the actual errdisable reasons for faster lookup
+        errdisable_reasons = [
+            {"reason": reason, "status": status, "interval": interval}
+            for line in command_output
+            if line.strip()  # Skip empty lines
+            for reason, status, interval in [line.split(None, 2)]  # Unpack split result
+        ]
+
         for error_reason in self.inputs.reasons:
-            input_reason = error_reason.reason
-            input_interval = error_reason.interval
-            reason_found = False
-
-            # Skip header and last empty line
-            lines = command_output.split("\n")[2:-1]
-            for line in lines:
-                # Skip empty lines
-                if not line.strip():
-                    continue
-                # Split by first two whitespaces
-                reason, status, interval = line.split(None, 2)
-                if reason != input_reason:
-                    continue
-                reason_found = True
-                actual_reason_data = {"interval": interval, "status": status}
-                expected_reason_data = {"interval": str(input_interval), "status": "Enabled"}
-                if actual_reason_data != expected_reason_data:
-                    failed_log = get_failed_logs(expected_reason_data, actual_reason_data)
-                    self.result.is_failure(f"`{input_reason}`:{failed_log}\n")
-                break
-
-            if not reason_found:
-                self.result.is_failure(f"`{input_reason}`: Not found.\n")
+            if not (reason_output := get_item(errdisable_reasons, "reason", error_reason.reason)):
+                self.result.is_failure(f"{error_reason} - Not found")
+                continue
+
+            if not all(
+                [
+                    error_reason.status == (act_status := reason_output["status"]),
+                    error_reason.interval == (act_interval := int(reason_output["interval"])),
+                ]
+            ):
+                self.result.is_failure(f"{error_reason} - Incorrect configuration - Status: {act_status} Interval: {act_interval}")
diff --git a/docs/api/tests.services.md b/docs/api/tests.services.md
index da8e1736..674f5c08 100644
--- a/docs/api/tests.services.md
+++ b/docs/api/tests.services.md
@@ -33,4 +33,6 @@ anta_title: ANTA catalog for services tests
       merge_init_into_class: false
       anta_hide_test_module_description: true
       show_labels: true
-      filters: ["!^__str__"]
+      filters:
+        - "!^__init__"
+        - "!^__str__"
diff --git a/examples/tests.yaml b/examples/tests.yaml
index 1efe0d33..ce3b851a 100644
--- a/examples/tests.yaml
+++ b/examples/tests.yaml
@@ -731,12 +731,14 @@ anta.tests.services:
           vrf: MGMT
           priority: 0
   - VerifyErrdisableRecovery:
-      # Verifies the errdisable recovery reason, status, and interval.
+      # Verifies the error disable recovery functionality.
       reasons:
         - reason: acl
           interval: 30
+          status: Enabled
         - reason: bpduguard
           interval: 30
+          status: Enabled
   - VerifyHostname:
       # Verifies the hostname of a device.
       hostname: s1-spine1
diff --git a/tests/units/anta_tests/test_services.py b/tests/units/anta_tests/test_services.py
index 439b8ea4..955aab0f 100644
--- a/tests/units/anta_tests/test_services.py
+++ b/tests/units/anta_tests/test_services.py
@@ -147,7 +147,7 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"reasons": [{"reason": "acl", "interval": 300}, {"reason": "arp-inspection", "interval": 30}, {"reason": "tapagg", "interval": 30}]},
         "expected": {
             "result": "failure",
-            "messages": ["`tapagg`: Not found."],
+            "messages": ["Reason: tapagg Status: Enabled Interval: 30 - Not found"],
         },
     },
     {
@@ -165,7 +165,7 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"reasons": [{"reason": "acl", "interval": 300}, {"reason": "arp-inspection", "interval": 30}]},
         "expected": {
             "result": "failure",
-            "messages": ["`acl`:\nExpected `Enabled` as the status, but found `Disabled` instead."],
+            "messages": ["Reason: acl Status: Enabled Interval: 300 - Incorrect configuration - Status: Disabled Interval: 300"],
         },
     },
     {
@@ -183,7 +183,9 @@ DATA: list[dict[str, Any]] = [
         "inputs": {"reasons": [{"reason": "acl", "interval": 30}, {"reason": "arp-inspection", "interval": 30}]},
         "expected": {
             "result": "failure",
-            "messages": ["`acl`:\nExpected `30` as the interval, but found `300` instead."],
+            "messages": [
+                "Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Enabled Interval: 300",
+            ],
         },
     },
     {
@@ -202,9 +204,9 @@ DATA: list[dict[str, Any]] = [
         "expected": {
             "result": "failure",
             "messages": [
-                "`acl`:\nExpected `30` as the interval, but found `300` instead.\nExpected `Enabled` as the status, but found `Disabled` instead.",
-                "`arp-inspection`:\nExpected `300` as the interval, but found `30` instead.",
-                "`tapagg`: Not found.",
+                "Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300",
+                "Reason: arp-inspection Status: Enabled Interval: 300 - Incorrect configuration - Status: Enabled Interval: 30",
+                "Reason: tapagg Status: Enabled Interval: 30 - Not found",
             ],
         },
     },