## $Id: tests.py 17783 2024-05-14 08:46:27Z henrik $
##
## Copyright (C) 2017 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import os
import unittest
import random
import json
import hashlib
import httplib
from urllib import urlencode
from datetime import datetime, timedelta, date
from zope.component import createObject, getUtility
from zope.catalog.interfaces import ICatalog
from hurry.workflow.interfaces import IWorkflowState
from waeup.kofa.students.tests.test_browser import StudentsFullSetup
from waeup.kofa.applicants.tests.test_browser import ApplicantsFullSetup
from waeup.kofa.configuration import SessionConfiguration
from kofacustom.nigeria.students.payments import NigeriaStudentOnlinePayment
from kofacustom.nigeria.testing import FunctionalLayer
from kofacustom.nigeria.etranzact.helpers import (
    query_history, query_payoutlet, ERROR_PART1, ERROR_PART2,
    )

#from kofacustom.nigeria.etranzact.helpers import (query_etranzact)

# Also run tests that send requests to external servers?
#   If you enable this, please make sure the external services
#   do exist really and are not bothered by being spammed by a test programme.

EXTERNAL_TESTS = False

TERMINAL_ID = '5003021194'
HOST = 'demo.etranzactng.com'
HTTPS = True
SECRET_KEY = 'DEMO_KEY'
LOGO_URL = 'https://iuokada.waeup.org/static_custom/iou_logo.png'
GATEWAY_AMT = 500.0

# Valid transaction id in Etranzact system
TID = 'p5689785145198'

def external_test(func):
    if not EXTERNAL_TESTS:
        myself = __file__
        if myself.endswith('.pyc'):
            myself = myself[:-1]
        print "WARNING: external tests are skipped!"
        print "WARNING: edit %s to enable them." % myself
        return
    return func

def post_caller(host, https, terminal_id, transaction_id, responseurl,
                        amount_auth, email, phone, display_fullname, hashvalue,
                        logo_url):
    headers={"Content-type": "application/x-www-form-urlencoded",
             "Accept": "text/plain"}
    url = "/webconnect/v3/caller.jsp"
    if https:
        h = httplib.HTTPSConnection(host)
    else:
        h = httplib.HTTPConnection(host)
    args = {'TERMINAL_ID': terminal_id,
            'TRANSACTION_ID': transaction_id,
            'RESPONSE_URL': responseurl,
            'AMOUNT': amount_auth,
            'EMAIL': email,
            'PHONENO': phone,
            'FULL_NAME': display_fullname,
            'CURRENCY_CODE': 'NGN',
            'CHECKSUM': hashvalue,
            'LOGO_URL': logo_url,
            }
    h.request('POST', url, urlencode(args), headers)
    return
    response = h.getresponse()
    if response.status!=200:
        return 'Connection error (%s, %s)' % (response.status, response.reason)
    resp = response.read()
    return resp

def create_transaction(transaction_id):
    responseurl = 'http://xxxx'
    amount = '4444.0'
    email = 'aa@aa.ng'
    phone = '12324'
    display_fullname = 'Tester'
    logo_url = 'http://xxxx'
    hashargs = 	amount + TERMINAL_ID + transaction_id \
        + responseurl + 'DEMO_KEY'
    hashvalue = hashlib.md5(hashargs).hexdigest()
    response = post_caller(HOST, HTTPS, TERMINAL_ID,
                         transaction_id, responseurl,
                         amount, email, phone,
                         display_fullname, hashvalue,
                         logo_url)
    return response


