Unit and Integration tests

conftest.py

import os
import pytest

from kount import config


def pytest_addoption(parser):
    parser.addoption('--conf-key', action='store',
                     default=os.environ.get('CONF_KEY', ''))
    parser.addoption('--api-key', action='store',
                     default=os.environ.get('RIS_SDK_SANDBOX_API_KEY', ''))
    parser.addoption('--merchant-id', action='store', 
                     default=os.environ.get('RIS_SDK_SANDBOX_MERCHANT_ID', ''))
    parser.addoption('--api-url', action='store', 
                     default=os.environ.get('RIS_SDK_SANDBOX_URL', 'https://risk.test.kount.net'))


@pytest.fixture(scope='session', autouse=True)
def conf_key(request):
    try:
        config.SDKConfig.setup(request.config.getoption('--conf-key'))
    except ValueError as e:
        if not config.SDKConfig.get_configuration_key():
            msg = "Configuration key not set, use --conf-key or " \
                  "set environment variable CONF_KEY"
        else:
            msg = 'Configuration key error: %s' % str(e)
        pytest.exit(msg)


@pytest.fixture(scope='class')
def api_key(request):
    request.cls.api_key = request.config.getoption('--api-key')


@pytest.fixture(scope='class')
def merchant_id(request):
    request.cls.merchant_id = request.config.getoption('--merchant-id')


@pytest.fixture(scope='class')
def api_url(request):
    request.cls.api_url = request.config.getoption('--api-url')

test_address.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
import unittest
from kount.util.address import Address
from kount.version import VERSION
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


class TestAddress(unittest.TestCase):
    """Address class test cases"""

    def test_address_valid(self):
        """valid address"""
        adr = Address(address1="567 West S2A1 Court North",
                      address2=None, state="Gnome", postal_code="AK",
                      premise="99762", country="US")
        self.assertTrue(isinstance(adr, Address))
        adr = Address("1234 North B2A1 Tree Lane South", None,
                      "Albuquerque", "NM", "87101", "US")
        self.assertTrue(isinstance(adr, Address))
        adr = Address("567 West S2A1 Court North", None,
                      "Gnome", "AK", "99762", "US")
        self.assertEqual("567 West S2A1 Court North", str(adr.address1))

    def test_address_incorrect_string(self):
        """incorrect address"""
        for bad_type in [42**42, "<script>alert(42)</script>", None, "", 42]:
            adr = Address(bad_type)
            self.assertEqual("", str(adr.country))

    def test_address_cyrillic(self):
        """incorrect address - cyrillic"""
        for bad_type in ["Сирма", "'%=:*-+<", "ъ"]:
            adr = Address(bad_type)
            self.assertEqual("", str(adr.country))
            self.assertEqual(bad_type, adr.address1)


if __name__ == "__main__":
    unittest.main()

test_api_kount.py

#!/usr/bin/env python
"""Test class TestAPIRIS"""
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
import logging
import unittest

import pytest

from kount.client import Client
from kount.version import VERSION

from .json_test import example_data_products
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS

LOGGER = logging.getLogger('kount')

expected1 = {
    'AUTO': 'R',
    'BRND': None,
    'BROWSER': None,
    'CARDS': '1',
    'COOKIES': None,
    'COUNTERS_TRIGGERED': 0,
    'COUNTRY': None,
    'DDFS': None,
    'DEVICES': '1',
    'DEVICE_LAYERS': '....',
    'DSR': None,
    'EMAILS': '1',
    'FINGERPRINT': None,
    'FLASH': None,
    'GEOX': 'US',
    'HTTP_COUNTRY': None,
    'IP_CITY': None,
    'IP_COUNTRY': None,
    'IP_IPAD': None,
    'IP_LAT': None,
    'IP_LON': None,
    'IP_ORG': None,
    'IP_REGION': None,
    'JAVASCRIPT': None,
    'KAPT': 'N',
    'LANGUAGE': None,
    'LOCALTIME': ' ',
    'MERC': '',  # will be replaced
    'MOBILE_DEVICE': None,
    'MOBILE_FORWARDER': None,
    'MOBILE_TYPE': None,
    'MODE': 'Q',
    'NETW': 'N',
    'ORDR': 'F8E874A38B7B',
    'OS': None,
    'PC_REMOTE': None,
    'PIP_CITY': None,
    'PIP_COUNTRY': None,
    'PIP_IPAD': None,
    'PIP_LAT': None,
    'PIP_LON': None,
    'PIP_ORG': None,
    'PIP_REGION': None,
    'PREVIOUSLY_WHITELISTED': 'N',
    'PROXY': None,
    'REASON_CODE': None,
    'REGION': None,
    'REGN': None,
    'RULES_TRIGGERED': 1,
    'RULE_DESCRIPTION_0': 'Review if order total > $1000 USD',
    'SCOR': '34',
    'OMNISCORE':36.3,
    'SESS': 'F8E874A38B7B4B6DBB71492A584A969D',
    'SITE': 'DEFAULT',
    'THREE_DS_MERCHANT_RESPONSE': None,
    'TIMEZONE': None,
    'UAS': None,
    'VERS': '0710',
    'VOICE_DEVICE': None,
    'WARNING_COUNT': 0}


def dict_compare(dict1, dict2):
    """compare 2 dictionaries"""
    dict1_keys = set(dict1.keys())
    dict2_keys = set(dict2.keys())
    intersect_keys = dict1_keys.intersection(dict2_keys)
    added = dict1_keys - dict2_keys
    removed = dict2_keys - dict1_keys
    modified = {o: (dict1[o],
                    dict2[o]) for o in intersect_keys if dict1[o] != dict2[o]}
    same = set(o for o in intersect_keys if dict1[o] == dict2[o])
    return added, removed, modified, same


CURLED = {
    'ANID': '',
    'AUTH': 'A',
    'AVST': 'M',
    'AVSZ': 'M',
    'B2A1': '1234 North B2A1 Tree Lane South',
    'B2CC': 'US',
    'B2CI': 'Albuquerque',
    'B2PC': '87101',
    'B2PN': '555+867-5309',
    'B2ST': 'NM',
    'CASH': '4444',
    'CURR': 'USD',
    'CVVR': 'M',
    'EMAL': 'curly.riscaller15@kountqa.com',
    'FRMT': 'JSON',
    'IPAD': '4.127.51.215',
    'LAST4': '2514',
    'MACK': 'Y',
    'MERC': '999666',
    'MODE': 'Q',
    'NAME': 'Goofy Grumpus',
    'ORDR': '088E9F496135',
    'PROD_DESC[]': '3000 CANDLEPOWER PLASMA FLASHLIGHT',
    'PROD_ITEM[]': 'SG999999',
    'PROD_PRICE[]': '68990',
    'PROD_QUANT[]': '2',
    'PROD_TYPE[]': 'SPORTING_GOODS',
    'PTOK': '0007380568572514',
    'PTYP': 'CARD',
    'S2A1': '567 West S2A1 Court North',
    'S2CC': 'US',
    'S2CI': 'Gnome',
    'S2EM': 'sdkTestShipTo@kountsdktestdomain.com',
    'S2NM': 'SdkTestShipToFirst SdkShipToLast',
    'S2PC': '99762',
    'S2PN': '208 777-1212',
    'S2ST': 'AK',
    'SESS': '088E9F4961354D4F90041988B8D5C66B',
    'SITE': 'DEFAULT',
    'TOTL': '123456',
    'UNIQ': '088E9F4961354D4F9004',
    'VERS': '0710'}


