Repository: arista-eosplus/ztpserver (language: python)

Issue:
test/client/client_test_lib.py uses deprecated asyncore and smtpd
asyncore and smtpd are marked as deprecated and will be removed in python 3.12


Discussion / hints:

Fix patch:
diff --git a/requirements.txt b/requirements.txt
index 5dc2764..724e27c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,3 +5,4 @@ requests
 sleekxmpp    # dev only
 mock         # dev only
 jsonrpclib   # dev only
+aiosmtpd     # dev only


Test patch:
diff --git a/test/actions/test_send_email.py b/test/actions/test_send_email.py
index 4f68532..8833f51 100644
--- a/test/actions/test_send_email.py
+++ b/test/actions/test_send_email.py
@@ -36,6 +36,7 @@
     random_string,
     startup_config_action,
 )
+from test.client.smtp_server import SmtpServer
 
 
 class FailureTest(ActionFailureTest):
@@ -56,18 +57,23 @@ def test_missing_receivers(self):
 
 
 class SuccessTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.smtp_server = SmtpServer()
+        self.smtp_server.start()
+
+    def tearDown(self) -> None:
+        self.smtp_server.stop()
+
     def test_success(self):
         bootstrap = Bootstrap(ztps_default_config=True)
 
-        smarthost = f"{str(bootstrap.server).split(':', maxsplit=1)[0]}:2525"
-
         bootstrap.ztps.set_definition_response(
             actions=[
                 {"action": "startup_config_action"},
                 {
                     "action": "test_action",
                     "attributes": {
-                        "smarthost": smarthost,
+                        "smarthost": f"{self.smtp_server.hostname}:{self.smtp_server.port}",
                         "sender": "ztps@localhost",
                         "receivers": ["ztps@localhost"],
                     },
diff --git a/test/client/client_test_lib.py b/test/client/client_test_lib.py
index 9935970..9b38fd8 100755
--- a/test/client/client_test_lib.py
+++ b/test/client/client_test_lib.py
@@ -28,13 +28,11 @@
 # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import _thread
-import asyncore  # pylint: disable=W4901
 import json
 import os
 import random
 import re
 import shutil
-import smtpd  # pylint: disable=W4901
 import string
 import subprocess
 import time
@@ -58,9 +56,6 @@
 EAPI_SERVER = "127.0.0.1"
 EAPI_PORT = 1080
 
-SMTP_SERVER = "127.0.0.1"
-SMTP_PORT = 2525
-
 BOOTSTRAP_FILE = "client/bootstrap"
 
 CLI_LOG = "/tmp/FastCli-log"
@@ -99,17 +94,6 @@ def start_ztp_server():
     return ztps
 
 
-smtp = None  # pylint: disable=C0103
-
-
-def start_smtp_server():
-    global smtp  # pylint: disable=W0603
-    if not smtp:
-        smtp = SmtpServer()
-        smtp.start()
-    return smtp
-
-
 eapis = None  # pylint: disable=C0103
 
 
@@ -300,7 +284,6 @@ def __init__(self, server=None, eapi_port=None, ztps_default_config=False):
 
         self.eapi = start_eapi_server()
         self.ztps = start_ztp_server()
-        self.smtp = start_smtp_server()
 
         self.flash = f"/tmp/ztps-flash-{os.getpid()}"
         self.temp = f"/tmp/ztps-tmp-{os.getpid()}"
@@ -641,33 +624,6 @@ def do_POST(req):
             print(time.asctime(), f"ZTPS: Server stops - {ZTPS_SERVER}:{ZTPS_PORT}")
 
 
-class SmtpServer:
-    # pylint: disable=E0211
-
-    def start(self):
-        _thread.start_new_thread(self._run, ())
-
-    @classmethod
-    def _run(cls):
-        class SMTPServer(smtpd.SMTPServer):
-            def __init__(self, *args, **kwargs):
-                print("SMTP: Running smtp server on port 2525")
-                super().__init__(*args, **kwargs)
-
-            def process_message(self, *args, **kwargs):
-                pass
-
-        smtp_server = SMTPServer(localaddr=(SMTP_SERVER, SMTP_PORT), remoteaddr=None)
-        print(time.asctime(), f"SMTP: Server starts - {SMTP_SERVER}:{SMTP_PORT}")
-        try:
-            asyncore.loop()
-        except KeyboardInterrupt:
-            pass
-        finally:
-            smtp_server.close()
-            print(time.asctime(), f"SMTPS: Server stops - {SMTP_SERVER}:{SMTP_PORT}")
-
-
 class ActionFailureTest(unittest.TestCase):
     # pylint: disable=R0904
 
diff --git a/test/client/smtp_server.py b/test/client/smtp_server.py
new file mode 100644
index 0000000..fa0fdf0
--- /dev/null
+++ b/test/client/smtp_server.py
@@ -0,0 +1,27 @@
+import time
+
+from aiosmtpd.controller import Controller
+
+SMTP_SERVER = "127.0.0.1"
+SMTP_PORT = 2525
+
+
+class CustomHandler:
+    async def handle_DATA(self, server, session, envelope):  # pylint: disable=C0103
+        del server, session, envelope
+        return "250 OK"
+
+
+class SmtpServer:
+    def __init__(self, hostname=SMTP_SERVER, port=SMTP_PORT):
+        self.hostname = hostname
+        self.port = port
+        self.smtpd = Controller(CustomHandler(), hostname=hostname, port=port)
+
+    def start(self):
+        print(time.asctime(), f"SMTP: Server starts - {self.smtpd.hostname}:{self.smtpd.port}")
+        self.smtpd.start()
+
+    def stop(self):
+        print(time.asctime(), f"SMTPS: Server stops - {self.smtpd.hostname}:{self.smtpd.port}")
+        self.smtpd.stop()