feat: add TDengine connector + schema modules with tests
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""connector 模块测试(mock taosws.connect)。"""
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
from ts_anomaly_td.connector import TDConnection, ConnectionError, _MAX_RETRY_DURATION
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_connect():
|
||||
"""mock taosws.connect 返回假连接。"""
|
||||
with patch("ts_anomaly_td.connector.taosws.connect") as m:
|
||||
mock_conn = MagicMock()
|
||||
mock_conn.execute.return_value = [[("3.3.6.0",)]]
|
||||
m.return_value = mock_conn
|
||||
yield m
|
||||
|
||||
|
||||
def test_connect_success(mock_connect):
|
||||
"""首次连接成功。"""
|
||||
conn = TDConnection()
|
||||
assert conn.is_healthy()
|
||||
mock_connect.assert_called_once()
|
||||
|
||||
|
||||
def test_execute_query(mock_connect):
|
||||
"""execute 返回行列表。"""
|
||||
mock_conn = mock_connect.return_value
|
||||
# taosws 返回可迭代的行对象;每行 row 是 (1, 'a') / (2, 'b')
|
||||
mock_conn.execute.return_value = [(1, "a"), (2, "b")]
|
||||
|
||||
conn = TDConnection()
|
||||
rows = conn.execute("SELECT 1")
|
||||
assert rows == [(1, "a"), (2, "b")]
|
||||
|
||||
|
||||
def test_execute_no_result(mock_connect):
|
||||
"""execute_no_result 不返回值。"""
|
||||
conn = TDConnection()
|
||||
result = conn.execute_no_result("CREATE DATABASE test")
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_retry_logic(mock_connect):
|
||||
"""前 2 次失败、第 3 次成功。"""
|
||||
import ts_anomaly_td.connector as mod
|
||||
|
||||
# 缩短退避以加速测试
|
||||
mod._MAX_BACKOFF = 0.01
|
||||
try:
|
||||
mock_connect.side_effect = [
|
||||
Exception("connection refused"),
|
||||
Exception("connection refused"),
|
||||
MagicMock(execute=MagicMock(return_value=[[("3.3.6.0",)]])),
|
||||
]
|
||||
|
||||
conn = TDConnection()
|
||||
assert conn.is_healthy()
|
||||
assert mock_connect.call_count == 3
|
||||
finally:
|
||||
mod._MAX_BACKOFF = 300
|
||||
|
||||
|
||||
def test_retry_timeout(mock_connect):
|
||||
"""持续失败超过 30 分钟应抛出 ConnectionError。"""
|
||||
import ts_anomaly_td.connector as mod
|
||||
|
||||
mod._MAX_RETRY_DURATION = 0.1
|
||||
mod._MAX_BACKOFF = 0.01
|
||||
try:
|
||||
mock_connect.side_effect = Exception("always fails")
|
||||
|
||||
with pytest.raises(ConnectionError, match="30"):
|
||||
conn = TDConnection()
|
||||
# 触发懒加载连接:访问 .conn 属性(非 is_healthy,后者会吞掉异常)
|
||||
_ = conn.conn
|
||||
finally:
|
||||
mod._MAX_RETRY_DURATION = 1800
|
||||
mod._MAX_BACKOFF = 300
|
||||
|
||||
|
||||
def test_close(mock_connect):
|
||||
"""close 关闭底层连接。"""
|
||||
conn = TDConnection()
|
||||
# 触发懒加载连接
|
||||
conn.is_healthy()
|
||||
conn.close()
|
||||
mock_connect.return_value.close.assert_called_once()
|
||||
|
||||
|
||||
def test_batch_insert(mock_connect):
|
||||
"""batch_insert 拼接 INSERT SQL。"""
|
||||
conn = TDConnection()
|
||||
rows = [(1000, 1.0, 0), (2000, 2.0, 1)]
|
||||
conn.batch_insert("test", rows)
|
||||
|
||||
mock_conn = mock_connect.return_value
|
||||
# 验证 execute 被调用
|
||||
calls = [str(c[0][0]) for c in mock_conn.execute.call_args_list]
|
||||
assert any("INSERT INTO s_test VALUES" in c for c in calls)
|
||||
Reference in New Issue
Block a user