@pytest.mark.usefixtures("api_url", "api_key", "merchant_id")
class TestAPIRIS(unittest.TestCase):
    """
    implemented curl from https://kopana.atlassian.net/wiki/display/KS/Testing
    """
    maxDiff = None

    timeout = 5

    def _expected_response(self):
        r = dict(expected1)
        r['MERC'] = self.merchant_id
        return r

    def test_api_kount(self):
        """expected modified 'TRAN'"""
        data = CURLED
        self.assertIn('MODE', CURLED)
        expected = {
            "VERS": "0710", "MODE": "Q", "TRAN": "PTPN0Z04P8Y6",
            "MERC": "999666", "SESS": "088E9F4961354D4F90041988B8D5C66B",
            "ORDR": "088E9F496135", "AUTO": "R", "SCOR": "29", "GEOX": "US",
            "BRND": None, "REGN": None, "NETW": "N", "KAPT": "N", "CARDS": "1",
            "DEVICES": "1", "EMAILS": "1", "VELO": "0",
            "VMAX": "0", "SITE": "DEFAULT", "DEVICE_LAYERS": "....",
            "FINGERPRINT": None, "TIMEZONE": None, "LOCALTIME": " ",
            "REGION": None,
            "COUNTRY": None, "PROXY": None, "JAVASCRIPT": None, "FLASH": None,
            "COOKIES": None, "HTTP_COUNTRY": None, "LANGUAGE": None,
            "MOBILE_DEVICE": None, "MOBILE_TYPE": None,
            "MOBILE_FORWARDER": None,
            "VOICE_DEVICE": None, "PC_REMOTE": None, "RULES_TRIGGERED": 1,
            "RULE_ID_0": "1024842",
            "RULE_DESCRIPTION_0": "Review if order total > $1000 USD",
            "COUNTERS_TRIGGERED": 0,
            "REASON_CODE": None, "DDFS": None, "DSR": None,
            "UAS": None, "BROWSER": None,
            "OS": None, "PIP_IPAD": None, "PIP_LAT": None, "PIP_LON": None,
            "PIP_COUNTRY": None,
            "PIP_REGION": None, "PIP_CITY": None, "PIP_ORG": None,
            "IP_IPAD": None,
            "IP_LAT": None, "IP_LON": None, "IP_COUNTRY": None,
            "IP_REGION": None,
            "IP_CITY": None, "IP_ORG": None, "WARNING_COUNT": 0,"OMNISCORE":None, "PREVIOUSLY_WHITELISTED": "N", "THREE_DS_MERCHANT_RESPONSE":None}
        for raise_errors in [True, False]:
            actual = self._client(raise_errors=raise_errors)._execute(data)
            added, removed, modified, _ = dict_compare(actual, expected)
            self.assertEqual(added, set())
            self.assertEqual(removed, set())
            modified_exp = {
                'REGN': (actual['REGN'], expected['REGN']),
                'TRAN': (actual['TRAN'], expected['TRAN']),
                'SCOR': (actual['SCOR'], expected['SCOR']),
                'OMNISCORE': (actual['OMNISCORE'],  expected['OMNISCORE'])
                }
            self.assertEqual(sorted(modified), sorted(modified_exp))

    def test_api_kount_2_items(self):
        "expected modified 'TRAN'"
        data = example_data_products.copy()
        self.assertIn('MODE', data)
        for raise_errors in [True, False]:
            actual = self._client(raise_errors=raise_errors)._execute(data)
            del (actual['TRAN'], actual['RULE_ID_0'],
                 actual['VELO'], actual['VMAX'])
            self.assertEqual(actual, self._expected_response())

    def test_last_2_items_bad_email(self):
        "last_2_items_bad_email"
        data = example_data_products.copy()
        self.assertIn('MODE', CURLED)
        bad = CURLED['EMAL'].replace('@', "%40")
        data["EMAL"] = bad
        
        expected = {
            'ERROR_0': 
                "321 BAD_EMAL Cause: [Invalid email address], Field: [EMAL],"
                " Value: [%s]" % (bad),
            'ERRO': 321,
            'ERROR_COUNT': 1,
            'WARNING_COUNT': 0,
            'MODE': 'E'}
        actual = self._client(raise_errors=False)._execute(data)
        self.assertEqual(actual, expected)

    def test_2_items_bad_s2em(self):
        """bad S2EM"""
        bad = example_data_products["S2EM"].replace('@', "%40")
        data = example_data_products.copy()
        data["S2EM"] = bad
        actual = self._client(raise_errors=False)._execute(params=data)
        del (actual['TRAN'], actual['RULE_ID_0'],
             actual['VELO'], actual['VMAX'])
        self.assertEqual(actual, self._expected_response())

    def test_two_items_none_email(self):
        "email = None"
        data = example_data_products.copy()
        data["EMAL"] = None
        self.assertIn('MODE', data)
        expected = {
            'ERRO': 221, 'ERROR_COUNT': 1,
            'MODE': 'E', 'WARNING_COUNT': 0,
            'ERROR_0': "221 MISSING_EMAL Cause: "
                       "[Non-empty value was required in this case], "
                       "Field: [EMAL], Value: []"}
        for raise_errors in [True, False]:
            actual = self._client(raise_errors=raise_errors)._execute(data)
            self.assertEqual(actual, expected)

    def test_two_items_missing_or_long_email(self):
        "missing or long incorrect email"
        data = example_data_products.copy()
        del data["EMAL"]
        self.assertIn('MODE', data)
        expected = {
            'ERRO': 221, 'ERROR_COUNT': 1,
            'MODE': 'E', 'WARNING_COUNT': 0,
            'ERROR_0': "221 MISSING_EMAL Cause: "
                       "[Non-empty value was required in this case], "
                       "Field: [EMAL], Value: []"}
        for raise_errors in [True, False]:
            actual = self._client(raise_errors=raise_errors)._execute(data)
            self.assertEqual(actual, expected)
        data["EMAL"] = "a" * 57 + "@aaa.com"
        response = self._client(raise_errors=False)._execute(data)
        self.assertEqual(321, response['ERRO'])

    def test_api_kount_empty_data(self):
        "empty data"
        data = {'FRMT': 'JSON'}
        expected = {"MODE": "E", "ERRO": "201"}
        actual = self._client(raise_errors=False)._execute(data)
        self.assertEqual(actual, expected)

    def _client(self, **kwargs):
        kwargs['api_url'] = self.api_url
        kwargs['api_key'] = self.api_key
        kwargs['timeout'] = self.timeout
        return Client(**kwargs)

test_basic_connectivity.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"""Test Cases from sdk documentation
generate_unique_id
default_inquiry
Test Basic Connectivity
"""
import unittest
import pytest

from kount.client import Client
from kount.util.payment import CardPayment
from kount.version import VERSION

from .test_inquiry import generate_unique_id, default_inquiry

from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


PTOK = "0007380568572514"
EMAIL = 'predictive@kount.com'


