taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 9b16e959286bc24810185d6569ffe13855df30b3
parent 87793376701af498e0ad366adc00bdd5ccca5de7
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  6 Dec 2025 17:39:50 +0100

expand PDF generation test (#10666), lots of FIXMEs for Dold

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-tops-aml-pdf.ts | 840++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 835 insertions(+), 5 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-pdf.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-pdf.ts @@ -25,6 +25,62 @@ import { setupMeasuresTestEnvironment } from "../harness/tops.js"; export const logger = new Logger("test-tops-aml-measures.ts"); /** + * Helper function to create a mock PDF file attachment. + */ +function createPdfAttachment(content: string): string { + // Escape parentheses for PDF literal strings + const contentEscaped = content + .replace(/\(/g, "\\(") + .replace(/\)/g, "\\)"); + + // Minimal PDF + const pdf = `%PDF-1.1 +1 0 obj +<< /Type /Catalog /Pages 2 0 R>> +endobj +2 0 obj +<< /Type /Pages /Kids[3 0 R]/Count 1>> +endobj +3 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox[0 0 200 50] +/Resources<< /Font<< /F<< /Type/Font /Subtype/Type3 /FontBBox[0 0 0 0] +/FontMatrix[0.001 0 0 0.001 0 0] /CharProcs<< /H 4 0 R >> /Encoding<< /Type/Encoding/MissingWidth 1 /Differences[0/H] >> +/FirstChar 0 /LastChar 0 /Widths[1000] >> >> /Contents 5 0 R >> +endobj +4 0 obj +<< /Length 8>> +stream +0 0 m +endstream +endobj +5 0 obj +<< /Length ${35 + contentEscaped.length - "Hello World".length}>> +stream +BT /F 12 Tf 10 20 Td (${contentEscaped}) Tj ET +endstream +endobj +xref +0 6 +0000000000 65535 f +0000000009 00000 n +0000000056 00000 n +0000000107 00000 n +0000000334 00000 n +0000000409 00000 n +trailer<< /Size 6 /Root 1 0 R>> +startxref +475 +%%EOF`; + + // FIXME: Dold: which encoding are we using? Base64 or Crockford? + // Also: this doesn't yet wrap in "FILE". "File" data type seems + // unspecified in tops.rst! + return Buffer.from(pdf, "utf8").toString("base64"); +} + + + +/** * Test that invokes all measures defined for the TOPS deployment. */ export async function runTopsAmlPdfTest(t: GlobalTestState) { @@ -40,6 +96,7 @@ export async function runTopsAmlPdfTest(t: GlobalTestState) { accountPaytoHash, } = await setupMeasuresTestEnvironment(t); + // Test vqf_902_1_customer form - LEGAL_ENTITY variant { await decideMeasure("kyx"); await expectNoInvestigate(); @@ -47,9 +104,113 @@ export async function runTopsAmlPdfTest(t: GlobalTestState) { FORM_ID: "vqf_902_1_customer", FORM_VERSION: 1, CUSTOMER_TYPE: "LEGAL_ENTITY", + COMPANY_NAME: "Acme Corporation", + REGISTERED_OFFICE_ADDRESS: "Castle St. 1\nWondertown\n8000 Zurich", + CONTACT_PERSON_NAME: "Bob Smith", + CONTACT_PHONE: "+41123456789", + CONTACT_EMAIL: "bob@acme.com", + LEGAL_ENTITY_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("company-id-doc"), + ESTABLISHER_LIST: [ + { + FULL_NAME: "Alice Johnson", + DOMICILE_ADDRESS: "Main St. 10\n8001 Zurich", + DATE_OF_BIRTH: "1980-05-15", + NATIONALITY: "CH", + PERSONAL_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("alice-id"), + SIGNING_AUTHORITY_TYPE: "SINGLE", + SIGNING_AUTHORITY_EVIDENCE_TYPE: "CR", + SIGNING_AUTHORITY_EVIDENCE_DOCUMENT_COPY: createPdfAttachment("alice-cr"), + }, + { + FULL_NAME: "Charlie Brown", + DOMICILE_ADDRESS: "Park Ave. 20\n8002 Zurich", + DATE_OF_BIRTH: "1975-08-22", + NATIONALITY: "DE", + PERSONAL_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("charlie-id"), + SIGNING_AUTHORITY_TYPE: "COLLECTIVE_TWO", + SIGNING_AUTHORITY_EVIDENCE_TYPE: "MANDATE", + SIGNING_AUTHORITY_EVIDENCE_DOCUMENT_COPY: createPdfAttachment("charlie-mandate"), + }, + { + FULL_NAME: "Diana Prince", + DOMICILE_ADDRESS: "Lake Road 30\n8003 Zurich", + DATE_OF_BIRTH: "1985-12-01", + NATIONALITY: "US", + PERSONAL_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("diana-id"), + SIGNING_AUTHORITY_TYPE: "OTHER", + SIGNING_AUTHORITY_EVIDENCE_TYPE: "OTHER", + SIGNING_AUTHORITY_EVIDENCE_OTHER: "Board Resolution", + SIGNING_AUTHORITY_EVIDENCE_DOCUMENT_COPY: createPdfAttachment("diana-resolution"), + }, + ], + CORRESPONDENCE_LANGUAGE: "en", + CUSTOMER_TYPE_VQF: "OPERATIONAL", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + console.log(`got ${res.byteLength} bytes`); + const f = t.testDir + `/aml-form-vqf_902_1_customer-legal.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_1_customer form - NATURAL_PERSON variant (without sole proprietor) + { + await decideMeasure("kyx"); + await expectNoInvestigate(); + await submitForm("vqf_902_1_customer", { + FORM_ID: "vqf_902_1_customer", + FORM_VERSION: 1, + CUSTOMER_TYPE: "NATURAL_PERSON", + FULL_NAME: "John Doe", + DOMICILE_ADDRESS: "Elm Street 5\n8010 Zurich", + CONTACT_PHONE: "+41987654321", + CONTACT_EMAIL: "john.doe@example.com", + DATE_OF_BIRTH: "1990-03-12", + NATIONALITY: "CH", + PERSONAL_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("john-passport"), + CUSTOMER_IS_SOLE_PROPRIETOR: false, + CORRESPONDENCE_LANGUAGE: "de", + CUSTOMER_TYPE_VQF: "NATURAL", + }); + await expectInvestigate(); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_1_customer-natural.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_1_customer form - NATURAL_PERSON variant (with sole proprietor) + { + await decideMeasure("kyx"); + await submitForm("vqf_902_1_customer", { + FORM_ID: "vqf_902_1_customer", + FORM_VERSION: 1, + CUSTOMER_TYPE: "NATURAL_PERSON", + FULL_NAME: "Jane Smith", + DOMICILE_ADDRESS: "Oak Avenue 12\n8020 Zurich", + CONTACT_PHONE: "+41555123456", + CONTACT_EMAIL: "jane@smithconsulting.ch", + DATE_OF_BIRTH: "1985-07-20", + NATIONALITY: "FR", + PERSONAL_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("jane-id"), + CUSTOMER_IS_SOLE_PROPRIETOR: true, + COMPANY_NAME: "Smith Consulting GmbH", + REGISTERED_OFFICE_ADDRESS: "Business Park 3\n8021 Zurich", + LEGAL_ENTITY_IDENTIFICATION_DOCUMENT_COPY: createPdfAttachment("smith-biz-reg"), + CORRESPONDENCE_LANGUAGE: "fr", CUSTOMER_TYPE_VQF: "OTHER", - FULL_NAME: "Alice A", - DOMICILE_ADDRESS: "Castle St. 1\nWondertown", }); await expectInvestigate(); @@ -59,12 +220,680 @@ export async function runTopsAmlPdfTest(t: GlobalTestState) { accountPaytoHash, ), ); - console.log(`got ${res.byteLength} bytes`); - const f = t.testDir + `/aml-form-vqf_902_1_customer.pdf`; + const f = t.testDir + `/aml-form-vqf_902_1_customer-sole-proprietor.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_1_officer form + if (false) // FIXME: Dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_1_officer", { + FORM_ID: "vqf_902_1_officer", + FORM_VERSION: 1, + ACCEPTANCE_DATE: "2025-12-01", + ACCEPTANCE_METHOD: "FACE_TO_FACE", + ACCEPTANCE_FURTHER_INFO: "Meeting at branch office with ID verification", + EMBARGO_TERRORISM_CHECK_RESULT: "NOT_LISTED", + EMBARGO_TERRORISM_CHECK_DATE: "2025-12-01", + SUPPLEMENTAL_FILES_LIST: [ + { + FILE: createPdfAttachment("meeting-notes"), + DESCRIPTION: "Meeting notes from customer onboarding", + }, + { + FILE: createPdfAttachment("id-verification"), + DESCRIPTION: "ID verification documentation", + }, + { + FILE: createPdfAttachment("background-check"), + DESCRIPTION: "Background check results", + }, + ], + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_1_officer.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_1_officer form with LISTED terrorism check + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_1_officer", { + FORM_ID: "vqf_902_1_officer", + FORM_VERSION: 1, + ACCEPTANCE_DATE: "2025-12-02", + ACCEPTANCE_METHOD: "AUTHENTICATED_COPY", + ACCEPTANCE_FURTHER_INFO: "Authenticated copy received via postal service", + EMBARGO_TERRORISM_CHECK_RESULT: "LISTED", + EMBARGO_TERRORISM_CHECK_DATE: "2025-12-02", + EMBARGO_TERRORISM_INFO: "Name match found on sanctions list - requires further investigation", + SUPPLEMENTAL_FILES_LIST: [ + { + FILE: createPdfAttachment("sanctions-report"), + DESCRIPTION: "Detailed sanctions screening report", + }, + { + FILE: createPdfAttachment("compliance-review"), + DESCRIPTION: "Compliance team review", + }, + { + FILE: createPdfAttachment("escalation-doc"), + DESCRIPTION: "Escalation to senior management", + }, + ], + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_1_officer-listed.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_4 form - without PEP or high risk + if (false) // FIXME: dold + { + await decideMeasure("kyx"); // FIXME: which measure? + await submitForm("vqf_902_4", { + FORM_ID: "vqf_902_4", + FORM_VERSION: 1, + PEP_FOREIGN: false, + PEP_DOMESTIC: false, + PEP_INTERNATIONAL_ORGANIZATION: false, + HIGH_RISK_COUNTRY: false, + COUNTRY_RISK_NATIONALITY_TYPE: ["NATIONALITY_CUSTOMER", "DOMICILE_CUSTOMER"], + COUNTRY_RISK_NATIONALITY_LEVEL: "LOW", + COUNTRY_RISK_BUSINESS_TYPE: ["CUSTOMER"], + COUNTRY_RISK_BUSINESS_LEVEL: "LOW", + COUNTRY_RISK_PAYMENTS_LEVEL: "LOW", + INDUSTRY_RISK_TYPE: "CUSTOMER", + INDUSTRY_RISK_LEVEL: "TRANSPARENT", + CONTACT_RISK_LEVEL: "LOW", + RISK_RATIONALE: "Customer is well-known local business with transparent operations", + RISK_CLASSIFICATION_LEVEL: "NO_HIGH_RISK", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_4-low-risk.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_4 form - with PEP and high risk + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_4", { + FORM_ID: "vqf_902_4", + FORM_VERSION: 1, + PEP_FOREIGN: true, + PEP_DOMESTIC: true, + PEP_INTERNATIONAL_ORGANIZATION: false, + PEP_HIGH_RISK: true, + PEP_ACCEPTANCE_DATE: "2025-11-15", + HIGH_RISK_COUNTRY: true, + HIGH_RISK_ACCEPTANCE_DATE: "2025-11-15", + COUNTRY_RISK_NATIONALITY_TYPE: [ + "NATIONALITY_CUSTOMER", + "NATIONALITY_OWNER", + "DOMICILE_CUSTOMER", + ], + COUNTRY_RISK_NATIONALITY_LEVEL: "HIGH", + COUNTRY_RISK_BUSINESS_TYPE: ["CUSTOMER", "OWNER"], + COUNTRY_RISK_BUSINESS_LEVEL: "HIGH", + COUNTRY_RISK_PAYMENTS_LEVEL: "MEDIUM", + INDUSTRY_RISK_TYPE: "CUSTOMER", + INDUSTRY_RISK_LEVEL: "HIGH_RISK_TRADE", + CONTACT_RISK_LEVEL: "HIGH", + RISK_RATIONALE: "Customer is foreign PEP with connections to high-risk jurisdictions. Enhanced due diligence applied.", + RISK_CLASSIFICATION_LEVEL: "HIGH_RISK", + }); + await expectInvestigate(); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_4-high-risk.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_5 form - with SAVINGS origin + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await expectNoInvestigate(); + await submitForm("vqf_902_5", { + FORM_ID: "vqf_902_5", + FORM_VERSION: 1, + BIZREL_PROFESSION: "Software Engineer at Tech Company\nPreviously worked as IT Consultant\n15 years experience", + BIZREL_FINANCIAL_CIRCUMSTANCES: "Annual income: CHF 120,000\nTotal assets: CHF 500,000\nNo significant liabilities", + BIZREL_ORIGIN_NATURE: "Cash deposits and investment returns\nAmount: CHF 200,000\nCurrency: CHF", + BIZREL_ORIGIN_CATEGORY: ["SAVINGS", "OWN_BUSINESS"], + BIZREL_ORIGIN_DETAIL: "Accumulated savings from employment over 10 years. Additional income from freelance software consulting projects.", + BIZREL_PURPOSE: "Investment account for long-term wealth management and retirement planning", + BIZREL_DEVELOPMENT: "Expect regular monthly deposits of CHF 3,000-5,000. Occasional larger deposits from consulting projects.", + BIZREL_FINANCIAL_VOLUME: "Average monthly volume: CHF 4,000. Expected annual growth of 10-15%.", + BIZREL_FINANCIAL_BENEFICIARIES_FULL_NAME: "Self (account holder)", + BIZREL_THIRDPARTY_RELATIONSHIP: "No third parties involved. Account holder is sole beneficial owner.", + BIZREL_THIRDPARTY_AMLA_FILES: "None", + BIZREL_THIRDPARTY_REFERENCES: "Introduced by existing client Marcus Weber, Account #CH-12345", + BIZREL_FURTHER_INFO: "Customer has been transparent about income sources. All documentation verified.", + }); + await expectInvestigate(); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_5-savings.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_5 form - with INHERITANCE and OTHER origin + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await expectNoInvestigate(); + await submitForm("vqf_902_5", { + FORM_ID: "vqf_902_5", + FORM_VERSION: 1, + BIZREL_PROFESSION: "Retired business owner\nFormer CEO of manufacturing company\nRetired since 2020", + BIZREL_FINANCIAL_CIRCUMSTANCES: "Pension income: CHF 80,000 per year\nTotal net worth: CHF 2,500,000\nNo debt", + BIZREL_ORIGIN_NATURE: "Inheritance and business sale proceeds\nAmount: CHF 1,500,000\nCurrency: CHF and EUR", + BIZREL_ORIGIN_CATEGORY: ["INHERITANCE", "OTHER"], + BIZREL_ORIGIN_CATEGORY_OTHER: "Sale of family business in 2020", + BIZREL_ORIGIN_DETAIL: "CHF 800,000 from inheritance after parents' estate settlement in 2019. CHF 700,000 from sale of 60% stake in family manufacturing business to private equity firm.", + BIZREL_PURPOSE: "Wealth preservation and estate planning for children", + BIZREL_DEVELOPMENT: "Minimal transaction activity expected. Quarterly dividend reinvestment.", + BIZREL_FINANCIAL_VOLUME: "Low volume - primarily passive investment portfolio", + BIZREL_FINANCIAL_BENEFICIARIES_FULL_NAME: "Account holder and designated heirs: Maria Schmidt, Thomas Schmidt", + BIZREL_THIRDPARTY_RELATIONSHIP: "Trust arrangement for benefit of children listed above", + BIZREL_THIRDPARTY_AMLA_FILES: "Linked to estate file #EST-2019-445", + BIZREL_THIRDPARTY_REFERENCES: "Legal advisor: Dr. Hans Mueller, Zurich Law Firm", + BIZREL_FURTHER_INFO: "Estate documentation and business sale agreement on file. All sources verified through notarized documents.", + }); + await expectInvestigate(); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_5-inheritance.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_9_customer form + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await expectNoInvestigate(); + await submitForm("vqf_902_9_customer", { + FORM_ID: "vqf_902_9_customer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Global Trading Ltd.\nCommerce Street 45\n8050 Zurich", + IDENTITY_LIST: [ + { + FULL_NAME: "Michael Anderson", + DATE_OF_BIRTH: "1970-04-10", + DOMICILE_ADDRESS: "Riverside Drive 88\n8051 Zurich", + NATIONALITY: "GB", + }, + { + FULL_NAME: "Sarah Johnson", + DATE_OF_BIRTH: "1975-09-25", + DOMICILE_ADDRESS: "Mountain View 22\n8052 Zurich", + NATIONALITY: "US", + }, + { + FULL_NAME: "Roberto Martinez", + DATE_OF_BIRTH: "1968-11-30", + DOMICILE_ADDRESS: "Garden Plaza 7\n8053 Zurich", + NATIONALITY: "ES", + }, + ], + SIGNATURE: "Michael Anderson", + SIGN_DATE: "2025-12-05", + }); + await expectInvestigate(); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_9_customer.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_9_officer form + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_9_officer", { + FORM_ID: "vqf_902_9_officer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Tech Innovations AG\nInnovation Park 12\n8060 Zurich", + IDENTITY_LIST: [ + { + FULL_NAME: "Emma Thompson", + DATE_OF_BIRTH: "1982-06-18", + DOMICILE_ADDRESS: "Sunset Boulevard 15\n8061 Zurich", + NATIONALITY: "CH", + }, + { + FULL_NAME: "David Chen", + DATE_OF_BIRTH: "1978-03-05", + DOMICILE_ADDRESS: "Tech Valley 99\n8062 Zurich", + NATIONALITY: "SG", + }, + { + FULL_NAME: "Isabella Rossi", + DATE_OF_BIRTH: "1985-12-22", + DOMICILE_ADDRESS: "Lake View 44\n8063 Zurich", + NATIONALITY: "IT", + }, + ], + ATTACHMENT_SIGNED_DOCUMENT: createPdfAttachment("signed-form-902-9"), + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_9_officer.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_11_customer form - HAS_25_MORE_RIGHTS variant, without third party + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_11_customer", { + FORM_ID: "vqf_902_11_customer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Manufacturing Solutions Ltd.\nIndustrial Zone 78\n8070 Zurich", + CONTROL_REASON: "HAS_25_MORE_RIGHTS", + IDENTITY_LIST: [ + { + FULL_NAME: "William Brown", + DOMICILE_ADDRESS: "Highland Road 33\n8071 Zurich", + }, + { + FULL_NAME: "Olivia Davis", + DOMICILE_ADDRESS: "Valley Street 56\n8072 Zurich", + }, + { + FULL_NAME: "James Wilson", + DOMICILE_ADDRESS: "Forest Avenue 11\n8073 Zurich", + }, + ], + THIRD_PARTY_OWNERSHIP: false, + SIGNATURE: "William Brown", + SIGN_DATE: "2025-12-04", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_11_customer-25percent.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_11_customer form - OTHER_WAY variant, with third party + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_11_customer", { + FORM_ID: "vqf_902_11_customer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Service Holdings AG\nService Center 90\n8080 Zurich", + CONTROL_REASON: "OTHER_WAY", + IDENTITY_LIST: [ + { + FULL_NAME: "Sophia Martinez", + DOMICILE_ADDRESS: "Central Plaza 77\n8081 Zurich", + }, + { + FULL_NAME: "Lucas Garcia", + DOMICILE_ADDRESS: "North Street 24\n8082 Zurich", + }, + { + FULL_NAME: "Mia Rodriguez", + DOMICILE_ADDRESS: "South Boulevard 13\n8083 Zurich", + }, + ], + THIRD_PARTY_OWNERSHIP: true, + SIGNATURE: "Sophia Martinez", + SIGN_DATE: "2025-12-03", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_11_customer-other-way.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_11_customer form - DIRECTOR variant + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_11_customer", { + FORM_ID: "vqf_902_11_customer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Consulting Partners GmbH\nConsulting Tower 5\n8090 Zurich", + CONTROL_REASON: "DIRECTOR", + IDENTITY_LIST: [ + { + FULL_NAME: "Alexander Mueller", + DOMICILE_ADDRESS: "Executive Lane 66\n8091 Zurich", + }, + { + FULL_NAME: "Charlotte Weber", + DOMICILE_ADDRESS: "Management Street 88\n8092 Zurich", + }, + { + FULL_NAME: "Benjamin Schneider", + DOMICILE_ADDRESS: "Director Avenue 99\n8093 Zurich", + }, + ], + THIRD_PARTY_OWNERSHIP: false, + SIGNATURE: "Alexander Mueller", + SIGN_DATE: "2025-12-02", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_11_customer-director.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_11_officer form - HAS_25_MORE_RIGHTS variant + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_11_officer", { + FORM_ID: "vqf_902_11_officer", + FORM_VERSION: 1, + IDENTITY_CONTRACTING_PARTNER: "Investment Group SA\nFinance District 101\n8100 Zurich", + CONTROL_REASON: "HAS_25_MORE_RIGHTS", + IDENTITY_LIST: [ + { + FULL_NAME: "Victoria Fischer", + DOMICILE: "Investor Road 55\n8101 Zurich", + }, + { + FULL_NAME: "Daniel Hoffmann", + DOMICILE: "Capital Street 77\n8102 Zurich", + }, + { + FULL_NAME: "Sophie Zimmermann", + DOMICILE: "Fund Avenue 22\n8103 Zurich", + }, + ], + THIRD_PARTY_OWNERSHIP: true, + ATTACHMENT_SIGNED_DOCUMENT: createPdfAttachment("signed-form-902-11"), + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_11_officer.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_14 form - NO_SUSPICION result + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_14", { + FORM_ID: "vqf_902_14", + FORM_VERSION: 1, + INCRISK_REASON: "Large cash deposit of CHF 50,000 triggered monitoring alert. Customer explanation requested.", + INCRISK_MEANS: "GATHERING", + INCRISK_SUMMARY: "Customer provided proof of property sale. Documentation includes notarized sales agreement and bank transfer records. All information is consistent and plausible.", + INCRISK_DOCUMENTS: "Property sales agreement (notarized), Bank transfer confirmation, Tax declaration", + INCRISK_RESULT: "NO_SUSPICION", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_14-no-suspicion.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_14 form - SUBSTANTIATED_SUSPICION result + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await expectNoInvestigate(); + await submitForm("vqf_902_14", { + FORM_ID: "vqf_902_14", + FORM_VERSION: 1, + INCRISK_REASON: "Multiple transactions to high-risk jurisdictions. Inconsistent explanations provided. Transaction patterns suggest possible structuring.", + INCRISK_MEANS: "CONSULTATION", + INCRISK_SUMMARY: "Customer's stated business activity does not align with transaction patterns. Multiple red flags identified including: (1) transactions just below reporting threshold, (2) payments to shell companies in offshore jurisdictions, (3) inconsistent business documentation.", + INCRISK_DOCUMENTS: "Transaction logs, Customer interviews, Public records search, Third-party database checks", + INCRISK_RESULT: "SUBSTANTIATED_SUSPICION", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_14-substantiated.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test vqf_902_14 form - OTHER result with explanation + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("vqf_902_14", { + FORM_ID: "vqf_902_14", + FORM_VERSION: 1, + INCRISK_REASON: "Unusual activity pattern detected. Customer requested clarification on enhanced monitoring procedures.", + INCRISK_MEANS: "OTHER", + INCRISK_MEANS_OTHER: "Video conference with customer and legal counsel, review of corporate governance documents, consultation with external compliance specialist", + INCRISK_SUMMARY: "Customer is undergoing corporate restructuring. Increased transaction volume is temporary and related to merger activity. All parties have been identified and verified.", + INCRISK_DOCUMENTS: "Merger agreement, Board resolutions, Legal opinions, Updated beneficial ownership structure", + INCRISK_RESULT: "OTHER", + INCRISK_RESULT_OTHER: "Enhanced monitoring period established - 6-month review cycle with quarterly reporting requirements", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-vqf_902_14-other.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test generic_note form + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("generic_note", { + FORM_ID: "generic_note", + FORM_VERSION: 1, + NOTE_TEXT: "Follow-up meeting held with customer regarding recent transaction patterns. Customer provided satisfactory explanations and supporting documentation. No further concerns at this time.", + SUPPLEMENTAL_FILES_LIST: [ + { + DESCRIPTION: "Meeting minutes from customer interview", + FILE: createPdfAttachment("meeting-minutes-20251205"), + }, + { + DESCRIPTION: "Supporting bank statements", + FILE: createPdfAttachment("bank-statements-q4-2025"), + }, + { + DESCRIPTION: "Customer signed acknowledgment", + FILE: createPdfAttachment("customer-acknowledgment"), + }, + ], + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-generic_note.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test generic_upload form + if (false) // FIXME: dold + { + await decideMeasure("kyx"); + await submitForm("generic_upload", { + FORM_ID: "generic_upload", + FORM_VERSION: 1, + NOTE_TEXT: "Customer requested to upload current business license and updated company registry extract as part of periodic review.", + FILE: createPdfAttachment("business-license-2025"), + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-generic_upload.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test accept-tos form + { + await decideMeasure("accept-tos"); + await submitForm("accept-tos", { + FORM_ID: "accept-tos", + FORM_VERSION: 1, + ACCEPTED_TERMS_OF_SERVICE: "v1", + DOWNLOADED_TERMS_OF_SERVICE: true, + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-accept-tos.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test challenger-postal form + if (false) // FIXME: Dold: This is not exactly a 'form'. How to submit? + { + await decideMeasure("postal-registration"); + await submitForm("challenger-postal", { + FORM_ID: "challenger-postal", + FORM_VERSION: 1, + CONTACT_NAME: "Hans Müller", + ADDRESS_LINES: "Bahnhofstrasse 123\n8001 Zürich", + ADDRESS_COUNTRY: "CH", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-challenger-postal.pdf`; + fs.writeFileSync(f, Buffer.from(res)); + console.log(`written to ${f}`); + } + + // Test challenger-sms form + if (false) // FIXME: Dold: This is not exactly a 'form'. How to submit? + { + await decideMeasure("sms-registration"); + await submitForm("challenger-sms", { + FORM_ID: "challenger-sms", + FORM_VERSION: 1, + CONTACT_PHONE: "+41791234567", + }); + + const res = succeedOrThrow( + await exchangeClient.getAmlAttributesForAccountAsPdf( + officerAcc, + accountPaytoHash, + ), + ); + const f = t.testDir + `/aml-form-challenger-sms.pdf`; fs.writeFileSync(f, Buffer.from(res)); console.log(`written to ${f}`); } + // Export accounts in different formats { const res = succeedOrThrow( await exchangeClient.getAmlAccountsAsOtherFormat(officerAcc, "text/csv"), @@ -99,4 +928,4 @@ export async function runTopsAmlPdfTest(t: GlobalTestState) { } } -runTopsAmlPdfTest.suites = ["wallet"]; +runTopsAmlPdfTest.suites = ["wallet"]; +\ No newline at end of file