Coverage for jbank/tests.py: 100%
167 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-27 13:36 +0700
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-27 13:36 +0700
1import os
2import subprocess
3from datetime import date, datetime, timedelta
4from decimal import Decimal
5from os.path import join
6import pytz
7import zeep
8from django.conf import settings
9from django.core.exceptions import ValidationError
10from django.core.management import call_command
11from django.template.loader import get_template
12from django.test import TestCase
13from django.utils.timezone import now
14from jacc.models import Account
15from jbank.csr_helpers import create_private_key, create_csr_pem, get_private_key_pem, strip_pem_header_and_footer
16from jbank.ecb import parse_euro_exchange_rates_xml
17from jbank.helpers import validate_xml, parse_date_or_relative_date
18from jbank.models import WsEdiConnection, WsEdiSoapCall, Payout, PayoutParty, ReferencePaymentBatchFile, ReferencePaymentRecord
19from jbank.tito import parse_tiliote_statements_from_file
20from jbank.svm import parse_svm_batches_from_file
21from jbank.sepa import Pain001, Pain002, PAIN001_REMITTANCE_INFO_OCR, PAIN001_REMITTANCE_INFO_OCR_ISO
22from jbank.x509_helpers import get_x509_cert_from_file
23from jutil.format import format_xml
24from jutil.parse import parse_datetime
25from jutil.validators import iban_bic
26from lxml import etree # type: ignore # pytype: disable=import-error
27from zeep.wsse import BinarySignature # type: ignore
30class Tests(TestCase):
31 def setUp(self):
32 pass
34 def tearDown(self):
35 pass
37 def test_pain001(self):
38 debtor_acc = "FI4947300010416310"
39 p = Pain001(
40 "201802071211XJANITEST",
41 "Vuokrahelppi",
42 debtor_acc,
43 iban_bic(debtor_acc),
44 "020840699",
45 ["Koukkukankareentie 29", "20320 Turku"],
46 "FI",
47 )
48 creditor_acc = "FI8847304720017517"
49 p.add_payment("201802071339A0001", "Jani Kajala", creditor_acc, iban_bic(creditor_acc), Decimal("49.00"), "vuokratilitys")
50 p.add_payment(
51 "201802071339A0001",
52 "Jani Kajala",
53 creditor_acc,
54 iban_bic(creditor_acc),
55 Decimal("49.00"),
56 "302300",
57 PAIN001_REMITTANCE_INFO_OCR,
58 )
59 p.add_payment(
60 "201802071339A0001",
61 "Jani Kajala",
62 creditor_acc,
63 iban_bic(creditor_acc),
64 Decimal("49.00"),
65 "RF92 1229",
66 PAIN001_REMITTANCE_INFO_OCR_ISO,
67 )
68 xml_str = format_xml(p.render_to_bytes().decode())
69 # print(xml_str)
71 filename = "/tmp/pain001.xml"
72 with open(filename, "wt", encoding="utf-8") as fp:
73 fp.write(xml_str)
74 # print(filename, 'written')
76 # /usr/bin/xmllint --format --pretty 1 --load-trace --debug --schema $1 $2
77 res = subprocess.run(
78 [
79 "/usr/bin/xmllint",
80 "--noout",
81 # '--format',
82 # '--pretty', '1',
83 # '--load-trace',
84 # '--debug',
85 "--schema",
86 join(settings.BASE_DIR, "data/pain001/pain.001.001.03.xsd"),
87 filename,
88 ]
89 )
90 self.assertEqual(res.returncode, 0)
92 def test_to(self):
93 filename = join(settings.BASE_DIR, "data/to/547404896.TO")
94 statements = parse_tiliote_statements_from_file(filename)
95 rec = statements[0]["records"][0]
96 # pprint(rec)
97 self.assertEqual(rec["amount"], Decimal("-1799.00"))
98 self.assertEqual(rec["archive_identifier"], "180203473047IE5807")
99 self.assertEqual(rec["paid_date"], date(2018, 2, 3))
100 self.assertEqual(rec["sepa"]["iban_account_number"], "FI8847304720017517")
102 def test_svm(self):
103 filename = join(settings.BASE_DIR, "data/svm/547392460.SVM")
104 batches = parse_svm_batches_from_file(filename)
105 recs = batches[0]["records"]
106 self.assertEqual(len(recs), 1)
107 rec = recs[0]
108 # pprint(rec)
109 self.assertEqual(rec["amount"], Decimal("49.00"))
110 self.assertEqual(rec["archive_identifier"], "02042588WWRV0212")
111 self.assertEqual(rec["remittance_info"], "00000000000000013013")
113 def test_xp(self):
114 filename = join(settings.BASE_DIR, "data/xp/547958656.XP")
115 with open(filename, "rb") as fp:
116 file_content = fp.read()
117 p = Pain002(file_content)
118 self.assertEqual(p.original_msg_id, "201802071211XJANITEST")
119 self.assertEqual(p.msg_id, "V000000009726773")
120 self.assertEqual(p.group_status, "ACCP")
121 self.assertEqual(p.is_accepted, True)
123 def test_ecb_rates(self):
124 filename = join(settings.BASE_DIR, "data/ecb-rates-2019-08-15.xml")
125 with open(filename, "rt") as fp:
126 content = fp.read()
127 rates = parse_euro_exchange_rates_xml(content)
128 # for record_date, currency, rate in rates[20:21]:
129 # print(record_date, currency, rate)
130 record_date, currency, rate = rates[20]
131 self.assertEqual(record_date, date(2019, 8, 15))
132 self.assertEqual(currency, "HKD")
133 self.assertEqual(rate, Decimal("8.744"))
135 def normalize_soap_env(self, content: bytes) -> bytes:
136 doc = etree.fromstring(content)
137 doc.find(".//{http://www.w3.org/2000/09/xmldsig#}DigestValue").text = "x"
138 doc.find(".//{http://www.w3.org/2000/09/xmldsig#}Reference").attrib["URI"] = "x"
139 doc.find(".//{http://www.w3.org/2000/09/xmldsig#}SignatureValue").text = "x"
140 doc.find(".//{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Reference").attrib["URI"] = "x"
141 doc.find(".//{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}BinarySecurityToken").attrib[
142 "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Id"
143 ] = "x"
144 doc.find(".//{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}BinarySecurityToken").text = "x"
145 doc.find(".//{http://schemas.xmlsoap.org/soap/envelope/}Body").attrib[
146 "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Id"
147 ] = "x"
148 return etree.tostring(doc)
150 def test_x509(self):
151 print("MEDIA_ROOT = {}".format(settings.MEDIA_ROOT))
152 ws = WsEdiConnection(
153 name="test",
154 sender_identifier="12319203",
155 receiver_identifier="123192031",
156 target_identifier="1",
157 environment="TEST",
158 soap_endpoint="http://localhost",
159 )
160 ws.signing_cert_file.name = "data/x509/cert.pem"
161 ws.signing_key_file.name = "data/x509/key.pem"
162 ws.save()
163 cert = get_x509_cert_from_file("data/x509/cert.pem")
164 not_valid_before, not_valid_after = pytz.utc.localize(cert.not_valid_before), pytz.utc.localize(cert.not_valid_after)
165 self.assertEqual(not_valid_before, pytz.utc.localize(datetime(2019, 12, 3, 17, 54, 41)))
166 self.assertEqual(not_valid_after, pytz.utc.localize(datetime(2019, 12, 13, 17, 54, 41)))
167 self.assertEqual(WsEdiConnection.objects.get_by_receiver_identifier("123192031").id, ws.id)
168 app = open("data/x509/appreq.xml", "rb").read()
169 signed = ws.sign_application_request(app)
170 ref_signed = b'<?xml version="1.0"?>\n<ApplicationRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://bxd.fi/xmldata/">\n <CustomerId>061133</CustomerId>\n <Timestamp>2012-02-20T08:50:59.4319012+01:00</Timestamp>\n <Environment>TEST</Environment>\n <FileReferences>\n <FileReference>1202170046-1202171334</FileReference>\n </FileReferences>\n <SoftwareId>DBSEPAClient</SoftwareId>\n <FileType>pain.002.001.02</FileType>\n <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">\n <SignedInfo>\n <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>\n <Reference URI="">\n <Transforms>\n <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>\n </Transforms>\n <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>\n <DigestValue>D4Bg9OJdQHgsXMQM2ecyoqNwCn4=</DigestValue>\n </Reference>\n </SignedInfo>\n <SignatureValue>NwocqJ57CuLnQJaIA0Jm8JM2WlNOrNSM65Qt9nq69az4QMCgZBwRIGudZ+uMVWu0\n8TRriTHgrCTw5W50cVnhC/z+sw0mcNuYskXmGpr+4nUASYbcbT6EzktrHHkbhVTZ\nI4X4NdN8skU06TdgzF1Jvw/GzKENrm4l9hBLGbQyIaJppGmK1lN0g1suxnyvzgZK\ngFEEJZdFgFWcP+mCU+88FkcYOVjoX48rLtr0hSXyWRgUC9x3Seiw5GoS3toaY9dE\nmo6GREWUFEX9TjaH2sOy4WexiejlYisCyEGzBnyBGZ/Xi5EdoZSRoGrh60r+XDLX\n3Vu3vCJm5zD3SPX5fV9o/A==</SignatureValue>\n <KeyInfo>\n <X509Data>\n <X509IssuerSerial>\n <X509IssuerName>Issuer: {{ ws.signing_cert.issuer.rfc4514_string }}</X509IssuerName>\n <X509SerialNumber>{{ ws.signing_cert.serial_number }}</X509SerialNumber>\n </X509IssuerSerial>\n <X509Certificate>MIIDVDCCAjygAwIBAgIUGhIGnbdNdnLHN2Gb3GCgUeSJjQYwDQYJKoZIhvcNAQEL\nBQAwVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlRYMQ8wDQYDVQQHDAZEYWxsYXMx\nFTATBgNVBAoMDEthamFsYSBHcm91cDETMBEGA1UEAwwKa2FqYWxhLmNvbTAeFw0x\nOTEyMDMxNzU0NDFaFw0xOTEyMTMxNzU0NDFaMFcxCzAJBgNVBAYTAlVTMQswCQYD\nVQQIDAJUWDEPMA0GA1UEBwwGRGFsbGFzMRUwEwYDVQQKDAxLYWphbGEgR3JvdXAx\nEzARBgNVBAMMCmthamFsYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQDzpgdgMXiW9SOxnFPLdeFIltlCxH2rAz/nj7bLGK9ycVMMOW08M55jLhMK\nfOnyrHECWSP1aSKV6ZsNrRqm87JK8LEHgnIIDauqmJY4bEqB9nj7SzhAJWw5dJWN\nVNQto0csItaywXm0OjYRo4spDoKlyUbgXEcIouH4nWe91fvpvCfZiTHlfToTqKJy\nHtJ+kPi2+PSySs4+167g3A+v4YnoHgxTJD+q6KSimQQ0VGpqcV4Mt7+U3VV3Jb61\nSJNoanB31DLDugTjCOYm6PfT1/abQgPi7TOkizNa6HIMtMO/KFXC/6P+Bg05cQM9\n1Qq7iZR2zIg62AjC7z41xivdf8PdAgMBAAGjGDAWMBQGA1UdEQQNMAuCCWxvY2Fs\naG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAn4D/QY8pdvbPYx+yDeYPlHnYv68ErBK\n7Ib2rrtM7jtumBVL9BCneacjdsLmsrXwNdQkQMynxl6bMa+uR3YkXyQSVs+aSwKy\nIkz++rI5ALRI5KQr/DGzWrrmlIbBrXtQkLUR2mnyw9t+ozSMPtdedVCr50c88B5j\ndnksF6odkXet2gpVa5aZ8T2HUl+DtixkKoQ66Ra0/cXXdi3pk6zfRAm8/wtVIzQY\nX2+rur2NOPUuCzdWAvNjzuWCgmH8A2BxCOWBAMJ/GpsVJpqJp0tgm7ah1N+r1y0c\nRqxXZw7bu3NXedB1YqmOvYRcAGK2WXH4aV7I/rDRCc+aMs1GCOocbw==</X509Certificate>\n </X509Data>\n </KeyInfo>\n </Signature>\n</ApplicationRequest>\n'
171 self.assertEqual(signed, ref_signed)
172 encoded = ws.encode_application_request(signed)
173 ref_encoded = b"PEFwcGxpY2F0aW9uUmVxdWVzdCB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxucz0iaHR0cDovL2J4ZC5maS94bWxkYXRhLyI+CiAgICA8Q3VzdG9tZXJJZD4wNjExMzM8L0N1c3RvbWVySWQ+CiAgICA8VGltZXN0YW1wPjIwMTItMDItMjBUMDg6NTA6NTkuNDMxOTAxMiswMTowMDwvVGltZXN0YW1wPgogICAgPEVudmlyb25tZW50PlRFU1Q8L0Vudmlyb25tZW50PgogICAgPEZpbGVSZWZlcmVuY2VzPgogICAgPEZpbGVSZWZlcmVuY2U+MTIwMjE3MDA0Ni0xMjAyMTcxMzM0PC9GaWxlUmVmZXJlbmNlPgogICAgPC9GaWxlUmVmZXJlbmNlcz4KICAgIDxTb2Z0d2FyZUlkPkRCU0VQQUNsaWVudDwvU29mdHdhcmVJZD4KICAgIDxGaWxlVHlwZT5wYWluLjAwMi4wMDEuMDI8L0ZpbGVUeXBlPgogICAgPFNpZ25hdHVyZSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPFNpZ25lZEluZm8+CiAgICAgICAgICA8Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICAgICAgPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogICAgICAgICAgPFJlZmVyZW5jZSBVUkk9IiI+CiAgICAgICAgICAgIDxUcmFuc2Zvcm1zPgogICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPgogICAgICAgICAgICA8L1RyYW5zZm9ybXM+CiAgICAgICAgICAgIDxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgogICAgICAgICAgICA8RGlnZXN0VmFsdWU+RDRCZzlPSmRRSGdzWE1RTTJlY3lvcU53Q240PTwvRGlnZXN0VmFsdWU+CiAgICAgICAgICA8L1JlZmVyZW5jZT4KICAgICAgICA8L1NpZ25lZEluZm8+CiAgICAgICAgPFNpZ25hdHVyZVZhbHVlPk53b2NxSjU3Q3VMblFKYUlBMEptOEpNMldsTk9yTlNNNjVRdDlucTY5YXo0UU1DZ1pCd1JJR3VkWit1TVZXdTAKOFRScmlUSGdyQ1R3NVc1MGNWbmhDL3orc3cwbWNOdVlza1htR3ByKzRuVUFTWWJjYlQ2RXprdHJISGtiaFZUWgpJNFg0TmROOHNrVTA2VGRnekYxSnZ3L0d6S0VOcm00bDloQkxHYlF5SWFKcHBHbUsxbE4wZzFzdXhueXZ6Z1pLCmdGRUVKWmRGZ0ZXY1ArbUNVKzg4RmtjWU9Wam9YNDhyTHRyMGhTWHlXUmdVQzl4M1NlaXc1R29TM3RvYVk5ZEUKbW82R1JFV1VGRVg5VGphSDJzT3k0V2V4aWVqbFlpc0N5RUd6Qm55QkdaL1hpNUVkb1pTUm9Hcmg2MHIrWERMWAozVnUzdkNKbTV6RDNTUFg1ZlY5by9BPT08L1NpZ25hdHVyZVZhbHVlPgogICAgICAgIDxLZXlJbmZvPgogICAgICAgICAgPFg1MDlEYXRhPgogICAgICAgICAgICA8WDUwOUlzc3VlclNlcmlhbD4KICAgICAgICAgICAgICA8WDUwOUlzc3Vlck5hbWU+SXNzdWVyOiB7eyB3cy5zaWduaW5nX2NlcnQuaXNzdWVyLnJmYzQ1MTRfc3RyaW5nIH19PC9YNTA5SXNzdWVyTmFtZT4KICAgICAgICAgICAgICA8WDUwOVNlcmlhbE51bWJlcj57eyB3cy5zaWduaW5nX2NlcnQuc2VyaWFsX251bWJlciB9fTwvWDUwOVNlcmlhbE51bWJlcj4KICAgICAgICAgICAgPC9YNTA5SXNzdWVyU2VyaWFsPgogICAgICAgICAgICA8WDUwOUNlcnRpZmljYXRlPk1JSURWRENDQWp5Z0F3SUJBZ0lVR2hJR25iZE5kbkxITjJHYjNHQ2dVZVNKalFZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1Z6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWxSWU1ROHdEUVlEVlFRSERBWkVZV3hzWVhNeApGVEFUQmdOVkJBb01ERXRoYW1Gc1lTQkhjbTkxY0RFVE1CRUdBMVVFQXd3S2EyRnFZV3hoTG1OdmJUQWVGdzB4Ck9URXlNRE14TnpVME5ERmFGdzB4T1RFeU1UTXhOelUwTkRGYU1GY3hDekFKQmdOVkJBWVRBbFZUTVFzd0NRWUQKVlFRSURBSlVXREVQTUEwR0ExVUVCd3dHUkdGc2JHRnpNUlV3RXdZRFZRUUtEQXhMWVdwaGJHRWdSM0p2ZFhBeApFekFSQmdOVkJBTU1DbXRoYW1Gc1lTNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUR6cGdkZ01YaVc5U094bkZQTGRlRklsdGxDeEgyckF6L25qN2JMR0s5eWNWTU1PVzA4TTU1akxoTUsKZk9ueXJIRUNXU1AxYVNLVjZac05yUnFtODdKSzhMRUhnbklJRGF1cW1KWTRiRXFCOW5qN1N6aEFKV3c1ZEpXTgpWTlF0bzBjc0l0YXl3WG0wT2pZUm80c3BEb0tseVViZ1hFY0lvdUg0bldlOTFmdnB2Q2ZaaVRIbGZUb1RxS0p5Ckh0SitrUGkyK1BTeVNzNCsxNjdnM0ErdjRZbm9IZ3hUSkQrcTZLU2ltUVEwVkdwcWNWNE10NytVM1ZWM0piNjEKU0pOb2FuQjMxRExEdWdUakNPWW02UGZUMS9hYlFnUGk3VE9raXpOYTZISU10TU8vS0ZYQy82UCtCZzA1Y1FNOQoxUXE3aVpSMnpJZzYyQWpDN3o0MXhpdmRmOFBkQWdNQkFBR2pHREFXTUJRR0ExVWRFUVFOTUF1Q0NXeHZZMkZzCmFHOXpkREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQW40RC9RWThwZHZiUFl4K3lEZVlQbEhuWXY2OEVyQksKN0liMnJydE03anR1bUJWTDlCQ25lYWNqZHNMbXNyWHdOZFFrUU15bnhsNmJNYSt1UjNZa1h5UVNWcythU3dLeQpJa3orK3JJNUFMUkk1S1FyL0RHeldycm1sSWJCclh0UWtMVVIybW55dzl0K296U01QdGRlZFZDcjUwYzg4QjVqCmRua3NGNm9ka1hldDJncFZhNWFaOFQySFVsK0R0aXhrS29RNjZSYTAvY1hYZGkzcGs2emZSQW04L3d0Vkl6UVkKWDIrcnVyMk5PUFV1Q3pkV0F2Tmp6dVdDZ21IOEEyQnhDT1dCQU1KL0dwc1ZKcHFKcDB0Z203YWgxTityMXkwYwpScXhYWnc3YnUzTlhlZEIxWXFtT3ZZUmNBR0syV1hINGFWN0kvckRSQ2MrYU1zMUdDT29jYnc9PTwvWDUwOUNlcnRpZmljYXRlPgogICAgICAgICAgPC9YNTA5RGF0YT4KICAgICAgICA8L0tleUluZm8+CiAgICA8L1NpZ25hdHVyZT4KPC9BcHBsaWNhdGlvblJlcXVlc3Q+Cg=="
174 self.assertEqual(encoded, ref_encoded)
175 timestamp = pytz.timezone("Europe/Helsinki").localize(datetime(2015, 2, 3, 14, 30))
176 soap_call = WsEdiSoapCall.objects.create(connection=ws, command="HelloWorld", created=timestamp)
177 soap_body = get_template("jbank/soap_template.xml").render({"soap_call": soap_call, "payload": encoded.decode()})
178 body_bytes = soap_body.encode()
179 envelope = etree.fromstring(body_bytes)
180 binary_signature = BinarySignature(ws.signing_key_full_path, ws.signing_cert_full_path)
181 soap_headers = {}
182 envelope, soap_headers = binary_signature.apply(envelope, soap_headers)
183 signed_body_bytes = etree.tostring(envelope)
184 ref_bytes = b'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://model.bxd.fi" xmlns:ns2="http://bxd.fi/CorporateFileService">\n <SOAP-ENV:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">\n<SignedInfo>\n<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>\n<Reference URI="#id-311224dc-3b4c-4729-b057-b3abb3bcd95d">\n<Transforms>\n<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n</Transforms>\n<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>\n<DigestValue>FGoo2ZirnTBuQ9N22TPlCO7eXaw=</DigestValue>\n</Reference>\n</SignedInfo>\n<SignatureValue>MNseos+F09uzlAH+1B1o01OBPslz80vJZWmi4yJ4NniZBXAD0rtPBRhbVwF4gSw1\nbLt1Eb6YKDl+nyRMb0f1H/QUuIibMXvlKu2IVYyTUktovW29oKkmXKfJtix9Eh+w\nzq4XEDZ3BjbkHW7FrS5ZZGRJB+IeePTHSZZ1JSxiBnOg5BQ0MlypUhdEaaHmJUM+\n7vSp/pttXc8vNdB/8pFCV0Yw/DrjANeNbFv2HXG0p1R1Dhmk2Tj3+cWDpWoFuWoj\nOg1y0eoTHW/5SSob9S9mEQPJi6aYr1ni89dF1I78o7kRDxK/JbyTNBq0YnLaXA2m\n6hJlwwfj9iYH3AkYe9YXsw==</SignatureValue>\n<KeyInfo>\n<wsse:SecurityTokenReference><wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#id-e328e4ba-0ee6-4570-8476-89c88611e5ac"/></wsse:SecurityTokenReference></KeyInfo>\n</Signature><wsse:BinarySecurityToken xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ns1:Id="id-e328e4ba-0ee6-4570-8476-89c88611e5ac">MIIDVDCCAjygAwIBAgIUGhIGnbdNdnLHN2Gb3GCgUeSJjQYwDQYJKoZIhvcNAQEL\nBQAwVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlRYMQ8wDQYDVQQHDAZEYWxsYXMx\nFTATBgNVBAoMDEthamFsYSBHcm91cDETMBEGA1UEAwwKa2FqYWxhLmNvbTAeFw0x\nOTEyMDMxNzU0NDFaFw0xOTEyMTMxNzU0NDFaMFcxCzAJBgNVBAYTAlVTMQswCQYD\nVQQIDAJUWDEPMA0GA1UEBwwGRGFsbGFzMRUwEwYDVQQKDAxLYWphbGEgR3JvdXAx\nEzARBgNVBAMMCmthamFsYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQDzpgdgMXiW9SOxnFPLdeFIltlCxH2rAz/nj7bLGK9ycVMMOW08M55jLhMK\nfOnyrHECWSP1aSKV6ZsNrRqm87JK8LEHgnIIDauqmJY4bEqB9nj7SzhAJWw5dJWN\nVNQto0csItaywXm0OjYRo4spDoKlyUbgXEcIouH4nWe91fvpvCfZiTHlfToTqKJy\nHtJ+kPi2+PSySs4+167g3A+v4YnoHgxTJD+q6KSimQQ0VGpqcV4Mt7+U3VV3Jb61\nSJNoanB31DLDugTjCOYm6PfT1/abQgPi7TOkizNa6HIMtMO/KFXC/6P+Bg05cQM9\n1Qq7iZR2zIg62AjC7z41xivdf8PdAgMBAAGjGDAWMBQGA1UdEQQNMAuCCWxvY2Fs\naG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAn4D/QY8pdvbPYx+yDeYPlHnYv68ErBK\n7Ib2rrtM7jtumBVL9BCneacjdsLmsrXwNdQkQMynxl6bMa+uR3YkXyQSVs+aSwKy\nIkz++rI5ALRI5KQr/DGzWrrmlIbBrXtQkLUR2mnyw9t+ozSMPtdedVCr50c88B5j\ndnksF6odkXet2gpVa5aZ8T2HUl+DtixkKoQ66Ra0/cXXdi3pk6zfRAm8/wtVIzQY\nX2+rur2NOPUuCzdWAvNjzuWCgmH8A2BxCOWBAMJ/GpsVJpqJp0tgm7ah1N+r1y0c\nRqxXZw7bu3NXedB1YqmOvYRcAGK2WXH4aV7I/rDRCc+aMs1GCOocbw==</wsse:BinarySecurityToken></wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns0:Id="id-311224dc-3b4c-4729-b057-b3abb3bcd95d">\n <ns2:helloWorldin>\n <ns1:RequestHeader>\n <ns1:SenderId>12319203</ns1:SenderId>\n <ns1:RequestId>1</ns1:RequestId>\n <ns1:Timestamp>2015-02-03T14:30:00+02:00</ns1:Timestamp>\n <ns1:Language>FI</ns1:Language>\n <ns1:UserAgent>Kajala WS</ns1:UserAgent>\n <ns1:ReceiverId>123192031</ns1:ReceiverId>\n </ns1:RequestHeader>\n <ns1:ApplicationRequest>PEFwcGxpY2F0aW9uUmVxdWVzdCB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxucz0iaHR0cDovL2J4ZC5maS94bWxkYXRhLyI+CiAgICA8Q3VzdG9tZXJJZD4wNjExMzM8L0N1c3RvbWVySWQ+CiAgICA8VGltZXN0YW1wPjIwMTItMDItMjBUMDg6NTA6NTkuNDMxOTAxMiswMTowMDwvVGltZXN0YW1wPgogICAgPEVudmlyb25tZW50PlRFU1Q8L0Vudmlyb25tZW50PgogICAgPEZpbGVSZWZlcmVuY2VzPgogICAgPEZpbGVSZWZlcmVuY2U+MTIwMjE3MDA0Ni0xMjAyMTcxMzM0PC9GaWxlUmVmZXJlbmNlPgogICAgPC9GaWxlUmVmZXJlbmNlcz4KICAgIDxTb2Z0d2FyZUlkPkRCU0VQQUNsaWVudDwvU29mdHdhcmVJZD4KICAgIDxGaWxlVHlwZT5wYWluLjAwMi4wMDEuMDI8L0ZpbGVUeXBlPgogICAgPFNpZ25hdHVyZSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPFNpZ25lZEluZm8+CiAgICAgICAgICA8Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICAgICAgPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogICAgICAgICAgPFJlZmVyZW5jZSBVUkk9IiI+CiAgICAgICAgICAgIDxUcmFuc2Zvcm1zPgogICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPgogICAgICAgICAgICA8L1RyYW5zZm9ybXM+CiAgICAgICAgICAgIDxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgogICAgICAgICAgICA8RGlnZXN0VmFsdWU+RDRCZzlPSmRRSGdzWE1RTTJlY3lvcU53Q240PTwvRGlnZXN0VmFsdWU+CiAgICAgICAgICA8L1JlZmVyZW5jZT4KICAgICAgICA8L1NpZ25lZEluZm8+CiAgICAgICAgPFNpZ25hdHVyZVZhbHVlPk53b2NxSjU3Q3VMblFKYUlBMEptOEpNMldsTk9yTlNNNjVRdDlucTY5YXo0UU1DZ1pCd1JJR3VkWit1TVZXdTAKOFRScmlUSGdyQ1R3NVc1MGNWbmhDL3orc3cwbWNOdVlza1htR3ByKzRuVUFTWWJjYlQ2RXprdHJISGtiaFZUWgpJNFg0TmROOHNrVTA2VGRnekYxSnZ3L0d6S0VOcm00bDloQkxHYlF5SWFKcHBHbUsxbE4wZzFzdXhueXZ6Z1pLCmdGRUVKWmRGZ0ZXY1ArbUNVKzg4RmtjWU9Wam9YNDhyTHRyMGhTWHlXUmdVQzl4M1NlaXc1R29TM3RvYVk5ZEUKbW82R1JFV1VGRVg5VGphSDJzT3k0V2V4aWVqbFlpc0N5RUd6Qm55QkdaL1hpNUVkb1pTUm9Hcmg2MHIrWERMWAozVnUzdkNKbTV6RDNTUFg1ZlY5by9BPT08L1NpZ25hdHVyZVZhbHVlPgogICAgICAgIDxLZXlJbmZvPgogICAgICAgICAgPFg1MDlEYXRhPgogICAgICAgICAgICA8WDUwOUlzc3VlclNlcmlhbD4KICAgICAgICAgICAgICA8WDUwOUlzc3Vlck5hbWU+SXNzdWVyOiB7eyB3cy5zaWduaW5nX2NlcnQuaXNzdWVyLnJmYzQ1MTRfc3RyaW5nIH19PC9YNTA5SXNzdWVyTmFtZT4KICAgICAgICAgICAgICA8WDUwOVNlcmlhbE51bWJlcj57eyB3cy5zaWduaW5nX2NlcnQuc2VyaWFsX251bWJlciB9fTwvWDUwOVNlcmlhbE51bWJlcj4KICAgICAgICAgICAgPC9YNTA5SXNzdWVyU2VyaWFsPgogICAgICAgICAgICA8WDUwOUNlcnRpZmljYXRlPk1JSURWRENDQWp5Z0F3SUJBZ0lVR2hJR25iZE5kbkxITjJHYjNHQ2dVZVNKalFZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1Z6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWxSWU1ROHdEUVlEVlFRSERBWkVZV3hzWVhNeApGVEFUQmdOVkJBb01ERXRoYW1Gc1lTQkhjbTkxY0RFVE1CRUdBMVVFQXd3S2EyRnFZV3hoTG1OdmJUQWVGdzB4Ck9URXlNRE14TnpVME5ERmFGdzB4T1RFeU1UTXhOelUwTkRGYU1GY3hDekFKQmdOVkJBWVRBbFZUTVFzd0NRWUQKVlFRSURBSlVXREVQTUEwR0ExVUVCd3dHUkdGc2JHRnpNUlV3RXdZRFZRUUtEQXhMWVdwaGJHRWdSM0p2ZFhBeApFekFSQmdOVkJBTU1DbXRoYW1Gc1lTNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUR6cGdkZ01YaVc5U094bkZQTGRlRklsdGxDeEgyckF6L25qN2JMR0s5eWNWTU1PVzA4TTU1akxoTUsKZk9ueXJIRUNXU1AxYVNLVjZac05yUnFtODdKSzhMRUhnbklJRGF1cW1KWTRiRXFCOW5qN1N6aEFKV3c1ZEpXTgpWTlF0bzBjc0l0YXl3WG0wT2pZUm80c3BEb0tseVViZ1hFY0lvdUg0bldlOTFmdnB2Q2ZaaVRIbGZUb1RxS0p5Ckh0SitrUGkyK1BTeVNzNCsxNjdnM0ErdjRZbm9IZ3hUSkQrcTZLU2ltUVEwVkdwcWNWNE10NytVM1ZWM0piNjEKU0pOb2FuQjMxRExEdWdUakNPWW02UGZUMS9hYlFnUGk3VE9raXpOYTZISU10TU8vS0ZYQy82UCtCZzA1Y1FNOQoxUXE3aVpSMnpJZzYyQWpDN3o0MXhpdmRmOFBkQWdNQkFBR2pHREFXTUJRR0ExVWRFUVFOTUF1Q0NXeHZZMkZzCmFHOXpkREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQW40RC9RWThwZHZiUFl4K3lEZVlQbEhuWXY2OEVyQksKN0liMnJydE03anR1bUJWTDlCQ25lYWNqZHNMbXNyWHdOZFFrUU15bnhsNmJNYSt1UjNZa1h5UVNWcythU3dLeQpJa3orK3JJNUFMUkk1S1FyL0RHeldycm1sSWJCclh0UWtMVVIybW55dzl0K296U01QdGRlZFZDcjUwYzg4QjVqCmRua3NGNm9ka1hldDJncFZhNWFaOFQySFVsK0R0aXhrS29RNjZSYTAvY1hYZGkzcGs2emZSQW04L3d0Vkl6UVkKWDIrcnVyMk5PUFV1Q3pkV0F2Tmp6dVdDZ21IOEEyQnhDT1dCQU1KL0dwc1ZKcHFKcDB0Z203YWgxTityMXkwYwpScXhYWnc3YnUzTlhlZEIxWXFtT3ZZUmNBR0syV1hINGFWN0kvckRSQ2MrYU1zMUdDT29jYnc9PTwvWDUwOUNlcnRpZmljYXRlPgogICAgICAgICAgPC9YNTA5RGF0YT4KICAgICAgICA8L0tleUluZm8+CiAgICA8L1NpZ25hdHVyZT4KPC9BcHBsaWNhdGlvblJlcXVlc3Q+Cg==</ns1:ApplicationRequest>\n </ns2:helloWorldin>\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>'
185 self.assertEqual(self.normalize_soap_env(ref_bytes), self.normalize_soap_env(signed_body_bytes))
187 def test_x509_data(self):
188 with open(os.path.join(settings.BASE_DIR, "data/x509/x509data.xml"), "rb") as fp:
189 xml_bytes = fp.read()
190 x509_data = etree.fromstring(xml_bytes)
191 cert = x509_data.find(etree.QName(zeep.ns.DS, "X509Certificate"))
192 self.assertIsNotNone(cert)
194 def test_validate_xml(self):
195 xml = os.path.join(settings.BASE_DIR, "data/finvoice/xsd-test.xml")
196 xsd = os.path.join(settings.BASE_DIR, "data/finvoice/xsd-test.xsd")
197 with open(xml, "rb") as fp:
198 validate_xml(fp.read(), xsd)
200 def test_payout_validation(self):
201 payer = PayoutParty.objects.all().first()
202 recipient = PayoutParty.objects.all().last()
203 connection = WsEdiConnection.objects.all().first()
204 acc = Account.objects.all().first()
205 p = Payout(connection=connection, payer=payer, recipient=recipient, messages="testi", account=acc)
206 with self.assertRaisesMessage(ValidationError, "> 0"):
207 p.full_clean()
209 def test_rsa_csr(self):
210 pk = create_private_key()
211 csr = create_csr_pem(pk, common_name="kajala.com", country_name="FI", organization_name="Kajala Group Ltd")
212 self.assertEqual(csr.decode().split("\n")[0], "-----BEGIN CERTIFICATE REQUEST-----")
213 pk_pem = get_private_key_pem(pk)
214 self.assertEqual(pk_pem.decode().split("\n")[0], "-----BEGIN PRIVATE KEY-----")
215 self.assertFalse(strip_pem_header_and_footer(pk_pem).startswith(b"-----BEGIN"))
217 def test_parse_xt(self):
218 if os.path.isdir("./downloads/xt"):
219 call_command("parse_xt", "downloads/xt", auto_create_accounts=True)
220 if os.path.isdir("./downloads/svm"):
221 call_command("parse_svm", "downloads/svm", auto_create_accounts=True)
222 call_command("parse_xt", "data/xt", auto_create_accounts=True)
223 call_command("parse_svm", "data/svm", auto_create_accounts=True)
224 call_command("parse_to", "data/to", auto_create_accounts=True)
226 def test_parse_date_or_relative_date(self):
227 tz = pytz.utc
228 time_now = now().astimezone(tz)
229 date_now = time_now.date()
230 self.assertEqual(parse_date_or_relative_date("yesterday", tz=tz), date_now - timedelta(days=1))
231 self.assertEqual(parse_date_or_relative_date("prev_60d", tz=tz), date_now - timedelta(days=60))
232 self.assertEqual(parse_date_or_relative_date(date_now.isoformat(), tz=tz), date_now)
234 def test_parse_xm(self):
235 ReferencePaymentBatchFile.objects.all().delete()
236 call_command("parse_xm", "data/xm", verbose=True, auto_create_accounts=True)
237 file = ReferencePaymentBatchFile.objects.all().order_by("-id").first()
238 assert isinstance(file, ReferencePaymentBatchFile)
239 self.assertEqual(file.get_total_amount(), Decimal("7048.00"))
240 self.assertEqual(ReferencePaymentRecord.objects.filter(batch__file=file).count(), 4)