class HelperTests(unittest.TestCase):

    terminal_id = TERMINAL_ID

    @external_test
    def test_query_history(self):
        transaction_id = str(random.randint(100000000, 999999999))
        raw, formvars = query_history(HOST, self.terminal_id,
                                transaction_id, HTTPS)
        self.assertTrue(
            'Transaction with the given transaction_id (%s) not found'
            % transaction_id in raw)
        # Okay, let's create a transaction
        caller_response = create_transaction(transaction_id)
        self.assertEqual(caller_response, None)
        # It seems that the transaction has been created but we don't get a
        # useful response
        raw, formvars = query_history(HOST, self.terminal_id,
                                transaction_id, HTTPS)
        self.assertTrue(
            '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\r\n    '
            '"http://www.w3.org/TR/html4/loose.dtd">' in raw)
        # The same, an 'empty' response obviously means that the transaction
        # was found but no result was  sent to the response_url because the
        # payment was cancelled.

        # Peter: No response would be returned because payment status information
        # was not saved. Whenever you get a null response, means payment was
        # not received only payments with error code 0 is successful.
        # So in this case transaction fails.

        # We manually created and tried to pay a transaction 
        # which seems to be valid till next restart of the demo portal.
        raw, formvars = query_history(HOST, self.terminal_id, TID, HTTPS)
        # Now Etranzact is redirecting but the response is still useless
        self.assertTrue('Redirecting...' in raw)
        self.assertEqual(formvars['TRANSACTION_ID'], TID)
        return

    @external_test
    def test_query_payoutlet(self):
        # We've got some test numbers from Etranzact for testing
        payment = NigeriaStudentOnlinePayment()
        payment.p_id = 'p5723474039401'
        success, msg, log = query_payoutlet(
            HOST, '5003021194', '500854291572447457669', payment, True)
        self.assertTrue(msg, 'Wrong amount')
        payment.amount_auth = 200000.0
        success, msg, log = query_payoutlet(
            HOST, '5003021194', '500854291572447457669', payment, True)
        self.assertTrue(success)
        payment.p_id = 'xyz'
        success, msg, log = query_payoutlet(
            HOST, '5003021194', '500854291572447457669', payment, True)
        self.assertTrue(msg, 'Wrong payment id')
        return



class EtranzactTestsApplicants(ApplicantsFullSetup):
    """Tests for the Etranzact payment gateway.
    """

    layer = FunctionalLayer

    def setUp(self):
        super(EtranzactTestsApplicants, self).setUp()
        configuration = SessionConfiguration()
        configuration.academic_session = datetime.now().year - 2
        configuration.etranzact_webconnect_enabled = True
        configuration.etranzact_payoutlet_enabled = True
        self.app['configuration'].addSessionConfiguration(configuration)
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.manage_path)
        #IWorkflowState(self.student).setState('started')
        super(EtranzactTestsApplicants, self).fill_correct_values()
        self.applicantscontainer.application_fee = 3333.0
        self.browser.getControl(name="form.nationality").value = ['NG']
        self.browser.getControl(name="transition").value = ['start']
        self.browser.getControl("Save").click()
        self.browser.getControl("Add online").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.payment = self.applicant.values()[0]
        self.payment_url = self.browser.url

    @external_test
    def test_applicant_views(self):
        # Manager can access Etranzact form
        self.browser.getLink("Pay via Etranzact WebConnect").click()
        self.assertTrue("Pay now" in self.browser.contents)
        # Means of testing end here.
        # We requery an existing paiment now.
        self.payment.p_id = TID
        self.browser.open(self.payment_url)
        self.browser.getLink("Requery Etranzact WebConnect History").click()
        self.assertTrue('Wrong checksum.' in self.browser.contents)
        # ... probably because responseurl of the transaction stored in the
        # system and the responseurl generated in process_response are
        # different
        # Means of testing end here again.
        return