@pytest.mark.usefixtures("api_url", "api_key", "merchant_id")
class TestBasicConnectivity(unittest.TestCase):
    """Test Basic Connectivity"""
    maxDiff = None

    def _client(self, **kwargs):
        kwargs['api_url'] = self.api_url
        kwargs['api_key'] = self.api_key
        return Client(**kwargs)

    def _process(self, request, **client_kwargs):
        return self._client(**client_kwargs).process(request)

    def setUp(self):
        self.session_id = generate_unique_id()[:32]
        self.email_client = EMAIL
        payment = CardPayment(PTOK, khashed=False)
        self.inq = default_inquiry(self.merchant_id,
                                   self.session_id,
                                   self.email_client,
                                   payment=payment)

    def test_12_expected_score(self):
        "test_12_expected_score"
        self.inq.params["UDF[~K!_SCOR]"] = '42'
        res = self._process(self.inq)
        self.assertIsNotNone(res)
        self.assertEqual('42', res.get_score())

    def request_with_Lbin(self):
        "test_Lbin_set_in_requst"
        self.inq.params["LBIN"] = '1234567'
        self.assertEqual('1234567', self.inq.params.get("LBIN"))
        res = self._process(self.inq)
        self.assertEqual(0, len(res.get_errors))

    def request_without_Lbin(self):
        "test_Lbin_not_set_in_requst"
        res = self._process(self.inq)
        self.assertEqual(0, len(res.get_errors))

    def test_13_expected_decision(self):
        """test_13_expected_decision"""
        self.inq.params["UDF[~K!_AUTO]"] = 'R'
        res = self._process(self.inq)
        self.assertIsNotNone(res)
        self.assertEqual("R", res.get_auto())

    def test_16_expected_geox(self):
        """test_16_expected_geox"""
        self.inq.params["UDF[~K!_SCOR]"] = '42'
        self.inq.params["UDF[~K!_AUTO]"] = 'D'
        self.inq.params["UDF[~K!_GEOX]"] = 'NG'
        res = self._process(self.inq)
        self.assertIsNotNone(res)
        self.assertEqual("D", res.get_auto())
        self.assertEqual("NG", res.get_geox())
        self.assertEqual("42", res.get_score())

    def test_cyrillic(self):
        """test_cyrillic"""
        bad = u'Сирма :ы№'
        self.inq.params["S2NM"] = bad
        self.inq.params["EMAL"] = bad
        res = self._process(self.inq, raise_errors=False)
        self.assertIsNotNone(res)
        self.assertEqual({
            u'ERRO': 321,
            u'ERROR_0': u"321 BAD_EMAL Cause: [Invalid email address]"\
                 ", Field: [EMAL], Value: [%s]" % (bad),
            u'ERROR_COUNT': 1, u'MODE': u'E', u'WARNING_COUNT': 0},
            {u'ERRO':res.get_error_code(),
            u'ERROR_0': res.get_errors()[0],
            u'ERROR_COUNT': len(res.get_errors()),
            u'MODE': res.get_mode(),
            u'WARNING_COUNT': len(res.get_warnings())})

    def test_long(self):
        """test_long request"""
        bad_list = [
            'Сирма :ы№',
            'abcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 12345']
        expected = """Neither JSON nor String """\
            """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n"""\
            "<html><head>\n"\
            "<title>413 Request Entity Too Large</title>\n"\
            "</head><body>\n"\
            "<h1>Request Entity Too Large</h1>\n"\
            "The requested resource<br />/<br />\n"\
            "does not allow request data with POST requests, or the"\
            " amount of data provided in\n"\
            "the request exceeds the capacity limit.\n"\
            "</body></html>\n"\
            "MODE=E\n"\
            "ERRO=201"
        inq = self.inq
        for bad in bad_list:
            inq.params["S2NM"] = bad
            try:
                self._process(inq, raise_errors=False)
            except ValueError as vale:
                self.assertEqual(expected, str(vale))


class TestBasicConnectivityKhashed(TestBasicConnectivity):
    """Test Basic Connectivity Khashed"""
    maxDiff = None

    def setUp(self):
        self.session_id = generate_unique_id()[:32]
        self.email_client = EMAIL
        payment = CardPayment(PTOK)
        self.inq = default_inquiry(
            self.merchant_id, self.session_id,
            self.email_client, payment=payment)


if __name__ == "__main__":
    unittest.main(
        # defaultTest="TestBasicConnectivity.test_16_expected_geox"
    )

json_test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"example data from https://kopana.atlassian.net/wiki/display/KS/Testing"
from kount.version import VERSION
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


example_data = {
    'ANID': '',
    'AUTH': 'A',
    'AVST': 'M',
    'AVSZ': 'M',
    'B2A1': '1234+North+B2A1+Tree+Lane+South',
    'B2CC': 'US',
    'B2CI': 'Albuquerque',
    'B2PC': '87101',
    'B2PN': '555+867-5309',
    'B2ST': 'NM',
    'CASH': '4444',
    'CURR': 'USD',
    'CVVR': 'M',
    'EMAL': 'curly.riscaller15@kountqa.com',
    'FRMT': 'JSON',
    'IPAD': '4.127.51.215',
    'LAST4': '2514',
    'MACK': 'Y',
    'MERC': '999666',
    'MODE': 'Q',
    'NAME': 'Goofy+Grumpus',
    'ORDR': '088E9F496135',
    'PROD_DESC[]': '3000+CANDLEPOWER+PLASMA+FLASHLIGHT',
    'PROD_ITEM[]': 'SG999999',
    'PROD_PRICE[]': '68990',
    'PROD_QUANT[]': '2',
    'PROD_TYPE[]': 'SPORTING%5FGOODS',
    'PTOK': '0007380568572514',
    'PTYP': 'CARD',
    'S2A1': '567+West+S2A1+Court+North',
    'S2CC': 'US',
    'S2CI': 'Gnome',
    'S2EM': 'sdkTestShipTo@kountsdktestdomain.com',
    'S2NM': 'SdkTestShipToFirst+SdkShipToLast',
    'S2PC': '99762',
    'S2PN': '208+777-1212',
    'S2ST': 'AK',
    'SESS': '088E9F4961354D4F90041988B8D5C66B',
    'SITE': 'DEFAULT',
    'TOTL': '123456',
    'UAGT': 'Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10%5F9%5F5%29+'\
            'AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+'\
            'Chrome%2F37.0.2062.124+Safari%2F537.36',
    'UNIQ': '088E9F4961354D4F9004',
    'VERS': '0710'
}

example_data_products = {
    'ANID': '',
    'AUTH': 'A',
    'AVST': 'M',
    'AVSZ': 'M',
    'B2A1': '1234+North+B2A1+Tree+Lane+South',
    'B2CC': 'US',
    'B2CI': 'Albuquerque',
    'B2PC': '87101',
    'B2PN': '555+867-5309',
    'B2ST': 'NM',
    'CASH': '4444',
    'CURR': 'USD',
    'CVVR': 'M',
    'EMAL': 'curly.riscaller15@kountqa.com',
    'FRMT': 'JSON', # set if not via sdk
    'IPAD': '129.173.116.98',
    'MACK': 'Y',
    'MERC': '999666',
    'MODE': 'Q',
    'NAME': 'Goofy+Grumpus',
    'ORDR': 'F8E874A38B7B',
    'PROD_DESC[0]': '3000+CANDLEPOWER+PLASMA+FLASHLIGHT',
    'PROD_DESC[1]': '3000+HP+NUCLEAR+TOILET',
    'PROD_ITEM[0]': 'SG999999',
    'PROD_ITEM[1]': 'TP999999',
    'PROD_PRICE[0]': '68990',
    'PROD_PRICE[1]': '1000990',
    'PROD_QUANT[0]': '2',
    'PROD_QUANT[1]': '44',
    'PROD_TYPE[0]': 'SPORTING%5FGOODS',
    'PROD_TYPE[1]': 'SPORTING%5FGOODS2',
    'PTOK': '0055071350519059',
    'PTYP': 'CARD',
    'S2A1': '567+West+S2A1+Court+North',
    'S2CC': 'US',
    'S2CI': 'Gnome',
    'S2EM': 'sdkTestShipTo@kountsdktestdomain.com',
    'S2NM': 'SdkTestShipToFirst+SdkShipToLast',
    'S2PC': '99762',
    'S2PN': '208+777-1212',
    'S2ST': 'AK',
    'SESS': 'F8E874A38B7B4B6DBB71492A584A969D',
    'SITE': 'DEFAULT',
    'TOTL': '107783',
    'UAGT': 'Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10%5F9%5F5%29+'\
            'AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+'\
            'Chrome%2F37.0.2062.124+Safari%2F537.36',
    'UNIQ': 'F8E874A38B7B4B6DBB71',
    'SDK': 'PYTH',
    'VERS': '0710'
    }

test_inquiry.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"""Test Cases for Inquiry class"""
import pytest
import unittest
import uuid

from kount.client import Client
from kount.request import (AuthStatus, BankcardReply, InquiryMode,
                           CurrencyType, MerchantAcknowledgment)
from kount.inquiry import Inquiry
from kount.util.payment import CardPayment, Payment, GiftCardPayment
from kount.util.cartitem import CartItem
from kount.util.address import Address
from kount.config import SDKConfig
from kount.version import VERSION

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS

EMAIL_CLIENT = "sdkTest@kountsdktestdomain.com"
PTOK = "0007380568572514"
BILLING_ADDRESS = Address("1234 North B2A1 Tree Lane South",
                          "", "Albuquerque", "NM", "87101", "US")
SHIPPING_ADDRESS = Address("567 West S2A1 Court North", "",
                           "Gnome", "AK", "99762", "US")

