Files
JobcardSystem/tests/UserAdminServiceTest.php

89 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../app/Domain/User/PasswordPolicy.php';
require_once __DIR__ . '/../app/Domain/User/UserRecord.php';
require_once __DIR__ . '/../app/Domain/User/RoleRecord.php';
require_once __DIR__ . '/../app/Domain/User/PermissionMatrix.php';
require_once __DIR__ . '/../app/Domain/User/UserAdminService.php';
require_once __DIR__ . '/../app/Domain/User/RolePermissionService.php';
use App\Domain\User\RolePermissionService;
use App\Domain\User\UserAdminService;
function user_admin_assert_same(mixed $expected, mixed $actual, string $message): void
{
if ($expected !== $actual) {
throw new RuntimeException($message . "\nExpected: " . var_export($expected, true) . "\nActual: " . var_export($actual, true));
}
}
$users = new UserAdminService();
$existing = [
['id' => 7, 'name' => 'Alice Example', 'email' => 'alice@example.test', 'role_name' => 'Accounts', 'role_id' => 2, 'is_active' => true],
['id' => 8, 'name' => 'Bob Example', 'email' => 'bob@example.test', 'role_name' => 'Technician', 'role_id' => 3, 'is_active' => false],
];
$edited = $users->validateForEdit(7, [
'name' => ' Alice Updated ', 'email' => ' ALICE.UPDATED@EXAMPLE.TEST ', 'role_id' => '3', 'is_active' => 'yes',
], $existing);
user_admin_assert_same(true, $edited['valid'], 'A valid user edit should pass.');
user_admin_assert_same('alice.updated@example.test', $edited['email'], 'User edit email should be normalized.');
user_admin_assert_same(7, $edited['id'], 'User edit should retain the explicit ID.');
$duplicate = $users->validateForEdit(7, [
'name' => 'Alice Updated', 'email' => ' bob@example.test ', 'role_id' => 2, 'is_active' => true,
], $existing);
user_admin_assert_same(false, $duplicate['valid'], 'A duplicate user email should fail edit validation.');
if (!isset($duplicate['errors']['email'])) throw new RuntimeException('Duplicate user email should produce an email error.');
user_admin_assert_same(
['valid' => true, 'id' => 7, 'is_active' => false, 'errors' => []],
$users->validateDeactivate($existing[0]),
'Active users should be deactivatable.'
);
user_admin_assert_same(
['valid' => true, 'id' => 8, 'is_active' => true, 'errors' => []],
$users->validateReactivate($existing[1]),
'Inactive users should be reactivatable.'
);
$protected = ['id' => 1, 'name' => 'Root', 'email' => 'root@example.test', 'role_name' => ' Administrator ', 'role_id' => 1, 'is_active' => true];
$protectedResult = $users->validateDeactivate($protected);
user_admin_assert_same(false, $protectedResult['valid'], 'The Administrator account must not be deactivated.');
if (!isset($protectedResult['errors']['role'])) throw new RuntimeException('Protected Administrator deactivation should produce a role error.');
$reset = $users->validatePasswordReset($existing[0], 'Unique&Secure123');
user_admin_assert_same(true, $reset['valid'], 'A strong password reset should pass.');
if (array_key_exists('password', $reset)) throw new RuntimeException('Password reset validation must not return plaintext passwords.');
$weakReset = $users->validatePasswordReset($existing[0], 'Password123!');
user_admin_assert_same(false, $weakReset['valid'], 'Weak password reset input should fail.');
if (!isset($weakReset['errors']['password'])) throw new RuntimeException('Weak password reset should produce a password error.');
$payloadReset = $users->validateReset(['password' => 'Unique&Secure123']);
user_admin_assert_same(true, $payloadReset['valid'], 'Password-only reset payloads should be supported.');
// Administrator identity is authoritative from the immutable user ID, even when
// a controller passes only editable fields and omits current role/name fields.
$protectedEdit = $users->validateEdit(1, [
'name' => 'Renamed', 'email' => 'admin.updated@example.test', 'role_id' => 2, 'is_active' => true,
]);
user_admin_assert_same(false, $protectedEdit['valid'], 'User ID 1 must remain protected without current fields.');
if (!isset($protectedEdit['errors']['role_id'])) throw new RuntimeException('Administrator reassignment must be rejected from ID alone.');
user_admin_assert_same(false, $users->validateDeactivate(['id' => 1, 'is_active' => true])['valid'], 'Administrator deactivation must be rejected from ID alone.');
$roles = new RolePermissionService();
$available = ['clients.view', 'clients.manage', 'reports.view'];
$assignment = $roles->validateAssignment(['id' => 4, 'name' => 'Support'], [' CLIENTS.VIEW ', 'reports.view', 'clients.view'], $available);
user_admin_assert_same(true, $assignment['valid'], 'Known permissions should be assignable to a custom role.');
user_admin_assert_same(['clients.view', 'reports.view'], $assignment['permissions'], 'Permission assignments should be canonical and de-duplicated.');
$unknown = $roles->validateAssignment(['id' => 4, 'name' => 'Support'], ['clients.view', 'users.delete'], $available);
user_admin_assert_same(false, $unknown['valid'], 'Unknown permissions must not be assignable.');
if (!isset($unknown['errors']['permissions'])) throw new RuntimeException('Unknown permission should produce a permissions error.');
$protectedRole = $roles->validateAssignment(['id' => 1, 'name' => 'Administrator'], ['clients.view'], $available);
user_admin_assert_same(false, $protectedRole['valid'], 'Administrator permissions must be protected.');
if (!isset($protectedRole['errors']['role'])) throw new RuntimeException('Administrator permission changes should produce a role error.');
printf("User administration service tests: 8 passed\n");