class EtranzactTestsStudents(StudentsFullSetup):
    """Tests for the Etranzact payment gateway.
    """

    layer = FunctionalLayer

    def setUp(self):
        super(EtranzactTestsStudents, self).setUp()
        self.app['configuration']['2004'].etranzact_webconnect_enabled = True
        self.app['configuration']['2004'].etranzact_payoutlet_enabled = True
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.payments_path)
        IWorkflowState(self.student).setState('cleared')
        self.student.nationality = u'NG'
        self.browser.open(self.payments_path + '/addop')
        self.browser.getControl(name="form.p_category").value = ['schoolfee']
        self.browser.getControl("Create ticket").click()
        self.assertMatches('...ticket created...',
                           self.browser.contents)
        self.browser.open(self.payments_path)
        ctrl = self.browser.getControl(name='val_id')
        self.value = ctrl.options[0]
        self.browser.getLink(self.value).click()
        self.assertMatches('...Amount Authorized...',
                           self.browser.contents)
        self.assertTrue('<span>40000.0</span>', self.browser.contents)
        self.payment_url = self.browser.url
        self.payment = self.student['payments'][self.value]

    @external_test
    def test_student_views(self):
        # Manager can access Etranzact form
        self.browser.getLink("Pay via Etranzact WebConnect").click()
        self.assertTrue("Pay now" in self.browser.contents)
        # Means of testing end here.
        # We requery an existing paiment now.
        self.payment.p_id = TID
        self.browser.open(self.payment_url)
        self.browser.getLink("Requery Etranzact WebConnect History").click()
        self.assertTrue('Wrong checksum.' in self.browser.contents)
        # ... probably because responseurl and amount stored in the
        # system and the responseurl generated in process_response are
        # different
        # Means of testing end here again.
        return

    @external_test
    def test_student_payoutlet_views(self):
        self.browser.getLink("Pay via Etranzact PayOutlet").click()
        self.browser.getControl(name="confirmation_number").value = '600854291572447457669'
        self.browser.getControl("Requery now").click()
        # This response is strange
        self.assertTrue('-3:Wrong Setup' in self.browser.contents)
        self.browser.getLink("Pay via Etranzact PayOutlet").click()
        # This confirmation number exists
        self.browser.getControl(name="confirmation_number").value = '500854291572447457669'
        self.browser.getControl("Requery now").click()
        self.assertTrue('Wrong amount' in self.browser.contents)
        # Some attributes have been set
        self.assertEqual(self.payment.r_desc, 'Test Test Test-CLEARANCE -001-p5723474039401')
        return

    def test_webservice(self):
        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=%s&PAYMENT_TYPE=SCHOOLFEE'
            % self.payment.p_id)
        self.assertEqual(self.browser.contents,
            'PayeeName=Anna Tester~'
            'Faculty=fac1~Department=dep1~'
            'Level=100~ProgrammeType=CERT1~'
            'StudyType=ug_ft~Session=2004/2005~'
            'PayeeID=%s~'
            'Amount=40000.0~FeeStatus=unpaid~'
            'Semester=N/A~PaymentType=School Fee~'
            'MatricNumber=234~Email=aa@aa.ng~'
            'PhoneNumber=1234' % self.payment.p_id)
        self.browser.open('http://localhost/app/feerequest')
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Missing PAYEE_ID' + ERROR_PART2)

        self.browser.open('http://localhost/app/feerequest?NONSENSE=nonsense')
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Missing PAYEE_ID' + ERROR_PART2)

        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=nonsense&PAYMENT_TYPE=SCHOOLFEE')
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Invalid PAYEE_ID' + ERROR_PART2)

        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=%s&PAYMENT_TYPE=NONSENSE'
            % self.payment.p_id)
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Invalid PAYMENT_TYPE' + ERROR_PART2)

        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=%s' % self.payment.p_id)
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Invalid PAYMENT_TYPE' + ERROR_PART2)

        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=%s&PAYMENT_TYPE=CLEARANCE'
            % self.payment.p_id)
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'Wrong PAYMENT_TYPE' + ERROR_PART2)

        # Change payment state
        self.student['payments'][self.payment.p_id].p_state = 'paid'

        self.browser.open(
            'http://localhost/app/feerequest?PAYEE_ID=%s&PAYMENT_TYPE=SCHOOLFEE'
            % self.payment.p_id)
        self.assertEqual(self.browser.contents, ERROR_PART1 + 'PAYEE_ID already used' + ERROR_PART2)

# Credo tests

from kofacustom.nigeria.etranzact.helpers import (
    query_credo_payment, get_JSON_response_initialize)

SECRET_API_KEY = "0PRI0500LB11cBSSLcW0DcW1BcWgmf45"
API_PUBLIC_KEY = "0PUB0500wuoOe9sdtSOLj5peqHKc8Q9W"
EXTERNAL_TESTS_CREDO = True
CREDO_HOST = "api.credodemo.com"

def external_test_credo(func):
    if not EXTERNAL_TESTS_CREDO:
        myself = __file__
        if myself.endswith('.pyc'):
            myself = myself[:-1]
        print "WARNING: external Credo tests are skipped!"
        print "WARNING: edit %s to enable them." % myself
        return
    return func