expected = {
    'ANID': '',
    'AUTH': 'A',
    'AVST': 'M',
    'AVSZ': 'M',
    'B2A1': '1234 North B2A1 Tree Lane South',
    'B2A2': '',
    'B2CC': 'US',
    'B2CI': 'Albuquerque',
    'B2PC': '87101',
    'B2PN': '555-867-5309',
    'B2ST': 'NM',
    'BPREMISE': '',
    'BSTREET': '',
    'CASH': '4444',
    'CURR': 'USD',
    'CVVR': 'M',
    'EMAL': EMAIL_CLIENT,
    'FRMT': 'JSON',
    # 'IPAD': '131.206.45.21',
    'LAST4': '2514',
    'MACK': 'Y',
    'MERC': '999666',
    'MODE': 'Q',
    'NAME': 'SdkTestFirstName SdkTestLastName',
    'PENC': 'KHASH',
    # 'PENC': '',
    'PROD_DESC[0]': '3000 CANDLEPOWER PLASMA FLASHLIGHT',
    'PROD_ITEM[0]': 'SG999999',
    'PROD_PRICE[0]': '68990',
    'PROD_QUANT[0]': '2',
    'PROD_TYPE[0]': 'SPORTING_GOODS',
    # 'PTOK': '0007380568572514',
    'PTOK': '000738F16NA2S935A5HY',  # for khashed=True in Payment
    'PTYP': 'CARD',
    'S2A1': '567 West S2A1 Court North',
    'S2A2': '',
    'S2CC': 'US',
    'S2CI': 'Gnome',
    'S2EM': 'sdkTestShipToEmail@kountsdktestdomain.com',
    'S2NM': 'SdkShipToFN SdkShipToLN',
    'S2PC': '99762',
    'S2PN': '555-777-1212',
    'S2ST': 'AK',
    'SDK': 'PYTH',
    'SDK_VERSION': 'Sdk-Ris-%s-%s' % (SDKConfig.LANG, SDKConfig.SDK_VERSION),
    'SITE': 'DEFAULT',
    'SPREMISE': '',
    'SSTREET': '',
    'TOTL': '123456',
    'VERS': SDKConfig.VERS,
}


def generate_unique_id():
    """unique session id"""
    return str(uuid.uuid4()).replace('-', '').upper()


def default_inquiry(merchant_id, session_id, email_client, payment):
    """default_inquiry, PENC is not set"""
    inq = Inquiry()
    inq.set_request_mode(InquiryMode.DEFAULT)
    inq.set_shipping_address(SHIPPING_ADDRESS)
    inq.set_shipping_name("SdkShipToFN SdkShipToLN")  # S2NM
    inq.set_billing_address(BILLING_ADDRESS)
    inq.set_currency(CurrencyType.USD)  # CURR
    inq.set_total('123456')  # TOTL
    inq.set_billing_phone_number("555-867-5309")  # B2PN
    inq.set_shipping_phone_number("555-777-1212")  # S2PN
    inq.set_email_client(email_client)
    inq.set_customer_name("SdkTestFirstName SdkTestLastName")
    inq.set_unique_customer_id(session_id[:20])  # UNIQ
    inq.set_website("DEFAULT")  # SITE
    inq.set_email_shipping("sdkTestShipToEmail@kountsdktestdomain.com")
    inq.set_ip_address("4.127.51.215")  # IPAD
    cart_items = list()
    cart_items.append(CartItem("SPORTING_GOODS", "SG999999",
                               "3000 CANDLEPOWER PLASMA FLASHLIGHT",
                               '2', '68990'))
    inq.set_shopping_cart(cart_items)
    inq.version()
    inq.set_version(SDKConfig.VERS)  # 0695
    inq.set_merchant(merchant_id)
    inq.set_payment(payment)  # PTOK
    inq.set_session_id(session_id)  # SESS
    inq.set_order_number(session_id[:10])  # ORDR
    inq.set_authorization_status(AuthStatus.APPROVE)  # AUTH
    inq.set_avs_zip_reply(BankcardReply.MATCH)
    inq.set_avs_address_reply(BankcardReply.MATCH)
    inq.set_avs_cvv_reply(BankcardReply.MATCH)
    inq.set_merchant_acknowledgment(MerchantAcknowledgment.TRUE)  # "MACK"
    inq.set_cash('4444')
    return inq

@pytest.mark.usefixtures("api_url", "api_key", "merchant_id")
class TestInquiry(unittest.TestCase):
    """Inquiry class tests"""
    maxDiff = None

    def setUp(self):
        self.session_id = str(generate_unique_id())
        self.client = Client(self.api_url, self.api_key)

    def test_utilities(self):
        """test_utilities"""
        payment = Payment(
            payment_type="CARD",
            payment_token=PTOK,
            khashed=False)
        self.assertEqual(payment._payment_type, 'CARD')
        self.assertEqual(payment.last4, '2514')
        self.assertEqual(payment.payment_token, '0007380568572514')
        self.assertFalse(payment.khashed)
        inq = default_inquiry(
            merchant_id=self.merchant_id,
            session_id=self.session_id,
            email_client=EMAIL_CLIENT,
            payment=payment)

        expected_not_khashed = expected.copy()
        expected_not_khashed["PTOK"] = '0007380568572514'
        actual = inq.params
        self.assertEqual(actual['PTYP'], 'CARD')
        self.assertIn(expected_not_khashed['SDK_VERSION'],
                      actual['SDK_VERSION'])

        del (actual['UNIQ'],
             actual['IPAD'],
             actual['SDK_VERSION'],
             actual['SESS'],
             actual['ORDR'],
             expected_not_khashed['SDK_VERSION'],
             expected_not_khashed['PENC'])

        self.assertEqual(actual, expected_not_khashed)

    def test_utilities_khashed(self):
        """test_utilities khashed"""
        _expected = expected.copy()
        payment = CardPayment(PTOK)
        self.assertEqual(payment._payment_type, 'CARD')
        self.assertEqual(payment.last4, '2514')
        self.assertEqual(payment.payment_token, '000738F16NA2S935A5HY')
        self.assertTrue(payment.khashed)
        result = default_inquiry(
            session_id=self.session_id,
            merchant_id=self.merchant_id,
            email_client=EMAIL_CLIENT,
            payment=payment)
        actual = result.params
        self.assertEqual(actual['PTYP'], 'CARD')
        self.assertIn(_expected['SDK_VERSION'], actual['SDK_VERSION'])
        del (actual['UNIQ'],
             actual['IPAD'],
             actual['SDK_VERSION'],
             actual['SESS'],
             actual['ORDR'],
             _expected['SDK_VERSION'])
        self.assertEqual(actual, _expected)

    def test_utilities_gift_khashed(self):
        """test_utilities GIFT khashed"""
        _expected = expected.copy()
        payment = GiftCardPayment(PTOK)
        self.assertEqual(payment._payment_type, 'GIFT')
        self.assertEqual(payment.last4, '2514')
        self.assertEqual(payment.payment_token, '000738F16NA2S935A5HY')
        self.assertTrue(payment.khashed)
        result = default_inquiry(
            session_id=self.session_id,
            merchant_id=self.merchant_id,
            email_client=EMAIL_CLIENT,
            payment=payment)
        actual = result.params
        self.assertEqual(actual['PTYP'], 'GIFT')
        self.assertIn(_expected['SDK_VERSION'], actual['SDK_VERSION'])
        del (_expected['SDK_VERSION'],
             _expected['PTYP'],
             actual['PTYP'],
             actual['UNIQ'],
             actual['IPAD'],
             actual['SDK_VERSION'],
             actual['SESS'],
             actual['ORDR'])
        self.assertEqual(actual, _expected)

    def test_inquiry_with_masked_payment(self):
        """test inquiry with masked payment"""
        session_id = self.session_id
        merchant_id = "999666"
        email_client = EMAIL_CLIENT

        inq = Inquiry()
        inq.set_request_mode(InquiryMode.DEFAULT)
        inq.set_shipping_address(SHIPPING_ADDRESS)
        inq.set_shipping_name("SdkShipToFN SdkShipToLN")  # S2NM
        inq.set_billing_address(BILLING_ADDRESS)
        inq.set_currency(CurrencyType.USD)  # CURR
        inq.set_total('123456')  # TOTL
        inq.set_billing_phone_number("555-867-5309")  # B2PN
        inq.set_shipping_phone_number("555-777-1212")  # S2PN
        inq.set_email_client(email_client)
        inq.set_customer_name("SdkTestFirstName SdkTestLastName")
        inq.set_unique_customer_id(session_id[:20])  # UNIQ
        inq.set_website("DEFAULT")  # SITE
        inq.set_email_shipping("sdkTestShipToEmail@kountsdktestdomain.com")
        inq.set_ip_address("4.127.51.215")  # IPAD
        cart_items = list()
        cart_items.append(CartItem("SPORTING_GOODS", "SG999999",
                                "3000 CANDLEPOWER PLASMA FLASHLIGHT",
                                '2', '68990'))
        inq.set_shopping_cart(cart_items)
        inq.version()
        inq.set_version(SDKConfig.VERS)  # 0695
        inq.set_merchant(merchant_id)
        inq.set_session_id(session_id)  # SESS
        inq.set_order_number(session_id[:10])  # ORDR
        inq.set_authorization_status(AuthStatus.APPROVE)  # AUTH
        inq.set_avs_zip_reply(BankcardReply.MATCH)
        inq.set_avs_address_reply(BankcardReply.MATCH)
        inq.set_avs_cvv_reply(BankcardReply.MATCH)
        inq.set_merchant_acknowledgment(MerchantAcknowledgment.TRUE)  # "MACK"
        inq.set_cash('4444')

        payment = CardPayment(PTOK, khashed=False)
        inq.set_masked_payment(payment)

        _expected = expected.copy()
        _expected["PTOK"] = "000738XXXXXX2514"
        _expected["PENC"] = "MASK"
        _expected["PTYP"] = "CARD"

        actual = inq.params
        del (actual['UNIQ'],
             actual['IPAD'],
             actual['SESS'],
             actual['ORDR'])
        self.assertEqual(actual, _expected)

