forked from Rockachopa/Timmy-time-dashboard
test: add 157 functional tests covering 8 low-coverage modules
Analyze test coverage (75.3% → 85.4%) and add functional test suites for the major gaps identified: - test_agent_core.py: Full coverage for agent_core/interface.py (0→100%) and agent_core/ollama_adapter.py (0→100%) — data classes, factories, abstract enforcement, perceive/reason/act/recall workflow, effect logging - test_docker_runner.py: Full coverage for swarm/docker_runner.py (0→100%) — container spawn/stop/list lifecycle with mocked subprocess - test_timmy_tools.py: Tool usage tracking, persona toolkit mapping, catalog generation, graceful degradation without Agno - test_routes_tools.py: /tools page, API stats endpoint, and WebSocket /swarm/live connect/disconnect/send lifecycle (41→82%) - test_voice_tts_functional.py: VoiceTTS init, speak, volume clamping, voice listing, graceful degradation (41→94%) - test_watchdog_functional.py: _run_tests, watch loop state transitions, regression detection, KeyboardInterrupt (47→97%) - test_lnd_backend.py: LND init from params/env, grpc stub enforcement, method-level BackendNotAvailableError, settle returns False (25→61%) - test_swarm_routes_functional.py: Agent spawn/stop, task CRUD, auction, insights, UI partials, error paths (63→92%) https://claude.ai/code/session_01WU4h3cQQiouMwmgYmAgkMM
This commit is contained in:
129
tests/test_lnd_backend.py
Normal file
129
tests/test_lnd_backend.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""Functional tests for lightning.lnd_backend — LND gRPC backend.
|
||||
|
||||
gRPC is stubbed via sys.modules; tests verify initialization, error
|
||||
handling, and the placeholder method behavior.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from lightning.base import (
|
||||
BackendNotAvailableError,
|
||||
Invoice,
|
||||
LightningError,
|
||||
)
|
||||
|
||||
|
||||
def _make_grpc_mock():
|
||||
"""Create a mock grpc module with required attributes."""
|
||||
mock_grpc = MagicMock()
|
||||
mock_grpc.StatusCode.NOT_FOUND = "NOT_FOUND"
|
||||
mock_grpc.RpcError = type("RpcError", (Exception,), {
|
||||
"code": lambda self: "NOT_FOUND",
|
||||
"details": lambda self: "mocked error",
|
||||
})
|
||||
return mock_grpc
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lnd_module():
|
||||
"""Reload lnd_backend with grpc stubbed so GRPC_AVAILABLE=True."""
|
||||
grpc_mock = _make_grpc_mock()
|
||||
old = sys.modules.get("grpc")
|
||||
sys.modules["grpc"] = grpc_mock
|
||||
try:
|
||||
import lightning.lnd_backend as mod
|
||||
importlib.reload(mod)
|
||||
yield mod
|
||||
finally:
|
||||
if old is not None:
|
||||
sys.modules["grpc"] = old
|
||||
else:
|
||||
sys.modules.pop("grpc", None)
|
||||
# Reload to restore original state
|
||||
import lightning.lnd_backend as mod2
|
||||
importlib.reload(mod2)
|
||||
|
||||
|
||||
class TestLndBackendInit:
|
||||
def test_init_with_explicit_params(self, lnd_module):
|
||||
backend = lnd_module.LndBackend(
|
||||
host="localhost:10009",
|
||||
tls_cert_path="/fake/tls.cert",
|
||||
macaroon_path="/fake/admin.macaroon",
|
||||
verify_ssl=True,
|
||||
)
|
||||
assert backend._host == "localhost:10009"
|
||||
assert backend._tls_cert_path == "/fake/tls.cert"
|
||||
assert backend._macaroon_path == "/fake/admin.macaroon"
|
||||
assert backend._verify_ssl is True
|
||||
|
||||
def test_init_from_env_vars(self, lnd_module):
|
||||
env = {
|
||||
"LND_GRPC_HOST": "remote:9999",
|
||||
"LND_TLS_CERT_PATH": "/env/tls.cert",
|
||||
"LND_MACAROON_PATH": "/env/macaroon",
|
||||
"LND_VERIFY_SSL": "false",
|
||||
}
|
||||
with patch.dict(os.environ, env):
|
||||
backend = lnd_module.LndBackend()
|
||||
assert backend._host == "remote:9999"
|
||||
assert backend._verify_ssl is False
|
||||
|
||||
def test_init_raises_without_grpc(self):
|
||||
from lightning.lnd_backend import LndBackend
|
||||
with pytest.raises(LightningError, match="grpcio not installed"):
|
||||
LndBackend()
|
||||
|
||||
def test_name_is_lnd(self, lnd_module):
|
||||
assert lnd_module.LndBackend.name == "lnd"
|
||||
|
||||
def test_grpc_available_true_after_reload(self, lnd_module):
|
||||
assert lnd_module.GRPC_AVAILABLE is True
|
||||
|
||||
|
||||
class TestLndBackendMethods:
|
||||
@pytest.fixture
|
||||
def backend(self, lnd_module):
|
||||
return lnd_module.LndBackend(
|
||||
host="localhost:10009",
|
||||
macaroon_path="/fake/path",
|
||||
)
|
||||
|
||||
def test_check_stub_raises_not_available(self, backend):
|
||||
"""_check_stub should raise BackendNotAvailableError when stub is None."""
|
||||
with pytest.raises(BackendNotAvailableError, match="not fully implemented"):
|
||||
backend._check_stub()
|
||||
|
||||
def test_create_invoice_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.create_invoice(1000, memo="test")
|
||||
|
||||
def test_check_payment_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.check_payment("abc123")
|
||||
|
||||
def test_get_invoice_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.get_invoice("abc123")
|
||||
|
||||
def test_settle_invoice_returns_false(self, backend):
|
||||
"""LND auto-settles, so manual settle always returns False."""
|
||||
result = backend.settle_invoice("hash", "preimage")
|
||||
assert result is False
|
||||
|
||||
def test_list_invoices_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.list_invoices()
|
||||
|
||||
def test_get_balance_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.get_balance_sats()
|
||||
|
||||
def test_health_check_raises_not_available(self, backend):
|
||||
with pytest.raises(BackendNotAvailableError):
|
||||
backend.health_check()
|
||||
Reference in New Issue
Block a user