class CredoTestsStudents(StudentsFullSetup):

    layer = FunctionalLayer

    P_ID = 'p7120340727304' # works only if such a payment was made on the test platform

    def setUp(self):
        super(CredoTestsStudents, self).setUp()
        self.app['configuration']['2004'].etranzact_credo_enabled = True
        payment = createObject('waeup.StudentOnlinePayment')
        payment.p_id = self.P_ID
        payment.p_category = 'schoolfee'
        payment.amount_auth = 100000.0
        self.student['payments'][payment.p_id] = payment
        self.payment2 = payment

    @external_test_credo
    def test_initialize(self):

        public_api_key = API_PUBLIC_KEY
        callbackUrl = 'aa.aa.aa'
        response = get_JSON_response_initialize(self.payment2, CREDO_HOST, callbackUrl, public_api_key, None)

        ## A typical response

        #{u'status': 200,
        # u'execTime': 5.109764,
        # u'message': u'Successfully processed',
        # u'data':
        #    {u'credoReference': u'vsb200B5oM0521Mb00og',
        #     u'reference': u'xyz',
        #     u'authorizationUrl': u'https://pay.credodemo.com/vsb200B5oM0521Mb00og'
        #     },
        # u'error': []
        #}

        # Payment already exists
        self.assertEqual(response, {'error': u'Reference must be unique. '})

        #self.assertEqual(response['status'], 200)
        #self.assertEqual(response['message'], 'Successfully processed')
        #self.assertEqual(response['data']['reference'], self.P_ID)
        #self.assertTrue('https://pay.credodemo.com/' in response['data']['authorizationUrl'])
        return

    @external_test_credo
    def test_verify(self):
        self.payment2.p_id = 'anything'
        success, msg, log = query_credo_payment(self.payment2, CREDO_HOST, SECRET_API_KEY)
        self.assertEqual(log, 'Transaction with ref# anything not found')
        self.payment2.p_id = self.P_ID # manually paid
        success, msg, log = query_credo_payment(self.payment2, CREDO_HOST, SECRET_API_KEY)
        self.assertEqual(msg, 'Successful callback received')
        self.assertEqual(self.payment2.r_code, '0')
        self.assertEqual(self.payment2.r_desc, 'Successfully processed')
        self.assertEqual(self.payment2.p_state, 'paid')
        return

    @external_test_credo
    def test_student_credo_views(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.payments_path)
        self.browser.getLink(self.P_ID).click()
        self.browser.getLink("Requery Etranzact Credo").click()
        self.assertTrue('<span>Successfully processed</span>' in self.browser.contents)
        self.assertEqual(self.payment2.r_desc, 'Successfully processed')
        return

class CredoTestsApplicants(ApplicantsFullSetup):

    layer = FunctionalLayer

    P_ID = 'p7120808915674' # works only if such a payment was made on the test platform

    def setUp(self):
        super(CredoTestsApplicants, self).setUp()
        # Add session configuration object
        configuration = SessionConfiguration()
        configuration.academic_session =  self.applicant.__parent__.year
        configuration.etranzact_credo_enabled = True
        self.app['configuration'].addSessionConfiguration(configuration)
        payment = createObject('waeup.ApplicantOnlinePayment')
        payment.p_id = self.P_ID
        payment.p_category = 'application'
        payment.amount_auth = 5000.0
        self.applicant.email = 'xx@xx.xx'
        self.applicant[payment.p_id] = payment
        self.payment2 = payment

    @external_test_credo
    def test_initialize(self):

        public_api_key = API_PUBLIC_KEY
        callbackUrl = 'aa.aa.aa'
        response = get_JSON_response_initialize(self.payment2, CREDO_HOST, callbackUrl, public_api_key, None)

        # Payment already exists
        self.assertEqual(response, {'error': u'Reference must be unique. '})

        #self.assertEqual(response['status'], 200)
        #self.assertEqual(response['message'], 'Successfully processed')
        #self.assertEqual(response['data']['reference'], self.P_ID)
        #self.assertTrue('https://pay.credodemo.com/' in response['data']['authorizationUrl'])
        return

    @external_test_credo
    def test_verify(self):
        self.payment2.p_id = 'anything'
        success, msg, log = query_credo_payment(self.payment2, CREDO_HOST, SECRET_API_KEY)
        self.assertEqual(log, 'Transaction with ref# anything not found')
        self.payment2.p_id = self.P_ID # manually paid
        success, msg, log = query_credo_payment(self.payment2, CREDO_HOST, SECRET_API_KEY)
        self.assertEqual(msg, 'Successful callback received')
        self.assertEqual(self.payment2.r_code, '0')
        self.assertEqual(self.payment2.r_desc, 'Successfully processed')
        self.assertEqual(self.payment2.p_state, 'paid')
        return

    @external_test_credo
    def test_applicant_credo_views(self):
        IWorkflowState(self.applicant).setState('started')
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.view_path)
        self.browser.getLink(self.P_ID).click()
        self.browser.getLink("Requery Etranzact Credo").click()
        self.assertTrue('<span>Successfully processed</span>' in self.browser.contents)
        self.assertEqual(self.payment2.r_desc, 'Successfully processed')
        self.assertEqual(self.applicant.state, 'paid')
        return