if __name__ == "__main__":
    unittest.main()

test_payment.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"Test Payment Type"
import unittest
import pytest

from kount.util.khash import Khash
from kount.util.payment import (
    BillMeLaterPayment, CardPayment, CheckPayment,
    GiftCardPayment, GooglePayment,
    GreenDotMoneyPakPayment, NoPayment,
    Payment, PaypalPayment)

from kount.version import VERSION
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


@pytest.mark.usefixtures("conf_key")
class TestPaymentType(unittest.TestCase):
    """Test Payment Type"""
    def setUp(self):
        self.test = 1234567890*1000000000

    def test_giftcardpayment(self):
        """giftcard payment"""
        ptype = GiftCardPayment(gift_card_number=self.test, khashed=False)
        self.assertTrue(isinstance(ptype, Payment))
        self.assertEqual(ptype.last4, str(self.test)[-4:])
        self.assertFalse(ptype.khashed)
        self.assertEqual(ptype.payment_type, "GIFT")
        self.assertEqual(ptype.payment_token, str(self.test))

    def test_payments(self):
        "all predefined payments"
        plist = (Payment, BillMeLaterPayment, CardPayment,
                 CheckPayment, GiftCardPayment, GooglePayment,
                 GreenDotMoneyPakPayment, NoPayment, Payment, PaypalPayment)
        payment_dict = {
            "BLML": BillMeLaterPayment(self.test, khashed=False),
            "CARD": CardPayment(self.test, khashed=False),
            "CHEK": CheckPayment(self.test, khashed=False),
            "CHECK": CheckPayment(self.test, khashed=False), # backwards compatibility
            "GIFT": GiftCardPayment(self.test, khashed=False),
            "GOOG": GooglePayment(self.test, khashed=False),
            "GDMP": GreenDotMoneyPakPayment(self.test, khashed=False),
            "NONE": NoPayment(),
            "PYPL": PaypalPayment(self.test, khashed=False),
            }
        ptypes = []
        for current in payment_dict:
            curp = payment_dict[current]
            if current == "NONE":
                self.assertEqual(curp.last4, "NONE")
                self.assertIsNone(curp.payment_token)
            else:
                self.assertEqual(curp.last4, str(self.test)[-4:])
                self.assertEqual(curp.payment_token, str(self.test))
            if current == "CHECK":
                self.assertEqual(curp._payment_type, "CHEK")
            else:
                self.assertEqual(curp._payment_type, current)
            ptypes.append(payment_dict[current])
            self.assertIsInstance(payment_dict[current], plist)
            if curp.payment_token is not None:
                self.assertEqual(curp.payment_token, str(self.test))

    def test_user_defined_payment(self):
        "user defined payments"""
        curp = Payment("PM42", self.test, False)
        self.assertEqual(curp.last4, str(self.test)[-4:])
        self.assertEqual(curp.payment_token, str(self.test))
        self.assertFalse(curp.khashed)
        self.assertEqual(curp._payment_type, "PM42")
        self.assertEqual(curp.payment_token, str(self.test))
        self.assertIsInstance(curp, Payment)

    def test_user_defined_payment_khashed(self):
        "user defined payments with Payment - khashed token"
        curp = Payment("PM42", self.test, True)
        self.assertEqual(curp.last4, str(self.test)[-4:])
        self.assertEqual(curp.payment_token,
                         Khash.get().hash_payment_token(self.test))
        self.assertTrue(curp.khashed)
        self.assertEqual(curp._payment_type, "PM42")
        self.assertIsInstance(curp, Payment)

    def test_user_defined_newpayment(self):
        "user defined payments - token khashed and notkhashed "
        curp = Payment("PM42", self.test, khashed=False)
        self.assertEqual(curp.last4, str(self.test)[-4:])
        self.assertEqual(curp.payment_token, str((self.test)))
        self.assertFalse(curp.khashed)
        self.assertEqual(curp.payment_type, "PM42")
        self.assertIsInstance(curp, Payment)
        curp = Payment("PM42", self.test, True)
        self.assertEqual(curp.last4, str(self.test)[-4:])
        self.assertEqual(curp.payment_token,
                         Khash.get().hash_payment_token(self.test))
        self.assertTrue(curp.khashed)


if __name__ == "__main__":
    unittest.main(
        #~ defaultTest="TestPaymentType.test_payments"
        )

test_validation_error.py

test_khash.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
import unittest
import pytest

from kount.version import VERSION
from kount.util.khash import Khash
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


