124 lines
4.3 KiB
Python
124 lines
4.3 KiB
Python
"""Unit tests — no live Odoo or DB connection required."""
|
|
from __future__ import annotations
|
|
import sys, os
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
|
|
from mt_odoo_mcp.confirmation.manager import ConfirmationManager
|
|
from mt_odoo_mcp.audit.logger import _sanitize
|
|
|
|
|
|
# ─── Confirmation Manager ──────────────────────────────────────────────────────
|
|
|
|
def test_token_created():
|
|
mgr = ConfirmationManager()
|
|
pending = mgr.create_token("default", "project.task", 42, "Fix Bug")
|
|
assert pending.token.startswith("del_")
|
|
assert pending.model == "project.task"
|
|
assert pending.record_id == 42
|
|
|
|
|
|
def test_token_consumed():
|
|
mgr = ConfirmationManager()
|
|
pending = mgr.create_token("default", "res.partner", 99, "ACME Corp")
|
|
ok, err = mgr.consume_token(pending.token, "res.partner", 99)
|
|
assert ok is True
|
|
assert err == ""
|
|
|
|
|
|
def test_token_single_use():
|
|
mgr = ConfirmationManager()
|
|
pending = mgr.create_token("default", "sale.order", 7, "SO007")
|
|
mgr.consume_token(pending.token, "sale.order", 7)
|
|
ok, err = mgr.consume_token(pending.token, "sale.order", 7)
|
|
assert ok is False
|
|
assert "expired" in err.lower() or "invalid" in err.lower()
|
|
|
|
|
|
def test_token_wrong_model():
|
|
mgr = ConfirmationManager()
|
|
pending = mgr.create_token("default", "project.task", 10, "Task A")
|
|
ok, err = mgr.consume_token(pending.token, "sale.order", 10)
|
|
assert ok is False
|
|
assert "does not match" in err.lower()
|
|
|
|
|
|
def test_token_wrong_record_id():
|
|
mgr = ConfirmationManager()
|
|
pending = mgr.create_token("default", "project.task", 10, "Task A")
|
|
ok, _ = mgr.consume_token(pending.token, "project.task", 999)
|
|
assert ok is False
|
|
|
|
|
|
def test_invalid_token():
|
|
mgr = ConfirmationManager()
|
|
ok, _ = mgr.consume_token("del_notarealtoken", "project.task", 1)
|
|
assert ok is False
|
|
|
|
|
|
def test_list_pending():
|
|
mgr = ConfirmationManager()
|
|
mgr.create_token("default", "account.move", 55, "INV/2026/001")
|
|
assert any(p["model"] == "account.move" for p in mgr.list_pending())
|
|
|
|
|
|
def test_per_user_isolation():
|
|
"""Two ConfirmationManager instances must not share tokens."""
|
|
mgr_a = ConfirmationManager()
|
|
mgr_b = ConfirmationManager()
|
|
pending = mgr_a.create_token("default", "project.task", 1, "Task")
|
|
ok, _ = mgr_b.consume_token(pending.token, "project.task", 1)
|
|
assert ok is False # mgr_b has no knowledge of mgr_a's tokens
|
|
|
|
|
|
# ─── Audit Logger Sanitization ─────────────────────────────────────────────────
|
|
|
|
def test_sanitize_password():
|
|
assert _sanitize({"username": "admin", "password": "s3cret"})["password"] == "***"
|
|
|
|
|
|
def test_sanitize_api_key():
|
|
result = _sanitize({"api_key": "abc123", "model": "project.task"})
|
|
assert result["api_key"] == "***"
|
|
assert result["model"] == "project.task"
|
|
|
|
|
|
def test_sanitize_nested():
|
|
result = _sanitize({"instance": {"name": "prod", "api_key": "secret"}})
|
|
assert result["instance"]["api_key"] == "***"
|
|
assert result["instance"]["name"] == "prod"
|
|
|
|
|
|
def test_sanitize_list_passthrough():
|
|
result = _sanitize({"ids": [1, 2, 3], "token": "tok_abc"})
|
|
assert result["ids"] == [1, 2, 3]
|
|
assert result["token"] == "***"
|
|
|
|
|
|
def test_sanitize_plain():
|
|
data = {"model": "sale.order", "limit": 10}
|
|
assert _sanitize(data) == data
|
|
|
|
|
|
# ─── Auth Crypto ──────────────────────────────────────────────────────────────
|
|
|
|
def test_password_hash_verify():
|
|
from mt_odoo_mcp.auth.crypto import hash_password, verify_password
|
|
h = hash_password("mysecret123")
|
|
assert verify_password("mysecret123", h)
|
|
assert not verify_password("wrong", h)
|
|
|
|
|
|
def test_api_key_format():
|
|
from mt_odoo_mcp.auth.crypto import generate_api_key
|
|
full, key_hash, prefix = generate_api_key()
|
|
assert full.startswith("mtom_")
|
|
assert len(full) > 20
|
|
assert prefix == full[:12]
|
|
|
|
|
|
def test_api_key_verify():
|
|
from mt_odoo_mcp.auth.crypto import generate_api_key, verify_api_key
|
|
full, key_hash, _ = generate_api_key()
|
|
assert verify_api_key(full, key_hash)
|
|
assert not verify_api_key("mtom_wrong", key_hash)
|