## Summary Audit and (where needed) fix additional fields in the e-conomic export path. PR #391 covered the main order.* and order_item.* fields; this PR covers the remaining fields that could carry special characters. ## Changes 1. Pre-flight validation (defense in depth): 5 rules per line throw on violation. 2. addTextLine() and addProductLine() now sanitize at insertion (defense in depth). 3. Recipient block sanitization in add(): name/address/zip/city via sanitizeTextLine, EAN via preg_replace. 4. Audit document: documentation/economic/export-field-audit.md. 5. Tests: 94 tests / 171 assertions (14 + 19 + 6 + 24 new tests). ## Refs - TRU-193, TRU-188, TRU-194, PR #391 --------- Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io> Co-authored-by: Bugfix Subagent <bugfix@subagent.local>
102 lines
4.1 KiB
PHP
102 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace tests\Unit\Economic;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Unit tests for TRU-193 — the recipient-block sanitization in
|
|
* economic_invoices_drafts_endpoint::add().
|
|
*
|
|
* Since the endpoint's `add()` method makes a live HTTP request to
|
|
* e-conomic, we don't test it directly. Instead we test the building
|
|
* blocks (sanitizer rules + the file-shape contract) that the endpoint
|
|
* uses, so the behavior is regression-protected.
|
|
*/
|
|
class EconomicInvoiceDraftRecipientSanitizationTest extends TestCase
|
|
{
|
|
/**
|
|
* Verify the endpoint file still references the sanitizer for
|
|
* the recipient-block fields (defense in depth, even though the
|
|
* customer data comes from e-conomic).
|
|
*/
|
|
public function testEndpointSanitizesRecipientName(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
$this->assertStringContainsString(
|
|
'sanitizeTextLine($customer->getName() ?? \'Ukendt\', 100)',
|
|
$content,
|
|
'recipient.name must be sanitized via sanitizeTextLine with a 100-char cap'
|
|
);
|
|
}
|
|
|
|
public function testEndpointSanitizesRecipientAddress(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
$this->assertStringContainsString(
|
|
'sanitizeTextLine($customer->getAddress() ?? \'Ukendt\', 250)',
|
|
$content,
|
|
'recipient.address must be sanitized via sanitizeTextLine with a 250-char cap'
|
|
);
|
|
}
|
|
|
|
public function testEndpointSanitizesRecipientZip(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
$this->assertStringContainsString(
|
|
'sanitizeTextLine($customer->getZipCode() ?? \'Ukendt\', 20)',
|
|
$content,
|
|
'recipient.zip must be sanitized via sanitizeTextLine with a 20-char cap'
|
|
);
|
|
}
|
|
|
|
public function testEndpointSanitizesRecipientCity(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
$this->assertStringContainsString(
|
|
'sanitizeTextLine($customer->getCity() ?? \'Ukendt\', 100)',
|
|
$content,
|
|
'recipient.city must be sanitized via sanitizeTextLine with a 100-char cap'
|
|
);
|
|
}
|
|
|
|
public function testEndpointStripsNonDigitsFromEan(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
$this->assertStringContainsString(
|
|
"preg_replace('/[^0-9]/', '', \$customer_ean)",
|
|
$content,
|
|
'recipient.ean must be stripped to digits only'
|
|
);
|
|
}
|
|
|
|
public function testEndpointOmitsEmptyEanInsteadOfSendingEmptyString(): void
|
|
{
|
|
$path = __DIR__ . '/../../../modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php';
|
|
$content = file_get_contents($path);
|
|
$this->assertNotFalse($content);
|
|
// After stripping non-digits, if the result is empty we should remove the key
|
|
$this->assertStringContainsString(
|
|
"unset(\$recipient['ean']);",
|
|
$content,
|
|
'recipient.ean must be removed from the payload when the sanitized EAN is empty'
|
|
);
|
|
// Verify the conditional structure: if empty, unset
|
|
$this->assertMatchesRegularExpression(
|
|
"/\\\$recipient\\['ean'\\]\\s*=\\s*preg_replace\\(\\s*['\\/\\^0-9\\/']/",
|
|
$content,
|
|
'recipient.ean must be assigned via preg_replace with a non-digit-stripping pattern'
|
|
);
|
|
}
|
|
}
|