@pytest.mark.usefixtures("conf_key")
class TestKhash(unittest.TestCase):
    """Khash class test cases"""

    def setUp(self):
        self.list_for_hash = ["4111111111111111",
                              '5199185454061655',
                              4259344583883]
        self.expected = ['WMS5YA6FUZA1KC', '2NOQRXNKTTFL11', 'FEXQI1QS6TH2O5']
        self.merchant_id = '666666'
        self.khash = Khash.get()

    def test_token_valid(self):
        """valid token"""
        self.assertEqual(
            "BADTOKGM3BD98ZY871QB",
            self.khash.hash_payment_token(token="BADTOKEN"))

        self.assertEqual(
            "000738F16NA2S935A5HY",
            self.khash.hash_payment_token(token="0007380568572514"))

        for i, plain_text in enumerate(self.list_for_hash):
            card_hashed = self.khash.hash_payment_token(token=plain_text)
            expected = "%s%s" % (str(self.list_for_hash[i])[:6],
                                 self.expected[i])
            self.assertEqual(card_hashed, expected)
            self.assertTrue(self.khash.khashed(card_hashed))

    def test_token_invalid(self):
        """invalid token"""
        with self.assertRaises(ValueError):
            self.khash.hash_payment_token(token="")
        with self.assertRaises(ValueError):
            self.khash.hash_payment_token(token=None)
        card_hashed = self.khash.hash_payment_token(token="10**200")
        self.assertEqual(card_hashed, "10**20GA6AXR02LVUE5X")
        with self.assertRaises(ValueError):
            self.khash.hash_payment_token(token=-42)
        with self.assertRaises(ValueError):
            self.khash.hash_payment_token(token=10**200)
        with self.assertRaises(ValueError):
            self.khash.hash_payment_token(token=0)
        card_hashed = self.khash.hash_payment_token(token="Beatles")
        self.assertEqual(card_hashed, "Beatle5STRFTYPXBR14E")
        self.assertTrue(self.khash.khashed(card_hashed))
        bad = "John"
        try:
            self.khash.hash_payment_token(token=bad)
        except ValueError as vale:
            self.assertEqual("incorrect arg: [%s]" % bad, str(vale))
        with self.assertRaises(ValueError):
            self.assertTrue(self.khash.hash_payment_token(token=bad))

    def test_hash_gift_card(self):
        """gift card"""
        for i in range(len(self.list_for_hash)):
            card_hashed = self.khash.hash_gift_card(
                self.merchant_id, self.list_for_hash[i])
            expected = "%s%s" % (self.merchant_id, self.expected[i])
            self.assertEqual(card_hashed, expected)
            self.assertTrue(self.khash.khashed(card_hashed))

    def test_hash_gift_card_int_merchantid(self):
        """test_hash_gift_card_int_merchantid"""
        for i in range(len(self.list_for_hash)):
            card_hashed = self.khash.hash_gift_card(
                self.merchant_id, self.list_for_hash[i])
            expected = "%s%s" % (self.merchant_id, self.expected[i])
            self.assertEqual(card_hashed, expected)
            self.assertTrue(self.khash.khashed(card_hashed))

    def test_list_for_hash_empty(self):
        """list_for_hash_empty"""
        list_for_hash = ""
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(self.merchant_id, list_for_hash)

    def test_list_for_hash_none(self):
        """hash_none"""
        list_for_hash = None
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(self.merchant_id, list_for_hash)

    def test_gift_card_empty_values(self):
        """gift_card_empty_values"""
        list_for_hash = []
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(self.merchant_id, list_for_hash)

    def test_gift_card_no_merchant(self):
        """gift card without merchant"""
        list_for_hash = []
        merchant_id = ""
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(merchant_id, list_for_hash)

    def test_gift_card_merchant_empty_str(self):
        """gift_card_merchant_empty_str"""
        merchant_id = ""
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(merchant_id, self.list_for_hash)

    def test_list_for_hash_merchant_none(self):
        """list_for_hash_merchant_none"""
        list_for_hash = []
        merchant_id = None
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(merchant_id, list_for_hash)

    def test_list_for_hash_args_missing(self):
        """list_for_hash_args_missing"""
        list_for_hash = None
        merchant_id = None
        with self.assertRaises(ValueError):
            self.khash.hash_gift_card(merchant_id, list_for_hash)


if __name__ == "__main__":
    unittest.main(verbosity=2)

test_ris_test_suite.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"""Test Cases from sdk documentation"""
import unittest

import pytest

from kount.request import (AuthStatus, BankcardReply, InquiryMode,
                           CurrencyType, MerchantAcknowledgment)
from kount.request import Update, UpdateMode
from kount.util.khash import Khash
from kount.client import Client
from kount.util.cartitem import CartItem
from kount.util.payment import CardPayment
from kount.version import VERSION

from .test_basic_connectivity import generate_unique_id, default_inquiry

from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS

# raise_errors - if  True - raise errors instead of logging in debugger
_RAISE_ERRORS = False

PTOK = "0007380568572514"
EMAIL_CLIENT = "sdkTest@kountsdktestdomain.com"


