mirror of
https://github.com/mcloh/Enterprise-MCP-Service-Bus.git
synced 2026-11-05 22:14:11 +00:00
M0 e M1 revisados e fixes implementados
This commit is contained in:
163
RI/tests/unit/test_approval.py
Normal file
163
RI/tests/unit/test_approval.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""Approval workflow (EP-14-T01/T02/T03, README.md §26, §46 approval replay)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from emcp_bus.approval.models import compute_operation_hash
|
||||
from emcp_bus.approval.service import ApprovalNotFoundError, ApprovalService
|
||||
|
||||
|
||||
def test_operation_hash_is_stable_regardless_of_argument_order() -> None:
|
||||
a = compute_operation_hash(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000, "currency": "BRL"},
|
||||
)
|
||||
b = compute_operation_hash(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"currency": "BRL", "amount": 50_000},
|
||||
)
|
||||
assert a == b
|
||||
|
||||
|
||||
def test_operation_hash_differs_for_different_arguments() -> None:
|
||||
"""A grant for one exact call must never satisfy a different one (README.md §26)."""
|
||||
a = compute_operation_hash(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000},
|
||||
)
|
||||
b = compute_operation_hash(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 999_999},
|
||||
)
|
||||
assert a != b
|
||||
|
||||
|
||||
def test_unapproved_operation_is_not_consumed() -> None:
|
||||
service = ApprovalService()
|
||||
|
||||
consumed = service.check_and_consume(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000},
|
||||
)
|
||||
|
||||
assert consumed is False
|
||||
|
||||
|
||||
def test_request_then_grant_then_consume_succeeds_exactly_once() -> None:
|
||||
service = ApprovalService()
|
||||
arguments = {"amount": 50_000}
|
||||
request = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments=arguments,
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
)
|
||||
|
||||
service.grant(request.approval_id)
|
||||
|
||||
assert (
|
||||
service.check_and_consume(
|
||||
client_profile="Finance.Payments", tool="finance.payment.execute", arguments=arguments
|
||||
)
|
||||
is True
|
||||
)
|
||||
# README.md §46 "approval replay": the second attempt of the *same* call
|
||||
# must not be satisfied by an already-consumed grant.
|
||||
assert (
|
||||
service.check_and_consume(
|
||||
client_profile="Finance.Payments", tool="finance.payment.execute", arguments=arguments
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_denied_request_is_never_consumable() -> None:
|
||||
service = ApprovalService()
|
||||
arguments = {"amount": 50_000}
|
||||
request = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments=arguments,
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
)
|
||||
|
||||
service.deny(request.approval_id)
|
||||
|
||||
assert (
|
||||
service.check_and_consume(
|
||||
client_profile="Finance.Payments", tool="finance.payment.execute", arguments=arguments
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_grant_does_not_satisfy_a_different_operation() -> None:
|
||||
"""Granting approval for one amount must not let a *different* amount through."""
|
||||
service = ApprovalService()
|
||||
request = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000},
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
)
|
||||
service.grant(request.approval_id)
|
||||
|
||||
consumed = service.check_and_consume(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 999_999}, # different operation entirely
|
||||
)
|
||||
|
||||
assert consumed is False
|
||||
|
||||
|
||||
def test_expired_grant_is_not_consumable() -> None:
|
||||
service = ApprovalService()
|
||||
arguments = {"amount": 50_000}
|
||||
request = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments=arguments,
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
ttl_seconds=0,
|
||||
)
|
||||
service.grant(request.approval_id)
|
||||
|
||||
consumed = service.check_and_consume(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments=arguments,
|
||||
)
|
||||
|
||||
assert consumed is False
|
||||
|
||||
|
||||
def test_repeated_pending_requests_for_the_same_operation_do_not_duplicate() -> None:
|
||||
service = ApprovalService()
|
||||
first = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000},
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
)
|
||||
second = service.request(
|
||||
client_profile="Finance.Payments",
|
||||
tool="finance.payment.execute",
|
||||
arguments={"amount": 50_000},
|
||||
reason_code="APPROVAL_THRESHOLD_EXCEEDED",
|
||||
)
|
||||
|
||||
assert first.approval_id == second.approval_id
|
||||
|
||||
|
||||
def test_grant_unknown_approval_id_raises() -> None:
|
||||
service = ApprovalService()
|
||||
|
||||
with pytest.raises(ApprovalNotFoundError):
|
||||
service.grant("no-such-id")
|
||||
Reference in New Issue
Block a user