Files
api/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodRouteGuardsTest.php
T
Jeppe B 2a6a86c9c3 Resolve backend Qodana critical and high findings (#314)
Resolve recommended-profile Critical and High findings, retain narrow analyzer exceptions, and update the edge-broker WebSocket dependency to a non-vulnerable release.
2026-07-17 05:44:16 +02:00

134 lines
6.0 KiB
PHP

<?php
app_require('routes/InvoicingPeriodRoute.php');
use routes\InvoicingPeriodRoute;
it('requires superuser permission for invoicing period distribution all endpoint', function (): void {
$routeFile = app_path('routes/InvoicingPeriodRoute.php');
$content = file_get_contents($routeFile);
expect($content)->not->toBeFalse();
expect($content)->toMatch(
"/\\/superuser\\/invoicing\\/period\\/distribution\\/all'.*?\\\$this->requirePermission\\('superuser_invoicing_period'\\);/s"
);
});
it('uses shared date-range normalization across invoicing period endpoints', function (): void {
$routeFile = app_path('routes/InvoicingPeriodRoute.php');
$content = file_get_contents($routeFile);
expect($content)->not->toBeFalse();
expect(substr_count((string)$content, 'requireAndNormalizeDateRange()'))->toBeGreaterThanOrEqual(5);
});
it('keeps the main period response local-only for booked state and customer names', function (): void {
$routeFile = app_path('routes/InvoicingPeriodRoute.php');
$content = file_get_contents($routeFile);
expect($content)->not->toBeFalse();
$content = (string)$content;
expect($content)->not->toContain('isBooked(true)')
->and($content)->toContain('isTransactionBookedFromLocalState($transaction)')
->and($content)->toContain('SELECT booked_invoice_id FROM collected_order_invoices')
->and($content)->toContain('SELECT invoice_id FROM economic_module_orders')
->and($content)->toContain('getCustomerNames(array_keys($customer_numbers), false)')
->and($content)->toContain('getCustomerNames(array_map(\'intval\', $customer_numbers), false)');
});
it('streams the main period response instead of encoding the full payload at once', function (): void {
$routeFile = app_path('routes/InvoicingPeriodRoute.php');
$content = file_get_contents($routeFile);
expect($content)->not->toBeFalse();
$content = (string)$content;
expect($content)->toContain('private static function streamInvoicingPeriodResponse(array $period): void')
->and($content)->toContain("\$includeInvoicePeriodFlags = \$this->hasPermission('list_invoice_period_flags');")
->and($content)->toContain('$period = self::getInvoicingPeriod($dateFrom, $dateTo, $customerNumbers, $includeInvoicePeriodFlags);')
->and($content)->toContain('self::streamInvoicingPeriodResponse($period);')
->and($content)->not->toContain('$response->success([' . PHP_EOL . ' ...self::getInvoicingPeriod($dateFrom, $dateTo, $customerNumbers)')
->and($content)->toContain('echo self::jsonFragment($customer);');
});
it('only includes invoice period flags when the list permission is granted', function (): void {
$content = file_get_contents(app_path('routes/InvoicingPeriodRoute.php'));
expect($content)->not->toBeFalse();
$content = (string)$content;
expect($content)
->toContain("\$includeInvoicePeriodFlags = \$this->hasPermission('list_invoice_period_flags');")
->and($content)->toContain('bool $includeInvoicePeriodFlags = false')
->and($content)->toContain('if ($includeInvoicePeriodFlags) {')
->and($content)->toContain('applyFlagsToPeriodTypes(');
});
it('maps batched period transaction rows to the legacy transaction response shape', function (): void {
$reflection = new ReflectionClass(InvoicingPeriodRoute::class);
$method = $reflection->getMethod('constructTransactionObjectFromPeriodRow');
$transaction = $method->invokeArgs(null, [[
'id' => '42',
'created_at' => '2026-04-10 12:34:56',
'net_amount' => '123.50',
'booked' => '1',
'department_id' => '7',
'customer_id' => '27983',
'order_reference' => 'REF-42',
'order_po' => 'PO-42',
'order_notes' => 'Driver note',
'reg_1' => 'AB12345',
'reg_2' => 'CD67890',
'reg_3' => '',
'invoice_collection_id' => '314',
'include_in_invoice_effective' => '0',
]]);
expect($transaction)->toMatchArray([
'id' => 42,
'date' => '2026-04-10 12:34:56',
'amount' => 123.5,
'booked' => true,
'department_id' => 7,
'customer_number' => 27983,
'reference' => 'REF-42',
'po' => 'PO-42',
'notes' => 'Driver note',
'reg_1' => 'AB12345',
'reg_2' => 'CD67890',
'reg_3' => '',
'excluded' => true,
'invoice_collection_id' => 314,
'queue_status' => null,
'queue_job_id' => null,
]);
});
it('uses batched period transactions and keyed customer maps in the main period route', function (): void {
$content = file_get_contents(app_path('routes/InvoicingPeriodRoute.php'));
expect($content)->not->toBeFalse();
$content = (string)$content;
expect($content)->toContain('getPeriodTransactionsForCustomersInDateRange(')
->and($content)->toContain('private static function indexCustomersByNumber(array $customers): array')
->and($content)->toContain('$customers_by_number = self::indexCustomersByNumber($customersWithTransactions);')
->and($content)->toContain('invoicing_period_utils::filterPossibleDuplicates($ordersByRegistration, 86400)')
->and($content)->not->toContain('getOrdersWithPossibleDuplicates($dateFrom, $dateTo)');
});
it('falls back to configured e-conomic default department for missing customer default department in distributions', function (): void {
$content = file_get_contents(app_path('routes/InvoicingPeriodRoute.php'));
expect($content)->not->toBeFalse();
$content = (string)$content;
expect($content)
->toContain('private static function getEconomicFallbackDepartmentId(): int')
->and($content)->toContain('(new economic())->getDefaultDistributionDepartmentId()')
->and($content)->toContain(': self::getEconomicFallbackDepartmentId();')
->and($content)->toContain("\$department_totals[\$fallback_department_id] = (float)\$customer['meta']['fixed_pricing']['price'];");
});