as

Settings
Sign out
Notifications
Alexa
Amazonアプリストア
AWS
ドキュメント
Support
Contact Us
My Cases
Docs
Resources
Ecommerce Plug-ins
Publish
Connect
アクセスいただきありがとうございます。こちらのページは現在英語のみのご用意となっております。順次日本語化を進めてまいりますので、ご理解のほどよろしくお願いいたします。

Fraud and abuse feedback testing

1. Test Overview

API Endpoint: POST v1/feedback/payment-status
Purpose: Report shopper payment status to prevent repeat entry for shoppers with outstanding balance
Test Environment: Staging/Test environment with mock data

2. Test Scope

In Scope

  • Request validation
  • Response format verification
  • Error handling

Out of Scope

  • Performance testing
  • Load testing
  • Production data testing

3. Test Categories

3.1 Positive Test Cases

Test ID Test Case Input Expected Result
TC001 Valid payment accepted Valid payload with settled = total 200, status: "Accepted"
TC002 Valid payment rejected Valid payload 200, status: "Rejected"
TC003 Partial payment settledAmount < totalAmount 200, status: "Accepted"
TC004 Zero amount transaction amount: 0 200, status: "Accepted"
TC005 Multiple currency support EUR, GBP, CAD, JPY 200, status: "Accepted"

3.2 Input Validation Tests

Test ID Test Case Input Expected Result
TC006 Valid storeId formats "store123", "STORE_456", "store-789" 200
TC007 Valid shoppingTripId UUID format strings 200
TC008 Currency code validation "USD", "EUR", "GBP" 200
TC009 Amount precision Decimal values 200
TC010 Boundary values Max length strings (255 chars) 200

3.3 Negative Test Cases

Test ID Test Case Input Expected Result
TC011 Missing storeId Payload without storeId 400, BadRequestException
TC012 Missing shoppingTripId Payload without shoppingTripId 400, BadRequestException
TC013 Missing totalAmount Payload without totalAmount 400, BadRequestException
TC014 Missing settledAmount Payload without settledAmount 400, BadRequestException
TC015 Invalid storeId format "store@123", "store 123" 400, BadRequestException
TC016 Invalid currency code "US", "USDD", "123" 400, BadRequestException
TC017 Negative amounts amount: -10.00 400, BadRequestException
TC018 Oversized fields storeId > 255 chars 400, BadRequestException
TC019 Unknown shopping trip Non-existent shoppingTripId 400, UnknownShoppingTrip
TC020 Settled > Total amount settledAmount > totalAmount 400, BadRequestException

3.4 Error Handling Tests

Test ID Test Case Scenario Expected Result
TC021 Server error simulation Mock 500 error 500, ServiceException
TC022 Network timeout Timeout simulation Retry with exponential backoff
TC023 Invalid JSON format Malformed JSON 400, BadRequestException
TC024 Content-Type validation Wrong content type 400, BadRequestException

4. Test Data

4.1 Valid Test Data

{
  "storeId": "store123",
  "shoppingTripId": "trip-abc-123",
  "totalAmount": {
    "amount": 25.99,
    "code": "USD"
  },
  "settledAmount": {
    "amount": 25.99,
    "code": "USD"
  },
  "paymentStatusTime": 1640995200
}

4.2 Edge Case Data

  • Zero amounts
  • Large amounts (999999.99)
  • Maximum field lengths
  • Different currency combinations

5. Test Environment Setup

Prerequisites

  • Test API endpoint access
  • Valid authentication credentials
  • Test data setup
  • Mock service configuration

Tools Required

  • pytest framework
  • requests library
  • Mock/patch utilities
  • Test reporting tools

6. Execution Strategy

6.1 Test Phases

  1. Unit Tests - Individual field validation
  2. Integration Tests - End-to-end API testing
  3. Error Scenario Tests - Exception handling
  4. Regression Tests - Existing functionality

6.2 Test Execution Order

  1. Positive test cases first
  2. Input validation tests
  3. Negative test cases
  4. Error handling scenarios

7. Pass/Fail Criteria

Pass Criteria

  • All positive test cases return expected 200 responses
  • Error cases return appropriate 400/500 status codes
  • Response format matches API specification
  • All required fields validated correctly