@pytest.mark.usefixtures("merchant_id", "api_key", "api_url")
class TestRisTestSuite(unittest.TestCase):
    """Ris Test Suite
        default logging errors instead fo raising
        to raise errors - put raise_errors=True in Client:
        Client(url=URL_API, key=KOUNT_API_KEY,
               timeout=TIMEOUT, RAISE_ERRORS=True)
    """

    maxDiff = None

    def setUp(self):
        self.session_id = generate_unique_id()[:32]
        self.payment = CardPayment(PTOK, khashed=False)
        self.client = Client(self.api_url, self.api_key,
                             raise_errors=_RAISE_ERRORS)

    def inquiry(self):
        return default_inquiry(
            merchant_id=self.merchant_id,
            session_id=self.session_id,
            email_client=EMAIL_CLIENT,
            payment=self.payment)

    def test_1_ris_q_1_item_required_field_1_rule_review(self):
        """test_1_ris_q_1_item_required_field_1_rule_review"""
        res = self.client.process(self.inquiry())
        self.assertIsNotNone(res)
        self.assertEqual("R", res.get_auto())
        self.assertEqual(0, len(res.get_warnings()))
        expected = ['Review if order total > $1000 USD']
        actual = sorted(res.get_rules_triggered().values())
        self.assertEqual(expected, actual)
        self.assertEqual(self.session_id, res.get_session_id())
        self.assertEqual(res.get_session_id()[:10], res.get_order_id())

    def test_2_ris_q_multi_cart_items2optional_fields2rules_decline(self):
        """test_2_ris_q_multi_cart_items2optional_fields2rules_decline
        cart_item - PROD_TYPE[0, PROD_ITEM[0], PROD_DESC[0]
                    PROD_QUANT[0],PROD_PRICE[0]"""
        inq = self.inquiry()
        inq.set_user_agent(
            "Mozilla/5.0 (Macintosh; "
            "Intel Mac OS X 10_9_5) AppleWebKit/537.36 "
            "(KHTML, like Gecko) Chrome/37.0.2062.124 "
            "Safari/537.36")
        inq.set_total(123456789)
        cart_items = [
            CartItem(
                "cart item type 0", "cart item 0",
                "cart item 0 description", 10, 1000),
            CartItem(
                "cart item type 1", "cart item 1",
                "cart item 1 description", 11, 1001),
            CartItem(
                "cart item type 2", "cart item 2",
                "cart item 1 description", 12, 1002)]
        inq.set_shopping_cart(cart_items)
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("D", res.get_auto())
        self.assertEqual(0, len(res.get_warnings()))
        expected = sorted(
            {'1024842': 'Review if order total > $1000 USD',
             '1024844': 'Decline if order total > $1000000 USD'}.values())
        actual = sorted(res.get_rules_triggered().values())
        self.assertEqual(expected, actual)

    def test_3_ris_q_with_user_defined_fields(self):
        """test_3_ris_q_with_user_defined_fields"""
        udf1 = "ARBITRARY_ALPHANUM_UDF"
        udf2 = "ARBITRARY_NUMERIC_UDF"
        inq = self.inquiry()
        inq.set_user_defined_field(udf1, "alphanumeric trigger value")
        inq.set_user_defined_field(udf2, "777")
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("R", res.get_auto())
        self.assertEqual(3, len(res.get_rules_triggered()))
        self.assertEqual(0, len(res.get_warnings()))
        self.assertEqual(0, len(res.get_errors()))
        self.assertEqual(0, len(res.get_counters_triggered()))
        expected = sorted(
            {'1025086': 'review if %s contains "trigger"' % udf1,
             '1024842': 'Review if order total > $1000 USD',
             '1025088': "review if %s == 777" % udf2}.values())
        actual = sorted(res.get_rules_triggered().values())
        self.assertEqual(expected, actual)

    def test_4_ris_q_hard_error_expected(self):
        """test_4_ris_q hard_error_expected,
        overwrite the PTOK value to induce an error in the RIS"""
        inq = self.inquiry()
        inq.params["PENC"] = "KHASH"
        inq.params["PTOK"] = "BADPTOK"
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual(
            ["332 BAD_CARD Cause: [PTOK invalid format], "
             "Field: [PTOK], Value: [hidden]"],
            res.get_errors())
        self.assertEqual("E", res.get_mode())
        self.assertEqual(332, res.get_error_code())
        self.assertEqual(0, len(res.get_warnings()))

    def test_5_ris_q_warning_approved(self):
        """test_5_ris_q_warning_approved"""
        inq = self.inquiry()
        inq.set_total(1000)
        label = "UDF_DOESNOTEXIST"
        mesg = "throw a warning please!"
        inq.set_user_defined_field(label, mesg)
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("A", res.get_auto())
        self.assertEqual(2, len(res.get_warnings()))
        self.assertEqual(res.get_warnings()[0],
                         "399 BAD_OPTN Field: [UDF], Value: "
                         "[%s=>%s]" % (label, mesg))
        self.assertEqual(res.get_warnings()[1],
                         "399 BAD_OPTN Field: [UDF], Value: "
                         "[The label [%s]"
                         " is not defined for merchant ID [%s].]" % (
                             label, self.merchant_id))

    def test_6_ris_q_hard_soft_errors_expected(self):
        """test_6_ris_q_hard_soft_errors_expected"""
        inq = self.inquiry()
        inq.params["PENC"] = "KHASH"
        inq.params["PTOK"] = "BADPTOK"
        label = "UDF_DOESNOTEXIST"
        mess = "throw a warning please!"
        inq.params["UDF[%s]" % label] = mess
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("E", res.get_mode())
        self.assertEqual(332, res.get_error_code())
        self.assertEqual(1, len(res.get_errors()))
        self.assertEqual(
            [("332 BAD_CARD Cause: [PTOK invalid format], "
              "Field: [PTOK], Value: [hidden]")],
            res.get_errors())
        warnings = res.get_warnings()
        self.assertEqual(2, len(warnings))
        self.assertEqual(
            "399 BAD_OPTN Field: [UDF], Value: [%s=>%s]"
            % (label, mess), warnings[0])
        self.assertEqual(
            "399 BAD_OPTN Field: [UDF], Value: [The label [%s] "
            "is not defined for merchant ID [%s].]"
            % (label, self.merchant_id), warnings[1])

    def test_7_ris_w2_kc_rules_review(self):
        """test_7_ris_w2_kc_rules_review"""
        inq = self.inquiry()
        inq.set_request_mode(InquiryMode.WITH_THRESHOLDS)
        inq.set_total(10001)
        inq.set_kount_central_customer_id("KCentralCustomerOne")
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual(res.get_kc_decision(), 'R')
        self.assertEqual(len(res.get_kc_warnings()), 0)
        self.assertEqual(len(res.get_kc_events()), 2)
        events = res.get_kc_events()
        print(events)
        self.assertEqual(events[0].code, 'billingToShippingAddressReview')
        self.assertEqual(events[1].expression, '10001 > 10000')
        self.assertEqual(events[0].decision, 'R')
        self.assertEqual(events[1].code, 'orderTotalReview')
        self.assertEqual(events[0].expression, '5053 > 1')
        self.assertEqual(events[1].decision, 'R')

    def test_8_ris_j_1_kount_central_rule_decline(self):
        """test_8_ris_j_1_kount_central_rule_decline"""
        inq = self.inquiry()
        inq.set_request_mode(InquiryMode.JUST_THRESHOLDS)
        inq.set_total(1000)
        inq.set_kount_central_customer_id("KCentralCustomerDeclineMe")
        if not _RAISE_ERRORS:
            res = self.client.process(inq)
            self.assertIsNotNone(res)
            self.assertEqual("D", res.get_kc_decision())
            self.assertEqual(0, len(res.get_kc_warnings()))
            kc_events = res.get_kc_events()
            self.assertEqual(1, len(kc_events), )
            self.assertEqual(kc_events[0].code, "orderTotalDecline")

    def test_9_mode_u_after_mode_q(self):
        """test_9_mode_u_after_mode_q"""
        res = self.client.process(self.inquiry())
        self.assertIsNotNone(res)
        transaction_id = res.get_transaction_id()
        session_id = res.get_session_id()
        order_id = res.get_order_id()

        update1 = Update()
        update1.set_mode(UpdateMode.NO_RESPONSE)
        update1.set_transaction_id(transaction_id)
        update1.set_merchant(self.merchant_id)
        update1.set_session_id(session_id)
        update1.set_order_number(order_id)
        # PTOK has to be khashed manually because of its explicit setting
        token_new = "5386460135176807"
        update1.params["PTOK"] = Khash.get().hash_payment_token(token_new)
        update1.params["LAST4"] = token_new[-4:]
        update1.params["FRMT"] = 'JSON'
        update1.set_khash_payment_encoding(True)
        update1.set_merchant_acknowledgment(MerchantAcknowledgment.TRUE)
        update1.set_authorization_status(AuthStatus.APPROVE)
        update1.set_avs_zip_reply(BankcardReply.MATCH)
        update1.set_avs_address_reply(BankcardReply.MATCH)
        update1.set_avs_cvv_reply(BankcardReply.MATCH)
        res = self.client.process(update1)
        self.assertIsNotNone(res)
        self.assertEqual("U", res.get_mode())
        self.assertIsNone(res.get_geox())
        self.assertIsNone(res.get_score())
        self.assertIsNone(res.get_auto())

    def test_10_mode_x_after_mode_q(self):
        """test_10_mode_x_after_mode_q
        PTOK has to be khashed manually because of
        its explicit setting"""
        res = self.client.process(self.inquiry())
        self.assertIsNotNone(res)
        transaction_id = res.get_transaction_id()
        session_id = res.get_session_id()
        order_id = res.get_order_id()
        update1 = Update()
        update1.set_mode(UpdateMode.WITH_RESPONSE)
        update1.set_transaction_id(transaction_id)
        update1.set_merchant(self.merchant_id)
        update1.set_session_id(session_id)
        update1.set_order_number(order_id)
        token_new = "5386460135176807"
        update1.set_khash_payment_encoding(self.payment.khashed)
        if self.payment.khashed:
            token_new = Khash.get().hash_payment_token(token_new)
        update1.params["PTOK"] = token_new
        update1.params["LAST4"] = token_new[-4:]
        update1.params["FRMT"] = 'JSON'
        update1.set_merchant_acknowledgment(MerchantAcknowledgment.TRUE)
        update1.set_authorization_status(AuthStatus.APPROVE)
        update1.set_avs_zip_reply(BankcardReply.MATCH)
        update1.set_avs_address_reply(BankcardReply.MATCH)
        update1.set_avs_cvv_reply(BankcardReply.MATCH)
        res = self.client.process(update1)
        self.assertIsNotNone(res)
        self.assertEqual("X", res.get_mode())
        self.assertIsNotNone(res.get_geox())
        self.assertIsNotNone(res.get_score())
        self.assertIsNotNone(res.get_auto())

    def test_11_mode_p(self):
        res = self.client.process(self.inquiry())
        self.assertIsNotNone(res)
        inq = self.inquiry()
        inq.set_request_mode(InquiryMode.PHONE)
        inq.set_anid("2085551212")
        inq.set_total(1000)
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("P", res.get_mode())
        self.assertEqual("A", res.get_auto())

    def test_14_ris_q_using_payment_encoding_mask_valid(self):
        """test_14_ris_q_using_payment_encoding_mask_valid"""
        ptok_2 = "370070XXXXX9797"
        last4 = ptok_2[-4:]
        penc = 'MASK'
        res = self.client.process(self.inquiry())
        self.assertIsNotNone(res)
        inq = self.inquiry()
        inq.params['LAST4'] = last4
        inq.params['PTOK'] = ptok_2
        inq.params['PENC'] = penc
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual("AMEX", res.get_brand())

    def test_15_ris_q_using_payment_encoding_mask_error(self):
        """test_15_ris_q_using_payment_encoding_mask_error"""
        ptok_2 = "370070538959797"
        last4 = ptok_2[-4:]
        penc = 'MASK'
        inq = self.inquiry()
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        inq.params['LAST4'] = last4
        inq.params['PTOK'] = ptok_2
        inq.params['PENC'] = penc
        res = self.client.process(inq)
        self.assertIsNotNone(res)
        self.assertEqual({
            'ERRO': 340,
            'ERROR_0':
                '340 BAD_MASK Cause: [value [%s] did not match regex '
                '/^\\d{6}X{5,9}\\d{1,4}$/], Field: [PTOK], Value: '
                '[%s]' % (ptok_2, ptok_2),
            'ERROR_COUNT': 1,
            'MODE': 'E',
            'WARNING_COUNT': 0}, res.params)


