Repository: mindsdb/mindsdb

Issue:
Run tests on windows and OSX
Part of the reason we wanted tests with remote databases was to be able to test mindsdb on windows and OSX.

This is currently being done for native but not for mindsdb,  current issues that stop us from testing on OSX and windows:

* `psutil.net_connections` requires root privileges on osx/windows
* `ssh` command will fail on windows
* ??? 


Discussion:

Resolving patch:
diff --git a/mindsdb/utilities/ps.py b/mindsdb/utilities/ps.py
--- a/mindsdb/utilities/ps.py
+++ b/mindsdb/utilities/ps.py
@@ -1,11 +1,44 @@
-import psutil
+import sys
 import time
+from collections import namedtuple
+import psutil
+
+
+def net_connections():
+    """Cross-platform psutil.net_connections like interface"""
+    if sys.platform.lower().startswith('linux'):
+        return psutil.net_connections()
+
+    all_connections = []
+    Pconn = None
+    for p in psutil.process_iter(['pid']):
+        try:
+            process = psutil.Process(p.pid)
+            connections = process.connections()
+            if connections:
+                for conn in connections:
+                    # Adding pid to the returned instance
+                    # for consistency with psutil.net_connections()
+                    if Pconn is None:
+                        fields = list(conn._fields)
+                        fields.append('pid')
+                        _conn = namedtuple('Pconn', fields)
+                    for attr in conn._fields:
+                        setattr(_conn, attr, getattr(conn, attr))
+                    _conn.pid = p.pid
+                    all_connections.append(_conn)
+
+        except (psutil.AccessDenied, psutil.ZombieProcess, psutil.NoSuchProcess):
+            pass
+    return all_connections
 
 
 def is_port_in_use(port_num):
-    portsinuse = []
-    conns = psutil.net_connections()
-    portsinuse = [x.laddr[1] for x in conns if x.status == 'LISTEN']
+    """Check does any of child process uses specified port."""
+    parent_process = psutil.Process()
+    child_pids = [x.pid for x in parent_process.children(recursive=True)]
+    conns = net_connections()
+    portsinuse = [x.laddr[1] for x in conns if x.pid in child_pids and x.status == 'LISTEN']
     portsinuse.sort()
     return int(port_num) in portsinuse
 


Test patch:
diff --git a/tests/integration_tests/api/test_http.py b/tests/integration_tests/api/test_http.py
--- a/tests/integration_tests/api/test_http.py
+++ b/tests/integration_tests/api/test_http.py
@@ -7,9 +7,9 @@
 from uuid import uuid1
 
 import requests
-import psutil
 
 from mindsdb.utilities.config import Config
+from mindsdb.utilities.ps import net_connections
 
 common_path = Path(__file__).parent.parent.absolute().joinpath('flows/common.py').resolve()
 spec = importlib.util.spec_from_file_location("common", str(common_path))
@@ -46,11 +46,10 @@ def setUpClass(cls):
     @classmethod
     def tearDownClass(cls):
         try:
-            conns = psutil.net_connections()
+            conns = net_connections()
             pid = [x.pid for x in conns if x.status == 'LISTEN' and x.laddr[1] == 47334 and x.pid is not None]
             if len(pid) > 0:
                 os.kill(pid[0], 9)
-            cls.sp.kill()
         except Exception:
             pass
 
diff --git a/tests/integration_tests/flows/common.py b/tests/integration_tests/flows/common.py
--- a/tests/integration_tests/flows/common.py
+++ b/tests/integration_tests/flows/common.py
@@ -1,21 +1,21 @@
 import time
-from pathlib import Path
+import os
 import json
-import requests
 import subprocess
 import atexit
-import os
 import asyncio
 import shutil
 import csv
+from pathlib import Path
 
+import requests
 from pandas import DataFrame
 
 from mindsdb.utilities.fs import create_dirs_recursive
 from mindsdb.utilities.config import Config
 from mindsdb.interfaces.native.mindsdb import MindsdbNative
 from mindsdb.interfaces.datastore.datastore import DataStore
-from mindsdb.utilities.ps import wait_port, is_port_in_use
+from mindsdb.utilities.ps import wait_port, is_port_in_use, net_connections
 from mindsdb_native import CONFIG
 
 
@@ -187,12 +187,17 @@ def make_test_csv(name, data):
 def stop_mindsdb(sp=None):
     if sp:
         sp.kill()
-    sp = subprocess.Popen('kill -9 $(lsof -t -i:47334)', shell=True)
-    sp.wait()
-    sp = subprocess.Popen('kill -9 $(lsof -t -i:47335)', shell=True)
-    sp.wait()
-    sp = subprocess.Popen('kill -9 $(lsof -t -i:47336)', shell=True)
-    sp.wait()
+    conns = net_connections()
+    pids = [x.pid for x in conns
+            if x.pid is not None and x.status in ['LISTEN', 'CLOSE_WAIT']
+            and x.laddr[1] in (47334, 47335, 47336)]
+
+    for pid in pids:
+        try:
+            os.kill(pid, 9)
+        # process may be killed by OS due to some reasons in that moment
+        except ProcessLookupError:
+            pass
 
 
 def run_environment(config, apis=['mysql'], override_integration_config={}, override_api_config={}, mindsdb_database='mindsdb', clear_storage=True):
diff --git a/tests/integration_tests/flows/test_mistakes.py b/tests/integration_tests/flows/test_mistakes.py
--- a/tests/integration_tests/flows/test_mistakes.py
+++ b/tests/integration_tests/flows/test_mistakes.py
@@ -65,17 +65,6 @@ def test_1_wrong_integration(self):
 
         check_ds_not_exists(TEST_DS)
 
-        # TODO creating DS from unexists integration raise not critical error in code.
-        # need fix it and return human-readable error
-        # related issue: https://github.com/mindsdb/mindsdb/issues/945
-        # data = {
-        #     "integration_id": 'unexists_integration',
-        #     "name": TEST_DS,
-        #     "query": f"select * from test_data.{TEST_DATASET} limit 50;"
-        # }
-        # res = requests.put(f'{HTTP_API_ROOT}/datasources/{TEST_DS}', json=data)
-        # assert res ?
-
         # check create DS with wrong integration password
         data = {
             "integration_id": 'default_mariadb',