Fail Criteria

  • Any unexpected status codes
  • Missing or incorrect response fields
  • Security vulnerabilities identified
  • Performance degradation beyond acceptable limits

8. Test Deliverables

  • Test execution report
  • Defect report (if any)
  • Test coverage metrics
  • Recommendations for improvements

9. Risk Assessment

High Risk Areas

  • Payment amount validation
  • Currency code handling
  • Shopping trip ID validation
  • Error message exposure

Unit tests


import pytest
import requests
import json
from unittest.mock import Mock, patch

class TestFraudAbuseFeedbackAPI:
    """Comprehensive test cases for the Fraud and Abuse Feedback API"""
    
    BASE_URL = "https://api.example.com/v1/feedback/payment-status"
    
    def setup_method(self):
        """Setup test data"""
        self.valid_payload = {
            "storeId": "store123",
            "shoppingTripId": "trip-abc-123",
            "totalAmount": {
                "amount": 25.99,
                "code": "USD"
            },
            "settledAmount": {
                "amount": 25.99,
                "code": "USD"
            },
            "paymentStatusTime": 1640995200
        }
    
    # SUCCESS SCENARIOS
    
    @patch('requests.post')
    def test_successful_payment_status_accepted(self, mock_post):
        """Test successful payment status report - Accepted"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "status": "Accepted",
            "reasonCode": "PAYMENT_PROCESSED"
        }
        mock_post.return_value = mock_response
        
        response = requests.post(self.BASE_URL, json=self.valid_payload)
        
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "Accepted"
        assert "reasonCode" in data
    
    @patch('requests.post')
    def test_successful_payment_status_rejected(self, mock_post):
        """Test successful payment status report - Rejected"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "status": "Rejected",
            "reasonCode": "INSUFFICIENT_FUNDS"
        }
        mock_post.return_value = mock_response
        
        response = requests.post(self.BASE_URL, json=self.valid_payload)
        
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "Rejected"
        assert data["reasonCode"] == "INSUFFICIENT_FUNDS"
    
    @patch('requests.post')
    def test_partial_payment_scenario(self, mock_post):
        """Test partial payment where settled amount < total amount"""
        partial_payload = self.valid_payload.copy()
        partial_payload["settledAmount"]["amount"] = 20.00
        
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "status": "Accepted",
            "reasonCode": "PARTIAL_PAYMENT"
        }
        mock_post.return_value = mock_response
        
        response = requests.post(self.BASE_URL, json=partial_payload)
        
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "Accepted"
    
    # VALIDATION TESTS
    
    @patch('requests.post')
    def test_valid_store_id_formats(self, mock_post):
        """Test various valid storeId formats"""
        valid_store_ids = ["store123", "STORE_456", "store-789", "123ABC"]
        
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "OK"}
        mock_post.return_value = mock_response
        
        for store_id in valid_store_ids:
            payload = self.valid_payload.copy()
            payload["storeId"] = store_id
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 200
    
    @patch('requests.post')
    def test_valid_currency_codes(self, mock_post):
        """Test various valid currency codes"""
        valid_currencies = ["USD", "EUR", "GBP", "CAD", "JPY"]
        
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "OK"}
        mock_post.return_value = mock_response
        
        for currency in valid_currencies:
            payload = self.valid_payload.copy()
            payload["totalAmount"]["code"] = currency
            payload["settledAmount"]["code"] = currency
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 200
    
    # ERROR SCENARIOS - 400 Bad Request
    
    @patch('requests.post')
    def test_missing_required_fields(self, mock_post):
        """Test missing required fields"""
        required_fields = ["storeId", "shoppingTripId", "totalAmount", "settledAmount"]
        
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Missing required field"}
        mock_post.return_value = mock_response
        
        for field in required_fields:
            payload = self.valid_payload.copy()
            del payload[field]
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 400
    
    @patch('requests.post')
    def test_invalid_store_id_format(self, mock_post):
        """Test invalid storeId formats"""
        invalid_store_ids = ["", "store@123", "store 123", "a" * 256]
        
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Invalid storeId format"}
        mock_post.return_value = mock_response
        
        for store_id in invalid_store_ids:
            payload = self.valid_payload.copy()
            payload["storeId"] = store_id
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 400
    
    @patch('requests.post')
    def test_invalid_shopping_trip_id(self, mock_post):
        """Test invalid shoppingTripId formats"""
        invalid_trip_ids = ["", "trip@123", "trip 123", "a" * 256]
        
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Invalid shoppingTripId format"}
        mock_post.return_value = mock_response
        
        for trip_id in invalid_trip_ids:
            payload = self.valid_payload.copy()
            payload["shoppingTripId"] = trip_id
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 400
    
    @patch('requests.post')
    def test_invalid_currency_code(self, mock_post):
        """Test invalid currency codes"""
        invalid_currencies = ["", "US", "USDD", "us", "123"]
        
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Invalid currency code"}
        mock_post.return_value = mock_response
        
        for currency in invalid_currencies:
            payload = self.valid_payload.copy()
            payload["totalAmount"]["code"] = currency
            response = requests.post(self.BASE_URL, json=payload)
            assert response.status_code == 400
    
    @patch('requests.post')
    def test_negative_amounts(self, mock_post):
        """Test negative amounts"""
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Amount cannot be negative"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["totalAmount"]["amount"] = -10.00
        response = requests.post(self.BASE_URL, json=payload)
        assert response.status_code == 400
    
    @patch('requests.post')
    def test_unknown_shopping_trip(self, mock_post):
        """Test 400 UnknownShoppingTrip error"""
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "UnknownShoppingTrip"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["shoppingTripId"] = "nonexistent-trip-id"
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 400
        assert "UnknownShoppingTrip" in response.json()["errorMsg"]
    
    @patch('requests.post')
    def test_bad_request_exception(self, mock_post):
        """Test 400 BadRequestException"""
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "BadRequestException"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["storeId"] = "invalid@store"
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 400
        assert "BadRequestException" in response.json()["errorMsg"]
    
    # ERROR SCENARIOS - 500 Server Error
    
    @patch('requests.post')
    def test_service_exception(self, mock_post):
        """Test 500 ServiceException"""
        mock_response = Mock()
        mock_response.status_code = 500
        mock_response.json.return_value = {"errorMsg": "ServiceException"}
        mock_post.return_value = mock_response
        
        response = requests.post(self.BASE_URL, json=self.valid_payload)
        
        assert response.status_code == 500
        assert "ServiceException" in response.json()["errorMsg"]
    
    # EDGE CASES
    
    @patch('requests.post')
    def test_zero_amounts(self, mock_post):
        """Test zero amounts"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "ZERO_AMOUNT"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["totalAmount"]["amount"] = 0
        payload["settledAmount"]["amount"] = 0
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 200
    
    @patch('requests.post')
    def test_large_amounts(self, mock_post):
        """Test large amounts"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "LARGE_AMOUNT"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["totalAmount"]["amount"] = 999999.99
        payload["settledAmount"]["amount"] = 999999.99
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 200
    
    @patch('requests.post')
    def test_mismatched_currencies(self, mock_post):
        """Test mismatched currencies between total and settled amounts"""
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Currency mismatch"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["totalAmount"]["code"] = "USD"
        payload["settledAmount"]["code"] = "EUR"
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 400
    
    @patch('requests.post')
    def test_settled_greater_than_total(self, mock_post):
        """Test settled amount greater than total amount"""
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMsg": "Settled amount exceeds total"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["totalAmount"]["amount"] = 20.00
        payload["settledAmount"]["amount"] = 25.00
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 400
    
    # BOUNDARY TESTS
    
    @patch('requests.post')
    def test_max_length_store_id(self, mock_post):
        """Test maximum length storeId (255 characters)"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "OK"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["storeId"] = "a" * 255
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 200
    
    @patch('requests.post')
    def test_max_length_shopping_trip_id(self, mock_post):
        """Test maximum length shoppingTripId (255 characters)"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"status": "Accepted", "reasonCode": "OK"}
        mock_post.return_value = mock_response
        
        payload = self.valid_payload.copy()
        payload["shoppingTripId"] = "a" * 255
        response = requests.post(self.BASE_URL, json=payload)
        
        assert response.status_code == 200

if __name__ == "__main__":
    pytest.main([__file__])