class TestRisTestSuiteKhashed(TestRisTestSuite):
    """Ris Test Suite Khashed
        default logging errors instead fo raising
        to raise errors - put raise_errors=True in Client:
        Client(url=URL_API, key=KOUNT_API_KEY,
               timeout=TIMEOUT, RAISE_ERRORS=True)
    """
    maxDiff = None

    def setUp(self):
        self.session_id = generate_unique_id()[:32]
        self.payment = CardPayment(PTOK)
        self.client = Client(self.api_url, self.api_key,
                             raise_errors=_RAISE_ERRORS)


if __name__ == "__main__":
    unittest.main(verbosity=2)

test_ris_validator.py

test_xmlparser.py

test_bed_examples.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
"""Test Cases for an example implementation
generate_unique_id
put test data in user_inquiry
"""
import unittest
import pytest

from kount.client import Client
from kount.config import SDKConfig
from kount.util.payment import CardPayment
from kount.inquiry import Inquiry
from kount.request import (AuthStatus, BankcardReply, InquiryMode,
                           CurrencyType, MerchantAcknowledgment)
from kount.util.cartitem import CartItem
from kount.util.address import Address
from kount.version import VERSION

from .test_inquiry import generate_unique_id

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


PTOK = "4111111111111111"
EMAIL = 'john@test.com'
BILLING_ADDRESS = Address("", "", "Manchester", "NH", "03109", "US")
BILLING_PHONE = "555-888-5678"


def user_inquiry(session_id, merchant_id, email_client, payment):
    """user_inquiry, PENC is not set"""
    result = Inquiry()
    result.set_request_mode(InquiryMode.DEFAULT)
    result.set_billing_address(BILLING_ADDRESS)
    result.set_currency(CurrencyType.USD)  # CURR
    result.set_total(3500)  # TOTL
    result.set_billing_phone_number(BILLING_PHONE)  # B2PN
    result.set_email_client(email_client)
    result.set_customer_name("J Test")
    result.set_unique_customer_id(session_id[:20])  # UNIQ
    result.set_website("DEFAULT")  # SITE
    # result.set_ip_address("4.127.51.215")  # IPAD
    result.set_ip_address('2001:0:3238:DFE1:63::FEFB')  # IPAD
    cart_items = [CartItem("1", "8482", "Standard Monthly Plan", 1, '3500')]
    result.set_shopping_cart(cart_items)
    result.version()
    result.set_version(SDKConfig.VERS)  # 0710
    result.set_merchant(merchant_id)
    result.set_payment(payment)  # PTOK
    result.set_session_id(session_id)  # SESS
    result.set_order_number(session_id[:10])  # ORDR
    result.set_authorization_status(AuthStatus.APPROVE)  # AUTH
    result.set_avs_zip_reply(BankcardReply.MATCH)
    result.set_avs_address_reply(BankcardReply.MATCH)
    result.set_avs_cvv_reply(BankcardReply.MATCH)
    result.set_merchant_acknowledgment(MerchantAcknowledgment.TRUE)  # "MACK"
    return result


expected = {
    'ANID': '',
    'AUTH': 'A',
    'AVST': 'M',
    'AVSZ': 'M',
    'B2A1': '',
    'B2A2': '',
    'B2CC': 'US',
    'B2CI': 'Manchester',
    'B2PC': '03109',
    'B2PN': BILLING_PHONE,
    'B2ST': 'NH',
    'BPREMISE': '',
    'BSTREET': '',
    'CURR': 'USD',
    'CVVR': 'M',
    'EMAL': EMAIL,
    'FRMT': 'JSON',
    'IPAD': '2001:0:3238:DFE1:63::FEFB',
    'LAST4': '1111',
    'MACK': 'Y',
    'MERC': '999666',
    'MODE': 'Q',
    'NAME': 'J Test',
    # 'ORDR': '4F7132C2FE',
    # 'PENC': 'KHASH',
    'PROD_DESC[0]': 'Standard Monthly Plan',
    'PROD_ITEM[0]': '8482',
    'PROD_PRICE[0]': '3500',
    'PROD_QUANT[0]': 1,
    'PROD_TYPE[0]': '1',
    'PTOK': PTOK,
    'PTYP': 'CARD',
    'SDK': 'PYTH',
    # 'SDK_VERSION': 'Sdk-Ris-Python-0695-201708301601',
    # 'SESS': '4F7132C2FE8547928CD9329B78AA0A59',
    'SITE': 'DEFAULT',
    'TOTL': 3500,
    # 'UNIQ': '4F7132C2FE8547928CD9',
    'VERS': '0720'}


@pytest.mark.usefixtures("api_url", "api_key", "merchant_id")
class TestBed(unittest.TestCase):
    """Test Bed for use-cases, with & without Khash"""
    maxDiff = None

    def setUp(self):
        self.session_id = generate_unique_id()[:32]
        self.email_client = EMAIL

    def test_not_khashed(self):
        """test without khashed card"""
        # required khashed=False
        payment = CardPayment(PTOK, False)
        self.inq = user_inquiry(
            self.session_id, self.merchant_id, self.email_client,
            payment=payment)
        self.assertNotIn('PENC', self.inq.params)
        self.compare(expected)

    def test_khashed(self):
        """test with khashed card"""
        # not required default khashed=True
        payment = CardPayment(PTOK)
        self.inq = user_inquiry(
            self.session_id, self.merchant_id, self.email_client,
            payment=payment)
        self.assertIn('PENC', self.inq.params)
        self.assertEqual('KHASH', self.inq.params['PENC'])
        expected_khashed = expected.copy()
        expected_khashed['PENC'] = 'KHASH'
        expected_khashed['PTOK'] = '411111WMS5YA6FUZA1KC'
        self.compare(expected_khashed)

    def compare(self, expected_dict):
        """common method for both tests"""
        res = Client(self.api_url, self.api_key).process(self.inq)
        self.assertIsNotNone(res)
        self.assertNotIn('ERRO', repr(res))
        actual = self.inq.params.copy()
        remove = ['SDK_VERSION', 'SESS', 'UNIQ', 'ORDR']
        for k in remove:
            if k in actual:
                del actual[k]
        self.assertEqual(expected_dict, actual)


if __name__ == "__main__":
    unittest.main(verbosity=2)

test_base85_encode_decode.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Kount python sdk project
# https://github.com/Kount/kount-ris-python-sdk/)
# Copyright (C) 2017 Kount Inc. All Rights Reserved.
import unittest

import sys
from kount.util.a85 import a85decode, a85encode
from kount.version import VERSION
from kount.config import SDKConfig

__author__ = SDKConfig.SDK_AUTHOR
__version__ = VERSION
__maintainer__ = SDKConfig.SDK_MAINTAINER
__email__ = SDKConfig.MAINTAINER_EMAIL
__status__ = SDKConfig.STATUS


class Base85EncodeDecodeTest(unittest.TestCase):
    """Base85EncodeDecodeTest"""

    plain_text = "This is sample text for testing purposes."
    encoded_text = b"<+oue+DGm>F(&p)Ch4`2AU&;>AoD]4FCfN8Bl7Q+E-62?Df]K2/c"

    def test_encode(self):
        """test valid encode"""
        encoded = a85encode(self.plain_text.encode('utf-8'))
        decoded = a85decode(encoded)
        self.assertEqual(encoded, self.encoded_text)
        self.assertEqual(decoded, self.plain_text.encode('utf-8'))

    def test_decode(self):
        """test valid decode"""
        decoded = a85decode(self.encoded_text)
        self.assertEqual(decoded, self.plain_text.encode('utf-8'))

    def test_decode_invalid(self):
        """test invalid decode"""
        self.assertEqual(a85decode(b''), b'')
        self.assertRaises(ValueError, a85decode, self.plain_text)

    def test_encode_invalid(self):
        """test invalid encode"""
        self.assertEqual(a85encode(b''), b'')
        if sys.version_info[0] > 2:  # TODO
            self.assertRaises(TypeError, a85encode, '')
            self.assertRaises(TypeError, a85encode, self.plain_text)


if __name__ == "__main__":
    unittest.main(verbosity=2)