160 lines
4.6 KiB
PHP
160 lines
4.6 KiB
PHP
<?php
|
|
|
|
require_once dirname(__DIR__, 3) . '/routes/xlvaskUsageLogsRoute.php';
|
|
|
|
class XLVaskUsageHallScopeRouteHarness extends \routes\xlvaskUsageLogsRoute
|
|
{
|
|
public bool $hasAllPermission = false;
|
|
|
|
public function __construct()
|
|
{
|
|
// Avoid request initialization in this focused unit test.
|
|
}
|
|
|
|
public function hasPermission(
|
|
string|\classes\permission_node $permission,
|
|
?int $customer_number = null
|
|
): bool {
|
|
$permissionKey = $permission instanceof \classes\permission_node
|
|
? (string)$permission->permission
|
|
: $permission;
|
|
|
|
return $this->hasAllPermission && $permissionKey === 'list_xlvask_usage_orders_all';
|
|
}
|
|
}
|
|
|
|
class XLVaskUsageHallScopeDbFake
|
|
{
|
|
public int $queryCount = 0;
|
|
private bool $softDeleteQueryAttempted = false;
|
|
|
|
/** @param array<int,array{HallId:mixed}> $rows */
|
|
public function __construct(
|
|
private readonly array $rows,
|
|
private readonly bool $deletedAtColumnMissing = false
|
|
)
|
|
{
|
|
}
|
|
|
|
public function query(string $sql): object
|
|
{
|
|
$this->queryCount++;
|
|
if ($this->deletedAtColumnMissing
|
|
&& !$this->softDeleteQueryAttempted
|
|
&& str_contains($sql, 'deleted_at IS NULL')) {
|
|
$this->softDeleteQueryAttempted = true;
|
|
throw new \Exception("Unknown column 'deleted_at' in 'WHERE'");
|
|
}
|
|
|
|
expect($sql)->toContain('SELECT DISTINCT HallId FROM plate_scanners');
|
|
|
|
return new stdClass();
|
|
}
|
|
|
|
/** @return array<int,array{HallId:mixed}> */
|
|
public function fetch_all(object $result): array
|
|
{
|
|
return $this->rows;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array<int,mixed> $groupHallIds
|
|
*/
|
|
function xlvask_usage_hall_scope_user(array $groupHallIds): object
|
|
{
|
|
return new class($groupHallIds) {
|
|
/** @param array<int,mixed> $groupHallIds */
|
|
public function __construct(private readonly array $groupHallIds)
|
|
{
|
|
}
|
|
|
|
public function getGroup(): object
|
|
{
|
|
return new class($this->groupHallIds) {
|
|
/** @param array<int,mixed> $groupHallIds */
|
|
public function __construct(private readonly array $groupHallIds)
|
|
{
|
|
}
|
|
|
|
/** @return array<int,mixed> */
|
|
public function getDepartmentsScannersHallIds(): array
|
|
{
|
|
return $this->groupHallIds;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
/** @param array<int,mixed> $groupHallIds */
|
|
function resolve_xlvask_usage_hall_scope(
|
|
XLVaskUsageHallScopeRouteHarness $route,
|
|
XLVaskUsageHallScopeDbFake $dbFake,
|
|
array $groupHallIds
|
|
): array {
|
|
$hadDb = array_key_exists('db', $GLOBALS);
|
|
$originalDb = $GLOBALS['db'] ?? null;
|
|
$GLOBALS['db'] = $dbFake;
|
|
|
|
try {
|
|
$method = new ReflectionMethod(\routes\xlvaskUsageLogsRoute::class, 'allowedHallIdsForUser');
|
|
|
|
return $method->invoke($route, xlvask_usage_hall_scope_user($groupHallIds));
|
|
} finally {
|
|
if ($hadDb) {
|
|
$GLOBALS['db'] = $originalDb;
|
|
} else {
|
|
unset($GLOBALS['db']);
|
|
}
|
|
}
|
|
}
|
|
|
|
it('uses every normalized scanner hall for users with all-scope permission', function (): void {
|
|
$route = new XLVaskUsageHallScopeRouteHarness();
|
|
$route->hasAllPermission = true;
|
|
$dbFake = new XLVaskUsageHallScopeDbFake([
|
|
['HallId' => ' Hall-A '],
|
|
['HallId' => ''],
|
|
['HallId' => 'Hall-A'],
|
|
['HallId' => str_repeat('x', 192)],
|
|
['HallId' => 'Hall-B'],
|
|
]);
|
|
|
|
$hallIds = resolve_xlvask_usage_hall_scope($route, $dbFake, ['Own-Hall']);
|
|
|
|
expect($hallIds)->toBe(['Hall-A', 'Hall-B'])
|
|
->and($dbFake->queryCount)->toBe(1);
|
|
});
|
|
|
|
it('falls back when plate scanners table has no deleted_at column', function (): void {
|
|
$route = new XLVaskUsageHallScopeRouteHarness();
|
|
$route->hasAllPermission = true;
|
|
$dbFake = new XLVaskUsageHallScopeDbFake([
|
|
['HallId' => ' Hall-A '],
|
|
['HallId' => 'Hall-B'],
|
|
], true);
|
|
|
|
$hallIds = resolve_xlvask_usage_hall_scope($route, $dbFake, ['Own-Hall']);
|
|
|
|
expect($hallIds)->toBe(['Hall-A', 'Hall-B'])
|
|
->and($dbFake->queryCount)->toBe(2);
|
|
});
|
|
|
|
it('keeps own-scope users limited to normalized group halls', function (): void {
|
|
$route = new XLVaskUsageHallScopeRouteHarness();
|
|
$dbFake = new XLVaskUsageHallScopeDbFake([
|
|
['HallId' => 'Global-Hall'],
|
|
]);
|
|
|
|
$hallIds = resolve_xlvask_usage_hall_scope($route, $dbFake, [
|
|
' Own-Hall-A ',
|
|
'',
|
|
'Own-Hall-A',
|
|
'Own-Hall-B',
|
|
]);
|
|
|
|
expect($hallIds)->toBe(['Own-Hall-A', 'Own-Hall-B'])
|
|
->and($dbFake->queryCount)->toBe(0);
|
|
});
|