diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 049fed0..2c834b2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 # Sets up python - - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v7 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v5 with: python-version: 3.12 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39806ef..a4d4c99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,52 +1,52 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: CI - Lint and Tests +name: CI on: + push: + branches: ["master"] pull_request: - branches: - - 'master' + branches: ["master"] jobs: - build: - + test: + name: Test on Python ${{ matrix.python-version }} runs-on: ubuntu-latest + strategy: + fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12'] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v7 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip "setuptools>=83.0.0" - python -m pip install requests - - - name: Lint with Pylint - run: | - pip install pylint - pylint mercadopago - pylint --rcfile=tests/.pylintrc tests - - - name: Lint with Isort - run: | - pip install isort - isort . - - - name: Test with unittest - run: | - python -m unittest discover tests/ - env: - ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}} - - - name: Install pip-audit - run: pip install pip-audit - - - name: Dependency audit - run: pip-audit + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 + + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip "setuptools>=83.0.0" + python -m pip install requests pylint isort pip-audit + python -m pip install -e . + + - name: Lint with Pylint + run: | + pylint mercadopago + pylint --rcfile=tests/.pylintrc tests + + - name: Lint with isort + run: isort --check-only . + + - name: Run unit tests + run: python -m unittest discover tests/ + + - name: Dependency audit + run: pip-audit + + - name: Dependency review + if: github.event_name == 'pull_request' + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v4 + with: + fail-on-severity: high diff --git a/examples/order/create_order_automatic_payment.py b/examples/order/create_order_automatic_payment.py index 172ccad..546d255 100644 --- a/examples/order/create_order_automatic_payment.py +++ b/examples/order/create_order_automatic_payment.py @@ -4,6 +4,7 @@ subsequent recurring charge (referencing the previous transaction). """ import dataclasses + import mercadopago from mercadopago.resources.order_automatic_payments import OrderAutomaticPayments from mercadopago.resources.order_stored_credential import OrderStoredCredential @@ -13,6 +14,7 @@ OrderSubscriptionSequence, ) + sdk = mercadopago.SDK("YOUR_ACCESS_TOKEN") diff --git a/examples/order/create_order_credential_on_file.py b/examples/order/create_order_credential_on_file.py index 43a080c..abc72f3 100644 --- a/examples/order/create_order_credential_on_file.py +++ b/examples/order/create_order_credential_on_file.py @@ -21,6 +21,7 @@ """ import mercadopago + sdk = mercadopago.SDK("YOUR_ACCESS_TOKEN") diff --git a/mercadopago/__init__.py b/mercadopago/__init__.py index 416d814..3d2ab2b 100644 --- a/mercadopago/__init__.py +++ b/mercadopago/__init__.py @@ -8,31 +8,34 @@ sdk = mercadopago.SDK("YOUR_ACCESS_TOKEN") payment = sdk.payment().create({...}) """ -from mercadopago.sdk import SDK +from mercadopago.errors.constants import ( + MPOrderErrors, + MPPaymentErrors, +) from mercadopago.errors.exceptions import ( MercadoPagoError, - MPBadRequestError, MPAuthenticationError, - MPPaymentError, + MPBadRequestError, + MPConnectionError, + MPDependencyError, MPForbiddenError, - MPNotFoundError, MPIdempotencyError, - MPValidationError, - MPResourceLockedError, - MPDependencyError, + MPNotFoundError, + MPPaymentError, MPRateLimitError, + MPResourceLockedError, MPServerError, - MPConnectionError, + MPValidationError, ) -from mercadopago.errors.constants import MPOrderErrors, MPPaymentErrors from mercadopago.errors.response import MPResponse from mercadopago.resources.status import ( - PaymentStatus, + MerchantOrderStatus, OrderStatus, + PaymentStatus, PreapprovalStatus, - MerchantOrderStatus, RefundStatus, ) +from mercadopago.sdk import SDK __all__ = ( diff --git a/mercadopago/config/request_options.py b/mercadopago/config/request_options.py index ce4a814..92dc5d5 100644 --- a/mercadopago/config/request_options.py +++ b/mercadopago/config/request_options.py @@ -7,10 +7,10 @@ from .config import Config from .defaults import ( - DEFAULT_TIMEOUT_SECONDS, - DEFAULT_MAX_RETRIES, - DEFAULT_MAX_DELAY, DEFAULT_INITIAL_DELAY, + DEFAULT_MAX_DELAY, + DEFAULT_MAX_RETRIES, + DEFAULT_TIMEOUT_SECONDS, ) diff --git a/mercadopago/errors/__init__.py b/mercadopago/errors/__init__.py index b438fbb..21928ed 100644 --- a/mercadopago/errors/__init__.py +++ b/mercadopago/errors/__init__.py @@ -1,22 +1,23 @@ """Errors package for the MercadoPago Python SDK.""" from .exceptions import ( MercadoPagoError, - MPBadRequestError, MPAuthenticationError, - MPPaymentError, + MPBadRequestError, + MPConnectionError, + MPDependencyError, MPForbiddenError, - MPNotFoundError, MPIdempotencyError, - MPValidationError, - MPResourceLockedError, - MPDependencyError, + MPNotFoundError, + MPPaymentError, MPRateLimitError, + MPResourceLockedError, MPServerError, - MPConnectionError, + MPValidationError, build_error, ) from .response import MPResponse + __all__ = [ "MercadoPagoError", "MPBadRequestError", diff --git a/mercadopago/examples/order/cancel_order.py b/mercadopago/examples/order/cancel_order.py index c848e13..901ebd8 100644 --- a/mercadopago/examples/order/cancel_order.py +++ b/mercadopago/examples/order/cancel_order.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/capture_order.py b/mercadopago/examples/order/capture_order.py index bbd5d8d..0f8240a 100644 --- a/mercadopago/examples/order/capture_order.py +++ b/mercadopago/examples/order/capture_order.py @@ -1,6 +1,8 @@ import os + from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/create_order_checkout_pro.py b/mercadopago/examples/order/create_order_checkout_pro.py index 93833c7..d616f58 100644 --- a/mercadopago/examples/order/create_order_checkout_pro.py +++ b/mercadopago/examples/order/create_order_checkout_pro.py @@ -7,12 +7,12 @@ from mercadopago import SDK from mercadopago.resources.order_checkout_pro import ( OrderCheckoutProConfig, + OrderCheckoutProDict, OrderCheckoutProInstallments, OrderCheckoutProInterestFree, OrderCheckoutProOnlineConfig, OrderCheckoutProPaymentMethod, OrderCheckoutProTrack, - OrderCheckoutProDict, ) diff --git a/mercadopago/examples/order/create_transaction.py b/mercadopago/examples/order/create_transaction.py index 87018e7..99d922d 100644 --- a/mercadopago/examples/order/create_transaction.py +++ b/mercadopago/examples/order/create_transaction.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/delete_transaction.py b/mercadopago/examples/order/delete_transaction.py index 9f31c2f..529bd34 100644 --- a/mercadopago/examples/order/delete_transaction.py +++ b/mercadopago/examples/order/delete_transaction.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/get_order.py b/mercadopago/examples/order/get_order.py index 2c128ef..d83784c 100644 --- a/mercadopago/examples/order/get_order.py +++ b/mercadopago/examples/order/get_order.py @@ -1,6 +1,8 @@ import os + from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/process_order.py b/mercadopago/examples/order/process_order.py index a60a023..bef2a14 100644 --- a/mercadopago/examples/order/process_order.py +++ b/mercadopago/examples/order/process_order.py @@ -1,6 +1,8 @@ import os + from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/refund_partial.py b/mercadopago/examples/order/refund_partial.py index c260f9a..1fde39a 100644 --- a/mercadopago/examples/order/refund_partial.py +++ b/mercadopago/examples/order/refund_partial.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/refund_total.py b/mercadopago/examples/order/refund_total.py index 251b0ca..6bdad69 100644 --- a/mercadopago/examples/order/refund_total.py +++ b/mercadopago/examples/order/refund_total.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/examples/order/update_transaction.py b/mercadopago/examples/order/update_transaction.py index f552071..79db476 100644 --- a/mercadopago/examples/order/update_transaction.py +++ b/mercadopago/examples/order/update_transaction.py @@ -3,6 +3,7 @@ from mercadopago import SDK + def main(): # Define the authentication token access_token = "" diff --git a/mercadopago/http/http_client.py b/mercadopago/http/http_client.py index cc32427..80458f8 100644 --- a/mercadopago/http/http_client.py +++ b/mercadopago/http/http_client.py @@ -10,6 +10,7 @@ from mercadopago.config.defaults import DEFAULT_RETRY_ON + class HttpClient: """Default HTTP transport for all MercadoPago REST calls. diff --git a/mercadopago/pagination/__init__.py b/mercadopago/pagination/__init__.py index 9ea686c..54e1e3a 100644 --- a/mercadopago/pagination/__init__.py +++ b/mercadopago/pagination/__init__.py @@ -2,4 +2,5 @@ from .iterator import search_auto_paging_iter from .page import Paging + __all__ = ["search_auto_paging_iter", "Paging"] diff --git a/mercadopago/pagination/iterator.py b/mercadopago/pagination/iterator.py index 7dba919..76bd067 100644 --- a/mercadopago/pagination/iterator.py +++ b/mercadopago/pagination/iterator.py @@ -11,6 +11,7 @@ """ from .page import Paging + def search_auto_paging_iter(search_fn, filters=None, request_options=None, limit=100): """Lazy generator that auto-fetches all pages of a search result. diff --git a/mercadopago/resources/__init__.py b/mercadopago/resources/__init__.py index c75d60d..7b2447b 100644 --- a/mercadopago/resources/__init__.py +++ b/mercadopago/resources/__init__.py @@ -19,12 +19,12 @@ from mercadopago.resources.order import Order from mercadopago.resources.order_checkout_pro import ( OrderCheckoutProConfig, + OrderCheckoutProDict, OrderCheckoutProInstallments, OrderCheckoutProInterestFree, OrderCheckoutProOnlineConfig, OrderCheckoutProPaymentMethod, OrderCheckoutProTrack, - OrderCheckoutProDict, ) from mercadopago.resources.payment import Payment from mercadopago.resources.payment_methods import PaymentMethods diff --git a/mercadopago/resources/order.py b/mercadopago/resources/order.py index 96ab70e..b855d21 100644 --- a/mercadopago/resources/order.py +++ b/mercadopago/resources/order.py @@ -10,6 +10,7 @@ from mercadopago.core import MPBase from mercadopago.pagination.iterator import search_auto_paging_iter as _paging_iter + class Order(MPBase): """Manages orders and their associated transactions. diff --git a/mercadopago/resources/order_checkout_pro.py b/mercadopago/resources/order_checkout_pro.py index 35e7e8a..e50031a 100644 --- a/mercadopago/resources/order_checkout_pro.py +++ b/mercadopago/resources/order_checkout_pro.py @@ -1,6 +1,16 @@ """Dataclasses for Checkout Pro fields in order requests.""" -from dataclasses import asdict, dataclass, field, is_dataclass -from typing import Any, Dict, List, Optional +from dataclasses import ( + asdict, + dataclass, + field, + is_dataclass, +) +from typing import ( + Any, + Dict, + List, + Optional, +) def OrderCheckoutProDict(value): # pylint: disable=invalid-name diff --git a/mercadopago/resources/payment.py b/mercadopago/resources/payment.py index 9b54c96..59f85cd 100644 --- a/mercadopago/resources/payment.py +++ b/mercadopago/resources/payment.py @@ -6,6 +6,7 @@ `API reference `_ """ import warnings + from mercadopago.core import MPBase from mercadopago.pagination.iterator import search_auto_paging_iter as _paging_iter diff --git a/mercadopago/resources/preference.py b/mercadopago/resources/preference.py index d4bcdc3..9f2e239 100644 --- a/mercadopago/resources/preference.py +++ b/mercadopago/resources/preference.py @@ -6,6 +6,7 @@ `API reference `_ """ import warnings + from mercadopago.core import MPBase from mercadopago.pagination.iterator import search_auto_paging_iter as _paging_iter diff --git a/mercadopago/webhook/validator.py b/mercadopago/webhook/validator.py index 3b6e6c5..637bf35 100644 --- a/mercadopago/webhook/validator.py +++ b/mercadopago/webhook/validator.py @@ -8,11 +8,11 @@ depend on any SDK configuration object; the integrator passes the secret signature explicitly on every call. """ -import hmac +from enum import Enum import hashlib +import hmac import re import time -from enum import Enum class SignatureFailureReason(Enum): diff --git a/tests/base_client_test.py b/tests/base_client_test.py new file mode 100644 index 0000000..04dce40 --- /dev/null +++ b/tests/base_client_test.py @@ -0,0 +1,58 @@ +"""Base class for mock-based unit tests of MercadoPago SDK resources.""" +import json +import os +import unittest +from unittest.mock import MagicMock + +import mercadopago +from mercadopago.http.http_client import HttpClient + + +def _load_fixture(filename): + """Load a JSON fixture from tests/resources/mocks/.""" + base = os.path.dirname(__file__) + path = os.path.join(base, "resources", "mocks", filename) + with open(path, encoding="utf-8") as fh: + return json.load(fh) + + +class BaseClientTest(unittest.TestCase): + """Provides a mock HTTP client and an SDK instance for unit tests. + + Sub-classes should call ``self.mock_get``, ``self.mock_post``, + ``self.mock_put``, or ``self.mock_delete`` to set up the expected + response *before* calling the resource method under test. + """ + + def setUp(self): + self.mock_http = MagicMock(spec=HttpClient) + self.sdk = mercadopago.SDK("TEST_TOKEN", http_client=self.mock_http) + + # ------------------------------------------------------------------ + # Helpers – configure the mock for a single HTTP verb + # ------------------------------------------------------------------ + + def mock_get(self, response_dict, status=200): + """Make the next ``http_client.get(...)`` return *response_dict*.""" + self.mock_http.get.return_value = {"status": status, "response": response_dict} + + def mock_post(self, response_dict, status=201): + """Make the next ``http_client.post(...)`` return *response_dict*.""" + self.mock_http.post.return_value = {"status": status, "response": response_dict} + + def mock_put(self, response_dict, status=200): + """Make the next ``http_client.put(...)`` return *response_dict*.""" + self.mock_http.put.return_value = {"status": status, "response": response_dict} + + def mock_delete(self, response_dict=None, status=204): + """Make the next ``http_client.delete(...)`` return *response_dict*.""" + self.mock_http.delete.return_value = {"status": status, "response": response_dict} + + # ------------------------------------------------------------------ + # Fixture loader exposed to sub-classes + # ------------------------------------------------------------------ + + @staticmethod + def load_fixture(filename): + """Return the parsed JSON fixture *filename* from resources/mocks/.""" + return _load_fixture(filename) diff --git a/tests/resources/mocks/advanced_payment_create.json b/tests/resources/mocks/advanced_payment_create.json new file mode 100644 index 0000000..f5e8963 --- /dev/null +++ b/tests/resources/mocks/advanced_payment_create.json @@ -0,0 +1,30 @@ +{ + "id": 999, + "status": "pending", + "status_detail": "pending_waiting_payment", + "application_id": "app-001", + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "payer": { + "email": "test_user@testuser.com", + "identification": {"type": "CPF", "number": "19119119100"} + }, + "payments": [ + { + "id": 1001, + "status": "pending", + "payment_method_id": "visa", + "transaction_amount": 100.0, + "installments": 1 + } + ], + "disbursements": [ + { + "id": 2001, + "collector_id": 843382748, + "amount": 100.0, + "external_reference": "ref-001", + "status": "pending" + } + ] +} diff --git a/tests/resources/mocks/advanced_payment_get.json b/tests/resources/mocks/advanced_payment_get.json new file mode 100644 index 0000000..721c957 --- /dev/null +++ b/tests/resources/mocks/advanced_payment_get.json @@ -0,0 +1,32 @@ +{ + "id": 999, + "status": "approved", + "status_detail": "accredited", + "application_id": "app-001", + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:01.000-03:00", + "payer": { + "id": "123456789", + "email": "test_user@testuser.com", + "identification": {"type": "CPF", "number": "19119119100"} + }, + "payments": [ + { + "id": 1001, + "status": "approved", + "payment_method_id": "visa", + "transaction_amount": 100.0, + "installments": 1, + "token": "token-001" + } + ], + "disbursements": [ + { + "id": 2001, + "collector_id": 843382748, + "amount": 100.0, + "external_reference": "ref-001", + "status": "approved" + } + ] +} diff --git a/tests/resources/mocks/advanced_payment_search.json b/tests/resources/mocks/advanced_payment_search.json new file mode 100644 index 0000000..b871eb3 --- /dev/null +++ b/tests/resources/mocks/advanced_payment_search.json @@ -0,0 +1,12 @@ +{ + "results": [ + { + "id": 999, + "status": "approved", + "status_detail": "accredited", + "date_created": "2024-01-15T10:30:00.000-03:00", + "payer": {"email": "test_user@testuser.com"} + } + ], + "paging": {"total": 1, "limit": 10, "offset": 0} +} diff --git a/tests/resources/mocks/advanced_payment_update.json b/tests/resources/mocks/advanced_payment_update.json new file mode 100644 index 0000000..c980c70 --- /dev/null +++ b/tests/resources/mocks/advanced_payment_update.json @@ -0,0 +1,6 @@ +{ + "id": 999, + "status": "approved", + "status_detail": "accredited", + "date_last_updated": "2024-01-15T11:00:00.000-03:00" +} diff --git a/tests/resources/mocks/card_create.json b/tests/resources/mocks/card_create.json new file mode 100644 index 0000000..ecf0d11 --- /dev/null +++ b/tests/resources/mocks/card_create.json @@ -0,0 +1,14 @@ +{ + "id": "1562188766852", + "customer_id": "1068193981-pXRewrKqlP6pnn", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "cardholder": {"name": "APRO", "identification": {"type": "CPF", "number": "19119119100"}}, + "payment_method": {"id": "master", "name": "Mastercard", "payment_type_id": "credit_card"}, + "issuer": {"id": "24", "name": "Mastercard"}, + "security_code": {"length": 3, "card_location": "back"} +} diff --git a/tests/resources/mocks/card_get.json b/tests/resources/mocks/card_get.json new file mode 100644 index 0000000..ecf0d11 --- /dev/null +++ b/tests/resources/mocks/card_get.json @@ -0,0 +1,14 @@ +{ + "id": "1562188766852", + "customer_id": "1068193981-pXRewrKqlP6pnn", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "cardholder": {"name": "APRO", "identification": {"type": "CPF", "number": "19119119100"}}, + "payment_method": {"id": "master", "name": "Mastercard", "payment_type_id": "credit_card"}, + "issuer": {"id": "24", "name": "Mastercard"}, + "security_code": {"length": 3, "card_location": "back"} +} diff --git a/tests/resources/mocks/card_list_all.json b/tests/resources/mocks/card_list_all.json new file mode 100644 index 0000000..128be5d --- /dev/null +++ b/tests/resources/mocks/card_list_all.json @@ -0,0 +1,14 @@ +[ + { + "id": "1562188766852", + "customer_id": "1068193981-pXRewrKqlP6pnn", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "cardholder": {"name": "APRO"}, + "payment_method": {"id": "master", "name": "Mastercard", "payment_type_id": "credit_card"}, + "issuer": {"id": "24", "name": "Mastercard"} + } +] diff --git a/tests/resources/mocks/card_token_create.json b/tests/resources/mocks/card_token_create.json new file mode 100644 index 0000000..f32de1b --- /dev/null +++ b/tests/resources/mocks/card_token_create.json @@ -0,0 +1,13 @@ +{ + "id": "a78sd6f1a9s8d7f1a", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_due": "2024-01-15T12:30:00.000-03:00", + "luhn_validation": true, + "status": "active", + "card_id": null, + "cardholder": {"name": "APRO", "identification": {"type": "CPF", "number": "19119119100"}} +} diff --git a/tests/resources/mocks/card_token_get.json b/tests/resources/mocks/card_token_get.json new file mode 100644 index 0000000..f32de1b --- /dev/null +++ b/tests/resources/mocks/card_token_get.json @@ -0,0 +1,13 @@ +{ + "id": "a78sd6f1a9s8d7f1a", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_due": "2024-01-15T12:30:00.000-03:00", + "luhn_validation": true, + "status": "active", + "card_id": null, + "cardholder": {"name": "APRO", "identification": {"type": "CPF", "number": "19119119100"}} +} diff --git a/tests/resources/mocks/card_update.json b/tests/resources/mocks/card_update.json new file mode 100644 index 0000000..f042364 --- /dev/null +++ b/tests/resources/mocks/card_update.json @@ -0,0 +1,14 @@ +{ + "id": "1562188766852", + "customer_id": "1068193981-pXRewrKqlP6pnn", + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T11:00:00.000-03:00", + "cardholder": {"name": "APRO"}, + "payment_method": {"id": "master", "name": "Mastercard", "payment_type_id": "credit_card"}, + "issuer": {"id": "24", "name": "Mastercard"}, + "security_code": {"length": 3, "card_location": "back"} +} diff --git a/tests/resources/mocks/chargeback_get.json b/tests/resources/mocks/chargeback_get.json new file mode 100644 index 0000000..8707450 --- /dev/null +++ b/tests/resources/mocks/chargeback_get.json @@ -0,0 +1,9 @@ +{ + "id": "cb-001", + "payment_id": 17014025134, + "amount": 58.80, + "status": "in_process", + "date_created": "2024-01-20T09:00:00.000-03:00", + "currency_id": "BRL", + "reason": "buyer_complaint" +} diff --git a/tests/resources/mocks/chargeback_search.json b/tests/resources/mocks/chargeback_search.json new file mode 100644 index 0000000..b684cc0 --- /dev/null +++ b/tests/resources/mocks/chargeback_search.json @@ -0,0 +1,12 @@ +{ + "results": [ + { + "id": "cb-001", + "payment_id": 17014025134, + "amount": 58.80, + "status": "in_process", + "date_created": "2024-01-20T09:00:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 10, "offset": 0} +} diff --git a/tests/resources/mocks/customer_create.json b/tests/resources/mocks/customer_create.json new file mode 100644 index 0000000..b77d622 --- /dev/null +++ b/tests/resources/mocks/customer_create.json @@ -0,0 +1,16 @@ +{ + "id": "1068193981-pXRewrKqlP6pnn", + "email": "test_user@testuser.com", + "first_name": "Test", + "last_name": "User", + "phone": {"area_code": "11", "number": "987654321"}, + "identification": {"type": "CPF", "number": "19119119100"}, + "address": {"zip_code": "01313000", "street_name": "Av Paulista", "street_number": "1000"}, + "date_registered": "2024-01-01T00:00:00.000-03:00", + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "description": "Test customer", + "metadata": {}, + "cards": [], + "default_card": null +} diff --git a/tests/resources/mocks/customer_delete.json b/tests/resources/mocks/customer_delete.json new file mode 100644 index 0000000..b714913 --- /dev/null +++ b/tests/resources/mocks/customer_delete.json @@ -0,0 +1,3 @@ +{ + "id": "1068193981-pXRewrKqlP6pnn" +} diff --git a/tests/resources/mocks/customer_get.json b/tests/resources/mocks/customer_get.json new file mode 100644 index 0000000..b77d622 --- /dev/null +++ b/tests/resources/mocks/customer_get.json @@ -0,0 +1,16 @@ +{ + "id": "1068193981-pXRewrKqlP6pnn", + "email": "test_user@testuser.com", + "first_name": "Test", + "last_name": "User", + "phone": {"area_code": "11", "number": "987654321"}, + "identification": {"type": "CPF", "number": "19119119100"}, + "address": {"zip_code": "01313000", "street_name": "Av Paulista", "street_number": "1000"}, + "date_registered": "2024-01-01T00:00:00.000-03:00", + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "description": "Test customer", + "metadata": {}, + "cards": [], + "default_card": null +} diff --git a/tests/resources/mocks/customer_search.json b/tests/resources/mocks/customer_search.json new file mode 100644 index 0000000..4371e15 --- /dev/null +++ b/tests/resources/mocks/customer_search.json @@ -0,0 +1,12 @@ +{ + "results": [ + { + "id": "1068193981-pXRewrKqlP6pnn", + "email": "test_user@testuser.com", + "first_name": "Test", + "last_name": "User", + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 30, "offset": 0} +} diff --git a/tests/resources/mocks/customer_update.json b/tests/resources/mocks/customer_update.json new file mode 100644 index 0000000..d2bf8a8 --- /dev/null +++ b/tests/resources/mocks/customer_update.json @@ -0,0 +1,13 @@ +{ + "id": "1068193981-pXRewrKqlP6pnn", + "email": "test_user@testuser.com", + "first_name": "Test", + "last_name": "Updated", + "phone": {"area_code": "11", "number": "987654321"}, + "identification": {"type": "CPF", "number": "19119119100"}, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_last_updated": "2024-01-15T11:00:00.000-03:00", + "metadata": {}, + "cards": [], + "default_card": null +} diff --git a/tests/resources/mocks/disbursement_refund_create.json b/tests/resources/mocks/disbursement_refund_create.json new file mode 100644 index 0000000..cdb7325 --- /dev/null +++ b/tests/resources/mocks/disbursement_refund_create.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "disbursement_id": 10, + "amount": 25.0, + "status": "approved", + "date_created": "2024-01-16T09:00:00.000-03:00" +} diff --git a/tests/resources/mocks/disbursement_refund_create_all.json b/tests/resources/mocks/disbursement_refund_create_all.json new file mode 100644 index 0000000..7dd0dd2 --- /dev/null +++ b/tests/resources/mocks/disbursement_refund_create_all.json @@ -0,0 +1,7 @@ +{ + "id": 1, + "advanced_payment_id": 999, + "status": "approved", + "amount": 100.0, + "date_created": "2024-01-16T09:00:00.000-03:00" +} diff --git a/tests/resources/mocks/disbursement_refund_list_all.json b/tests/resources/mocks/disbursement_refund_list_all.json new file mode 100644 index 0000000..fc4da71 --- /dev/null +++ b/tests/resources/mocks/disbursement_refund_list_all.json @@ -0,0 +1,9 @@ +[ + { + "id": 1, + "advanced_payment_id": 999, + "amount": 50.0, + "status": "approved", + "date_created": "2024-01-16T09:00:00.000-03:00" + } +] diff --git a/tests/resources/mocks/identification_type_list_all.json b/tests/resources/mocks/identification_type_list_all.json new file mode 100644 index 0000000..c8e07fe --- /dev/null +++ b/tests/resources/mocks/identification_type_list_all.json @@ -0,0 +1 @@ +[{"id": "CPF", "name": "CPF", "type": "number", "min_length": 11, "max_length": 11}, {"id": "CNPJ", "name": "CNPJ", "type": "number", "min_length": 14, "max_length": 14}] diff --git a/tests/resources/mocks/invoice_get.json b/tests/resources/mocks/invoice_get.json new file mode 100644 index 0000000..5299c3b --- /dev/null +++ b/tests/resources/mocks/invoice_get.json @@ -0,0 +1,9 @@ +{ + "id": "inv-001", + "status": "processed", + "transaction_amount": 29.90, + "currency_id": "BRL", + "preapproval_id": "2c938084726fca480172750000000000", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:01.000-03:00" +} diff --git a/tests/resources/mocks/invoice_search.json b/tests/resources/mocks/invoice_search.json new file mode 100644 index 0000000..b4c0106 --- /dev/null +++ b/tests/resources/mocks/invoice_search.json @@ -0,0 +1,13 @@ +{ + "results": [ + { + "id": "inv-001", + "status": "processed", + "transaction_amount": 29.90, + "currency_id": "BRL", + "preapproval_id": "2c938084726fca480172750000000000", + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 10, "offset": 0} +} diff --git a/tests/resources/mocks/merchant_order_create.json b/tests/resources/mocks/merchant_order_create.json new file mode 100644 index 0000000..a1a644c --- /dev/null +++ b/tests/resources/mocks/merchant_order_create.json @@ -0,0 +1,23 @@ +{ + "id": 4049696864, + "status": "opened", + "external_reference": "ext-ref-001", + "preference_id": "843382748-18d90a57-a4ce-4718", + "payments": [], + "items": [ + { + "id": "item-001", + "title": "Point Mini", + "description": "Maquininha", + "quantity": 1, + "unit_price": 58.80, + "currency_id": "BRL" + } + ], + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_updated": "2024-01-15T10:30:00.000-03:00", + "total_amount": 58.80, + "paid_amount": 0.0, + "refunded_amount": 0.0, + "notification_url": "https://example.com/notifications" +} diff --git a/tests/resources/mocks/merchant_order_get.json b/tests/resources/mocks/merchant_order_get.json new file mode 100644 index 0000000..a1a644c --- /dev/null +++ b/tests/resources/mocks/merchant_order_get.json @@ -0,0 +1,23 @@ +{ + "id": 4049696864, + "status": "opened", + "external_reference": "ext-ref-001", + "preference_id": "843382748-18d90a57-a4ce-4718", + "payments": [], + "items": [ + { + "id": "item-001", + "title": "Point Mini", + "description": "Maquininha", + "quantity": 1, + "unit_price": 58.80, + "currency_id": "BRL" + } + ], + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_updated": "2024-01-15T10:30:00.000-03:00", + "total_amount": 58.80, + "paid_amount": 0.0, + "refunded_amount": 0.0, + "notification_url": "https://example.com/notifications" +} diff --git a/tests/resources/mocks/merchant_order_search.json b/tests/resources/mocks/merchant_order_search.json new file mode 100644 index 0000000..f90a748 --- /dev/null +++ b/tests/resources/mocks/merchant_order_search.json @@ -0,0 +1,14 @@ +{ + "elements": [ + { + "id": 4049696864, + "status": "opened", + "external_reference": "ext-ref-001", + "preference_id": "843382748-18d90a57-a4ce-4718", + "total_amount": 58.80, + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "total": 1, + "next_offset": 0 +} diff --git a/tests/resources/mocks/merchant_order_update.json b/tests/resources/mocks/merchant_order_update.json new file mode 100644 index 0000000..18109dc --- /dev/null +++ b/tests/resources/mocks/merchant_order_update.json @@ -0,0 +1,8 @@ +{ + "id": 4049696864, + "status": "opened", + "external_reference": "ext-updated", + "preference_id": "843382748-18d90a57-a4ce-4718", + "last_updated": "2024-01-15T11:00:00.000-03:00", + "total_amount": 58.80 +} diff --git a/tests/resources/mocks/oauth_create.json b/tests/resources/mocks/oauth_create.json new file mode 100644 index 0000000..5d76906 --- /dev/null +++ b/tests/resources/mocks/oauth_create.json @@ -0,0 +1,9 @@ +{ + "access_token": "APP_USR-001", + "token_type": "bearer", + "expires_in": 15552000, + "scope": "offline_access payments read write", + "user_id": 123456789, + "refresh_token": "TG-001-test-refresh-token", + "live_mode": true +} diff --git a/tests/resources/mocks/order_cancel.json b/tests/resources/mocks/order_cancel.json new file mode 100644 index 0000000..8b16453 --- /dev/null +++ b/tests/resources/mocks/order_cancel.json @@ -0,0 +1,7 @@ +{ + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "status": "canceled", + "status_detail": "order_canceled", + "total_amount": "1000.00", + "last_updated_date": "2024-01-15T11:00:00.000Z" +} diff --git a/tests/resources/mocks/order_capture.json b/tests/resources/mocks/order_capture.json new file mode 100644 index 0000000..71d200b --- /dev/null +++ b/tests/resources/mocks/order_capture.json @@ -0,0 +1,8 @@ +{ + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "status": "processed", + "status_detail": "fully_processed", + "total_amount": "1000.00", + "total_paid_amount": "1000.00", + "last_updated_date": "2024-01-15T10:30:02.000Z" +} diff --git a/tests/resources/mocks/order_create.json b/tests/resources/mocks/order_create.json new file mode 100644 index 0000000..1019b2c --- /dev/null +++ b/tests/resources/mocks/order_create.json @@ -0,0 +1,29 @@ +{ + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "type": "online", + "processing_mode": "automatic", + "capture_mode": "automatic_async", + "status": "processed", + "status_detail": "fully_processed", + "external_reference": "ext-ref-001", + "total_amount": "1000.00", + "total_paid_amount": "1000.00", + "description": "Order description", + "country_code": "BR", + "created_date": "2024-01-15T10:30:00.000Z", + "last_updated_date": "2024-01-15T10:30:01.000Z", + "payer": {"email": "test_user@testuser.com", "entity_type": "individual"}, + "items": [{"id": "item-001", "title": "Point Mini", "quantity": 1, "unit_price": "1000.00"}], + "transactions": { + "payments": [ + { + "id": "pay-001", + "amount": "1000.00", + "status": "processed", + "reference_id": "ref-001", + "status_detail": "accredited" + } + ] + }, + "integration_data": {"application_id": "12345"} +} diff --git a/tests/resources/mocks/order_get.json b/tests/resources/mocks/order_get.json new file mode 100644 index 0000000..1019b2c --- /dev/null +++ b/tests/resources/mocks/order_get.json @@ -0,0 +1,29 @@ +{ + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "type": "online", + "processing_mode": "automatic", + "capture_mode": "automatic_async", + "status": "processed", + "status_detail": "fully_processed", + "external_reference": "ext-ref-001", + "total_amount": "1000.00", + "total_paid_amount": "1000.00", + "description": "Order description", + "country_code": "BR", + "created_date": "2024-01-15T10:30:00.000Z", + "last_updated_date": "2024-01-15T10:30:01.000Z", + "payer": {"email": "test_user@testuser.com", "entity_type": "individual"}, + "items": [{"id": "item-001", "title": "Point Mini", "quantity": 1, "unit_price": "1000.00"}], + "transactions": { + "payments": [ + { + "id": "pay-001", + "amount": "1000.00", + "status": "processed", + "reference_id": "ref-001", + "status_detail": "accredited" + } + ] + }, + "integration_data": {"application_id": "12345"} +} diff --git a/tests/resources/mocks/order_process.json b/tests/resources/mocks/order_process.json new file mode 100644 index 0000000..b62973b --- /dev/null +++ b/tests/resources/mocks/order_process.json @@ -0,0 +1,8 @@ +{ + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "status": "processed", + "status_detail": "fully_processed", + "total_amount": "1000.00", + "total_paid_amount": "1000.00", + "last_updated_date": "2024-01-15T10:30:01.000Z" +} diff --git a/tests/resources/mocks/order_refund.json b/tests/resources/mocks/order_refund.json new file mode 100644 index 0000000..08eed71 --- /dev/null +++ b/tests/resources/mocks/order_refund.json @@ -0,0 +1,6 @@ +{ + "id": "refund-01JKXTQ2-001", + "status": "approved", + "amount": "1000.00", + "date_created": "2024-01-16T09:00:00.000Z" +} diff --git a/tests/resources/mocks/order_search.json b/tests/resources/mocks/order_search.json new file mode 100644 index 0000000..c8970b3 --- /dev/null +++ b/tests/resources/mocks/order_search.json @@ -0,0 +1,12 @@ +{ + "data": [ + { + "id": "01JKXTQ2AZVE0RANCPYA6WBNPW", + "status": "processed", + "total_amount": "1000.00", + "created_date": "2024-01-15T10:30:00.000Z", + "payer": {"email": "test_user@testuser.com"} + } + ], + "paging": {"total": 1, "limit": 10, "offset": 0} +} diff --git a/tests/resources/mocks/payment_create.json b/tests/resources/mocks/payment_create.json new file mode 100644 index 0000000..397e051 --- /dev/null +++ b/tests/resources/mocks/payment_create.json @@ -0,0 +1,32 @@ +{ + "id": 17014025134, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_approved": null, + "date_last_updated": "2024-01-15T10:30:00.000-03:00", + "operation_type": "regular_payment", + "issuer_id": "24", + "payment_method_id": "visa", + "payment_type_id": "credit_card", + "status": "pending", + "status_detail": "pending_waiting_payment", + "currency_id": "BRL", + "description": "Point Mini", + "transaction_amount": 58.80, + "transaction_amount_refunded": 0.0, + "coupon_amount": 0.0, + "captured": false, + "binary_mode": false, + "installments": 1, + "processing_mode": "aggregator", + "external_reference": "MP0001", + "payer": { + "email": "test_user@testuser.com", + "identification": {"type": "CPF", "number": "19119119100"} + }, + "transaction_details": { + "net_received_amount": 0.0, + "total_paid_amount": 58.80, + "overpaid_amount": 0.0, + "installment_amount": 58.80 + } +} diff --git a/tests/resources/mocks/payment_get.json b/tests/resources/mocks/payment_get.json new file mode 100644 index 0000000..30c99fd --- /dev/null +++ b/tests/resources/mocks/payment_get.json @@ -0,0 +1,47 @@ +{ + "id": 17014025134, + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_approved": "2024-01-15T10:30:01.000-03:00", + "date_last_updated": "2024-01-15T10:30:01.000-03:00", + "date_of_expiration": null, + "money_release_date": "2024-01-29T10:30:01.000-03:00", + "operation_type": "regular_payment", + "issuer_id": "24", + "payment_method_id": "visa", + "payment_type_id": "credit_card", + "status": "approved", + "status_detail": "accredited", + "currency_id": "BRL", + "description": "Point Mini", + "transaction_amount": 58.80, + "transaction_amount_refunded": 0.0, + "coupon_amount": 0.0, + "captured": true, + "binary_mode": false, + "installments": 1, + "statement_descriptor": "MercadoPago", + "processing_mode": "aggregator", + "external_reference": "MP0001", + "notification_url": null, + "payer": { + "id": "123456789", + "email": "test_user@testuser.com", + "identification": {"type": "CPF", "number": "19119119100"}, + "type": "customer" + }, + "metadata": {}, + "additional_info": {}, + "transaction_details": { + "net_received_amount": 56.86, + "total_paid_amount": 58.80, + "overpaid_amount": 0.0, + "installment_amount": 58.80 + }, + "card": { + "first_six_digits": "503143", + "last_four_digits": "6351", + "expiration_month": 11, + "expiration_year": 2030, + "cardholder": {"name": "APRO"} + } +} diff --git a/tests/resources/mocks/payment_methods_list_all.json b/tests/resources/mocks/payment_methods_list_all.json new file mode 100644 index 0000000..2d8385d --- /dev/null +++ b/tests/resources/mocks/payment_methods_list_all.json @@ -0,0 +1 @@ +[{"id": "visa", "name": "Visa", "payment_type_id": "credit_card", "status": "active"}, {"id": "master", "name": "Mastercard", "payment_type_id": "credit_card", "status": "active"}] diff --git a/tests/resources/mocks/payment_search.json b/tests/resources/mocks/payment_search.json new file mode 100644 index 0000000..b569d6c --- /dev/null +++ b/tests/resources/mocks/payment_search.json @@ -0,0 +1,19 @@ +{ + "results": [ + { + "id": 17014025134, + "status": "approved", + "status_detail": "accredited", + "transaction_amount": 58.80, + "payment_method_id": "visa", + "payment_type_id": "credit_card", + "currency_id": "BRL", + "date_created": "2024-01-15T10:30:00.000-03:00", + "date_approved": "2024-01-15T10:30:01.000-03:00", + "payer": {"email": "test_user@testuser.com"}, + "installments": 1, + "captured": true + } + ], + "paging": {"total": 1, "limit": 30, "offset": 0} +} diff --git a/tests/resources/mocks/payment_update.json b/tests/resources/mocks/payment_update.json new file mode 100644 index 0000000..56795a3 --- /dev/null +++ b/tests/resources/mocks/payment_update.json @@ -0,0 +1,9 @@ +{ + "id": 17014025134, + "status": "cancelled", + "status_detail": "by_collector", + "date_last_updated": "2024-01-15T11:00:00.000-03:00", + "transaction_amount": 58.80, + "payment_method_id": "visa", + "currency_id": "BRL" +} diff --git a/tests/resources/mocks/plan_create.json b/tests/resources/mocks/plan_create.json new file mode 100644 index 0000000..c8083a3 --- /dev/null +++ b/tests/resources/mocks/plan_create.json @@ -0,0 +1,14 @@ +{ + "id": "2c938084726fca480172750000000002", + "reason": "Monthly Plan - Basic", + "status": "active", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?plan_id=2c938084726fca480172750000000002" +} diff --git a/tests/resources/mocks/plan_get.json b/tests/resources/mocks/plan_get.json new file mode 100644 index 0000000..c8083a3 --- /dev/null +++ b/tests/resources/mocks/plan_get.json @@ -0,0 +1,14 @@ +{ + "id": "2c938084726fca480172750000000002", + "reason": "Monthly Plan - Basic", + "status": "active", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?plan_id=2c938084726fca480172750000000002" +} diff --git a/tests/resources/mocks/plan_search.json b/tests/resources/mocks/plan_search.json new file mode 100644 index 0000000..67ee591 --- /dev/null +++ b/tests/resources/mocks/plan_search.json @@ -0,0 +1,11 @@ +{ + "results": [ + { + "id": "2c938084726fca480172750000000002", + "status": "active", + "reason": "Monthly Plan - Basic", + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 30, "offset": 0} +} diff --git a/tests/resources/mocks/plan_update.json b/tests/resources/mocks/plan_update.json new file mode 100644 index 0000000..fdfdb81 --- /dev/null +++ b/tests/resources/mocks/plan_update.json @@ -0,0 +1,12 @@ +{ + "id": "2c938084726fca480172750000000002", + "reason": "Monthly Plan - Basic", + "status": "inactive", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "last_modified": "2024-01-15T11:00:00.000-03:00" +} diff --git a/tests/resources/mocks/point_create.json b/tests/resources/mocks/point_create.json new file mode 100644 index 0000000..b95ea6f --- /dev/null +++ b/tests/resources/mocks/point_create.json @@ -0,0 +1,9 @@ +{ + "id": "intent-001", + "state": "OPEN", + "device_id": "device-001", + "amount": 58.80, + "description": "Test payment", + "payment": {"installments": 1, "type": "credit_card"}, + "additional_info": {"external_reference": "ref-001"} +} diff --git a/tests/resources/mocks/point_get.json b/tests/resources/mocks/point_get.json new file mode 100644 index 0000000..e9c0642 --- /dev/null +++ b/tests/resources/mocks/point_get.json @@ -0,0 +1,9 @@ +{ + "id": "intent-001", + "state": "FINISHED", + "device_id": "device-001", + "amount": 58.80, + "description": "Test payment", + "payment": {"installments": 1, "type": "credit_card"}, + "additional_info": {"external_reference": "ref-001"} +} diff --git a/tests/resources/mocks/point_get_devices.json b/tests/resources/mocks/point_get_devices.json new file mode 100644 index 0000000..5f08f1e --- /dev/null +++ b/tests/resources/mocks/point_get_devices.json @@ -0,0 +1,10 @@ +{ + "devices": [ + { + "id": "device-001", + "operating_mode": "PDV", + "store_id": "store-001", + "pos_id": "pos-001" + } + ] +} diff --git a/tests/resources/mocks/preapproval_create.json b/tests/resources/mocks/preapproval_create.json new file mode 100644 index 0000000..3f9f900 --- /dev/null +++ b/tests/resources/mocks/preapproval_create.json @@ -0,0 +1,19 @@ +{ + "id": "2c938084726fca480172750000000000", + "payer_id": 123456789, + "payer_email": "test_user@testuser.com", + "preapproval_plan_id": "2c938084726fca480172750000000001", + "reason": "Monthly subscription", + "external_reference": "sub-ext-001", + "status": "pending", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "payment_method_id": "master", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_id=2c938084726fca480172750000000000" +} diff --git a/tests/resources/mocks/preapproval_get.json b/tests/resources/mocks/preapproval_get.json new file mode 100644 index 0000000..2941ace --- /dev/null +++ b/tests/resources/mocks/preapproval_get.json @@ -0,0 +1,21 @@ +{ + "id": "2c938084726fca480172750000000000", + "payer_id": 123456789, + "payer_email": "test_user@testuser.com", + "preapproval_plan_id": "2c938084726fca480172750000000001", + "reason": "Monthly subscription", + "external_reference": "sub-ext-001", + "status": "authorized", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "next_payment_date": "2024-02-15T10:30:00.000-03:00", + "payment_method_id": "master", + "card_id": null, + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_id=2c938084726fca480172750000000000" +} diff --git a/tests/resources/mocks/preapproval_search.json b/tests/resources/mocks/preapproval_search.json new file mode 100644 index 0000000..57b3887 --- /dev/null +++ b/tests/resources/mocks/preapproval_search.json @@ -0,0 +1,12 @@ +{ + "results": [ + { + "id": "2c938084726fca480172750000000000", + "status": "authorized", + "reason": "Monthly subscription", + "payer_email": "test_user@testuser.com", + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 30, "offset": 0} +} diff --git a/tests/resources/mocks/preapproval_update.json b/tests/resources/mocks/preapproval_update.json new file mode 100644 index 0000000..76b19b8 --- /dev/null +++ b/tests/resources/mocks/preapproval_update.json @@ -0,0 +1,7 @@ +{ + "id": "2c938084726fca480172750000000000", + "status": "cancelled", + "reason": "Monthly subscription", + "last_modified": "2024-01-15T11:00:00.000-03:00", + "payer_email": "test_user@testuser.com" +} diff --git a/tests/resources/mocks/preference_create.json b/tests/resources/mocks/preference_create.json new file mode 100644 index 0000000..63aab09 --- /dev/null +++ b/tests/resources/mocks/preference_create.json @@ -0,0 +1,37 @@ +{ + "id": "843382748-18d90a57-a4ce-4718-bc17-1234567890", + "init_point": "https://www.mercadopago.com.br/checkout/v1/redirect?pref_id=843382748-18d90a57-a4ce-4718-bc17-1234567890", + "items": [ + { + "id": "item-001", + "title": "Point Mini", + "description": "Maquininha BT", + "quantity": 1, + "unit_price": 58.80, + "currency_id": "BRL" + } + ], + "payer": { + "name": "Test", + "surname": "User", + "email": "test_user@testuser.com" + }, + "payment_methods": { + "installments": 12, + "excluded_payment_types": [] + }, + "back_urls": { + "success": "https://example.com/success", + "failure": "https://example.com/failure", + "pending": "https://example.com/pending" + }, + "external_reference": "MP-PREF-001", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_updated": "2024-01-15T10:30:00.000-03:00", + "total_amount": 58.80, + "expires": false, + "collector_id": 843382748, + "client_id": "12345", + "marketplace": "NONE", + "marketplace_fee": 0.0 +} diff --git a/tests/resources/mocks/preference_get.json b/tests/resources/mocks/preference_get.json new file mode 100644 index 0000000..258ba43 --- /dev/null +++ b/tests/resources/mocks/preference_get.json @@ -0,0 +1,39 @@ +{ + "id": "843382748-18d90a57-a4ce-4718-bc17-1234567890", + "init_point": "https://www.mercadopago.com.br/checkout/v1/redirect?pref_id=843382748-18d90a57-a4ce-4718-bc17-1234567890", + "items": [ + { + "id": "item-001", + "title": "Point Mini", + "description": "Maquininha BT", + "quantity": 1, + "unit_price": 58.80, + "currency_id": "BRL" + } + ], + "payer": { + "name": "Test", + "surname": "User", + "email": "test_user@testuser.com" + }, + "payment_methods": { + "installments": 12, + "excluded_payment_types": [] + }, + "back_urls": { + "success": "https://example.com/success", + "failure": "https://example.com/failure", + "pending": "https://example.com/pending" + }, + "external_reference": "MP-PREF-001", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_updated": "2024-01-15T10:30:00.000-03:00", + "total_amount": 58.80, + "expires": false, + "expiration_date_from": null, + "expiration_date_to": null, + "collector_id": 843382748, + "client_id": "12345", + "marketplace": "NONE", + "marketplace_fee": 0.0 +} diff --git a/tests/resources/mocks/preference_search.json b/tests/resources/mocks/preference_search.json new file mode 100644 index 0000000..5a21f49 --- /dev/null +++ b/tests/resources/mocks/preference_search.json @@ -0,0 +1 @@ +{"elements": [{"id": "pref-001"}], "total": 1, "next_offset": 0} diff --git a/tests/resources/mocks/preference_update.json b/tests/resources/mocks/preference_update.json new file mode 100644 index 0000000..0d64ce2 --- /dev/null +++ b/tests/resources/mocks/preference_update.json @@ -0,0 +1,17 @@ +{ + "id": "843382748-18d90a57-a4ce-4718-bc17-1234567890", + "init_point": "https://www.mercadopago.com.br/checkout/v1/redirect?pref_id=843382748-18d90a57-a4ce-4718-bc17-1234567890", + "items": [ + { + "id": "item-002", + "title": "Updated Item", + "quantity": 1, + "unit_price": 30.0, + "currency_id": "BRL" + } + ], + "external_reference": "MP-PREF-001", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_updated": "2024-01-15T11:00:00.000-03:00", + "collector_id": 843382748 +} diff --git a/tests/resources/mocks/refund_create.json b/tests/resources/mocks/refund_create.json new file mode 100644 index 0000000..94d0c73 --- /dev/null +++ b/tests/resources/mocks/refund_create.json @@ -0,0 +1,9 @@ +{ + "id": 1091618291, + "payment_id": 17014025134, + "amount": 58.80, + "date_created": "2024-01-16T09:00:00.000-03:00", + "source": {"id": "123456789", "name": "Test User", "type": "operator"}, + "refund_mode": "standard", + "status": "approved" +} diff --git a/tests/resources/mocks/refund_get.json b/tests/resources/mocks/refund_get.json new file mode 100644 index 0000000..94d0c73 --- /dev/null +++ b/tests/resources/mocks/refund_get.json @@ -0,0 +1,9 @@ +{ + "id": 1091618291, + "payment_id": 17014025134, + "amount": 58.80, + "date_created": "2024-01-16T09:00:00.000-03:00", + "source": {"id": "123456789", "name": "Test User", "type": "operator"}, + "refund_mode": "standard", + "status": "approved" +} diff --git a/tests/resources/mocks/refund_list_all.json b/tests/resources/mocks/refund_list_all.json new file mode 100644 index 0000000..7f65d11 --- /dev/null +++ b/tests/resources/mocks/refund_list_all.json @@ -0,0 +1,11 @@ +[ + { + "id": 1091618291, + "payment_id": 17014025134, + "amount": 58.80, + "date_created": "2024-01-16T09:00:00.000-03:00", + "source": {"id": "123456789", "name": "Test User", "type": "operator"}, + "refund_mode": "standard", + "status": "approved" + } +] diff --git a/tests/resources/mocks/subscription_create.json b/tests/resources/mocks/subscription_create.json new file mode 100644 index 0000000..0caf408 --- /dev/null +++ b/tests/resources/mocks/subscription_create.json @@ -0,0 +1,18 @@ +{ + "id": "2c938084726fca480172750000000003", + "payer_id": 123456789, + "payer_email": "test_user@testuser.com", + "preapproval_plan_id": "2c938084726fca480172750000000002", + "reason": "Monthly subscription", + "external_reference": "sub-ext-001", + "status": "pending", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "payment_method_id": "master" +} diff --git a/tests/resources/mocks/subscription_get.json b/tests/resources/mocks/subscription_get.json new file mode 100644 index 0000000..dc37717 --- /dev/null +++ b/tests/resources/mocks/subscription_get.json @@ -0,0 +1,19 @@ +{ + "id": "2c938084726fca480172750000000003", + "payer_id": 123456789, + "payer_email": "test_user@testuser.com", + "preapproval_plan_id": "2c938084726fca480172750000000002", + "reason": "Monthly subscription", + "external_reference": "sub-ext-001", + "status": "authorized", + "date_created": "2024-01-15T10:30:00.000-03:00", + "last_modified": "2024-01-15T10:30:00.000-03:00", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 29.90, + "currency_id": "BRL" + }, + "next_payment_date": "2024-02-15T10:30:00.000-03:00", + "payment_method_id": "master" +} diff --git a/tests/resources/mocks/subscription_search.json b/tests/resources/mocks/subscription_search.json new file mode 100644 index 0000000..52562a1 --- /dev/null +++ b/tests/resources/mocks/subscription_search.json @@ -0,0 +1,13 @@ +{ + "results": [ + { + "id": "2c938084726fca480172750000000003", + "status": "authorized", + "reason": "Monthly subscription", + "payer_email": "test_user@testuser.com", + "preapproval_plan_id": "2c938084726fca480172750000000002", + "date_created": "2024-01-15T10:30:00.000-03:00" + } + ], + "paging": {"total": 1, "limit": 30, "offset": 0} +} diff --git a/tests/resources/mocks/subscription_update.json b/tests/resources/mocks/subscription_update.json new file mode 100644 index 0000000..f4f6189 --- /dev/null +++ b/tests/resources/mocks/subscription_update.json @@ -0,0 +1,7 @@ +{ + "id": "2c938084726fca480172750000000003", + "status": "cancelled", + "reason": "Monthly subscription", + "payer_email": "test_user@testuser.com", + "last_modified": "2024-01-15T11:00:00.000-03:00" +} diff --git a/tests/resources/mocks/user_get.json b/tests/resources/mocks/user_get.json new file mode 100644 index 0000000..47dd11c --- /dev/null +++ b/tests/resources/mocks/user_get.json @@ -0,0 +1 @@ +{"id": 12345, "email": "test@example.com", "site_id": "MLB", "country_id": "BR"} diff --git a/tests/test_advanced_payment.py b/tests/test_advanced_payment.py index 78f73db..3bc6921 100644 --- a/tests/test_advanced_payment.py +++ b/tests/test_advanced_payment.py @@ -1,116 +1,111 @@ -""" - Module: test_advanced_payment -""" -from datetime import ( - datetime, - timedelta, -) -import os +"""Unit tests for the AdvancedPayment resource using a mock HTTP client.""" +from datetime import datetime import unittest -import uuid -import mercadopago +from tests.base_client_test import BaseClientTest -class TestAdvancedPayment(unittest.TestCase): - """ - Test Module: Advanced Payment - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - @unittest.skip(reason="Outdated API usage") - def test_all(self): - """ - Test Function: Advanced Payment - """ - card_token_object = { - "card_number": "4074090000000004", - "security_code": "123", - "expiration_year": datetime.now().strftime("%Y"), - "expiration_month": "12", - "cardholder": { - "name": "APRO", - "identification": { - "CPF": "19119119100" - } - } - } +class TestAdvancedPayment(BaseClientTest): + """Test Module: AdvancedPayment""" - card_token_created = self.sdk.card_token().create(card_token_object) + def test_get(self): + fixture = self.load_fixture("advanced_payment_get.json") + self.mock_get(fixture) + result = self.sdk.advanced_payment().get(999) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertEqual("approved", resp["status"]) + self.assertEqual("accredited", resp["status_detail"]) + self.assertEqual("test_user@testuser.com", resp["payer"]["email"]) + self.assertIsInstance(resp["payments"], list) + self.assertEqual(1001, resp["payments"][0]["id"]) + self.assertEqual("approved", resp["payments"][0]["status"]) + self.assertIsInstance(resp["disbursements"], list) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() + def test_create(self): + fixture = self.load_fixture("advanced_payment_create.json") + self.mock_post(fixture, status=201) advanced_payment_object = { - "application_id": "59441713004005", - "payments": [{ - "payment_method_id": "master", - "payment_type_id": "credit_card", - "token": card_token_created["response"]["id"], - "date_of_expiration": (datetime.now() + timedelta(days=10)) - .strftime("%Y-%m-%d %H:%M:%S.%f"), - "transaction_amount": 100.0, - "installments": 1, - "processing_mode": "aggregator", - "description": "description", - "external_reference": str(uuid.uuid4().int), - "statement_descriptor": "ADVPAY" - }], - "disbursements": [{ - "collector_id": "488656838", - "amount": 60.0, - "external_reference": "Seller2" + str(uuid.uuid4().int), - "application_fee": 0.5 - }], - "payer": { - "id": "649457098-FybpOkG6zH8QRm", - "type": "customer", - "email": "test_payer_9999988@testuser.com", - "first_name": "Test", - "last_name": "User", - "address": { - "zip_code": "06233200", - "street_name": "Street", - "street_number": 123 - }, - "identification": { - "type": "CPF", - "number": "19119119100" - } - }, - "external_reference": "Adv" + str(uuid.uuid4().int), - "description": "description", - "binary_mode": False, - "capture": False, - "additional_info": { - "ip_address": "127.0.0.1", - "payer": { - "first_name": "Test", - "last_name": "User", - "registration_date": (datetime.now() - timedelta(days=10)) - .strftime("%Y-%m-%d %H:%M:%S.%f") - }, - "items": [{ - "id": "123", - "title": "title", - "picture_url": "https://www.mercadopago.com/logomp3.gif", - "description": "description", - "category_id": "category", - "quantity": 1, - "unit_price": 100.0 - }], - "shipments": { - "receiver_address": { - "zip_code": "06233200", - "street_name": "Street", - "street_number": 123 - } - } - } + "application_id": "app-001", + "payments": [{"payment_method_id": "visa", "token": "token-001", + "transaction_amount": 100.0, "installments": 1}], + "disbursements": [{"collector_id": 843382748, "amount": 100.0, "external_reference": "ref-001"}], + "payer": {"email": "test_user@testuser.com"}, } + result = self.sdk.advanced_payment().create(advanced_payment_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertEqual("pending", resp["status"]) + self.assertIsInstance(resp["payments"], list) + self.assertIsInstance(resp["disbursements"], list) + self.mock_http.post.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("advanced_payment_search.json") + self.mock_get(fixture) + result = self.sdk.advanced_payment().search({"status": "approved"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertIn("paging", resp) + self.assertEqual(999, resp["results"][0]["id"]) + self.assertEqual("approved", resp["results"][0]["status"]) + self.mock_http.get.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("advanced_payment_update.json") + self.mock_put(fixture) + result = self.sdk.advanced_payment().update(999, {"status": "approved"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertEqual("approved", resp["status"]) + self.mock_http.put.assert_called_once() + + def test_capture(self): + fixture = self.load_fixture("advanced_payment_update.json") + self.mock_put(fixture) + result = self.sdk.advanced_payment().capture(999) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertEqual("approved", resp["status"]) + self.mock_http.put.assert_called_once() + + def test_cancel(self): + self.mock_put({"id": 999, "status": "cancelled"}) + result = self.sdk.advanced_payment().cancel(999) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertEqual("cancelled", resp["status"]) + self.mock_http.put.assert_called_once() + + def test_update_release_date(self): + self.mock_post({"id": 999, "money_release_date": "2026-09-01 00:00:00.000000"}, status=201) + release_date = datetime(2026, 9, 1) + result = self.sdk.advanced_payment().update_release_date(999, release_date) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(999, resp["id"]) + self.assertIn("money_release_date", resp) + self.mock_http.post.assert_called_once() + + def test_update_release_date_raises_for_non_datetime(self): + with self.assertRaises(ValueError): + self.sdk.advanced_payment().update_release_date(999, "2026-09-01") - advanced_payment_created = self.sdk.advanced_payment().create(advanced_payment_object) - self.assertEqual(advanced_payment_created["status"], 201) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.advanced_payment().create("not-a-dict") - advanced_payment_found = self.sdk.advanced_payment().get( - advanced_payment_created["response"]["id"]) - self.assertEqual(advanced_payment_found["status"], 200) + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.advanced_payment().update(999, "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_card.py b/tests/test_card.py index 0564178..5ac7cdd 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -1,85 +1,89 @@ -""" - Module: test_card -""" -from datetime import datetime -import os -import random +"""Unit tests for the Card resource using a mock HTTP client.""" import unittest -import mercadopago - -class TestCard(unittest.TestCase): - """ - Test Module: Card - """ - - _customer_id = None - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - - @classmethod - def setUpClass(cls): - customer_data = cls.create_customer() - cls._customer_id = customer_data["response"]["id"] - - @classmethod - def tearDownClass(cls): - cls.delete_customer() - - def test_all(self): - """ - Test Function: Card - """ - - card_token_object = { - "card_number": "4074090000000004", - "security_code": "123", - "expiration_year": datetime.now().strftime("%Y"), - "expiration_month": "12", - "cardholder": { - "name": "APRO", - "identification": { - "CPF": "19119119100" - } - } - } - - card_token_created = self.sdk.card_token().create(card_token_object) - - card_object = { - "customer_id": self._customer_id, - "token": card_token_created["response"]["id"] - } - - card_created = self.sdk.card().create(self._customer_id, card_object) - self.assertIn(card_created["status"], [200, 201]) - self.assertEqual(self.sdk.card().get( - self._customer_id, card_created["response"]["id"])["status"], 200) - - self.sdk.card().delete(self._customer_id, card_created["response"]["id"]) - - @classmethod - def create_customer(cls): - random_email_id = random.randint(100000, 999999) - customer_object = { - "email": f"test_payer_{random_email_id}@testuser.com", - "first_name": "Rafa", - "last_name": "Williner", - "phone": { - "area_code": "03492", - "number": "432334" - }, - "identification": { - "type": "DNI", - "number": "29804555" - }, - "description": "customer description" - } - - return cls.sdk.customer().create(customer_object) - - @classmethod - def delete_customer(cls): - cls.sdk.customer().delete(cls._customer_id) +from tests.base_client_test import BaseClientTest + + +class TestCard(BaseClientTest): + """Test Module: Card""" + + def test_list_all(self): + fixture = self.load_fixture("card_list_all.json") + self.mock_get(fixture) + result = self.sdk.card().list_all("1068193981-pXRewrKqlP6pnn") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIsInstance(resp, list) + self.assertEqual("1562188766852", resp[0]["id"]) + self.assertEqual("503143", resp[0]["first_six_digits"]) + self.assertEqual("6351", resp[0]["last_four_digits"]) + self.assertIn("payment_method", resp[0]) + self.mock_http.get.assert_called_once() + + def test_get(self): + fixture = self.load_fixture("card_get.json") + self.mock_get(fixture) + result = self.sdk.card().get("1068193981-pXRewrKqlP6pnn", "1562188766852") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("1562188766852", resp["id"]) + self.assertEqual("1068193981-pXRewrKqlP6pnn", resp["customer_id"]) + self.assertEqual("503143", resp["first_six_digits"]) + self.assertEqual("6351", resp["last_four_digits"]) + self.assertEqual(11, resp["expiration_month"]) + self.assertEqual(2030, resp["expiration_year"]) + self.assertIn("cardholder", resp) + self.assertEqual("APRO", resp["cardholder"]["name"]) + self.assertIn("payment_method", resp) + self.assertEqual("master", resp["payment_method"]["id"]) + self.assertEqual("credit_card", resp["payment_method"]["payment_type_id"]) + self.assertIn("issuer", resp) + self.assertIn("security_code", resp) + self.assertIn("date_created", resp) + self.assertIn("date_last_updated", resp) + self.mock_http.get.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("card_create.json") + self.mock_post(fixture, status=201) + card_object = {"token": "token-001"} + result = self.sdk.card().create("1068193981-pXRewrKqlP6pnn", card_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("1562188766852", resp["id"]) + self.assertEqual("503143", resp["first_six_digits"]) + self.assertEqual("6351", resp["last_four_digits"]) + self.assertEqual(2030, resp["expiration_year"]) + self.assertIn("cardholder", resp) + self.assertIn("payment_method", resp) + self.assertIn("issuer", resp) + self.mock_http.post.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("card_update.json") + self.mock_put(fixture) + result = self.sdk.card().update( + "1068193981-pXRewrKqlP6pnn", "1562188766852", {"expiration_year": 2030}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("1562188766852", resp["id"]) + self.assertEqual(2030, resp["expiration_year"]) + self.assertIn("date_last_updated", resp) + self.mock_http.put.assert_called_once() + + def test_delete(self): + self.mock_delete({"id": "1562188766852"}, status=200) + result = self.sdk.card().delete("1068193981-pXRewrKqlP6pnn", "1562188766852") + self.assertEqual(200, result["status"]) + self.mock_http.delete.assert_called_once() + + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.card().create("1068193981-pXRewrKqlP6pnn", "not-a-dict") + + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.card().update("1068193981-pXRewrKqlP6pnn", "1562188766852", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_card_token.py b/tests/test_card_token.py index e56113f..431361d 100644 --- a/tests/test_card_token.py +++ b/tests/test_card_token.py @@ -1,40 +1,55 @@ -""" - Module: test_card_token -""" -from datetime import datetime -import os +"""Unit tests for the CardToken resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestCardToken(unittest.TestCase): - """ - Test Module: Card Token - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_all(self): - """ - Test Function: Card Token - """ +class TestCardToken(BaseClientTest): + """Test Module: CardToken""" + + def test_get(self): + fixture = self.load_fixture("card_token_get.json") + self.mock_get(fixture) + result = self.sdk.card_token().get("a78sd6f1a9s8d7f1a") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("a78sd6f1a9s8d7f1a", resp["id"]) + self.assertEqual("503143", resp["first_six_digits"]) + self.assertEqual("6351", resp["last_four_digits"]) + self.assertEqual(11, resp["expiration_month"]) + self.assertEqual(2030, resp["expiration_year"]) + self.assertTrue(resp["luhn_validation"]) + self.assertEqual("active", resp["status"]) + self.assertIn("date_created", resp) + self.assertIn("date_due", resp) + self.assertIn("cardholder", resp) + self.assertEqual("APRO", resp["cardholder"]["name"]) + self.mock_http.get.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("card_token_create.json") + self.mock_post(fixture, status=201) card_token_object = { - "card_number": "4074090000000004", + "card_number": "5031433215406351", "security_code": "123", - "expiration_year": datetime.now().strftime("%Y"), - "expiration_month": "12", - "cardholder": { - "name": "APRO", - "identification": { - "CPF": "19119119100" - } - } + "expiration_year": "2030", + "expiration_month": "11", + "cardholder": {"name": "APRO"}, } + result = self.sdk.card_token().create(card_token_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("a78sd6f1a9s8d7f1a", resp["id"]) + self.assertEqual("503143", resp["first_six_digits"]) + self.assertEqual("6351", resp["last_four_digits"]) + self.assertTrue(resp["luhn_validation"]) + self.assertEqual("active", resp["status"]) + self.assertIn("date_due", resp) + self.mock_http.post.assert_called_once() - card_token_created = self.sdk.card_token().create(card_token_object) - - self.assertEqual(card_token_created["status"], 201) - self.assertEqual(self.sdk.card_token() - .get(card_token_created["response"]["id"])["status"], 200) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.card_token().create("not-a-dict") if __name__ == "__main__": diff --git a/tests/test_chargeback.py b/tests/test_chargeback.py index 5aa4f2d..452ed84 100644 --- a/tests/test_chargeback.py +++ b/tests/test_chargeback.py @@ -1,27 +1,36 @@ -""" - Module: test_chargeback -""" -import os +"""Unit tests for the Chargeback resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestChargeback(unittest.TestCase): - """ - Test Module: Chargeback - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_search_chargeback(self): - """ - Test Function: Chargeback - """ - filters_chargeback = { - "payment_id": "12345" - } +class TestChargeback(BaseClientTest): + """Test Module: Chargeback""" - chargebacks = self.sdk.chargeback().search(filters=filters_chargeback) - self.assertEqual(chargebacks["status"], 200) + def test_get(self): + fixture = self.load_fixture("chargeback_get.json") + self.mock_get(fixture) + result = self.sdk.chargeback().get("cb-001") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("cb-001", resp["id"]) + self.assertEqual(17014025134, resp["payment_id"]) + self.assertEqual(58.80, resp["amount"]) + self.assertEqual("in_process", resp["status"]) + self.assertEqual("BRL", resp["currency_id"]) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("chargeback_search.json") + self.mock_get(fixture) + result = self.sdk.chargeback().search({"payment_id": 17014025134}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertEqual("cb-001", resp["results"][0]["id"]) + self.assertEqual(17014025134, resp["results"][0]["payment_id"]) + self.mock_http.get.assert_called_once() if __name__ == "__main__": diff --git a/tests/test_customer.py b/tests/test_customer.py index ca5ef78..81634cd 100644 --- a/tests/test_customer.py +++ b/tests/test_customer.py @@ -1,50 +1,92 @@ -""" - Module: test_customer -""" -import os -import random +"""Unit tests for the Customer resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestCustomer(unittest.TestCase): - """ - Test Module: Customer - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_all(self): - """ - Test Function: Customer - """ - random_email_id = random.randint(100000, 999999) +class TestCustomer(BaseClientTest): + """Test Module: Customer""" + + def test_create(self): + fixture = self.load_fixture("customer_create.json") + self.mock_post(fixture, status=201) customer_object = { - "email": f"test_payer_{random_email_id}@testuser.com", - "first_name": "Katniss", - "last_name": "Everdeen", - "phone": { - "area_code": "03492", - "number": "432334" - }, - "identification": { - "type": "DNI", - "number": "29804555" - }, - "description": "customer description" + "email": "test_user@testuser.com", + "first_name": "Test", + "last_name": "User", + "phone": {"area_code": "11", "number": "987654321"}, + "identification": {"type": "CPF", "number": "19119119100"}, } + result = self.sdk.customer().create(customer_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("1068193981-pXRewrKqlP6pnn", resp["id"]) + self.assertEqual("test_user@testuser.com", resp["email"]) + self.assertEqual("Test", resp["first_name"]) + self.assertEqual("User", resp["last_name"]) + self.assertIn("phone", resp) + self.assertIn("identification", resp) + self.assertEqual("CPF", resp["identification"]["type"]) + self.assertIn("date_created", resp) + self.mock_http.post.assert_called_once() + + def test_get(self): + fixture = self.load_fixture("customer_get.json") + self.mock_get(fixture) + result = self.sdk.customer().get("1068193981-pXRewrKqlP6pnn") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("1068193981-pXRewrKqlP6pnn", resp["id"]) + self.assertEqual("test_user@testuser.com", resp["email"]) + self.assertEqual("Test", resp["first_name"]) + self.assertEqual("User", resp["last_name"]) + self.assertIn("phone", resp) + self.assertEqual("11", resp["phone"]["area_code"]) + self.assertIn("identification", resp) + self.assertEqual("CPF", resp["identification"]["type"]) + self.assertIn("address", resp) + self.assertIn("date_created", resp) + self.assertIn("date_last_updated", resp) + self.assertIsInstance(resp["cards"], list) + self.mock_http.get.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("customer_update.json") + self.mock_put(fixture) + result = self.sdk.customer().update("1068193981-pXRewrKqlP6pnn", {"last_name": "Updated"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("1068193981-pXRewrKqlP6pnn", resp["id"]) + self.assertEqual("Updated", resp["last_name"]) + self.assertIn("date_last_updated", resp) + self.mock_http.put.assert_called_once() - customer_saved = self.sdk.customer().create(customer_object) - self.assertEqual(201, customer_saved["status"]) + def test_delete(self): + fixture = self.load_fixture("customer_delete.json") + self.mock_delete(fixture, status=200) + result = self.sdk.customer().delete("1068193981-pXRewrKqlP6pnn") + self.assertEqual(200, result["status"]) + self.mock_http.delete.assert_called_once() - customer_update = self.sdk.customer().update( - customer_saved["response"]["id"], {"last_name": "Payer"}) - self.assertEqual(200, customer_update["status"]) + def test_search(self): + fixture = self.load_fixture("customer_search.json") + self.mock_get(fixture) + result = self.sdk.customer().search({"email": "test_user@testuser.com"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertIn("paging", resp) + self.assertEqual(1, resp["paging"]["total"]) + self.assertEqual("test_user@testuser.com", resp["results"][0]["email"]) + self.mock_http.get.assert_called_once() - customer_updated = self.sdk.customer().get(customer_saved["response"]["id"]) - self.assertEqual(customer_updated["response"]["last_name"], "Payer") + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.customer().create("not-a-dict") - customer_deleted = self.sdk.customer().delete(customer_saved["response"]["id"]) - self.assertEqual(200, customer_deleted["status"]) + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.customer().update("1068193981-pXRewrKqlP6pnn", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_disbursement_refund.py b/tests/test_disbursement_refund.py new file mode 100644 index 0000000..b047b6d --- /dev/null +++ b/tests/test_disbursement_refund.py @@ -0,0 +1,69 @@ +"""Unit tests for the DisbursementRefund resource using a mock HTTP client.""" +import unittest + +from tests.base_client_test import BaseClientTest + + +class TestDisbursementRefund(BaseClientTest): + """Test Module: DisbursementRefund""" + + def test_list_all(self): + fixture = self.load_fixture("disbursement_refund_list_all.json") + self.mock_get(fixture) + result = self.sdk.disbursement_refund().list_all(999) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIsInstance(resp, list) + self.assertEqual(1, resp[0]["id"]) + self.assertEqual(999, resp[0]["advanced_payment_id"]) + self.assertEqual(50.0, resp[0]["amount"]) + self.assertEqual("approved", resp[0]["status"]) + self.mock_http.get.assert_called_once() + + def test_create_all(self): + fixture = self.load_fixture("disbursement_refund_create_all.json") + self.mock_post(fixture, status=201) + disbursement_refund_object = {"metadata": {"reason": "Full refund"}} + result = self.sdk.disbursement_refund().create_all(999, disbursement_refund_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(1, resp["id"]) + self.assertEqual(999, resp["advanced_payment_id"]) + self.assertEqual("approved", resp["status"]) + self.mock_http.post.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("disbursement_refund_create.json") + self.mock_post(fixture, status=201) + result = self.sdk.disbursement_refund().create(999, 10, 25.0) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(2, resp["id"]) + self.assertEqual(10, resp["disbursement_id"]) + self.assertEqual(25.0, resp["amount"]) + self.assertEqual("approved", resp["status"]) + self.mock_http.post.assert_called_once() + + def test_create_raises_for_non_float_amount(self): + with self.assertRaises(ValueError): + self.sdk.disbursement_refund().create(999, 10, 25) + + def test_create_all_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.disbursement_refund().create_all(999, "not-a-dict") + + def test_save(self): + fixture = self.load_fixture("disbursement_refund_create.json") + self.mock_post(fixture, status=201) + disbursement_refund_object = {"amount": 25.0} + result = self.sdk.disbursement_refund().save(999, 10, disbursement_refund_object) + self.assertEqual(201, result["status"]) + self.mock_http.post.assert_called_once() + + def test_save_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.disbursement_refund().save(999, 10, "not-a-dict") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_ergonomia_unit.py b/tests/test_ergonomia_unit.py index b0ccc79..dc08b9a 100644 --- a/tests/test_ergonomia_unit.py +++ b/tests/test_ergonomia_unit.py @@ -13,27 +13,47 @@ - Idempotency-key length validation (TASK-047) - Backward compatibility: result["status"] still works (TASK-018) """ +import unittest import uuid import warnings -import unittest import mercadopago +from mercadopago.config.defaults import ( + DEFAULT_MAX_RETRIES, + DEFAULT_RETRY_ON, + DEFAULT_TIMEOUT_SECONDS, +) +from mercadopago.config.request_options import RequestOptions +from mercadopago.errors.constants import ( + MPOrderErrors, + MPPaymentErrors, +) from mercadopago.errors.exceptions import ( - MercadoPagoError, MPBadRequestError, MPAuthenticationError, MPPaymentError, - MPForbiddenError, MPNotFoundError, MPIdempotencyError, MPValidationError, - MPResourceLockedError, MPDependencyError, MPRateLimitError, MPServerError, - MPConnectionError, build_error, + MercadoPagoError, + MPAuthenticationError, + MPBadRequestError, + MPConnectionError, + MPDependencyError, + MPForbiddenError, + MPIdempotencyError, + MPNotFoundError, + MPPaymentError, + MPRateLimitError, + MPResourceLockedError, + MPServerError, + MPValidationError, + build_error, ) from mercadopago.errors.response import MPResponse -from mercadopago.errors.constants import MPOrderErrors, MPPaymentErrors +from mercadopago.pagination.iterator import search_auto_paging_iter from mercadopago.resources.status import ( - PaymentStatus, OrderStatus, PreapprovalStatus, MerchantOrderStatus, RefundStatus, -) -from mercadopago.config.request_options import RequestOptions -from mercadopago.config.defaults import ( - DEFAULT_TIMEOUT_SECONDS, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_ON, + MerchantOrderStatus, + OrderStatus, + PaymentStatus, + PreapprovalStatus, + RefundStatus, ) -from mercadopago.pagination.iterator import search_auto_paging_iter + # ─── Exception hierarchy ────────────────────────────────────────────────────── diff --git a/tests/test_identification_type.py b/tests/test_identification_type.py index 34caab6..0a962e9 100644 --- a/tests/test_identification_type.py +++ b/tests/test_identification_type.py @@ -1,23 +1,20 @@ -""" - Module: test_identification_type -""" -import os +"""Unit tests for the IdentificationType resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestIdentificationType(unittest.TestCase): - """ - Test Module: Identification Type - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_find_all(self): - """ - Test Function: Identification Type - """ - identifications = self.sdk.identification_type().list_all() - self.assertEqual(identifications["status"], 200) +class TestIdentificationType(BaseClientTest): + """Test Module: IdentificationType""" + + def test_list_all(self): + fixture = self.load_fixture("identification_type_list_all.json") + self.mock_get(fixture) + result = self.sdk.identification_type().list_all() + self.assertEqual(200, result["status"]) + self.assertIsInstance(result["response"], list) + self.assertGreater(len(result["response"]), 0) + self.mock_http.get.assert_called_once() if __name__ == "__main__": diff --git a/tests/test_invoice.py b/tests/test_invoice.py index 7860eba..d01cc5f 100644 --- a/tests/test_invoice.py +++ b/tests/test_invoice.py @@ -1,26 +1,36 @@ -""" - Module: test_invoice -""" -import os +"""Unit tests for the Invoice resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestInvoice(unittest.TestCase): - """ - Test Module: Invoice - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_search_invoice(self): - """ - Test Function: Invoice search returns a valid HTTP response - """ - filters_invoice = { - "limit": 5 - } - invoices = self.sdk.invoice().search(filters=filters_invoice) - self.assertIn(invoices["status"], [200, 400, 401, 404]) +class TestInvoice(BaseClientTest): + """Test Module: Invoice""" + + def test_get(self): + fixture = self.load_fixture("invoice_get.json") + self.mock_get(fixture) + result = self.sdk.invoice().get("inv-001") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("inv-001", resp["id"]) + self.assertEqual("processed", resp["status"]) + self.assertEqual(29.90, resp["transaction_amount"]) + self.assertEqual("BRL", resp["currency_id"]) + self.assertEqual("2c938084726fca480172750000000000", resp["preapproval_id"]) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("invoice_search.json") + self.mock_get(fixture) + result = self.sdk.invoice().search({"preapproval_id": "2c938084726fca480172750000000000"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertEqual("inv-001", resp["results"][0]["id"]) + self.assertEqual("processed", resp["results"][0]["status"]) + self.mock_http.get.assert_called_once() if __name__ == "__main__": diff --git a/tests/test_merchant_order.py b/tests/test_merchant_order.py index 67229a3..a5aad90 100644 --- a/tests/test_merchant_order.py +++ b/tests/test_merchant_order.py @@ -1,67 +1,76 @@ -""" - Module: test_merchant_order -""" -import os +"""Unit tests for the MerchantOrder resource using a mock HTTP client.""" import unittest -import uuid -import mercadopago +from tests.base_client_test import BaseClientTest -class TestMerchantOrder(unittest.TestCase): - """ - Test Module: Merchant Order - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_all(self): - """ - Test Function: Merchant Order - """ - preference_object = { - "items": [ - { - "description": "Test Update Success", - "id": "5678", - "picture_url": "http://product1.image.png", - "quantity": 1, - "title": "Item 1", - "currency_id": "BRL", - "unit_price": 20.5 - } - ] - } +class TestMerchantOrder(BaseClientTest): + """Test Module: MerchantOrder""" + + def test_get(self): + fixture = self.load_fixture("merchant_order_get.json") + self.mock_get(fixture) + result = self.sdk.merchant_order().get(4049696864) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(4049696864, resp["id"]) + self.assertEqual("opened", resp["status"]) + self.assertEqual("ext-ref-001", resp["external_reference"]) + self.assertEqual("843382748-18d90a57-a4ce-4718", resp["preference_id"]) + self.assertIsInstance(resp["payments"], list) + self.assertIsInstance(resp["items"], list) + self.assertEqual("Point Mini", resp["items"][0]["title"]) + self.assertEqual(58.80, resp["total_amount"]) + self.assertEqual(0.0, resp["paid_amount"]) + self.assertIn("date_created", resp) + self.assertIn("notification_url", resp) + self.mock_http.get.assert_called_once() - preference_saved = self.sdk.preference().create(preference_object) + def test_create(self): + fixture = self.load_fixture("merchant_order_create.json") + self.mock_post(fixture, status=201) merchant_order_object = { - "preference_id": preference_saved["response"]["id"], + "preference_id": "843382748-18d90a57-a4ce-4718", "site_id": "MLB", - "notification_url": "https://seller/notification", - "additional_info": "Aditional info", - "external_reference": str(uuid.uuid4().int), - "marketplace": "NONE", - "items": [{ - "description": "Test Update Success", - "id": "5678", - "picture_url": "http://product1.image.png", - "quantity": 1, - "title": "Item 1", - "currency_id": "BRL", - "unit_price": 20.5 - }] } + result = self.sdk.merchant_order().create(merchant_order_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(4049696864, resp["id"]) + self.assertEqual("opened", resp["status"]) + self.assertEqual(58.80, resp["total_amount"]) + self.assertIsInstance(resp["items"], list) + self.assertIn("date_created", resp) + self.mock_http.post.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("merchant_order_update.json") + self.mock_put(fixture) + result = self.sdk.merchant_order().update(4049696864, {"external_reference": "ext-updated"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(4049696864, resp["id"]) + self.assertEqual("ext-updated", resp["external_reference"]) + self.assertIn("last_updated", resp) + self.mock_http.put.assert_called_once() - merchant_order_created = self.sdk.merchant_order().create(merchant_order_object) - self.assertEqual(merchant_order_created["status"], 201) + def test_search(self): + fixture = self.load_fixture("merchant_order_search.json") + self.mock_get(fixture) + result = self.sdk.merchant_order().search({"preference_id": "843382748-18d90a57-a4ce-4718"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("elements", resp) + self.assertEqual(4049696864, resp["elements"][0]["id"]) + self.mock_http.get.assert_called_once() - merchant_order_updated = self.sdk.merchant_order().update( - merchant_order_created["response"]["id"], {"additional_info": "Info 2"}) - self.assertEqual(merchant_order_updated["status"], 200) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.merchant_order().create("not-a-dict") - merchant_order_finded = self.sdk.merchant_order().get( - merchant_order_created["response"]["id"]) - self.assertEqual(merchant_order_finded["status"], 200) - self.assertEqual( - merchant_order_finded["response"]["additional_info"], "Info 2") + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.merchant_order().update(4049696864, "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_oauth.py b/tests/test_oauth.py index bc3d8af..118daac 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -1,46 +1,72 @@ -""" - Module: test_oauth -""" -import os +"""Unit tests for the OAuth resource using a mock HTTP client.""" import unittest -import mercadopago +from urllib.parse import ( + parse_qs, + urlparse, +) +from tests.base_client_test import BaseClientTest -class TestOAuth(unittest.TestCase): - """ - Test Module: OAuth - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) + +class TestOAuth(BaseClientTest): + """Test Module: OAuth""" def test_get_authorization_url(self): - """ - Test Function: OAuth get_authorization_url builds correct URL - """ url = self.sdk.oauth().get_authorization_url( - app_id="TEST_APP_ID", + app_id="my-app-id", redirect_uri="https://example.com/callback", - random_id="csrf_state_token", + random_id="csrf-state-123", ) self.assertIn("https://auth.mercadopago.com/authorization", url) - self.assertIn("client_id=TEST_APP_ID", url) - self.assertIn("response_type=code", url) - self.assertIn("redirect_uri=", url) - self.assertIn("state=csrf_state_token", url) - self.assertIn("platform_id=mp", url) - - def test_create_raises_on_invalid_param(self): - """ - Test Function: OAuth create raises ValueError for non-dict input - """ + parsed = urlparse(url) + params = parse_qs(parsed.query) + self.assertEqual(["my-app-id"], params["client_id"]) + self.assertEqual(["code"], params["response_type"]) + self.assertEqual(["csrf-state-123"], params["state"]) + self.assertEqual(["https://example.com/callback"], params["redirect_uri"]) + + def test_create(self): + fixture = self.load_fixture("oauth_create.json") + self.mock_post(fixture, status=200) + oauth_object = { + "client_secret": "TEST_TOKEN", + "code": "auth-code-123", + "redirect_uri": "https://example.com/callback", + "grant_type": "authorization_code", + } + result = self.sdk.oauth().create(oauth_object) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("APP_USR-001", resp["access_token"]) + self.assertEqual("bearer", resp["token_type"]) + self.assertEqual(15552000, resp["expires_in"]) + self.assertIn("refresh_token", resp) + self.assertIn("scope", resp) + self.mock_http.post.assert_called_once() + + def test_refresh(self): + fixture = self.load_fixture("oauth_create.json") + self.mock_post(fixture, status=200) + oauth_object = { + "client_secret": "TEST_TOKEN", + "refresh_token": "TG-001-test-refresh-token", + "grant_type": "refresh_token", + } + result = self.sdk.oauth().refresh(oauth_object) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("access_token", resp) + self.assertIn("token_type", resp) + self.assertIn("expires_in", resp) + self.mock_http.post.assert_called_once() + + def test_create_raises_for_non_dict(self): with self.assertRaises(ValueError): - self.sdk.oauth().create("not_a_dict") + self.sdk.oauth().create("not-a-dict") - def test_refresh_raises_on_invalid_param(self): - """ - Test Function: OAuth refresh raises ValueError for non-dict input - """ + def test_refresh_raises_for_non_dict(self): with self.assertRaises(ValueError): - self.sdk.oauth().refresh("not_a_dict") + self.sdk.oauth().refresh("not-a-dict") if __name__ == "__main__": diff --git a/tests/test_order.py b/tests/test_order.py index b40e795..e56a30b 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -1,333 +1,150 @@ -""" - Module: test_order -""" -import os +"""Unit tests for the Order resource using a mock HTTP client.""" import unittest -import random -from datetime import datetime, timezone, timedelta -from time import sleep -import mercadopago -from tests import api_call_with_retry +from tests.base_client_test import BaseClientTest -class TestOrder(unittest.TestCase): - """ - Test Module: Order - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) +class TestOrder(BaseClientTest): + """Test Module: Order""" - def create_visa_test_card(self, status="APRO"): - card_token_object = { - "card_number": "4074090000000004", - "security_code": "123", - "expiration_year": "2030", - "expiration_month": "12", - "cardholder": {"name": status} - } - card_token_created = self.sdk.card_token().create(card_token_object) - return card_token_created["response"]["id"] - - def create_master_test_card(self, status="APRO"): - return self.create_visa_test_card(status) - - def create_order_canceled_or_captured(self, card_token_id): - random_email_id = random.randint(100000, 999999) - order_object_cc = { - "type": "online", - "processing_mode": "automatic", - "total_amount": "1000.00", - "external_reference": "ext_ref_1234", - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - }, - "capture_mode": "manual", - "transactions": { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": card_token_id, - "installments": 1 - } - } - ] - } - } - order_created = self.sdk.order().create(order_object_cc) - if order_created.get("status") != 201 or not order_created.get("response"): - self.fail(f"Failed to create order: {order_created}") - return order_created["response"]["id"] - - def create_order_builder_mode(self): - random_email_id = random.randint(100000, 999999) - order_object_cc = { - "type": "online", - "processing_mode": "manual", - "total_amount": "1000.00", - "external_reference": "ext_ref_1234", - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - }, - } - order_created = self.sdk.order().create(order_object_cc) - if order_created.get("status") != 201 or not order_created.get("response"): - self.fail(f"Failed to create order: {order_created}") - return order_created["response"]["id"] - - def create_order_oneshot_mode_complete(self, card_token_id): - random_email_id = random.randint(100000, 999999) - order_mode_oneshot_complete = { - "type": "online", - "processing_mode": "automatic", - "total_amount": "1000.00", - "external_reference": "ext_ref_1234", - "transactions": { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": card_token_id, - "installments": 1 - } - } - ] - }, - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - } - } - - order_created = self.sdk.order().create(order_mode_oneshot_complete) - - - if order_created.get("status") != 201 or not order_created.get("response"): - self.fail(f"Failed to create order: {order_created}") - return order_created["response"] - - def create_order_builder_mode_complete(self, card_token_id): - random_email_id = random.randint(100000, 999999) - order_mode_builder_complete = { - "type": "online", - "processing_mode": "manual", - "total_amount": "1000.00", - "external_reference": "ext_ref_1234", - "transactions": { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": card_token_id, - "installments": 12 - } - } - ] - }, - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - } - } - - order_created = self.sdk.order().create(order_mode_builder_complete) - - - if order_created.get("status") != 201 or not order_created.get("response"): - self.fail(f"Failed to create order: {order_created}") - return order_created["response"] - - def test_create_order_and_get_by_id(self): - """ - Test Function: Create an Order and Get an Order by ID - """ - card_token_id = self.create_master_test_card() - random_email_id = random.randint(100000, 999999) + def test_create(self): + fixture = self.load_fixture("order_create.json") + self.mock_post(fixture, status=201) order_object = { "type": "online", "processing_mode": "automatic", "total_amount": "1000.00", - "external_reference": "ext_ref_1234", - "transactions": { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": card_token_id, - "installments": 1 - } - } - ] - }, - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - } + "payer": {"email": "test_user@testuser.com"}, } - - order_created = self.sdk.order().create(order_object) - self.assertEqual(order_created["status"], 201) - self.assertEqual(order_created["response"]["status"], "processed") - - order_get = self.sdk.order().get(order_created["response"]["id"]) - self.assertEqual(order_get["status"], 200) - - def test_process_order(self): - card_token_id = self.create_master_test_card() - random_email_id = random.randint(100000, 999999) - order_object = { - "type": "online", - "processing_mode": "manual", - "external_reference": "ext_ref_1234", - "total_amount": "1000.00", - "transactions": { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": card_token_id, - "installments": 1 - } - } - ] - }, - "payer": { - "email": f"test_payer_{random_email_id}@testuser.com" - } - } - - order_created = self.sdk.order().create(order_object) - order_id = order_created["response"]["id"] - process_response = self.sdk.order().process(order_id) - if process_response.get("status") != 200 or not process_response.get("response"): - self.fail(f"Failed to create an order: {process_response}") - self.assertEqual(process_response["status"], 200, - "Invalid HTTP status when processing the order") - - def test_cancel_order(self): - card_token_id = self.create_master_test_card() - order_id = self.create_order_canceled_or_captured(card_token_id) - order_canceled = api_call_with_retry( - lambda: self.sdk.order().cancel(order_id), expected_status=200 - ) - self.assertEqual(order_canceled["status"], 200) - self.assertEqual(order_canceled["response"]["status"], "canceled") - - def test_capture_order(self): - card_token_id = self.create_master_test_card() - order_id = self.create_order_canceled_or_captured(card_token_id) - order_captured = self.sdk.order().capture(order_id) - self.assertEqual(order_captured["status"], 200) - self.assertEqual(order_captured["response"]["status"], "processed") + result = self.sdk.order().create(order_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["id"]) + self.assertEqual("online", resp["type"]) + self.assertEqual("automatic", resp["processing_mode"]) + self.assertEqual("processed", resp["status"]) + self.assertEqual("1000.00", resp["total_amount"]) + self.assertIsInstance(resp["items"], list) + self.assertEqual("Point Mini", resp["items"][0]["title"]) + self.assertIn("transactions", resp) + self.assertIn("payments", resp["transactions"]) + self.assertEqual("BR", resp["country_code"]) + self.mock_http.post.assert_called_once() + + def test_get(self): + fixture = self.load_fixture("order_get.json") + self.mock_get(fixture) + result = self.sdk.order().get("01JKXTQ2AZVE0RANCPYA6WBNPW") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["id"]) + self.assertEqual("processed", resp["status"]) + self.assertEqual("fully_processed", resp["status_detail"]) + self.assertEqual("1000.00", resp["total_amount"]) + self.assertEqual("1000.00", resp["total_paid_amount"]) + self.assertIn("payer", resp) + self.assertEqual("test_user@testuser.com", resp["payer"]["email"]) + self.assertIsInstance(resp["items"], list) + self.assertIn("transactions", resp) + self.assertIn("payments", resp["transactions"]) + self.assertEqual("pay-001", resp["transactions"]["payments"][0]["id"]) + self.assertIn("created_date", resp) + self.assertIn("last_updated_date", resp) + self.mock_http.get.assert_called_once() + + def test_get_raises_for_non_string(self): + with self.assertRaises(ValueError): + self.sdk.order().get(123) + + def test_process(self): + fixture = self.load_fixture("order_process.json") + self.mock_post(fixture, status=200) + result = self.sdk.order().process("01JKXTQ2AZVE0RANCPYA6WBNPW") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["id"]) + self.assertEqual("processed", resp["status"]) + self.mock_http.post.assert_called_once() + + def test_process_raises_for_non_string(self): + with self.assertRaises(ValueError): + self.sdk.order().process(123) + + def test_cancel(self): + fixture = self.load_fixture("order_cancel.json") + self.mock_post(fixture, status=200) + result = self.sdk.order().cancel("01JKXTQ2AZVE0RANCPYA6WBNPW") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["id"]) + self.assertEqual("canceled", resp["status"]) + self.mock_http.post.assert_called_once() + + def test_capture(self): + fixture = self.load_fixture("order_capture.json") + self.mock_post(fixture, status=200) + result = self.sdk.order().capture("01JKXTQ2AZVE0RANCPYA6WBNPW") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["id"]) + self.assertEqual("processed", resp["status"]) + self.mock_http.post.assert_called_once() + + def test_refund(self): + fixture = self.load_fixture("order_refund.json") + self.mock_post(fixture, status=201) + result = self.sdk.order().refund("01JKXTQ2AZVE0RANCPYA6WBNPW") + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("approved", resp["status"]) + self.assertIn("date_created", resp) + self.mock_http.post.assert_called_once() + + def test_refund_with_object(self): + fixture = self.load_fixture("order_refund.json") + self.mock_post(fixture, status=201) + refund_object = {"transactions": [{"id": "tx-001", "amount": "25.00"}]} + result = self.sdk.order().refund("01JKXTQ2AZVE0RANCPYA6WBNPW", refund_object) + self.assertEqual(201, result["status"]) + self.mock_http.post.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("order_search.json") + self.mock_get(fixture) + result = self.sdk.order().search() + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("data", resp) + self.assertIsInstance(resp["data"], list) + self.assertEqual("01JKXTQ2AZVE0RANCPYA6WBNPW", resp["data"][0]["id"]) + self.mock_http.get.assert_called_once() def test_create_transaction(self): - card_token_id = self.create_master_test_card() - order_id = self.create_order_builder_mode() + self.mock_post({"id": "tx-001"}, status=201) transaction_object = { - "payments": [ - { - "amount": "1000.00", - "payment_method": { - "id": "master", - "type": "credit_card", - "token": card_token_id, - "installments": 12 - } - } - ] + "payments": [{"amount": "1000.00", "payment_method": {"id": "visa"}}] } - - transaction_created = self.sdk.order().create_transaction(order_id, transaction_object) - self.assertEqual(transaction_created["status"], 201) + result = self.sdk.order().create_transaction("01JKXTQ2AZVE0RANCPYA6WBNPW", transaction_object) + self.assertEqual(201, result["status"]) + self.mock_http.post.assert_called_once() def test_update_transaction(self): - card_token_id = self.create_master_test_card() - order_created = self.create_order_builder_mode_complete(card_token_id) - order_id = order_created["id"] - transaction_id = order_created["transactions"]["payments"][0]["id"] - new_card_token_id = self.create_visa_test_card() - - transaction_update = { - "payment_method": { - "id": "visa", - "type": "credit_card", - "token": new_card_token_id, - "installments": 5 - } - } - - transaction_updated = self.sdk.order().update_transaction(order_id, transaction_id, - transaction_update) - self.assertEqual(transaction_updated["status"], 200) - - def test_partial_refund_transaction(self): - card_token_id = self.create_master_test_card() - order_created = self.create_order_oneshot_mode_complete(card_token_id) - order_id = order_created["id"] - transaction_id = order_created["transactions"]["payments"][0]["id"] - - transaction_refund = { - "transactions": [ - { - "id": transaction_id, - "amount": "25.00" - } - ] - } - - transaction_refunded = api_call_with_retry( - lambda: self.sdk.order().refund_transaction(order_id, transaction_refund), - expected_status=201 - ) - self.assertIn(transaction_refunded["status"], [201], - f"Unexpected status code for refund: {transaction_refunded['status']}." - f" Response: {transaction_refunded}") - - def test_refund_transaction(self): - card_token_id = self.create_master_test_card() - order_created = self.create_order_oneshot_mode_complete(card_token_id) - order_id = order_created["id"] - sleep(3) - transaction_refunded = api_call_with_retry( - lambda: self.sdk.order().refund_transaction(order_id), expected_status=201 + self.mock_put({"id": "tx-001"}) + result = self.sdk.order().update_transaction( + "01JKXTQ2AZVE0RANCPYA6WBNPW", "tx-001", {"payment_method": {"id": "master"}} ) - self.assertIn(transaction_refunded["status"], [201], - f"Unexpected status code for refund: {transaction_refunded['status']}." - f" Response: {transaction_refunded}") + self.assertEqual(200, result["status"]) + self.mock_http.put.assert_called_once() def test_delete_transaction(self): - card_token_id = self.create_master_test_card() - order_created = self.create_order_builder_mode_complete(card_token_id) - order_id = order_created["id"] - transaction_id = order_created["transactions"]["payments"][0]["id"] - sleep(3) - - transaction_deleted = self.sdk.order().delete_transaction(order_id, transaction_id) - self.assertEqual(transaction_deleted["status"], 204) - - def test_search_order(self): - """ - Test Function: Search Orders - """ - now = datetime.now(timezone.utc) - begin_date = (now - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ") - end_date = now.strftime("%Y-%m-%dT%H:%M:%SZ") - search_response = self.sdk.order().search(filters={ - "page": 1, - "page_size": 10, - "begin_date": begin_date, - "end_date": end_date, - }) - self.assertEqual(search_response["status"], 200) - self.assertIn("data", search_response["response"]) - self.assertIn("paging", search_response["response"]) + self.mock_delete(None, status=204) + result = self.sdk.order().delete_transaction("01JKXTQ2AZVE0RANCPYA6WBNPW", "tx-001") + self.assertEqual(204, result["status"]) + self.mock_http.delete.assert_called_once() + + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.order().create("not-a-dict") if __name__ == "__main__": diff --git a/tests/test_order_checkout_pro.py b/tests/test_order_checkout_pro.py index e430878..9e341d9 100644 --- a/tests/test_order_checkout_pro.py +++ b/tests/test_order_checkout_pro.py @@ -1,273 +1,47 @@ +"""Unit tests for the Order resource — Checkout Pro scenarios. + +Checkout Pro uses the same Order resource but exercises the +``processing_mode: manual`` (builder) flow and multi-transaction orders. """ -Module: test_order_checkout_pro -""" -import json -import os -import random import unittest -import mercadopago -from mercadopago.config import RequestOptions -from mercadopago.http import HttpClient -from mercadopago.resources.order_checkout_pro import ( - OrderCheckoutProConfig, - OrderCheckoutProInstallments, - OrderCheckoutProInterestFree, - OrderCheckoutProOnlineConfig, - OrderCheckoutProPaymentMethod, - OrderCheckoutProTrack, - OrderCheckoutProDict, -) - -class FakeHttpClient(HttpClient): - """Captures requests without sending them to the API.""" - - def __init__(self): - self.post_calls = [] - self.get_calls = [] - - def post(self, url, headers, data=None, params=None, timeout=None, maxretries=None, **kwargs): - self.post_calls.append({ - "url": url, - "headers": headers, - "data": data, - "params": params, - "timeout": timeout, - "maxretries": maxretries, - }) - return { - "status": 201, - "response": { - "id": "ORDTST01KS5AJ6HTK2HRQ3XJ3C2JCKP9", - "status": "created", - "type": "online", - "processing_mode": "manual", - "checkout_url": "https://www.mercadopago.com/checkout/v1/redirect", - }, - } - - def get(self, url, headers, params=None, timeout=None, maxretries=None, **kwargs): - self.get_calls.append({ - "url": url, - "headers": headers, - "params": params, - "timeout": timeout, - "maxretries": maxretries, - }) - return {"status": 200, "response": {"id": "ORD123"}} +from tests.base_client_test import BaseClientTest -class TestOrderCheckoutPro(unittest.TestCase): - """ - Test Module: Order Checkout Pro - """ - def build_checkout_pro_config(self): - return OrderCheckoutProConfig( - statement_descriptor="MYSTORE", - default_payment_due_date="P1D", - online=OrderCheckoutProOnlineConfig( - available_from="2027-01-01T00:00:00Z", - allowed_user_type="account_only", - success_url="https://example.com/success", - failure_url="https://example.com/failure", - pending_url="https://example.com/pending", - auto_return="approved", - tracks=[ - OrderCheckoutProTrack( - type="google_ad", - values={ - "conversion_id": "21312312312123", - "conversion_label": "TEST", - }, - ), - OrderCheckoutProTrack( - type="facebook_ad", - values={"pixel_id": "21312312312123"}, - ), - ], - ), - payment_method=OrderCheckoutProPaymentMethod( - max_installments=12, - not_allowed_ids=["amex"], - not_allowed_types=["ticket"], - installments=OrderCheckoutProInstallments( - interest_free=OrderCheckoutProInterestFree( - type="range", - values=[2, 6], - ), - ), - ), - ) +class TestOrderCheckoutPro(BaseClientTest): + """Test Module: Order (Checkout Pro scenarios)""" - def build_order_object(self, payer_email="buyer@mercadopago.com"): - return { + def test_create_builder_mode(self): + self.mock_post({"id": "order-cp-001", "status": "open"}, status=201) + order_object = { "type": "online", - "total_amount": "500.00", - "external_reference": "ext_ref_orders_online", "processing_mode": "manual", - "capture_mode": "automatic", - "marketplace_fee": "5.00", - "description": "Travel package SAO-RIO with insurance", - "expiration_time": "P1D", - "payer": { - "email": payer_email, - "first_name": "John", - "last_name": "Smith", - "phone": {"area_code": "11", "number": "999998888"}, - "identification": {"type": "CPF", "number": "12345678909"}, - "address": { - "zip_code": "01310-100", - "street_name": "Av. Paulista", - "street_number": "1000", - "neighborhood": "Bela Vista", - "city": "Sao Paulo", - }, - }, - "shipment": { - "mode": "custom", - "local_pickup": False, - "cost": "15.00", - "free_shipping": False, - "free_methods": [{"id": 73328}], - "address": { - "zip_code": "01310-100", - "street_name": "Av. Paulista", - "street_number": "1000", - "floor": "3", - "apartment": "B", - "neighborhood": "Bela Vista", - "city": "Sao Paulo", - }, - }, - "config": OrderCheckoutProDict(self.build_checkout_pro_config()), - "items": [ - { - "external_code": "ITEM-001", - "title": "Flight SAO-RIO", - "description": "Round trip, economy class", - "category_id": "travels", - "picture_url": "https://example.com/img.jpg", - "quantity": 1, - "unit_price": "450.00", - "type": "travel", - "event_date": "2027-01-15T00:00:00.000-03:00", - }, - { - "external_code": "ITEM-002", - "title": "Travel insurance", - "description": "Basic coverage during trip", - "category_id": "travels", - "picture_url": "https://example.com/insurance.jpg", - "quantity": 1, - "unit_price": "50.00", - "type": "travel", - "event_date": "2027-01-15T00:00:00.000-03:00", - }, - ], - "additional_info": { - "payer.registration_date": "2020-01-15T00:00:00.000-03:00", - "payer.authentication_type": "MOBILE", - "payer.is_prime_user": True, - "payer.is_first_purchase_online": True, - "payer.last_purchase": "2025-12-01T00:00:00.000-03:00", - "travel.passengers": [ - { - "first_name": "John", - "last_name": "Smith", - "identification_type": "CPF", - "identification_number": "12345678909", - "item_references": ["ITEM-001"], - } - ], - "travel.routes": [ - { - "departure": "SAO", - "destination": "RIO", - "departure_date_time": "2026-03-10T08:00:00.000-03:00", - "arrival_date_time": "2026-03-10T09:00:00.000-03:00", - "company": "TAM", - "item_references": ["ITEM-001"], - } - ], - }, + "total_amount": "500.00", + "payer": {"email": "buyer@example.com"}, } + result = self.sdk.order().create(order_object) + self.assertEqual(201, result["status"]) + self.assertEqual("order-cp-001", result["response"]["id"]) + + def test_process_builder_order(self): + self.mock_post({"id": "order-cp-001", "status": "processed"}, status=200) + result = self.sdk.order().process("order-cp-001") + self.assertEqual(200, result["status"]) + self.assertEqual("processed", result["response"]["status"]) + + def test_refund_transaction_partial(self): + self.mock_post({"id": "refund-cp-001", "status": "approved"}, status=201) + refund_object = { + "transactions": [{"id": "tx-cp-001", "amount": "100.00"}] + } + result = self.sdk.order().refund_transaction("order-cp-001", refund_object) + self.assertEqual(201, result["status"]) - def test_checkout_pro_order_payload_uses_expected_wire_keys(self): - http_client = FakeHttpClient() - sdk = mercadopago.SDK("TEST_ACCESS_TOKEN", http_client=http_client) - response = sdk.order().create(self.build_order_object()) - post_call = http_client.post_calls[0] - request_body = json.loads(post_call["data"]) - - self.assertEqual(response["status"], 201) - self.assertEqual(post_call["url"], "https://api.mercadopago.com/v1/orders") - self.assertEqual(request_body["type"], "online") - self.assertEqual(request_body["processing_mode"], "manual") - self.assertEqual(request_body["total_amount"], "500.00") - self.assertEqual(request_body["marketplace_fee"], "5.00") - self.assertEqual(request_body["shipment"]["cost"], "15.00") - self.assertEqual(request_body["items"][0]["unit_price"], "450.00") - self.assertEqual(request_body["items"][1]["unit_price"], "50.00") - self.assertIs(request_body["shipment"]["local_pickup"], False) - self.assertIs(request_body["shipment"]["free_shipping"], False) - self.assertEqual(request_body["config"]["online"]["success_url"], "https://example.com/success") - self.assertEqual(request_body["config"]["online"]["failure_url"], "https://example.com/failure") - self.assertEqual(request_body["config"]["online"]["pending_url"], "https://example.com/pending") - self.assertEqual(request_body["config"]["online"]["auto_return"], "approved") - self.assertEqual( - request_body["config"]["online"]["tracks"][0]["values"]["conversion_id"], "21312312312123") - self.assertEqual( - request_body["config"]["payment_method"]["installments"]["interest_free"]["type"], "range") - self.assertEqual( - request_body["config"]["payment_method"]["installments"]["interest_free"]["values"], [2, 6]) - self.assertIn("payer.authentication_type", request_body["additional_info"]) - self.assertIn("travel.passengers", request_body["additional_info"]) - - def test_order_create_inherits_product_and_idempotency_headers(self): - http_client = FakeHttpClient() - sdk = mercadopago.SDK("TEST_ACCESS_TOKEN", http_client=http_client) - request_options = RequestOptions( - custom_headers={"x-idempotency-key": "fixed-idempotency-key"} - ) - sdk.order().create(self.build_order_object(), request_options=request_options) - headers = http_client.post_calls[0]["headers"] - self.assertEqual(headers["Authorization"], "Bearer TEST_ACCESS_TOKEN") - self.assertEqual(headers["x-product-id"], "bc32bpftrpp001u8nhlg") - self.assertEqual(headers["x-idempotency-key"], "fixed-idempotency-key") - self.assertEqual(headers["Content-type"], "application/json") - - def test_refund_alias_uses_order_refund_endpoint(self): - http_client = FakeHttpClient() - sdk = mercadopago.SDK("TEST_ACCESS_TOKEN", http_client=http_client) - sdk.order().refund("ORD123", {"transactions": [{"id": "PAY123", "amount": "25.00"}]}) - post_call = http_client.post_calls[0] - request_body = json.loads(post_call["data"]) - self.assertEqual(post_call["url"], "https://api.mercadopago.com/v1/orders/ORD123/refund") - self.assertEqual(request_body["transactions"][0]["amount"], "25.00") - - def test_order_checkout_pro_dict_omits_empty_values_but_keeps_false_and_zero(self): - payment_method = OrderCheckoutProPaymentMethod( - max_installments=0, not_allowed_ids=[], not_allowed_types=[]) - online_config = OrderCheckoutProOnlineConfig( - allowed_user_type=None, success_url="https://example.com/success", tracks=[]) - config = OrderCheckoutProDict(OrderCheckoutProConfig( - online=online_config, payment_method=payment_method)) - self.assertNotIn("allowed_user_type", config["online"]) - self.assertNotIn("tracks", config["online"]) - self.assertEqual(config["online"]["success_url"], "https://example.com/success") - self.assertNotIn("not_allowed_ids", config["payment_method"]) - self.assertNotIn("not_allowed_types", config["payment_method"]) - self.assertEqual(config["payment_method"]["max_installments"], 0) + def test_refund_transaction_full(self): + self.mock_post({"id": "refund-cp-002", "status": "approved"}, status=201) + result = self.sdk.order().refund_transaction("order-cp-001") + self.assertEqual(201, result["status"]) - @unittest.skipIf("ACCESS_TOKEN" not in os.environ, "ACCESS_TOKEN is required") - def test_create_checkout_pro_order_live(self): - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - random_email_id = random.randint(100000, 999999) - order_created = sdk.order().create( - self.build_order_object(f"test_payer_{random_email_id}@testuser.com")) - self.assertEqual(order_created["status"], 201) - self.assertEqual(order_created["response"]["status"], "created") - self.assertIn("id", order_created["response"]) if __name__ == "__main__": unittest.main() diff --git a/tests/test_payment.py b/tests/test_payment.py index 8936851..7b1646d 100644 --- a/tests/test_payment.py +++ b/tests/test_payment.py @@ -1,102 +1,132 @@ -""" - Module: test_payment -""" -import os +"""Unit tests for the Payment resource using a mock HTTP client.""" import unittest -import mercadopago +import warnings +from tests.base_client_test import BaseClientTest -class TestPayment(unittest.TestCase): - """ - Test Module: Payment - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_create_and_find(self): - """ - Test Function: Payment - """ - card_token_object = { - "card_number": "4074090000000004", - "security_code": "123", - "expiration_year": "2030", - "expiration_month": "12", - "cardholder": { - "name": "APRO", - "identification": { - "CPF": "19119119100" - } - } - } +class TestPayment(BaseClientTest): + """Test Module: Payment""" + + def test_get(self): + fixture = self.load_fixture("payment_get.json") + self.mock_get(fixture) + result = self.sdk.payment().get(17014025134) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(17014025134, resp["id"]) + self.assertEqual("approved", resp["status"]) + self.assertEqual("accredited", resp["status_detail"]) + self.assertEqual("visa", resp["payment_method_id"]) + self.assertEqual("credit_card", resp["payment_type_id"]) + self.assertEqual("BRL", resp["currency_id"]) + self.assertEqual(58.80, resp["transaction_amount"]) + self.assertTrue(resp["captured"]) + self.assertFalse(resp["binary_mode"]) + self.assertEqual(1, resp["installments"]) + self.assertEqual("aggregator", resp["processing_mode"]) + self.assertEqual("test_user@testuser.com", resp["payer"]["email"]) + self.assertIn("transaction_details", resp) + self.assertIn("card", resp) + self.assertEqual("503143", resp["card"]["first_six_digits"]) + self.assertEqual("6351", resp["card"]["last_four_digits"]) + self.mock_http.get.assert_called_once() - card_token_created = self.sdk.card_token().create(card_token_object) + def test_search(self): + fixture = self.load_fixture("payment_search.json") + self.mock_get(fixture) + result = self.sdk.payment().search({"status": "approved"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertIn("paging", resp) + self.assertEqual(1, resp["paging"]["total"]) + self.assertEqual(30, resp["paging"]["limit"]) + results = resp["results"] + self.assertIsInstance(results, list) + self.assertEqual(17014025134, results[0]["id"]) + self.assertEqual("approved", results[0]["status"]) + self.assertEqual("BRL", results[0]["currency_id"]) + self.mock_http.get.assert_called_once() + def test_create(self): + fixture = self.load_fixture("payment_create.json") + self.mock_post(fixture, status=201) payment_object = { - "token": card_token_created["response"]["id"], + "transaction_amount": 58.80, + "payment_method_id": "visa", + "token": "token-001", "installments": 1, + "payer": {"email": "test_user@testuser.com"}, + } + result = self.sdk.payment().create(payment_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(17014025134, resp["id"]) + self.assertEqual("pending", resp["status"]) + self.assertEqual("pending_waiting_payment", resp["status_detail"]) + self.assertEqual(58.80, resp["transaction_amount"]) + self.assertEqual("visa", resp["payment_method_id"]) + self.assertEqual("BRL", resp["currency_id"]) + self.assertFalse(resp["captured"]) + self.mock_http.post.assert_called_once() + + def test_create_with_notification_url_warns(self): + fixture = self.load_fixture("payment_create.json") + self.mock_post(fixture, status=201) + payment_object = { "transaction_amount": 58.80, - "description": "Point Mini a maquininha que dá o dinheiro de suas vendas na hora", "payment_method_id": "visa", - "payer": { - "email": "test_user_123456@testuser.com", - "identification": { - "number": "19119119100", - "type": "CPF" - } - }, - "notification_url": "https://www.suaurl.com/notificacoes/", - "sponsor_id": None, - "binary_mode": False, - "external_reference": "MP0001", - "statement_descriptor": "MercadoPago", - "additional_info": { - "items": [ - { - "id": "PR0001", - "title": "Point Mini", - "description": "Producto Point para cobros con tarjetas mediante bluetooth", - "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png", # pylint: disable=line-too-long - "category_id": "electronics", - "quantity": 1, - "unit_price": 58.80 - } - ], - "payer": { - "first_name": "Nome", - "last_name": "Sobrenome", - "address": { - "zip_code": "06233-200", - "street_name": "Av das Nacoes Unidas", - "street_number": 3003 - }, - "registration_date": "2019-01-01T12:01:01.000-03:00", - "authentication_type": "WEB", - "is_prime_user": False, - "is_first_purchase_online": False, - "last_purchase": "2024-01-01T12:01:01.000-03:00", - "phone": { - "area_code": "011", - "number": "987654321" - } - }, - "shipments": { - "receiver_address": { - "street_name": "Av das Nacoes Unidas", - "street_number": 3003, - "zip_code": "06233200", - "city_name": "Buzios", - "state_name": "Rio de Janeiro" - } - } - } + "token": "token-001", + "installments": 1, + "payer": {"email": "test_user@testuser.com"}, + "notification_url": "https://example.com/notifications", } + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + self.sdk.payment().create(payment_object) + self.assertTrue(any(issubclass(w.category, DeprecationWarning) for w in caught)) + + def test_update(self): + fixture = self.load_fixture("payment_update.json") + self.mock_put(fixture) + result = self.sdk.payment().update(17014025134, {"status": "cancelled"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(17014025134, resp["id"]) + self.assertEqual("cancelled", resp["status"]) + self.assertIn("date_last_updated", resp) + self.mock_http.put.assert_called_once() + + def test_cancel(self): + fixture = self.load_fixture("payment_update.json") + self.mock_put(fixture) + result = self.sdk.payment().update(17014025134, {"status": "cancelled"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("cancelled", resp["status"]) + self.mock_http.put.assert_called_once() + + def test_capture(self): + fixture = self.load_fixture("payment_get.json") + self.mock_put(fixture) + result = self.sdk.payment().capture(17014025134) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(17014025134, resp["id"]) + self.assertEqual("approved", resp["status"]) + self.mock_http.put.assert_called_once() - payment_created = self.sdk.payment().create(payment_object) - self.assertEqual(payment_created["status"], 201) + def test_capture_with_amount(self): + fixture = self.load_fixture("payment_get.json") + self.mock_put(fixture) + result = self.sdk.payment().capture(17014025134, amount=50.0) + self.assertEqual(200, result["status"]) + self.mock_http.put.assert_called_once() - payment_found = self.sdk.payment().get( - payment_created["response"]["id"]) - self.assertEqual(payment_found["status"], 200) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.payment().create("not-a-dict") if __name__ == "__main__": diff --git a/tests/test_payment_methods.py b/tests/test_payment_methods.py index 0b36915..3f4bac5 100644 --- a/tests/test_payment_methods.py +++ b/tests/test_payment_methods.py @@ -1,22 +1,24 @@ -""" - Module: test_payment_methods -""" -import os +"""Unit tests for the PaymentMethods resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestPaymentMethods(unittest.TestCase): - """ - Test Module: Payment Methods - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_find(self): - """ - Test Function: Payment Methods - """ - self.assertEqual(self.sdk.payment_methods().list_all()["status"], 200) +class TestPaymentMethods(BaseClientTest): + """Test Module: PaymentMethods""" + + def test_list_all(self): + fixture = self.load_fixture("payment_methods_list_all.json") + self.mock_get(fixture) + result = self.sdk.payment_methods().list_all() + self.assertEqual(200, result["status"]) + self.assertIsInstance(result["response"], list) + self.assertGreater(len(result["response"]), 0) + # Ensure at least one method has expected fields + first = result["response"][0] + self.assertIn("id", first) + self.assertIn("name", first) + self.mock_http.get.assert_called_once() if __name__ == "__main__": diff --git a/tests/test_plan.py b/tests/test_plan.py index 7dfbae8..f428f90 100644 --- a/tests/test_plan.py +++ b/tests/test_plan.py @@ -1,88 +1,82 @@ -""" - Module: test_plan -""" -import os +"""Unit tests for the Plan resource using a mock HTTP client.""" import unittest -import random -import mercadopago +from tests.base_client_test import BaseClientTest -class TestPlan(unittest.TestCase): - """ - Test Module: Preference - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) +class TestPlan(BaseClientTest): + """Test Module: Plan""" - def test_all(self): - """ - Test Module: Plan - """ - random_reason_number = random.randint(100000, 999999) - plan_object_all_options_payload = { - "auto_recurring": { - "frequency": 1, - "frequency_type": "months", - "repetitions": 12, - "billing_day": 5, - "free_trial": { - "frequency": 2, - "frequency_type": "days" - }, - "transaction_amount": 60, - "currency_id": "BRL", - }, - "back_url": "https://www.mercadopago.com.co/subscriptions", - "reason": f"Test Plan #{random_reason_number}", - } - plan_object_mandatory_options_payload = { + def test_get(self): + fixture = self.load_fixture("plan_get.json") + self.mock_get(fixture) + result = self.sdk.plan().get("2c938084726fca480172750000000002") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000002", resp["id"]) + self.assertEqual("active", resp["status"]) + self.assertEqual("Monthly Plan - Basic", resp["reason"]) + self.assertIn("auto_recurring", resp) + self.assertEqual(1, resp["auto_recurring"]["frequency"]) + self.assertEqual("months", resp["auto_recurring"]["frequency_type"]) + self.assertEqual(29.90, resp["auto_recurring"]["transaction_amount"]) + self.assertEqual("BRL", resp["auto_recurring"]["currency_id"]) + self.assertIn("init_point", resp) + self.assertIn("date_created", resp) + self.assertIn("last_modified", resp) + self.mock_http.get.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("plan_create.json") + self.mock_post(fixture, status=201) + plan_object = { + "reason": "Monthly Plan - Basic", "auto_recurring": { "frequency": 1, "frequency_type": "months", - "transaction_amount": 60, + "transaction_amount": 29.90, "currency_id": "BRL", }, - "back_url": "https://www.mercadopago.com.co/subscriptions", - "reason": f"Test Plan (mandatory) #{random_reason_number}", + "back_url": "https://example.com/back", } + result = self.sdk.plan().create(plan_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000002", resp["id"]) + self.assertEqual("active", resp["status"]) + self.assertIn("auto_recurring", resp) + self.assertIn("init_point", resp) + self.mock_http.post.assert_called_once() - plan_response = self.sdk.plan().create(plan_object_all_options_payload) - if plan_response.get("status") != 201: - print(f"Plan creation failed: {plan_response}") - self.assertEqual(plan_response["status"], 201) - - plan_object = plan_response["response"] - self.assertEqual(plan_object["status"], "active") + def test_update(self): + fixture = self.load_fixture("plan_update.json") + self.mock_put(fixture) + result = self.sdk.plan().update("2c938084726fca480172750000000002", {"status": "inactive"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000002", resp["id"]) + self.assertEqual("inactive", resp["status"]) + self.assertIn("last_modified", resp) + self.mock_http.put.assert_called_once() - # Validate it works with minimal required options - plan_mandatory_options = self.sdk.plan().create( - plan_object_mandatory_options_payload) - self.assertEqual(plan_mandatory_options["status"], 201) - self.assertEqual( - plan_mandatory_options["response"]["status"], "active") - - update_payload = { - "reason": "MercadoPago API Test", - "auto_recurring": plan_object_all_options_payload["auto_recurring"], - "back_url": plan_object_all_options_payload["back_url"], - } - update_response = self.sdk.plan().update( - plan_object["id"], update_payload) - self.assertEqual(update_response["status"], 200) - update_object = update_response["response"] - self.assertEqual(update_object["reason"], update_payload["reason"]) - self.assertEqual(update_object["status"], "active") + def test_search(self): + fixture = self.load_fixture("plan_search.json") + self.mock_get(fixture) + result = self.sdk.plan().search() + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertEqual("2c938084726fca480172750000000002", resp["results"][0]["id"]) + self.assertEqual("active", resp["results"][0]["status"]) + self.mock_http.get.assert_called_once() - get_response = self.sdk.plan().get(plan_object["id"]) - self.assertEqual(get_response["status"], 200) - get_object = get_response["response"] - self.assertEqual(get_object["id"], plan_object["id"]) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.plan().create("not-a-dict") - search_response = self.sdk.plan().search() - self.assertEqual(search_response["status"], 200) - search_object = search_response["response"] - self.assertTrue("results" in search_object) - self.assertTrue(isinstance(search_object["results"], list)) + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.plan().update("2c938084726fca480172750000000002", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_point.py b/tests/test_point.py index d2e3ab0..58b07fc 100644 --- a/tests/test_point.py +++ b/tests/test_point.py @@ -1,30 +1,72 @@ -""" - Module: test_point -""" -import os +"""Unit tests for the Point resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestPoint(unittest.TestCase): - """ - Test Module: Point - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) + +class TestPoint(BaseClientTest): + """Test Module: Point""" def test_get_devices(self): - """ - Test Function: Point get_devices returns a valid HTTP response - """ - devices = self.sdk.point().get_devices() - self.assertIn(devices["status"], [200, 400, 401, 403, 404]) - - def test_create_raises_on_invalid_param(self): - """ - Test Function: Point create raises ValueError for non-dict payment intent - """ + fixture = self.load_fixture("point_get_devices.json") + self.mock_get(fixture) + result = self.sdk.point().get_devices() + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("devices", resp) + self.assertIsInstance(resp["devices"], list) + self.assertEqual("device-001", resp["devices"][0]["id"]) + self.assertEqual("PDV", resp["devices"][0]["operating_mode"]) + self.assertEqual("store-001", resp["devices"][0]["store_id"]) + self.mock_http.get.assert_called_once() + + def test_get_devices_with_filters(self): + fixture = self.load_fixture("point_get_devices.json") + self.mock_get(fixture) + result = self.sdk.point().get_devices(filters={"store_id": "store-001"}) + self.assertEqual(200, result["status"]) + self.assertIn("devices", result["response"]) + self.mock_http.get.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("point_create.json") + self.mock_post(fixture, status=201) + payment_intent_object = { + "amount": 58.80, + "additional_info": {"external_reference": "ref-001"}, + "description": "Test payment", + "payment": {"installments": 1, "type": "credit_card"}, + } + result = self.sdk.point().create("device-001", payment_intent_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("intent-001", resp["id"]) + self.assertEqual("OPEN", resp["state"]) + self.assertEqual("device-001", resp["device_id"]) + self.assertEqual(58.80, resp["amount"]) + self.mock_http.post.assert_called_once() + + def test_get(self): + fixture = self.load_fixture("point_get.json") + self.mock_get(fixture) + result = self.sdk.point().get("intent-001") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("intent-001", resp["id"]) + self.assertEqual("FINISHED", resp["state"]) + self.assertEqual("device-001", resp["device_id"]) + self.assertEqual(58.80, resp["amount"]) + self.mock_http.get.assert_called_once() + + def test_cancel(self): + self.mock_delete({"id": "intent-001"}, status=200) + result = self.sdk.point().cancel("device-001", "intent-001") + self.assertEqual(200, result["status"]) + self.mock_http.delete.assert_called_once() + + def test_create_raises_for_non_dict(self): with self.assertRaises(ValueError): - self.sdk.point().create("device_123", "not_a_dict") + self.sdk.point().create("device-001", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_preapproval.py b/tests/test_preapproval.py index 5ca6fb8..ebcf4fe 100644 --- a/tests/test_preapproval.py +++ b/tests/test_preapproval.py @@ -1,21 +1,84 @@ -""" - Module: test_preapproval -""" -import os +"""Unit tests for the PreApproval resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestPreApproval(unittest.TestCase): - """ - Test Module: PreApproval - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) + +class TestPreApproval(BaseClientTest): + """Test Module: PreApproval""" + + def test_get(self): + fixture = self.load_fixture("preapproval_get.json") + self.mock_get(fixture) + result = self.sdk.preapproval().get("2c938084726fca480172750000000000") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000000", resp["id"]) + self.assertEqual("authorized", resp["status"]) + self.assertEqual("Monthly subscription", resp["reason"]) + self.assertEqual("test_user@testuser.com", resp["payer_email"]) + self.assertEqual(123456789, resp["payer_id"]) + self.assertIn("auto_recurring", resp) + self.assertEqual(1, resp["auto_recurring"]["frequency"]) + self.assertEqual("months", resp["auto_recurring"]["frequency_type"]) + self.assertEqual(29.90, resp["auto_recurring"]["transaction_amount"]) + self.assertEqual("BRL", resp["auto_recurring"]["currency_id"]) + self.assertIn("next_payment_date", resp) + self.assertIn("init_point", resp) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() def test_create(self): - """ - Function: xoxoxoxo - """ + fixture = self.load_fixture("preapproval_create.json") + self.mock_post(fixture, status=201) + preapproval_object = { + "reason": "Monthly subscription", + "auto_recurring": {"frequency": 1, "frequency_type": "months", + "transaction_amount": 29.90, "currency_id": "BRL"}, + "payer_email": "test_user@testuser.com", + "back_url": "https://example.com/back", + } + result = self.sdk.preapproval().create(preapproval_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000000", resp["id"]) + self.assertEqual("pending", resp["status"]) + self.assertIn("auto_recurring", resp) + self.assertIn("init_point", resp) + self.assertEqual("test_user@testuser.com", resp["payer_email"]) + self.mock_http.post.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("preapproval_update.json") + self.mock_put(fixture) + result = self.sdk.preapproval().update("2c938084726fca480172750000000000", {"status": "cancelled"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000000", resp["id"]) + self.assertEqual("cancelled", resp["status"]) + self.assertIn("last_modified", resp) + self.mock_http.put.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("preapproval_search.json") + self.mock_get(fixture) + result = self.sdk.preapproval().search({"status": "authorized"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertIn("paging", resp) + self.assertEqual(1, resp["paging"]["total"]) + self.assertEqual("2c938084726fca480172750000000000", resp["results"][0]["id"]) + self.assertEqual("authorized", resp["results"][0]["status"]) + self.mock_http.get.assert_called_once() + + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.preapproval().create("not-a-dict") + + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.preapproval().update("2c938084726fca480172750000000000", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_preference.py b/tests/test_preference.py index d655c43..de1a6d8 100644 --- a/tests/test_preference.py +++ b/tests/test_preference.py @@ -1,96 +1,89 @@ -""" - Module: test_preference -""" -import os +"""Unit tests for the Preference resource using a mock HTTP client.""" import unittest -import time -import mercadopago +import warnings +from tests.base_client_test import BaseClientTest -class TestPreference(unittest.TestCase): - """ - Test Module: Preference - """ - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_all(self): - """ - Test Module: Preference - """ +class TestPreference(BaseClientTest): + """Test Module: Preference""" + + def test_create(self): + fixture = self.load_fixture("preference_create.json") + self.mock_post(fixture, status=201) preference_object = { - "items": [ - { - "description": "Test Update Success", - "id": "456", - "picture_url": "http://product1.image.png", - "quantity": 1, - "title": "Item 1", - "currency_id": "BRL", - "unit_price": 20.5, - "warranty": False, - "type": "default", - "event_date": "2027-01-15T00:00:00.000-03:00", - "category_descriptor": { - "passenger": { - "first_name": "Nome", - "last_name": "Sobrenome", - "identification": { - "type": "CPF", - "number": "19119119100" - } - }, - "route": { - "departure": "SAO", - "destination": "RIO", - "departure_date_time": "2027-01-15T08:00:00.000-03:00", - "arrival_date_time": "2027-01-15T09:00:00.000-03:00", - "company": "TAM" - } - } - } - ], - "payer": { - "email": "test_user_123456@testuser.com", - "date_created": "2020-01-15T00:00:00.000-03:00", - "authentication_type": "WEB", - "is_prime_user": False, - "is_first_purchase_online": False, - "last_purchase": "2024-01-01T00:00:00.000-03:00", - "registration_date": "2020-01-15T00:00:00.000-03:00" - }, - "shipments": { - "receiver_address": { - "street_name": "Av das Nacoes Unidas", - "street_number": 3003, - "zip_code": "06233200", - "state_name": "Rio de Janeiro", - "city_name": "Buzios", - "floor": "2", - "apartment": "A" - }, - "express_shipment": False, - "local_pickup": False - } + "items": [{"title": "Point Mini", "quantity": 1, "unit_price": 58.80}] } - preference_saved = self.sdk.preference().create(preference_object) - self.assertEqual(preference_saved["status"], 201) - time.sleep(3) + result = self.sdk.preference().create(preference_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("843382748-18d90a57-a4ce-4718-bc17-1234567890", resp["id"]) + self.assertIn("init_point", resp) + self.assertIsInstance(resp["items"], list) + self.assertEqual("Point Mini", resp["items"][0]["title"]) + self.assertEqual(843382748, resp["collector_id"]) + self.assertIn("back_urls", resp) + self.assertIn("payment_methods", resp) + self.assertEqual(12, resp["payment_methods"]["installments"]) + self.mock_http.post.assert_called_once() + + def test_create_with_notification_url_warns(self): + fixture = self.load_fixture("preference_create.json") + self.mock_post(fixture, status=201) + preference_object = { + "items": [{"title": "Point Mini", "quantity": 1, "unit_price": 58.80}], + "notification_url": "https://example.com/notify", + } + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + self.sdk.preference().create(preference_object) + self.assertTrue(any(issubclass(w.category, DeprecationWarning) for w in caught)) + + def test_get(self): + fixture = self.load_fixture("preference_get.json") + self.mock_get(fixture) + result = self.sdk.preference().get("843382748-18d90a57-a4ce-4718-bc17-1234567890") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("843382748-18d90a57-a4ce-4718-bc17-1234567890", resp["id"]) + self.assertIn("init_point", resp) + self.assertIsInstance(resp["items"], list) + self.assertEqual("Point Mini", resp["items"][0]["title"]) + self.assertEqual(58.80, resp["items"][0]["unit_price"]) + self.assertEqual(843382748, resp["collector_id"]) + self.assertIn("back_urls", resp) + self.assertEqual("https://example.com/success", resp["back_urls"]["success"]) + self.assertEqual("MP-PREF-001", resp["external_reference"]) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() - preference_object["items"][0]["title"] = "Testando 1 2 3" + def test_update(self): + fixture = self.load_fixture("preference_update.json") + self.mock_put(fixture) + result = self.sdk.preference().update( + "843382748-18d90a57-a4ce-4718-bc17-1234567890", + {"items": [{"title": "Updated Item"}]}, + ) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("843382748-18d90a57-a4ce-4718-bc17-1234567890", resp["id"]) + self.assertIn("items", resp) + self.mock_http.put.assert_called_once() - preference_id = preference_saved["response"]["id"] - preference_update = self.sdk.preference().update(preference_id, preference_object) - self.assertEqual(preference_update["status"], 200) + def test_search(self): + fixture = self.load_fixture("preference_search.json") + self.mock_get(fixture) + result = self.sdk.preference().search() + self.assertEqual(200, result["status"]) + self.mock_http.get.assert_called_once() - time.sleep(3) - preference_saved = self.sdk.preference().get(preference_id) - self.assertEqual(preference_saved["status"], 200) - self.assertEqual(preference_saved["response"]["items"][0]["title"], - preference_object["items"][0]["title"]) + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.preference().create("not-a-dict") - time.sleep(3) - preference_search = self.sdk.preference().search() - self.assertEqual(preference_search["status"], 200) + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.preference().update("843382748-18d90a57-a4ce-4718-bc17-1234567890", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_refund_get.py b/tests/test_refund_get.py index 97155a9..73a560f 100644 --- a/tests/test_refund_get.py +++ b/tests/test_refund_get.py @@ -1,48 +1,66 @@ -"""Unit tests for Refund.get() using MagicMock.""" +"""Unit tests for the Refund resource using a mock HTTP client.""" import unittest -from unittest.mock import MagicMock -from mercadopago.config.request_options import RequestOptions -from mercadopago.resources.refund import Refund +from tests.base_client_test import BaseClientTest -class TestRefundGet(unittest.TestCase): - """Unit tests for Refund.get().""" +class TestRefund(BaseClientTest): + """Test Module: Refund""" - def _make_refund(self, http_client): - request_options = RequestOptions(access_token="TEST_TOKEN") - return Refund(request_options, http_client) + def test_list_all(self): + fixture = self.load_fixture("refund_list_all.json") + self.mock_get(fixture) + result = self.sdk.refund().list_all(17014025134) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIsInstance(resp, list) + self.assertEqual(1091618291, resp[0]["id"]) + self.assertEqual(17014025134, resp[0]["payment_id"]) + self.assertEqual(58.80, resp[0]["amount"]) + self.assertEqual("approved", resp[0]["status"]) + self.mock_http.get.assert_called_once() - def test_get_returns_refund_by_id(self): - """Verifies get() returns the correct refund and calls the expected URL.""" - http_client = MagicMock() - http_client.get.return_value = {"status": 200, "response": {"id": 99, "status": "approved"}} + def test_get(self): + fixture = self.load_fixture("refund_get.json") + self.mock_get(fixture) + result = self.sdk.refund().get(17014025134, 1091618291) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual(1091618291, resp["id"]) + self.assertEqual(17014025134, resp["payment_id"]) + self.assertEqual(58.80, resp["amount"]) + self.assertEqual("approved", resp["status"]) + self.assertEqual("standard", resp["refund_mode"]) + self.assertIn("date_created", resp) + self.assertIn("source", resp) + self.assertEqual("Test User", resp["source"]["name"]) + self.assertEqual("operator", resp["source"]["type"]) + self.mock_http.get.assert_called_once() - refund = self._make_refund(http_client) - result = refund.get(payment_id=123, refund_id=99) + def test_create_full_refund(self): + fixture = self.load_fixture("refund_create.json") + self.mock_post(fixture, status=201) + result = self.sdk.refund().create(17014025134) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual(1091618291, resp["id"]) + self.assertEqual(17014025134, resp["payment_id"]) + self.assertEqual(58.80, resp["amount"]) + self.assertEqual("approved", resp["status"]) + self.assertEqual("standard", resp["refund_mode"]) + self.mock_http.post.assert_called_once() - self.assertEqual(result["status"], 200) - self.assertEqual(result["response"]["id"], 99) - http_client.get.assert_called_once() - call_args = http_client.get.call_args - called_url = ( - call_args.kwargs.get("url") - or call_args[1].get("url") - or call_args[0][0] - ) - self.assertIn("/v1/payments/123/refunds/99", called_url) + def test_create_partial_refund(self): + fixture = self.load_fixture("refund_create.json") + self.mock_post(fixture, status=201) + result = self.sdk.refund().create(17014025134, {"amount": 50.0}) + self.assertEqual(201, result["status"]) + self.assertIn("date_created", result["response"]) + self.mock_http.post.assert_called_once() - def test_get_passes_request_options(self): - """Verifies custom request_options are forwarded to the HTTP client.""" - http_client = MagicMock() - http_client.get.return_value = {"status": 200, "response": {"id": 55}} - - custom_options = RequestOptions(access_token="CUSTOM_TOKEN") - refund = self._make_refund(http_client) - result = refund.get(payment_id=10, refund_id=55, request_options=custom_options) - - self.assertEqual(result["status"], 200) - http_client.get.assert_called_once() + def test_create_raises_for_non_dict_refund_object(self): + with self.assertRaises(ValueError): + self.sdk.refund().create(17014025134, "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_subscription.py b/tests/test_subscription.py index db56a07..36cdf54 100644 --- a/tests/test_subscription.py +++ b/tests/test_subscription.py @@ -1,180 +1,78 @@ -""" - Module: test_plan -""" -import os +"""Unit tests for the Subscription resource using a mock HTTP client.""" import unittest -import random -import mercadopago -from tests import api_call_with_retry - -class TestSubscription(unittest.TestCase): - """ - Test Module: Preference - """ - _customer_id = None - _customer_email = None - _plan_id = None - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - - @classmethod - def setUpClass(cls): - customer_data = cls.create_customer() - cls._customer_id = customer_data["response"]["id"] - cls._customer_email = customer_data["response"]["email"] - plan_data = cls.create_plan() - if plan_data.get("status") != 201 or "id" not in plan_data.get("response", {}): - raise RuntimeError(f"Failed to create plan: {plan_data}") - cls._plan_id = plan_data["response"]["id"] - - @classmethod - def tearDownClass(cls): - cls.delete_customer() - - def test_all(self): - """ - Test Module: Subscription - """ - card_token = self.create_card_token() - card_token_id = card_token['response']['id'] - - random_reason_number = random.randint(100000, 999999) - subscription_payload = { - "back_url": "https://www.mercadopago.com.co/subscriptions", - "reason": f"MercadoPago API Subscription #{random_reason_number}", - "external_reference": "CustomIdentifier", - "payer_email": self._customer_email, - "preapproval_plan_id": self._plan_id, - "card_token_id": card_token_id, - "status": "authorized", - "auto_recurring": { - "frequency": 1, - "frequency_type": "months", - "transaction_amount": 100, - "currency_id": "BRL" - } - } - - subscription_response = api_call_with_retry( - lambda: self.sdk.subscription().create(subscription_payload), expected_status=201 - ) - self.assertEqual(subscription_response["status"], 201) - - subscription_object = subscription_response['response'] - self.assertIn('init_point', subscription_object) - self.assertEqual( - subscription_object["external_reference"], subscription_payload["external_reference"]) - self.assertEqual(subscription_object["status"], "authorized") - - update_payload = { - "reason": f"MercadoPago API Subscription A #{random_reason_number}", - } - update_response = self.sdk.subscription().update( - subscription_object["id"], update_payload) - self.assertEqual(update_response["status"], 200) - update_object = update_response["response"] - self.assertEqual(update_object["reason"], update_payload["reason"]) - - get_response = self.sdk.subscription().get(subscription_object["id"]) - self.assertEqual(get_response["status"], 200) - get_object = get_response["response"] - self.assertEqual(get_object["id"], subscription_object["id"]) - - search_response = self.sdk.subscription().search() - self.assertEqual(search_response["status"], 200) - search_object = search_response["response"] - self.assertTrue("results" in search_object) - self.assertTrue(isinstance(search_object["results"], list)) - - def test_create_subscriptions_without_a_plan(self): - """ - Test Module: Subscription - - Test subscription creation without a plan - """ - card_token = self.create_card_token() - card_token_id = card_token['response']['id'] - - random_reason_number = random.randint(100000, 999999) - subscription_payload = { - "back_url": "https://www.mercadopago.com.co/subscriptions", - "reason": f"MercadoPago API Subscription B #{random_reason_number}", - "external_reference": "CustomIdentifier", - "payer_email": self._customer_email, - "card_token_id": card_token_id, - "auto_recurring": { - "frequency": 1, - "frequency_type": "months", - "transaction_amount": 100, - "currency_id": "BRL", - }, - "status": "authorized" - } - - subscription_response = api_call_with_retry( - lambda: self.sdk.subscription().create(subscription_payload), expected_status=201 - ) - self.assertEqual(subscription_response["status"], 201) - - subscription_object = subscription_response['response'] - self.assertIn('init_point', subscription_object) - self.assertEqual( - subscription_object["external_reference"], subscription_payload["external_reference"]) - self.assertEqual(subscription_object["status"], "authorized") - - @classmethod - def create_card_token(cls): - card_token_object = { - "card_number": "4074090000000004", - "security_code": "123", - "expiration_year": "2030", - "expiration_month": "12", - "cardholder": { - "name": "APRO", - "identification": { - "CPF": "19119119100" - } - } - } - return cls.sdk.card_token().create(card_token_object) - - @classmethod - def create_customer(cls): - random_email_id = random.randint(100000, 999999) - customer_object = { - "email": f"test_payer_{random_email_id}@testuser.com", - "first_name": "Python", - "last_name": "Mercado", - "phone": { - "area_code": "03492", - "number": "432334" - }, - "identification": { - "type": "DNI", - "number": "29804555" - }, - "description": "customer description" - } - - return cls.sdk.customer().create(customer_object) - - @classmethod - def delete_customer(cls): - cls.sdk.customer().delete(cls._customer_id) - - @classmethod - def create_plan(cls): - plan_object = { - "auto_recurring": { - "frequency": 1, - "frequency_type": "months", - "transaction_amount": 100, - "currency_id": "BRL", - }, - "back_url": "https://www.mercadopago.com.co/subscriptions", - "reason": f"Test Plan #{random.randint(100000, 999999)}", +from tests.base_client_test import BaseClientTest + + +class TestSubscription(BaseClientTest): + """Test Module: Subscription""" + + def test_get(self): + fixture = self.load_fixture("subscription_get.json") + self.mock_get(fixture) + result = self.sdk.subscription().get("2c938084726fca480172750000000003") + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000003", resp["id"]) + self.assertEqual("authorized", resp["status"]) + self.assertEqual("2c938084726fca480172750000000002", resp["preapproval_plan_id"]) + self.assertEqual("test_user@testuser.com", resp["payer_email"]) + self.assertEqual(123456789, resp["payer_id"]) + self.assertIn("auto_recurring", resp) + self.assertEqual(29.90, resp["auto_recurring"]["transaction_amount"]) + self.assertEqual("BRL", resp["auto_recurring"]["currency_id"]) + self.assertIn("next_payment_date", resp) + self.assertIn("date_created", resp) + self.mock_http.get.assert_called_once() + + def test_create(self): + fixture = self.load_fixture("subscription_create.json") + self.mock_post(fixture, status=201) + subscription_object = { + "preapproval_plan_id": "2c938084726fca480172750000000002", + "payer_email": "test_user@testuser.com", + "card_token_id": "a78sd6f1a9s8d7f1a", } - return cls.sdk.plan().create(plan_object) + result = self.sdk.subscription().create(subscription_object) + self.assertEqual(201, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000003", resp["id"]) + self.assertEqual("pending", resp["status"]) + self.assertEqual("2c938084726fca480172750000000002", resp["preapproval_plan_id"]) + self.assertIn("auto_recurring", resp) + self.assertIn("date_created", resp) + self.mock_http.post.assert_called_once() + + def test_update(self): + fixture = self.load_fixture("subscription_update.json") + self.mock_put(fixture) + result = self.sdk.subscription().update("2c938084726fca480172750000000003", {"status": "cancelled"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertEqual("2c938084726fca480172750000000003", resp["id"]) + self.assertEqual("cancelled", resp["status"]) + self.assertIn("last_modified", resp) + self.mock_http.put.assert_called_once() + + def test_search(self): + fixture = self.load_fixture("subscription_search.json") + self.mock_get(fixture) + result = self.sdk.subscription().search({"status": "authorized"}) + self.assertEqual(200, result["status"]) + resp = result["response"] + self.assertIn("results", resp) + self.assertEqual("2c938084726fca480172750000000003", resp["results"][0]["id"]) + self.assertEqual("authorized", resp["results"][0]["status"]) + self.assertEqual("test_user@testuser.com", resp["results"][0]["payer_email"]) + self.mock_http.get.assert_called_once() + + def test_create_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.subscription().create("not-a-dict") + + def test_update_raises_for_non_dict(self): + with self.assertRaises(ValueError): + self.sdk.subscription().update("2c938084726fca480172750000000003", "not-a-dict") if __name__ == "__main__": diff --git a/tests/test_user.py b/tests/test_user.py index 6076f54..c235537 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,16 +1,20 @@ -""" - Module: test_user -""" -import os +"""Unit tests for the User resource using a mock HTTP client.""" import unittest -import mercadopago +from tests.base_client_test import BaseClientTest -class TestUser(unittest.TestCase): - sdk = mercadopago.SDK(os.environ['ACCESS_TOKEN']) - def test_find_user(self): - self.assertEqual(self.sdk.user().get()["status"], 200) +class TestUser(BaseClientTest): + """Test Module: User""" + + def test_get(self): + fixture = self.load_fixture("user_get.json") + self.mock_get(fixture) + result = self.sdk.user().get() + self.assertEqual(200, result["status"]) + self.assertEqual(12345, result["response"]["id"]) + self.assertEqual("test@example.com", result["response"]["email"]) + self.mock_http.get.assert_called_once() if __name__ == "__main__":