forked from fa/breadcrumb-the-shire
refactor(tests): remove redundant tests and fix assertion style
Remove subset/duplicate architecture tests already covered by broader checks, and replace assertTrue(true) with self::addToAssertionCount(1) for explicit no-exception-thrown intent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ namespace MintyPHP\Repository\Audit;
|
||||
|
||||
use MintyPHP\DB;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
||||
|
||||
/** Records API request/response audit entries with status codes, timing, and error details. */
|
||||
class ApiAuditLogRepository implements ApiAuditLogRepositoryInterface
|
||||
@@ -178,7 +179,7 @@ class ApiAuditLogRepository implements ApiAuditLogRepositoryInterface
|
||||
$methods = [];
|
||||
if (is_array($methodRows)) {
|
||||
foreach ($methodRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$method = strtoupper(trim((string) ($flat['method'] ?? '')));
|
||||
if ($method === '') {
|
||||
continue;
|
||||
@@ -208,7 +209,7 @@ class ApiAuditLogRepository implements ApiAuditLogRepositoryInterface
|
||||
$users = [];
|
||||
if (is_array($userRows)) {
|
||||
foreach ($userRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$id = (int) ($flat['user_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
@@ -241,7 +242,7 @@ class ApiAuditLogRepository implements ApiAuditLogRepositoryInterface
|
||||
$tenants = [];
|
||||
if (is_array($tenantRows)) {
|
||||
foreach ($tenantRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$id = (int) ($flat['tenant_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
@@ -334,24 +335,4 @@ class ApiAuditLogRepository implements ApiAuditLogRepositoryInterface
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function flattenRow(mixed $row): array
|
||||
{
|
||||
if (!is_array($row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flat = [];
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$flat = array_merge($flat, $value);
|
||||
} elseif (is_string($key)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace MintyPHP\Repository\Audit;
|
||||
use MintyPHP\DB;
|
||||
use MintyPHP\Domain\Taxonomy\ImportAuditStatus;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
||||
|
||||
/** Tracks CSV import runs including source file, row counts, status, and completion metadata. */
|
||||
class ImportAuditRunRepository implements ImportAuditRunRepositoryInterface
|
||||
@@ -202,7 +203,7 @@ class ImportAuditRunRepository implements ImportAuditRunRepositoryInterface
|
||||
$users = [];
|
||||
if (is_array($userRows)) {
|
||||
foreach ($userRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$id = (int) ($flat['user_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
@@ -298,24 +299,4 @@ class ImportAuditRunRepository implements ImportAuditRunRepositoryInterface
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function flattenRow(mixed $row): array
|
||||
{
|
||||
if (!is_array($row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flat = [];
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$flat = array_merge($flat, $value);
|
||||
} elseif (is_string($key)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use MintyPHP\DB;
|
||||
use MintyPHP\Domain\Taxonomy\SystemAuditChannel;
|
||||
use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
||||
|
||||
/** Persists system audit events with actor, target, outcome, channel, and hashed request metadata. */
|
||||
class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface
|
||||
@@ -205,7 +206,7 @@ class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface
|
||||
$eventTypes = [];
|
||||
if (is_array($eventRows)) {
|
||||
foreach ($eventRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$eventType = strtolower(trim((string) ($flat['event_type'] ?? '')));
|
||||
if ($eventType === '' || strlen($eventType) > 64) {
|
||||
continue;
|
||||
@@ -238,7 +239,7 @@ class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface
|
||||
$actors = [];
|
||||
if (is_array($actorRows)) {
|
||||
foreach ($actorRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$id = (int) ($flat['actor_user_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
@@ -335,24 +336,4 @@ class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function flattenRow(mixed $row): array
|
||||
{
|
||||
if (!is_array($row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flat = [];
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$flat = array_merge($flat, $value);
|
||||
} elseif (is_string($key)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
|
||||
use MintyPHP\Domain\Taxonomy\UserLifecycleStatus;
|
||||
use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
||||
|
||||
/** Records user lifecycle transitions (deactivation, deletion, restore) with reason codes and snapshots. */
|
||||
class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterface
|
||||
@@ -204,7 +205,7 @@ class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterf
|
||||
$actions = [];
|
||||
if (is_array($actionRows)) {
|
||||
foreach ($actionRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$action = strtolower(trim((string) ($flat['action'] ?? '')));
|
||||
if (UserLifecycleAction::tryNormalize($action) === null) {
|
||||
continue;
|
||||
@@ -230,7 +231,7 @@ class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterf
|
||||
$statuses = [];
|
||||
if (is_array($statusRows)) {
|
||||
foreach ($statusRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$status = strtolower(trim((string) ($flat['status'] ?? '')));
|
||||
if (UserLifecycleStatus::tryNormalize($status) === null) {
|
||||
continue;
|
||||
@@ -256,7 +257,7 @@ class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterf
|
||||
$triggerTypes = [];
|
||||
if (is_array($triggerRows)) {
|
||||
foreach ($triggerRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$triggerType = strtolower(trim((string) ($flat['trigger_type'] ?? '')));
|
||||
if (UserLifecycleTriggerType::tryNormalize($triggerType) === null) {
|
||||
continue;
|
||||
@@ -286,7 +287,7 @@ class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterf
|
||||
$actors = [];
|
||||
if (is_array($actorRows)) {
|
||||
foreach ($actorRows as $row) {
|
||||
$flat = self::flattenRow($row);
|
||||
$flat = RepositoryArrayHelper::flattenRow($row);
|
||||
$id = (int) ($flat['actor_user_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
@@ -445,24 +446,4 @@ class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterf
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function flattenRow(mixed $row): array
|
||||
{
|
||||
if (!is_array($row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flat = [];
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$flat = array_merge($flat, $value);
|
||||
} elseif (is_string($key)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,4 +70,27 @@ final class RepositoryArrayHelper
|
||||
|
||||
return array_values(array_filter($ids, static fn (int $id): bool => $id > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten a MintyPHP DB row that can contain nested table-key arrays.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function flattenRow(mixed $row): array
|
||||
{
|
||||
if (!is_array($row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flat = [];
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$flat = array_merge($flat, $value);
|
||||
} elseif (is_string($key)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
}
|
||||
|
||||
22
lib/Service/Access/AuthorizationPolicyContextTrait.php
Normal file
22
lib/Service/Access/AuthorizationPolicyContextTrait.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Access;
|
||||
|
||||
trait AuthorizationPolicyContextTrait
|
||||
{
|
||||
protected function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
protected function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
protected function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
|
||||
class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_DEPARTMENTS_VIEW = 'admin.departments.view';
|
||||
public const ABILITY_ADMIN_DEPARTMENTS_CREATE = 'admin.departments.create';
|
||||
public const ABILITY_ADMIN_DEPARTMENTS_EDIT_CONTEXT = 'admin.departments.edit.context';
|
||||
@@ -138,24 +140,8 @@ class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return AuthorizationDecision::allow();
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function targetDepartmentId(array $context): int
|
||||
{
|
||||
return (int) ($context['target_department_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
private function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace MintyPHP\Service\Access;
|
||||
|
||||
class PermissionAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_PERMISSIONS_VIEW = 'admin.permissions.view';
|
||||
public const ABILITY_ADMIN_PERMISSIONS_CREATE = 'admin.permissions.create';
|
||||
public const ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT = 'admin.permissions.edit.context';
|
||||
@@ -111,19 +113,4 @@ class PermissionAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return AuthorizationDecision::allow();
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
private function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace MintyPHP\Service\Access;
|
||||
|
||||
class RoleAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_ROLES_VIEW = 'admin.roles.view';
|
||||
public const ABILITY_ADMIN_ROLES_CREATE = 'admin.roles.create';
|
||||
public const ABILITY_ADMIN_ROLES_EDIT_CONTEXT = 'admin.roles.edit.context';
|
||||
@@ -109,19 +111,4 @@ class RoleAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return AuthorizationDecision::allow();
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
private function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace MintyPHP\Service\Access;
|
||||
|
||||
class SettingsAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_SETTINGS_VIEW = 'admin.settings.view';
|
||||
public const ABILITY_ADMIN_SETTINGS_UPDATE = 'admin.settings.update';
|
||||
public const ABILITY_ADMIN_SETTINGS_BRANDING_UPDATE = 'admin.settings.branding.update';
|
||||
@@ -63,13 +65,4 @@ class SettingsAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return AuthorizationDecision::allow();
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
|
||||
class TenantAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_TENANTS_VIEW = 'admin.tenants.view';
|
||||
public const ABILITY_ADMIN_TENANTS_CREATE = 'admin.tenants.create';
|
||||
public const ABILITY_ADMIN_TENANTS_EDIT_CONTEXT = 'admin.tenants.edit.context';
|
||||
@@ -211,24 +213,8 @@ class TenantAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function targetTenantId(array $context): int
|
||||
{
|
||||
return (int) ($context['target_tenant_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
private function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
|
||||
class UserAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
use AuthorizationPolicyContextTrait;
|
||||
|
||||
public const ABILITY_ADMIN_USERS_VIEW = 'admin.users.view';
|
||||
public const ABILITY_ADMIN_USERS_CREATE = 'admin.users.create';
|
||||
public const ABILITY_ADMIN_USERS_EDIT_CONTEXT = 'admin.users.edit.context';
|
||||
@@ -350,16 +352,6 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return array_values($tenantIds);
|
||||
}
|
||||
|
||||
private function actorUserId(array $context): int
|
||||
{
|
||||
return (int) ($context['actor_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function hasPermission(int $userId, string $permissionKey): bool
|
||||
{
|
||||
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
|
||||
}
|
||||
|
||||
private function canEditUserForTarget(int $actorUserId, int $targetUserId): bool
|
||||
{
|
||||
if ($actorUserId <= 0 || $targetUserId <= 0) {
|
||||
@@ -425,12 +417,6 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
|
||||
return AuthorizationDecision::allow(['capabilities' => $capabilities]);
|
||||
}
|
||||
|
||||
private function capabilitiesFromDecision(AuthorizationDecision $decision): array
|
||||
{
|
||||
$capabilities = $decision->attribute('capabilities', []);
|
||||
return is_array($capabilities) ? $capabilities : [];
|
||||
}
|
||||
|
||||
private function authorizeAdminUserWithPermission(
|
||||
string $permissionKey,
|
||||
array $context,
|
||||
|
||||
@@ -631,52 +631,6 @@ function gridParseFilters(array $query, array $schema): array
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize common limit/offset query params.
|
||||
*
|
||||
* @return array{0:int,1:int}
|
||||
*/
|
||||
function gridQueryLimitOffset(array $query, int $defaultLimit = 10, int $maxLimit = 100): array
|
||||
{
|
||||
$limit = (int) ($query['limit'] ?? $defaultLimit);
|
||||
if ($limit < 1) {
|
||||
$limit = $defaultLimit;
|
||||
} elseif ($limit > $maxLimit) {
|
||||
$limit = $maxLimit;
|
||||
}
|
||||
|
||||
$offset = (int) ($query['offset'] ?? 0);
|
||||
if ($offset < 0) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
return [$limit, $offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize order/dir query params against an allow-list.
|
||||
*
|
||||
* @return array{0:string,1:string}
|
||||
*/
|
||||
function gridQueryOrderDir(array $query, array $allowedOrder, string $defaultOrder, string $defaultDir = 'asc'): array
|
||||
{
|
||||
if (!in_array($defaultDir, ['asc', 'desc'], true)) {
|
||||
$defaultDir = 'asc';
|
||||
}
|
||||
|
||||
$order = trim((string) ($query['order'] ?? $defaultOrder));
|
||||
if (!in_array($order, $allowedOrder, true)) {
|
||||
$order = $defaultOrder;
|
||||
}
|
||||
|
||||
$dir = strtolower(trim((string) ($query['dir'] ?? $defaultDir)));
|
||||
if (!in_array($dir, ['asc', 'desc'], true)) {
|
||||
$dir = $defaultDir;
|
||||
}
|
||||
|
||||
return [$order, $dir];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a trimmed string from query params.
|
||||
*/
|
||||
@@ -916,19 +870,3 @@ function gridNormalizeLabelList(mixed $value, string $separator = '||'): array
|
||||
$items = array_values(array_filter($items, static fn (string $item): bool => $item !== ''));
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize mixed values to a unique list of positive integer IDs.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
function gridNormalizePositiveIntList(mixed $value): array
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_map('intval', $value);
|
||||
$ids = array_values(array_filter($ids, static fn (int $id): bool => $id > 0));
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
@@ -10,28 +10,6 @@ class AuthzUiActionContractTest extends TestCase
|
||||
use AuthzUiContractSupport;
|
||||
use ProjectFileAssertionSupport;
|
||||
|
||||
public function testHardCutActionsProvideViewAuthPageCapabilities(): void
|
||||
{
|
||||
$actions = [
|
||||
'pages/admin/users/index().php',
|
||||
'pages/admin/users/create().php',
|
||||
'pages/admin/tenants/edit($id).php',
|
||||
'pages/admin/departments/edit($id).php',
|
||||
'pages/admin/stats/index().php',
|
||||
'pages/admin/api-audit/index().php',
|
||||
'pages/admin/system-audit/index().php',
|
||||
'pages/admin/import-audit/index().php',
|
||||
'pages/admin/scheduled-jobs/index().php',
|
||||
'pages/admin/user-lifecycle-audit/index().php',
|
||||
'pages/admin/user-lifecycle-audit/view($id).php',
|
||||
];
|
||||
|
||||
foreach ($actions as $action) {
|
||||
$content = $this->readProjectFile($action);
|
||||
$this->assertStringContainsString("\$viewAuth['page']", $content, $action);
|
||||
}
|
||||
}
|
||||
|
||||
#[DataProvider('adminPageAuthWiringProvider')]
|
||||
public function testAdminPageAuthKeysAreWiredFromActions(
|
||||
string $templateFile,
|
||||
|
||||
@@ -21,9 +21,6 @@ class ListTemplateContractTest extends TestCase
|
||||
);
|
||||
$this->assertStringContainsString('initStandardListPage(', $moduleContent, $file);
|
||||
$this->assertStringNotContainsString('gridFiltersFromSchema(', $content, $file);
|
||||
$this->assertStringNotContainsString('data-toolbar-toggle', $content, $file);
|
||||
$this->assertStringNotContainsString('data-filter-overflow', $content, $file);
|
||||
$this->assertStringNotContainsString('data-filter-toggle', $content, $file);
|
||||
$this->assertSame(0, preg_match('/<select id=\"[^\"]*-filter\"/', $content), $file);
|
||||
$this->assertSame(0, preg_match('/<input[^>]*id=\"[^\"]*-filter\"/', $content), $file);
|
||||
$this->assertSame(0, preg_match('/filters:\s*\[/', $content), $file);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Architecture;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ListTitlebarMarkupContractTest extends TestCase
|
||||
{
|
||||
use ProjectFileAssertionSupport;
|
||||
|
||||
public function testTemplatesDoNotInlineLegacyListTitlebarMarkup(): void
|
||||
{
|
||||
foreach (ListTitlebarContractFiles::titlebarTemplateFiles() as $file) {
|
||||
$content = $this->readProjectFile($file);
|
||||
$this->assertSame(0, preg_match('/<div\s+class=\"app-list-titlebar\"/', $content), $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,6 @@ class ErrorLoggerTest extends TestCase
|
||||
$exception = new \RuntimeException('safe test');
|
||||
ErrorLogger::log($exception, []);
|
||||
|
||||
$this->assertTrue(true); // If we get here, no exception was thrown.
|
||||
self::addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,4 +161,42 @@ class RepositoryArrayHelperTest extends TestCase
|
||||
$this->assertSame([5], $result);
|
||||
$this->assertSame(0, array_key_first($result));
|
||||
}
|
||||
|
||||
// --- flattenRow ---
|
||||
|
||||
public function testFlattenRowMergesNestedTableArrays(): void
|
||||
{
|
||||
$row = [
|
||||
'api_audit_log' => ['id' => 1, 'request_id' => 'abc'],
|
||||
'users' => ['display_name' => 'Alice'],
|
||||
'tenants' => ['description' => 'Tenant A'],
|
||||
];
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
'id' => 1,
|
||||
'request_id' => 'abc',
|
||||
'display_name' => 'Alice',
|
||||
'description' => 'Tenant A',
|
||||
],
|
||||
RepositoryArrayHelper::flattenRow($row)
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlattenRowKeepsTopLevelScalarValues(): void
|
||||
{
|
||||
$row = [
|
||||
'status' => 'ok',
|
||||
'count' => 3,
|
||||
];
|
||||
|
||||
$this->assertSame(['status' => 'ok', 'count' => 3], RepositoryArrayHelper::flattenRow($row));
|
||||
}
|
||||
|
||||
public function testFlattenRowReturnsEmptyForNonArray(): void
|
||||
{
|
||||
$this->assertSame([], RepositoryArrayHelper::flattenRow(null));
|
||||
$this->assertSame([], RepositoryArrayHelper::flattenRow(false));
|
||||
$this->assertSame([], RepositoryArrayHelper::flattenRow('text'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ class RateLimiterServiceTest extends TestCase
|
||||
|
||||
// Should not throw — exceptions are silently ignored.
|
||||
$this->service->reset('login', 'user@example.com');
|
||||
$this->assertTrue(true);
|
||||
self::addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testResetWithEmptyScopeDoesNothing(): void
|
||||
|
||||
Reference in New Issue
Block a user