Files
tsun-gen3-proxy/app/tests/test_inverter_g3.py
Stefan Allius 6974672ba0 S allius/issue334 (#335)
* move forward_at_cmd_resp into InfosG3P class

- the variable is shared between the two connections
of an inverter. One is for the TSUN cloud and the
other for the device.

* use inverter class to share values between
the two protocol instances of a proxy
- move forward_at_cmd_resp into class InverterG3P
- store inverter ptr in Solarman_V5 instances
- add inverter ptr to all constructurs of protocols
- adapt doku and unit tests-
- add integration tests for AT+ commands which
  check the forwarding from and to the TSUN cloud

* adapt and improve the unit tests
- fix node_id declaration, which always has a / at
  the end. See config grammar for this rule
- set global var test to default after test run
2025-04-05 14:37:52 +02:00

227 lines
6.2 KiB
Python

# test_with_pytest.py
import pytest
import asyncio
import sys,gc
from mock import patch
from enum import Enum
from infos import Infos
from cnf.config import Config
from proxy import Proxy
from inverter_base import InverterBase
from singleton import Singleton
from gen3.inverter_g3 import InverterG3
from async_stream import AsyncStream
from test_modbus_tcp import patch_mqtt_err, patch_mqtt_except, test_port, test_hostname
pytest_plugins = ('pytest_asyncio',)
# initialize the proxy statistics
Infos.static_init()
@pytest.fixture
def config_conn():
Config.act_config = {
'mqtt':{
'host': test_hostname,
'port': test_port,
'user': '',
'passwd': ''
},
'ha':{
'auto_conf_prefix': 'homeassistant',
'discovery_prefix': 'homeassistant',
'entity_prefix': 'tsun',
'proxy_node_id': 'test_1',
'proxy_unique_id': ''
},
'tsun':{'enabled': True, 'host': 'test_cloud.local', 'port': 1234}, 'inverters':{'allow_all':True}
}
@pytest.fixture(scope="module", autouse=True)
def module_init():
Singleton._instances.clear()
yield
class FakeReader():
def __init__(self):
self.on_recv = asyncio.Event()
async def read(self, max_len: int):
await self.on_recv.wait()
return b''
def feed_eof(self):
return
class FakeWriter():
def write(self, buf: bytes):
return
def get_extra_info(self, sel: str):
if sel == 'peername':
return ('47.1.2.3', 10000)
elif sel == 'sockname':
return 'sock:1234'
assert False
def is_closing(self):
return False
def close(self):
return
async def wait_closed(self):
return
class MockType(Enum):
RD_TEST_0_BYTES = 1
RD_TEST_TIMEOUT = 2
RD_TEST_EXCEPT = 3
test = MockType.RD_TEST_0_BYTES
@pytest.fixture
def patch_open_connection():
async def new_conn(conn):
await asyncio.sleep(0)
return FakeReader(), FakeWriter()
def new_open(host: str, port: int):
if test == MockType.RD_TEST_TIMEOUT:
raise ConnectionRefusedError
elif test == MockType.RD_TEST_EXCEPT:
raise ValueError("Value cannot be negative") # Compliant
return new_conn(None)
with patch.object(asyncio, 'open_connection', new_open) as conn:
yield conn
@pytest.fixture
def patch_healthy():
with patch.object(AsyncStream, 'healthy') as conn:
yield conn
def test_method_calls(patch_healthy):
spy = patch_healthy
reader = FakeReader()
writer = FakeWriter()
InverterBase._registry.clear()
with InverterG3(reader, writer) as inverter:
assert inverter.local.stream
assert inverter.local.ifc
for inv in InverterBase:
inv.healthy()
del inv
spy.assert_called_once()
del inverter
cnt = 0
for inv in InverterBase:
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_remote_conn(config_conn, patch_open_connection):
_ = config_conn
_ = patch_open_connection
assert asyncio.get_running_loop()
with InverterG3(FakeReader(), FakeWriter()) as inverter:
await inverter.create_remote()
await asyncio.sleep(0)
assert inverter.remote.stream
del inverter
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_remote_except(config_conn, patch_open_connection):
_ = config_conn
_ = patch_open_connection
assert asyncio.get_running_loop()
global test
test = MockType.RD_TEST_TIMEOUT
with InverterG3(FakeReader(), FakeWriter()) as inverter:
await inverter.create_remote()
await asyncio.sleep(0)
assert inverter.remote.stream==None
test = MockType.RD_TEST_EXCEPT
await inverter.create_remote()
await asyncio.sleep(0)
assert inverter.remote.stream==None
del inverter
test = MockType.RD_TEST_0_BYTES
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_mqtt_publish(config_conn, patch_open_connection):
_ = config_conn
_ = patch_open_connection
assert asyncio.get_running_loop()
Proxy.class_init()
with InverterG3(FakeReader(), FakeWriter()) as inverter:
stream = inverter.local.stream
await inverter.async_publ_mqtt() # check call with invalid unique_id
stream._Talent__set_serial_no(serial_no= "123344")
stream.new_data['inverter'] = True
stream.db.db['inverter'] = {}
await inverter.async_publ_mqtt()
assert stream.new_data['inverter'] == False
stream.new_data['env'] = True
stream.db.db['env'] = {}
await inverter.async_publ_mqtt()
assert stream.new_data['env'] == False
Infos.new_stat_data['proxy'] = True
await inverter.async_publ_mqtt()
assert Infos.new_stat_data['proxy'] == False
@pytest.mark.asyncio
async def test_mqtt_err(config_conn, patch_open_connection, patch_mqtt_err):
_ = config_conn
_ = patch_open_connection
_ = patch_mqtt_err
assert asyncio.get_running_loop()
Proxy.class_init()
with InverterG3(FakeReader(), FakeWriter()) as inverter:
stream = inverter.local.stream
stream._Talent__set_serial_no(serial_no= "123344")
stream.new_data['inverter'] = True
stream.db.db['inverter'] = {}
await inverter.async_publ_mqtt()
assert stream.new_data['inverter'] == True
@pytest.mark.asyncio
async def test_mqtt_except(config_conn, patch_open_connection, patch_mqtt_except):
_ = config_conn
_ = patch_open_connection
_ = patch_mqtt_except
assert asyncio.get_running_loop()
Proxy.class_init()
with InverterG3(FakeReader(), FakeWriter()) as inverter:
stream = inverter.local.stream
stream._Talent__set_serial_no(serial_no= "123344")
stream.new_data['inverter'] = True
stream.db.db['inverter'] = {}
await inverter.async_publ_mqtt()
assert stream.new_data['inverter'] == True