171 lines
5.5 KiB
Python
171 lines
5.5 KiB
Python
"""Unit tests for logging module."""
|
|
|
|
import logging
|
|
|
|
from grist_mcp.logging import truncate_token, extract_stats, format_tool_log
|
|
|
|
|
|
class TestTruncateToken:
|
|
def test_normal_token_shows_prefix_suffix(self):
|
|
token = "abcdefghijklmnop"
|
|
assert truncate_token(token) == "abc...nop"
|
|
|
|
def test_short_token_shows_asterisks(self):
|
|
token = "abcdefgh" # 8 chars
|
|
assert truncate_token(token) == "***"
|
|
|
|
def test_very_short_token_shows_asterisks(self):
|
|
token = "abc"
|
|
assert truncate_token(token) == "***"
|
|
|
|
def test_empty_token_shows_asterisks(self):
|
|
assert truncate_token("") == "***"
|
|
|
|
def test_boundary_token_shows_prefix_suffix(self):
|
|
token = "abcdefghi" # 9 chars - first to show truncation
|
|
assert truncate_token(token) == "abc...ghi"
|
|
|
|
|
|
class TestExtractStats:
|
|
def test_list_documents(self):
|
|
result = {"documents": [{"name": "a"}, {"name": "b"}, {"name": "c"}]}
|
|
assert extract_stats("list_documents", {}, result) == "3 docs"
|
|
|
|
def test_list_tables(self):
|
|
result = {"tables": ["Orders", "Products"]}
|
|
assert extract_stats("list_tables", {}, result) == "2 tables"
|
|
|
|
def test_describe_table(self):
|
|
result = {"columns": [{"id": "A"}, {"id": "B"}]}
|
|
assert extract_stats("describe_table", {}, result) == "2 columns"
|
|
|
|
def test_get_records(self):
|
|
result = {"records": [{"id": 1}, {"id": 2}]}
|
|
assert extract_stats("get_records", {}, result) == "2 records"
|
|
|
|
def test_sql_query(self):
|
|
result = {"records": [{"a": 1}, {"a": 2}, {"a": 3}]}
|
|
assert extract_stats("sql_query", {}, result) == "3 rows"
|
|
|
|
def test_add_records_from_args(self):
|
|
args = {"records": [{"a": 1}, {"a": 2}]}
|
|
assert extract_stats("add_records", args, {"ids": [1, 2]}) == "2 records"
|
|
|
|
def test_update_records_from_args(self):
|
|
args = {"records": [{"id": 1, "fields": {}}, {"id": 2, "fields": {}}]}
|
|
assert extract_stats("update_records", args, {}) == "2 records"
|
|
|
|
def test_delete_records_from_args(self):
|
|
args = {"record_ids": [1, 2, 3]}
|
|
assert extract_stats("delete_records", args, {}) == "3 records"
|
|
|
|
def test_create_table(self):
|
|
args = {"columns": [{"id": "A"}, {"id": "B"}]}
|
|
assert extract_stats("create_table", args, {}) == "2 columns"
|
|
|
|
def test_single_column_ops(self):
|
|
assert extract_stats("add_column", {}, {}) == "1 column"
|
|
assert extract_stats("modify_column", {}, {}) == "1 column"
|
|
assert extract_stats("delete_column", {}, {}) == "1 column"
|
|
|
|
def test_empty_result_returns_zero(self):
|
|
assert extract_stats("list_documents", {}, {"documents": []}) == "0 docs"
|
|
|
|
def test_unknown_tool(self):
|
|
assert extract_stats("unknown_tool", {}, {}) == "-"
|
|
|
|
|
|
class TestFormatToolLog:
|
|
def test_success_format(self):
|
|
line = format_tool_log(
|
|
agent_name="dev-agent",
|
|
token="abcdefghijklmnop",
|
|
tool="get_records",
|
|
document="sales",
|
|
stats="42 records",
|
|
status="success",
|
|
duration_ms=125,
|
|
)
|
|
assert "dev-agent" in line
|
|
assert "abc...nop" in line
|
|
assert "get_records" in line
|
|
assert "sales" in line
|
|
assert "42 records" in line
|
|
assert "success" in line
|
|
assert "125ms" in line
|
|
# Check pipe-delimited format
|
|
assert line.count("|") == 6
|
|
|
|
def test_no_document(self):
|
|
line = format_tool_log(
|
|
agent_name="dev-agent",
|
|
token="abcdefghijklmnop",
|
|
tool="list_documents",
|
|
document=None,
|
|
stats="3 docs",
|
|
status="success",
|
|
duration_ms=45,
|
|
)
|
|
assert "| - |" in line
|
|
|
|
def test_error_format(self):
|
|
line = format_tool_log(
|
|
agent_name="dev-agent",
|
|
token="abcdefghijklmnop",
|
|
tool="add_records",
|
|
document="inventory",
|
|
stats="5 records",
|
|
status="error",
|
|
duration_ms=89,
|
|
error_message="Grist API error: Invalid column 'foo'",
|
|
)
|
|
assert "error" in line
|
|
assert "\n Grist API error: Invalid column 'foo'" in line
|
|
|
|
|
|
class TestSetupLogging:
|
|
def test_default_level_is_info(self, monkeypatch):
|
|
monkeypatch.delenv("LOG_LEVEL", raising=False)
|
|
|
|
from grist_mcp.logging import setup_logging
|
|
setup_logging()
|
|
|
|
logger = logging.getLogger("grist_mcp")
|
|
assert logger.level == logging.INFO
|
|
|
|
def test_respects_log_level_env(self, monkeypatch):
|
|
monkeypatch.setenv("LOG_LEVEL", "DEBUG")
|
|
|
|
from grist_mcp.logging import setup_logging
|
|
setup_logging()
|
|
|
|
logger = logging.getLogger("grist_mcp")
|
|
assert logger.level == logging.DEBUG
|
|
|
|
def test_invalid_level_defaults_to_info(self, monkeypatch):
|
|
monkeypatch.setenv("LOG_LEVEL", "INVALID")
|
|
|
|
from grist_mcp.logging import setup_logging
|
|
setup_logging()
|
|
|
|
logger = logging.getLogger("grist_mcp")
|
|
assert logger.level == logging.INFO
|
|
|
|
|
|
class TestGetLogger:
|
|
def test_returns_child_logger(self):
|
|
from grist_mcp.logging import get_logger
|
|
|
|
logger = get_logger("server")
|
|
assert logger.name == "grist_mcp.server"
|
|
|
|
def test_inherits_parent_level(self, monkeypatch):
|
|
monkeypatch.setenv("LOG_LEVEL", "WARNING")
|
|
|
|
from grist_mcp.logging import setup_logging, get_logger
|
|
setup_logging()
|
|
|
|
logger = get_logger("test")
|
|
# Child inherits from parent when level is NOTSET
|
|
assert logger.getEffectiveLevel() == logging.WARNING
|