@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\App\Config;
|
||||
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
|
||||
class FinancialRouteIntegrityTest extends CIUnitTestCase
|
||||
{
|
||||
private string $routesFile;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->routesFile = ROOTPATH . 'app/Config/Routes.php';
|
||||
}
|
||||
|
||||
public function testFinancialBrowserWriteRoutesArePostOnlyAndFiltered(): void
|
||||
{
|
||||
$routes = file($this->routesFile, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$financialWrites = array_values(array_filter($routes, static function (string $line): bool {
|
||||
return str_contains($line, '$routes->post(')
|
||||
&& preg_match('/(refunds|expenses|reimbursements|discount|charges|payment|invoice|purchase|inventory)/i', $line);
|
||||
}));
|
||||
|
||||
$this->assertNotEmpty($financialWrites);
|
||||
foreach ($financialWrites as $line) {
|
||||
$this->assertStringContainsString("'filter'", $line, $line);
|
||||
$this->assertStringContainsString('auth:', $line, $line);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFinancialStateChangingRoutesDoNotUseGet(): void
|
||||
{
|
||||
$routes = file($this->routesFile, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
foreach ($routes as $line) {
|
||||
if (!str_contains($line, '$routes->get(')) {
|
||||
continue;
|
||||
}
|
||||
if (! preg_match('/\$routes->get\(\s*[\'"]([^\'"]+)/', $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routePath = $matches[1];
|
||||
if (!preg_match('/(refunds|expenses|reimbursements|discount|charges|payment|invoice|purchase|inventory)/i', $routePath)) {
|
||||
continue;
|
||||
}
|
||||
$this->assertDoesNotMatchRegularExpression('/(update|store|approve|reverse|void|delete|apply|recalculate)/i', $routePath, $line);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFinancialWritesDoNotUseGetPostMatchRoutes(): void
|
||||
{
|
||||
$routes = file($this->routesFile, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$checked = 0;
|
||||
foreach ($routes as $line) {
|
||||
if (!str_contains($line, '$routes->match(') || !str_contains($line, "'get'") || !str_contains($line, "'post'")) {
|
||||
continue;
|
||||
}
|
||||
if (!preg_match('/(refunds|expenses|reimbursements|discount|charges|payment|invoice|purchase|inventory)/i', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$checked++;
|
||||
$this->fail('Financial routes must split read GET and write POST handlers: ' . $line);
|
||||
}
|
||||
|
||||
$this->assertSame(0, $checked);
|
||||
}
|
||||
|
||||
public function testGenericApiDoesNotExposeFinancialMutations(): void
|
||||
{
|
||||
$text = file_get_contents($this->routesFile) ?: '';
|
||||
$forbidden = [
|
||||
"post('payments'",
|
||||
"put('payments/",
|
||||
"post('payment-transactions'",
|
||||
"get('payment-transactions'",
|
||||
"post('payment-notifications/send'",
|
||||
"post('expenses'",
|
||||
"put('expenses/",
|
||||
"post('reimbursements'",
|
||||
"put('reimbursements/",
|
||||
"post('refunds'",
|
||||
"put('refunds/",
|
||||
"post('discounts/apply'",
|
||||
"post('extra-charges'",
|
||||
"put('extra-charges/",
|
||||
"post('purchase-orders'",
|
||||
"put('purchase-orders/",
|
||||
"post('inventory'",
|
||||
"put('inventory/",
|
||||
"delete('inventory/",
|
||||
];
|
||||
|
||||
$apiV1Start = strpos($text, "\$routes->group('api/v1'");
|
||||
$this->assertIsInt($apiV1Start);
|
||||
$apiV1Text = substr($text, $apiV1Start);
|
||||
foreach ($forbidden as $needle) {
|
||||
$this->assertStringNotContainsString($needle, $apiV1Text, $needle);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user