Files
mousehold/tests/test_paypal_connector.py
2026-06-28 13:48:15 +02:00

124 lines
4.3 KiB
Python

from __future__ import annotations
from datetime import date
import pytest
from mousehold_agent.paypal_connector import (
PayPalAPIError,
PayPalConfig,
_paypal_error_message,
_token_payload_to_auth_check,
paypal_transaction_to_observation,
paypal_transaction_to_preview,
)
from mousehold_agent.paypal_connector import _validate_date_range as validate_date_range
def test_paypal_negative_transaction_becomes_confirmed_expense() -> None:
observation = paypal_transaction_to_observation(
{
"transaction_info": {
"transaction_id": "PAYPAL-001",
"transaction_initiation_date": "2026-06-20T12:30:00Z",
"transaction_updated_date": "2026-06-20T12:31:00Z",
"transaction_amount": {"currency_code": "EUR", "value": "-18.99"},
"transaction_status": "S",
"transaction_subject": "Payment to Example Merchant",
},
"payer_info": {"email_address": "merchant@example.test"},
},
household_id="household-1",
source_account_id="account-1",
owner_user_id="user-1",
)
assert observation["source_type"] == "paypal_api"
assert observation["provider_transaction_id"] == "PAYPAL-001"
assert observation["event_type"] == "expense"
assert observation["status"] == "confirmed"
assert observation["amount"] == "18.99"
assert observation["event_date"] == "2026-06-20"
def test_paypal_positive_transaction_becomes_income() -> None:
observation = paypal_transaction_to_observation(
{
"transaction_info": {
"transaction_id": "PAYPAL-002",
"transaction_initiation_date": "2026-06-21T09:10:00+0000",
"transaction_amount": {"currency_code": "EUR", "value": "42.50"},
"transaction_status": "S",
},
"payer_info": {
"payer_name": {"given_name": "Alice", "surname": "Example"},
},
},
household_id="household-1",
source_account_id="account-1",
owner_user_id="user-1",
)
assert observation["event_type"] == "income"
assert observation["amount"] == "42.50"
assert observation["counterparty"] == "Alice Example"
def test_paypal_pending_transaction_preview_is_announced() -> None:
preview = paypal_transaction_to_preview(
{
"transaction_info": {
"transaction_id": "PAYPAL-003",
"transaction_initiation_date": "2026-06-22T09:10:00Z",
"transaction_amount": {"currency_code": "EUR", "value": "-10.00"},
"transaction_status": "P",
}
}
)
assert preview["status"] == "announced"
assert preview["event_type"] == "expense"
def test_paypal_date_range_is_limited_to_31_days() -> None:
with pytest.raises(PayPalAPIError):
validate_date_range(date(2026, 6, 1), date(2026, 7, 2))
def test_paypal_not_authorized_error_mentions_transaction_search_scope() -> None:
message = _paypal_error_message(
403,
'{"name":"NOT_AUTHORIZED","message":"Authorization failed due to insufficient permissions."}',
)
assert "Transaction Search" in message
assert "https://uri.paypal.com/services/reporting/search/read" in message
def test_paypal_auth_check_reports_missing_transaction_search_scope() -> None:
check = _token_payload_to_auth_check(
PayPalConfig(client_id="client", client_secret="secret", environment="sandbox"),
{
"scope": "openid https://uri.paypal.com/services/invoicing",
"token_type": "Bearer",
"app_id": "APP-123",
"expires_in": 3600,
},
)
assert check["app_id"] == "APP-123"
assert check["environment"] == "sandbox"
assert check["has_transaction_search"] is False
def test_paypal_auth_check_parses_comma_separated_scope_text() -> None:
check = _token_payload_to_auth_check(
PayPalConfig(client_id="client", client_secret="secret", environment="sandbox"),
{
"scope": "openid,https://uri.paypal.com/services/reporting/search/read",
"token_type": "Bearer",
},
)
assert check["has_transaction_search"] is True
assert "https://uri.paypal.com/services/reporting/search/read" in check["scopes"]