instances added god may help
This commit is contained in:
33
tests/Service/Access/AccessServicesFactoryTest.php
Normal file
33
tests/Service/Access/AccessServicesFactoryTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Access;
|
||||
|
||||
use MintyPHP\Repository\Access\PermissionRepository;
|
||||
use MintyPHP\Repository\Access\RolePermissionRepository;
|
||||
use MintyPHP\Repository\Access\UserRoleRepository;
|
||||
use MintyPHP\Service\Access\AccessServicesFactory;
|
||||
use MintyPHP\Service\Access\PermissionGateway;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AccessServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new AccessServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(PermissionRepository::class, $factory->createPermissionRepository());
|
||||
$this->assertSame($factory->createPermissionRepository(), $factory->createPermissionRepository());
|
||||
|
||||
$this->assertInstanceOf(RolePermissionRepository::class, $factory->createRolePermissionRepository());
|
||||
$this->assertSame($factory->createRolePermissionRepository(), $factory->createRolePermissionRepository());
|
||||
|
||||
$this->assertInstanceOf(UserRoleRepository::class, $factory->createUserRoleRepository());
|
||||
$this->assertSame($factory->createUserRoleRepository(), $factory->createUserRoleRepository());
|
||||
|
||||
$this->assertInstanceOf('MintyPHP\\Service\\Access\\PermissionService', $factory->createPermissionService());
|
||||
$this->assertSame($factory->createPermissionService(), $factory->createPermissionService());
|
||||
|
||||
$this->assertInstanceOf(PermissionGateway::class, $factory->createPermissionGateway());
|
||||
$this->assertSame($factory->createPermissionGateway(), $factory->createPermissionGateway());
|
||||
}
|
||||
}
|
||||
33
tests/Service/Access/PermissionGatewayTest.php
Normal file
33
tests/Service/Access/PermissionGatewayTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Access;
|
||||
|
||||
use MintyPHP\Service\Access\PermissionGateway;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PermissionGatewayTest extends TestCase
|
||||
{
|
||||
public function testUserHasDelegatesToService(): void
|
||||
{
|
||||
$permissionService = $this->createMock('MintyPHP\\Service\\Access\\PermissionService');
|
||||
$permissionService->expects($this->once())
|
||||
->method('userHas')
|
||||
->with(7, 'users.view')
|
||||
->willReturn(true);
|
||||
|
||||
$gateway = new PermissionGateway($permissionService);
|
||||
$this->assertTrue($gateway->userHas(7, 'users.view'));
|
||||
}
|
||||
|
||||
public function testListPagedDelegatesToService(): void
|
||||
{
|
||||
$permissionService = $this->createMock('MintyPHP\\Service\\Access\\PermissionService');
|
||||
$permissionService->expects($this->once())
|
||||
->method('listPaged')
|
||||
->with(['limit' => 10])
|
||||
->willReturn(['data' => [], 'total' => 0]);
|
||||
|
||||
$gateway = new PermissionGateway($permissionService);
|
||||
$this->assertSame(['data' => [], 'total' => 0], $gateway->listPaged(['limit' => 10]));
|
||||
}
|
||||
}
|
||||
38
tests/Service/AddressBook/AddressBookServicesFactoryTest.php
Normal file
38
tests/Service/AddressBook/AddressBookServicesFactoryTest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\AddressBook;
|
||||
|
||||
use MintyPHP\Service\AddressBook\AddressBookAvatarGateway;
|
||||
use MintyPHP\Service\AddressBook\AddressBookCustomFieldGateway;
|
||||
use MintyPHP\Service\AddressBook\AddressBookDirectoryGateway;
|
||||
use MintyPHP\Service\AddressBook\AddressBookService;
|
||||
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AddressBookServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new AddressBookServicesFactory();
|
||||
|
||||
$directoryGatewayA = $factory->createAddressBookDirectoryGateway();
|
||||
$directoryGatewayB = $factory->createAddressBookDirectoryGateway();
|
||||
$this->assertInstanceOf(AddressBookDirectoryGateway::class, $directoryGatewayA);
|
||||
$this->assertSame($directoryGatewayA, $directoryGatewayB);
|
||||
|
||||
$customFieldGatewayA = $factory->createAddressBookCustomFieldGateway();
|
||||
$customFieldGatewayB = $factory->createAddressBookCustomFieldGateway();
|
||||
$this->assertInstanceOf(AddressBookCustomFieldGateway::class, $customFieldGatewayA);
|
||||
$this->assertSame($customFieldGatewayA, $customFieldGatewayB);
|
||||
|
||||
$avatarGatewayA = $factory->createAddressBookAvatarGateway();
|
||||
$avatarGatewayB = $factory->createAddressBookAvatarGateway();
|
||||
$this->assertInstanceOf(AddressBookAvatarGateway::class, $avatarGatewayA);
|
||||
$this->assertSame($avatarGatewayA, $avatarGatewayB);
|
||||
|
||||
$addressBookServiceA = $factory->createAddressBookService();
|
||||
$addressBookServiceB = $factory->createAddressBookService();
|
||||
$this->assertInstanceOf(AddressBookService::class, $addressBookServiceA);
|
||||
$this->assertSame($addressBookServiceA, $addressBookServiceB);
|
||||
}
|
||||
}
|
||||
30
tests/Service/Audit/AuditServicesFactoryTest.php
Normal file
30
tests/Service/Audit/AuditServicesFactoryTest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Audit;
|
||||
|
||||
use MintyPHP\Repository\Audit\ApiAuditLogRepository;
|
||||
use MintyPHP\Repository\Audit\UserLifecycleAuditRepository;
|
||||
use MintyPHP\Service\Audit\ApiAuditService;
|
||||
use MintyPHP\Service\Audit\AuditServicesFactory;
|
||||
use MintyPHP\Service\Audit\UserLifecycleAuditService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AuditServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new AuditServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(ApiAuditLogRepository::class, $factory->createApiAuditLogRepository());
|
||||
$this->assertSame($factory->createApiAuditLogRepository(), $factory->createApiAuditLogRepository());
|
||||
|
||||
$this->assertInstanceOf(UserLifecycleAuditRepository::class, $factory->createUserLifecycleAuditRepository());
|
||||
$this->assertSame($factory->createUserLifecycleAuditRepository(), $factory->createUserLifecycleAuditRepository());
|
||||
|
||||
$this->assertInstanceOf(ApiAuditService::class, $factory->createApiAuditService());
|
||||
$this->assertSame($factory->createApiAuditService(), $factory->createApiAuditService());
|
||||
|
||||
$this->assertInstanceOf(UserLifecycleAuditService::class, $factory->createUserLifecycleAuditService());
|
||||
$this->assertSame($factory->createUserLifecycleAuditService(), $factory->createUserLifecycleAuditService());
|
||||
}
|
||||
}
|
||||
72
tests/Service/Audit/ImportAuditServiceTest.php
Normal file
72
tests/Service/Audit/ImportAuditServiceTest.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Audit;
|
||||
|
||||
use MintyPHP\Repository\Audit\ImportAuditRunRepository;
|
||||
use MintyPHP\Service\Audit\ImportAuditService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ImportAuditServiceTest extends TestCase
|
||||
{
|
||||
public function testStartRunNormalizesFilenameAndMappedTargets(): void
|
||||
{
|
||||
$captured = null;
|
||||
$repository = $this->createMock(ImportAuditRunRepository::class);
|
||||
$repository->expects($this->once())
|
||||
->method('createRunning')
|
||||
->with($this->callback(function (array $payload) use (&$captured): bool {
|
||||
$captured = $payload;
|
||||
return true;
|
||||
}))
|
||||
->willReturn(77);
|
||||
|
||||
$service = new ImportAuditService($repository);
|
||||
$runId = $service->startRun('users', ['email', 'first_name', 'email'], '/tmp/../users.csv', 12, 5);
|
||||
|
||||
$this->assertSame(77, $runId);
|
||||
$this->assertSame('users.csv', (string) ($captured['source_filename'] ?? ''));
|
||||
$this->assertSame('email,first_name', (string) ($captured['mapped_targets_csv'] ?? ''));
|
||||
$this->assertSame(12, (int) ($captured['user_id'] ?? 0));
|
||||
$this->assertSame(5, (int) ($captured['current_tenant_id'] ?? 0));
|
||||
}
|
||||
|
||||
public function testFinishRunDerivesPartialStatusAndAggregatesErrors(): void
|
||||
{
|
||||
$captured = null;
|
||||
$repository = $this->createMock(ImportAuditRunRepository::class);
|
||||
$repository->expects($this->once())
|
||||
->method('finishById')
|
||||
->with(
|
||||
88,
|
||||
$this->callback(function (array $payload) use (&$captured): bool {
|
||||
$captured = $payload;
|
||||
return true;
|
||||
})
|
||||
)
|
||||
->willReturn(true);
|
||||
|
||||
$service = new ImportAuditService($repository);
|
||||
$service->finishRun(88, [
|
||||
'ok' => true,
|
||||
'processed' => 3,
|
||||
'created' => 1,
|
||||
'skipped' => 1,
|
||||
'failed' => 1,
|
||||
'errors' => [
|
||||
['code' => 'email_exists'],
|
||||
['code' => 'email_exists'],
|
||||
['code' => 'invalid_email'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertSame('partial', (string) ($captured['status'] ?? ''));
|
||||
$this->assertSame(3, (int) ($captured['rows_total'] ?? 0));
|
||||
$this->assertSame(1, (int) ($captured['created_count'] ?? 0));
|
||||
$this->assertSame(1, (int) ($captured['skipped_count'] ?? 0));
|
||||
$this->assertSame(1, (int) ($captured['failed_count'] ?? 0));
|
||||
$errorJson = (string) ($captured['error_codes_json'] ?? '');
|
||||
$this->assertStringContainsString('email_exists', $errorJson);
|
||||
$this->assertStringContainsString('invalid_email', $errorJson);
|
||||
}
|
||||
}
|
||||
|
||||
68
tests/Service/Auth/AuthServicesFactoryTest.php
Normal file
68
tests/Service/Auth/AuthServicesFactoryTest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Auth;
|
||||
|
||||
use MintyPHP\Service\Auth\ApiTokenService;
|
||||
use MintyPHP\Service\Auth\AuthService;
|
||||
use MintyPHP\Service\Auth\AuthServicesFactory;
|
||||
use MintyPHP\Service\Auth\EmailVerificationService;
|
||||
use MintyPHP\Service\Auth\MicrosoftOidcService;
|
||||
use MintyPHP\Service\Auth\MicrosoftOidcStateStoreService;
|
||||
use MintyPHP\Service\Auth\PasswordResetService;
|
||||
use MintyPHP\Service\Auth\RememberMeService;
|
||||
use MintyPHP\Service\Auth\SsoUserLinkService;
|
||||
use MintyPHP\Service\Auth\TenantSsoService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AuthServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new AuthServicesFactory();
|
||||
|
||||
$apiTokenServiceA = $factory->createApiTokenService();
|
||||
$apiTokenServiceB = $factory->createApiTokenService();
|
||||
$this->assertInstanceOf(ApiTokenService::class, $apiTokenServiceA);
|
||||
$this->assertSame($apiTokenServiceA, $apiTokenServiceB);
|
||||
|
||||
$passwordResetServiceA = $factory->createPasswordResetService();
|
||||
$passwordResetServiceB = $factory->createPasswordResetService();
|
||||
$this->assertInstanceOf(PasswordResetService::class, $passwordResetServiceA);
|
||||
$this->assertSame($passwordResetServiceA, $passwordResetServiceB);
|
||||
|
||||
$emailVerificationServiceA = $factory->createEmailVerificationService();
|
||||
$emailVerificationServiceB = $factory->createEmailVerificationService();
|
||||
$this->assertInstanceOf(EmailVerificationService::class, $emailVerificationServiceA);
|
||||
$this->assertSame($emailVerificationServiceA, $emailVerificationServiceB);
|
||||
|
||||
$rememberMeServiceA = $factory->createRememberMeService();
|
||||
$rememberMeServiceB = $factory->createRememberMeService();
|
||||
$this->assertInstanceOf(RememberMeService::class, $rememberMeServiceA);
|
||||
$this->assertSame($rememberMeServiceA, $rememberMeServiceB);
|
||||
|
||||
$authServiceA = $factory->createAuthService();
|
||||
$authServiceB = $factory->createAuthService();
|
||||
$this->assertInstanceOf(AuthService::class, $authServiceA);
|
||||
$this->assertSame($authServiceA, $authServiceB);
|
||||
|
||||
$ssoUserLinkServiceA = $factory->createSsoUserLinkService();
|
||||
$ssoUserLinkServiceB = $factory->createSsoUserLinkService();
|
||||
$this->assertInstanceOf(SsoUserLinkService::class, $ssoUserLinkServiceA);
|
||||
$this->assertSame($ssoUserLinkServiceA, $ssoUserLinkServiceB);
|
||||
|
||||
$tenantSsoServiceA = $factory->createTenantSsoService();
|
||||
$tenantSsoServiceB = $factory->createTenantSsoService();
|
||||
$this->assertInstanceOf(TenantSsoService::class, $tenantSsoServiceA);
|
||||
$this->assertSame($tenantSsoServiceA, $tenantSsoServiceB);
|
||||
|
||||
$stateStoreA = $factory->createMicrosoftOidcStateStore();
|
||||
$stateStoreB = $factory->createMicrosoftOidcStateStore();
|
||||
$this->assertInstanceOf(MicrosoftOidcStateStoreService::class, $stateStoreA);
|
||||
$this->assertSame($stateStoreA, $stateStoreB);
|
||||
|
||||
$microsoftOidcServiceA = $factory->createMicrosoftOidcService();
|
||||
$microsoftOidcServiceB = $factory->createMicrosoftOidcService();
|
||||
$this->assertInstanceOf(MicrosoftOidcService::class, $microsoftOidcServiceA);
|
||||
$this->assertSame($microsoftOidcServiceA, $microsoftOidcServiceB);
|
||||
}
|
||||
}
|
||||
70
tests/Service/Auth/MicrosoftOidcStateStoreServiceTest.php
Normal file
70
tests/Service/Auth/MicrosoftOidcStateStoreServiceTest.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Auth;
|
||||
|
||||
use MintyPHP\Service\Auth\MicrosoftOidcStateStoreService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MicrosoftOidcStateStoreServiceTest extends TestCase
|
||||
{
|
||||
private array $previousSession = [];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->previousSession = $_SESSION ?? [];
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$_SESSION = $this->previousSession;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testStoreAndConsumeIsSingleUse(): void
|
||||
{
|
||||
$store = new MicrosoftOidcStateStoreService(600, 50);
|
||||
$store->store('state-a', ['tenant_id' => 5, 'nonce' => 'n1']);
|
||||
|
||||
$first = $store->consume('state-a');
|
||||
$this->assertTrue($first['ok'] ?? false);
|
||||
$this->assertSame(5, (int) (($first['entry']['tenant_id'] ?? 0)));
|
||||
|
||||
$second = $store->consume('state-a');
|
||||
$this->assertFalse($second['ok'] ?? true);
|
||||
$this->assertSame('state_invalid', (string) ($second['error'] ?? ''));
|
||||
}
|
||||
|
||||
public function testConsumeReturnsExpiredForOldState(): void
|
||||
{
|
||||
$store = new MicrosoftOidcStateStoreService(1, 50);
|
||||
$store->store('state-old', [
|
||||
'tenant_id' => 9,
|
||||
'created_at' => time() - 120,
|
||||
]);
|
||||
|
||||
$result = $store->consume('state-old');
|
||||
$this->assertFalse($result['ok'] ?? true);
|
||||
$this->assertSame('state_expired', (string) ($result['error'] ?? ''));
|
||||
}
|
||||
|
||||
public function testStoreRespectsMaxEntriesAndPrunesOldStates(): void
|
||||
{
|
||||
$store = new MicrosoftOidcStateStoreService(600, 2);
|
||||
|
||||
$store->store('state-1', ['created_at' => time() - 3]);
|
||||
$store->store('state-2', ['created_at' => time() - 2]);
|
||||
$store->store('state-3', ['created_at' => time() - 1]);
|
||||
|
||||
$invalid = $store->consume('state-1');
|
||||
$this->assertFalse($invalid['ok'] ?? true);
|
||||
$this->assertSame('state_invalid', (string) ($invalid['error'] ?? ''));
|
||||
|
||||
$valid2 = $store->consume('state-2');
|
||||
$this->assertTrue($valid2['ok'] ?? false);
|
||||
|
||||
$valid3 = $store->consume('state-3');
|
||||
$this->assertTrue($valid3['ok'] ?? false);
|
||||
}
|
||||
}
|
||||
22
tests/Service/Branding/BrandingServicesFactoryTest.php
Normal file
22
tests/Service/Branding/BrandingServicesFactoryTest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Branding;
|
||||
|
||||
use MintyPHP\Service\Branding\BrandingFaviconService;
|
||||
use MintyPHP\Service\Branding\BrandingLogoService;
|
||||
use MintyPHP\Service\Branding\BrandingServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BrandingServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new BrandingServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(BrandingLogoService::class, $factory->createBrandingLogoService());
|
||||
$this->assertSame($factory->createBrandingLogoService(), $factory->createBrandingLogoService());
|
||||
|
||||
$this->assertInstanceOf(BrandingFaviconService::class, $factory->createBrandingFaviconService());
|
||||
$this->assertSame($factory->createBrandingFaviconService(), $factory->createBrandingFaviconService());
|
||||
}
|
||||
}
|
||||
46
tests/Service/Directory/DirectoryServicesFactoryTest.php
Normal file
46
tests/Service/Directory/DirectoryServicesFactoryTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Directory;
|
||||
|
||||
use MintyPHP\Repository\Access\RoleRepository;
|
||||
use MintyPHP\Repository\Org\DepartmentRepository;
|
||||
use MintyPHP\Repository\Tenant\TenantRepository;
|
||||
use MintyPHP\Service\Access\RoleService;
|
||||
use MintyPHP\Service\Directory\DirectoryScopeGateway;
|
||||
use MintyPHP\Service\Directory\DirectoryServicesFactory;
|
||||
use MintyPHP\Service\Directory\DirectorySettingsGateway;
|
||||
use MintyPHP\Service\Org\DepartmentService;
|
||||
use MintyPHP\Service\Tenant\TenantService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DirectoryServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new DirectoryServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(TenantRepository::class, $factory->createTenantRepository());
|
||||
$this->assertSame($factory->createTenantRepository(), $factory->createTenantRepository());
|
||||
|
||||
$this->assertInstanceOf(DepartmentRepository::class, $factory->createDepartmentRepository());
|
||||
$this->assertSame($factory->createDepartmentRepository(), $factory->createDepartmentRepository());
|
||||
|
||||
$this->assertInstanceOf(RoleRepository::class, $factory->createRoleRepository());
|
||||
$this->assertSame($factory->createRoleRepository(), $factory->createRoleRepository());
|
||||
|
||||
$this->assertInstanceOf(DirectorySettingsGateway::class, $factory->createDirectorySettingsGateway());
|
||||
$this->assertSame($factory->createDirectorySettingsGateway(), $factory->createDirectorySettingsGateway());
|
||||
|
||||
$this->assertInstanceOf(DirectoryScopeGateway::class, $factory->createDirectoryScopeGateway());
|
||||
$this->assertSame($factory->createDirectoryScopeGateway(), $factory->createDirectoryScopeGateway());
|
||||
|
||||
$this->assertInstanceOf(TenantService::class, $factory->createTenantService());
|
||||
$this->assertSame($factory->createTenantService(), $factory->createTenantService());
|
||||
|
||||
$this->assertInstanceOf(DepartmentService::class, $factory->createDepartmentService());
|
||||
$this->assertSame($factory->createDepartmentService(), $factory->createDepartmentService());
|
||||
|
||||
$this->assertInstanceOf(RoleService::class, $factory->createRoleService());
|
||||
$this->assertSame($factory->createRoleService(), $factory->createRoleService());
|
||||
}
|
||||
}
|
||||
65
tests/Service/Import/CsvReaderServiceTest.php
Normal file
65
tests/Service/Import/CsvReaderServiceTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Import;
|
||||
|
||||
use MintyPHP\Service\Import\CsvReaderService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CsvReaderServiceTest extends TestCase
|
||||
{
|
||||
private array $tmpFiles = [];
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
foreach ($this->tmpFiles as $file) {
|
||||
if (is_file($file)) {
|
||||
@unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testAnalyzeDetectsSemicolonAndBom(): void
|
||||
{
|
||||
$file = $this->newTmpFile("\xEF\xBB\xBFfirst_name;email\nMax;max@example.com\n");
|
||||
$service = new CsvReaderService();
|
||||
|
||||
$result = $service->analyze($file, 20000);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(';', $result['delimiter']);
|
||||
$this->assertSame(['first_name', 'email'], $result['headers']);
|
||||
$this->assertSame(1, $result['rows_total']);
|
||||
}
|
||||
|
||||
public function testAnalyzeReturnsInvalidHeaderForDuplicateColumns(): void
|
||||
{
|
||||
$file = $this->newTmpFile("email,email\nfoo@example.com,bar@example.com\n");
|
||||
$service = new CsvReaderService();
|
||||
|
||||
$result = $service->analyze($file, 20000);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('invalid_csv_header', $result['code']);
|
||||
}
|
||||
|
||||
public function testAnalyzeReturnsEmptyCsvWhenNoDataRows(): void
|
||||
{
|
||||
$file = $this->newTmpFile("email\n");
|
||||
$service = new CsvReaderService();
|
||||
|
||||
$result = $service->analyze($file, 20000);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('empty_csv', $result['code']);
|
||||
}
|
||||
|
||||
private function newTmpFile(string $content): string
|
||||
{
|
||||
$file = tempnam(sys_get_temp_dir(), 'im_csv_');
|
||||
$this->assertNotFalse($file);
|
||||
file_put_contents($file, $content);
|
||||
$this->tmpFiles[] = $file;
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
213
tests/Service/Import/ImportServiceTest.php
Normal file
213
tests/Service/Import/ImportServiceTest.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Import;
|
||||
|
||||
use MintyPHP\Service\Audit\ImportAuditService;
|
||||
use MintyPHP\Service\Access\PermissionGateway;
|
||||
use MintyPHP\Service\Import\CsvReaderService;
|
||||
use MintyPHP\Service\Import\ImportService;
|
||||
use MintyPHP\Service\Import\ImportStateStoreService;
|
||||
use MintyPHP\Service\Import\ImportTempFileService;
|
||||
use MintyPHP\Service\Import\Profile\ImportProfileInterface;
|
||||
use MintyPHP\Service\Settings\SettingGateway;
|
||||
use MintyPHP\Service\User\UserScopeGateway;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ImportServiceTest extends TestCase
|
||||
{
|
||||
public function testAnalyzeUploadBuildsTokenStateAndAutoMapping(): void
|
||||
{
|
||||
$profile = new ImportServiceTestProfile('users', false);
|
||||
$csvReader = $this->createMock(CsvReaderService::class);
|
||||
$csvReader->expects($this->once())
|
||||
->method('analyze')
|
||||
->willReturn([
|
||||
'ok' => true,
|
||||
'delimiter' => ';',
|
||||
'headers' => ['E-Mail'],
|
||||
'rows_total' => 2,
|
||||
]);
|
||||
|
||||
$tempFileService = $this->createMock(ImportTempFileService::class);
|
||||
$tempFileService->expects($this->once())
|
||||
->method('storeUploadedFile')
|
||||
->willReturn(['ok' => true, 'path' => '/tmp/import.csv']);
|
||||
|
||||
$stateStore = $this->createMock(ImportStateStoreService::class);
|
||||
$stateStore->expects($this->once())
|
||||
->method('setState')
|
||||
->with(
|
||||
$this->isString(),
|
||||
$this->callback(function (array $state): bool {
|
||||
return (string) ($state['path'] ?? '') === '/tmp/import.csv'
|
||||
&& (string) ($state['type'] ?? '') === 'users'
|
||||
&& is_int($state['created_at'] ?? null);
|
||||
})
|
||||
);
|
||||
|
||||
$service = $this->newImportService($profile, $csvReader, $tempFileService, $stateStore);
|
||||
$result = $service->analyzeUpload([
|
||||
'tmp_name' => '/tmp/php-upload',
|
||||
'name' => 'users.csv',
|
||||
'size' => 10,
|
||||
'error' => UPLOAD_ERR_OK,
|
||||
], 'users', 7);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(';', $result['delimiter']);
|
||||
$this->assertSame(['E-Mail' => 'email'], $result['auto_mapping']);
|
||||
}
|
||||
|
||||
public function testPreviewFailsWhenRequiredMappingIsMissing(): void
|
||||
{
|
||||
$profile = new ImportServiceTestProfile('users', false);
|
||||
$stateStore = $this->createMock(ImportStateStoreService::class);
|
||||
$stateStore->method('getState')->willReturn([
|
||||
'token' => 'tok',
|
||||
'type' => 'users',
|
||||
'headers' => ['email'],
|
||||
'path' => '/tmp/import.csv',
|
||||
'delimiter' => ',',
|
||||
'user_id' => 5,
|
||||
'created_at' => time(),
|
||||
]);
|
||||
|
||||
$service = $this->newImportService($profile, null, null, $stateStore);
|
||||
$result = $service->preview('tok', ['mapping' => []], 5);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('mapping_required_missing', $result['code']);
|
||||
}
|
||||
|
||||
public function testPreviewFailsWhenAssignmentPermissionIsMissing(): void
|
||||
{
|
||||
$profile = new ImportServiceTestProfile('users', true);
|
||||
$stateStore = $this->createMock(ImportStateStoreService::class);
|
||||
$stateStore->method('getState')->willReturn([
|
||||
'token' => 'tok',
|
||||
'type' => 'users',
|
||||
'headers' => ['email', 'tenant'],
|
||||
'path' => '/tmp/import.csv',
|
||||
'delimiter' => ',',
|
||||
'user_id' => 0,
|
||||
'created_at' => time(),
|
||||
]);
|
||||
|
||||
$service = $this->newImportService($profile, null, null, $stateStore);
|
||||
$result = $service->preview('tok', ['mapping' => ['email' => 'email', 'tenant' => 'tenant']], 0);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('assignment_permission_required', $result['code']);
|
||||
}
|
||||
|
||||
public function testMessageForCodeUsesKnownMappingAndFallback(): void
|
||||
{
|
||||
$service = $this->newImportService(new ImportServiceTestProfile('users', false));
|
||||
|
||||
$this->assertSame('Invalid file type', $service->messageForCode('invalid_file_type'));
|
||||
$this->assertSame('Unexpected error during import', $service->messageForCode('something_unknown'));
|
||||
}
|
||||
|
||||
private function newImportService(
|
||||
ImportProfileInterface $profile,
|
||||
?CsvReaderService $csvReader = null,
|
||||
?ImportTempFileService $tempFileService = null,
|
||||
?ImportStateStoreService $stateStore = null
|
||||
): ImportService {
|
||||
$csvReader = $csvReader ?? $this->createMock(CsvReaderService::class);
|
||||
$tempFileService = $tempFileService ?? $this->createMock(ImportTempFileService::class);
|
||||
$auditService = $this->createMock(ImportAuditService::class);
|
||||
$stateStore = $stateStore ?? $this->createMock(ImportStateStoreService::class);
|
||||
$permissionGateway = $this->createMock(PermissionGateway::class);
|
||||
$permissionGateway->method('userHas')->willReturn(false);
|
||||
$settingService = $this->createMock('MintyPHP\\Service\\Settings\\SettingService');
|
||||
$settingGateway = new SettingGateway($settingService);
|
||||
$userScopeGateway = $this->createMock(UserScopeGateway::class);
|
||||
$userScopeGateway->method('hasGlobalAccess')->willReturn(false);
|
||||
$userScopeGateway->method('getUserTenantIds')->willReturn([]);
|
||||
|
||||
return new ImportService(
|
||||
$csvReader,
|
||||
$tempFileService,
|
||||
$auditService,
|
||||
$stateStore,
|
||||
[$profile->key() => $profile],
|
||||
$permissionGateway,
|
||||
$settingGateway,
|
||||
$userScopeGateway
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImportServiceTestProfile implements ImportProfileInterface
|
||||
{
|
||||
public function __construct(private readonly string $key, private readonly bool $requiresAssignmentPermission)
|
||||
{
|
||||
}
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Test';
|
||||
}
|
||||
|
||||
public function allowedTargets(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'Email',
|
||||
'tenant' => 'Tenant',
|
||||
];
|
||||
}
|
||||
|
||||
public function requiredTargets(): array
|
||||
{
|
||||
return ['email'];
|
||||
}
|
||||
|
||||
public function autoMapAliases(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['e-mail', 'email'],
|
||||
'tenant' => ['tenant'],
|
||||
];
|
||||
}
|
||||
|
||||
public function supportsAssignments(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function requiresAssignmentPermission(): bool
|
||||
{
|
||||
return $this->requiresAssignmentPermission;
|
||||
}
|
||||
|
||||
public function validateMappedRow(array $mapped, array $context): array
|
||||
{
|
||||
return ['ok' => true, 'normalized' => $mapped, 'errors' => []];
|
||||
}
|
||||
|
||||
public function dryRunRow(array $normalized, array $context): array
|
||||
{
|
||||
return ['status' => 'would_create'];
|
||||
}
|
||||
|
||||
public function commitRow(array $normalized, array $context): array
|
||||
{
|
||||
return ['status' => 'created'];
|
||||
}
|
||||
|
||||
public function inFileDuplicateKey(array $normalized): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function rowIdentifier(array $normalized): string
|
||||
{
|
||||
return (string) ($normalized['email'] ?? '');
|
||||
}
|
||||
}
|
||||
33
tests/Service/Import/ImportServicesFactoryTest.php
Normal file
33
tests/Service/Import/ImportServicesFactoryTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Import;
|
||||
|
||||
use MintyPHP\Service\Audit\ImportAuditService;
|
||||
use MintyPHP\Service\Import\ImportService;
|
||||
use MintyPHP\Service\Import\ImportServicesFactory;
|
||||
use MintyPHP\Service\Import\ImportStateStoreService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ImportServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new ImportServicesFactory();
|
||||
|
||||
$importServiceA = $factory->createImportService();
|
||||
$importServiceB = $factory->createImportService();
|
||||
$this->assertInstanceOf(ImportService::class, $importServiceA);
|
||||
$this->assertSame($importServiceA, $importServiceB);
|
||||
|
||||
$auditServiceA = $factory->createImportAuditService();
|
||||
$auditServiceB = $factory->createImportAuditService();
|
||||
$this->assertInstanceOf(ImportAuditService::class, $auditServiceA);
|
||||
$this->assertSame($auditServiceA, $auditServiceB);
|
||||
|
||||
$stateStoreA = $factory->createImportStateStoreService();
|
||||
$stateStoreB = $factory->createImportStateStoreService();
|
||||
$this->assertInstanceOf(ImportStateStoreService::class, $stateStoreA);
|
||||
$this->assertSame($stateStoreA, $stateStoreB);
|
||||
}
|
||||
}
|
||||
|
||||
47
tests/Service/Import/ImportStateStoreServiceTest.php
Normal file
47
tests/Service/Import/ImportStateStoreServiceTest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Import;
|
||||
|
||||
use MintyPHP\Service\Import\ImportStateStoreService;
|
||||
use MintyPHP\Service\Import\ImportTempFileService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ImportStateStoreServiceTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
public function testSetGetAndClearState(): void
|
||||
{
|
||||
$tempFileService = $this->createMock(ImportTempFileService::class);
|
||||
$stateStore = new ImportStateStoreService($tempFileService);
|
||||
|
||||
$stateStore->setState('token_a', ['token' => 'token_a', 'created_at' => time(), 'path' => '/tmp/a.csv']);
|
||||
$loaded = $stateStore->getState('token_a');
|
||||
$this->assertIsArray($loaded);
|
||||
$this->assertSame('token_a', $loaded['token']);
|
||||
|
||||
$stateStore->clearState('token_a');
|
||||
$this->assertNull($stateStore->getState('token_a'));
|
||||
}
|
||||
|
||||
public function testExpiredStateGetsDeletedAndReturnsNull(): void
|
||||
{
|
||||
$tempFileService = $this->createMock(ImportTempFileService::class);
|
||||
$tempFileService->expects($this->once())
|
||||
->method('delete')
|
||||
->with('/tmp/expired.csv');
|
||||
|
||||
$stateStore = new ImportStateStoreService($tempFileService);
|
||||
$stateStore->setState('token_b', [
|
||||
'token' => 'token_b',
|
||||
'created_at' => time() - 7201,
|
||||
'path' => '/tmp/expired.csv',
|
||||
]);
|
||||
|
||||
$this->assertNull($stateStore->getState('token_b'));
|
||||
}
|
||||
}
|
||||
|
||||
26
tests/Service/Mail/MailServicesFactoryTest.php
Normal file
26
tests/Service/Mail/MailServicesFactoryTest.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Mail;
|
||||
|
||||
use MintyPHP\Repository\Mail\MailLogRepository;
|
||||
use MintyPHP\Service\Mail\MailLogService;
|
||||
use MintyPHP\Service\Mail\MailService;
|
||||
use MintyPHP\Service\Mail\MailServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MailServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new MailServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(MailLogRepository::class, $factory->createMailLogRepository());
|
||||
$this->assertSame($factory->createMailLogRepository(), $factory->createMailLogRepository());
|
||||
|
||||
$this->assertInstanceOf(MailService::class, $factory->createMailService());
|
||||
$this->assertSame($factory->createMailService(), $factory->createMailService());
|
||||
|
||||
$this->assertInstanceOf(MailLogService::class, $factory->createMailLogService());
|
||||
$this->assertSame($factory->createMailLogService(), $factory->createMailLogService());
|
||||
}
|
||||
}
|
||||
53
tests/Service/Scheduler/ScheduleCalculatorTest.php
Normal file
53
tests/Service/Scheduler/ScheduleCalculatorTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Scheduler;
|
||||
|
||||
use MintyPHP\Service\Scheduler\ScheduleCalculator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ScheduleCalculatorTest extends TestCase
|
||||
{
|
||||
public function testCalculateNextRunUtcDailySameDayWhenTimeIsInFuture(): void
|
||||
{
|
||||
$calculator = new ScheduleCalculator();
|
||||
$job = [
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => 1,
|
||||
'timezone' => 'UTC',
|
||||
'schedule_time' => '12:00',
|
||||
];
|
||||
$reference = new \DateTimeImmutable('2026-02-22 10:30:00', new \DateTimeZone('UTC'));
|
||||
|
||||
$next = $calculator->calculateNextRunUtc($job, $reference);
|
||||
|
||||
$this->assertInstanceOf(\DateTimeImmutable::class, $next);
|
||||
$this->assertSame('2026-02-22 12:00:00', $next?->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function testCalculateNextRunUtcDailyMovesToNextDayWhenTimeAlreadyPassed(): void
|
||||
{
|
||||
$calculator = new ScheduleCalculator();
|
||||
$job = [
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => 1,
|
||||
'timezone' => 'UTC',
|
||||
'schedule_time' => '08:00',
|
||||
];
|
||||
$reference = new \DateTimeImmutable('2026-02-22 10:30:00', new \DateTimeZone('UTC'));
|
||||
|
||||
$next = $calculator->calculateNextRunUtc($job, $reference);
|
||||
|
||||
$this->assertInstanceOf(\DateTimeImmutable::class, $next);
|
||||
$this->assertSame('2026-02-23 08:00:00', $next?->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function testNormalizeWeekdaysCsvRemovesDuplicatesAndInvalidValues(): void
|
||||
{
|
||||
$calculator = new ScheduleCalculator();
|
||||
|
||||
$normalized = $calculator->normalizeWeekdaysCsv(['7', '3', '3', '9', '0', '1']);
|
||||
|
||||
$this->assertSame('1,3,7', $normalized);
|
||||
}
|
||||
}
|
||||
|
||||
126
tests/Service/Scheduler/ScheduledJobServiceTest.php
Normal file
126
tests/Service/Scheduler/ScheduledJobServiceTest.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Scheduler;
|
||||
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRepository;
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRunRepository;
|
||||
use MintyPHP\Service\Scheduler\ScheduleCalculator;
|
||||
use MintyPHP\Service\Scheduler\ScheduledJobRegistry;
|
||||
use MintyPHP\Service\Scheduler\ScheduledJobService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ScheduledJobServiceTest extends TestCase
|
||||
{
|
||||
public function testUpdateFromAdminReturnsValidationErrorForWeeklyWithoutWeekdays(): void
|
||||
{
|
||||
$jobRepository = $this->createMock(ScheduledJobRepository::class);
|
||||
$runRepository = $this->createMock(ScheduledJobRunRepository::class);
|
||||
$registry = $this->createMock(ScheduledJobRegistry::class);
|
||||
$calculator = new ScheduleCalculator();
|
||||
|
||||
$registry->expects($this->once())->method('definitions')->willReturn([]);
|
||||
$jobRepository->expects($this->once())->method('find')->with(10)->willReturn([
|
||||
'id' => 10,
|
||||
'job_key' => 'user_lifecycle_run',
|
||||
'label' => 'User lifecycle run',
|
||||
'description' => '',
|
||||
'enabled' => 1,
|
||||
'timezone' => 'UTC',
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => 1,
|
||||
'schedule_time' => '02:15',
|
||||
'schedule_weekdays_csv' => null,
|
||||
'catchup_once' => 1,
|
||||
]);
|
||||
$registry->expects($this->once())->method('get')->with('user_lifecycle_run')->willReturn([
|
||||
'label' => 'User lifecycle run',
|
||||
'description' => '',
|
||||
'allowed_schedule_types' => ['hourly', 'daily', 'weekly'],
|
||||
]);
|
||||
$jobRepository->expects($this->never())->method('updateJobMeta');
|
||||
|
||||
$service = new ScheduledJobService($jobRepository, $runRepository, $registry, $calculator);
|
||||
$result = $service->updateFromAdmin(10, [
|
||||
'enabled' => '1',
|
||||
'timezone' => 'UTC',
|
||||
'schedule_type' => 'weekly',
|
||||
'schedule_interval' => '1',
|
||||
'schedule_time' => '03:00',
|
||||
]);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertNotEmpty($result['errors']);
|
||||
}
|
||||
|
||||
public function testUpdateFromAdminPersistsNormalizedValuesAndReturnsFreshJob(): void
|
||||
{
|
||||
$jobRepository = $this->createMock(ScheduledJobRepository::class);
|
||||
$runRepository = $this->createMock(ScheduledJobRunRepository::class);
|
||||
$registry = $this->createMock(ScheduledJobRegistry::class);
|
||||
$calculator = new ScheduleCalculator();
|
||||
|
||||
$registry->expects($this->exactly(2))->method('definitions')->willReturn([]);
|
||||
$jobRepository->expects($this->exactly(2))->method('find')->with(11)->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
'id' => 11,
|
||||
'job_key' => 'user_lifecycle_run',
|
||||
'label' => 'User lifecycle run',
|
||||
'description' => 'Runs lifecycle',
|
||||
'enabled' => 1,
|
||||
'timezone' => 'UTC',
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => 1,
|
||||
'schedule_time' => '02:15',
|
||||
'schedule_weekdays_csv' => null,
|
||||
'catchup_once' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 11,
|
||||
'job_key' => 'user_lifecycle_run',
|
||||
'label' => 'User lifecycle run',
|
||||
'description' => 'Runs lifecycle',
|
||||
'enabled' => 0,
|
||||
'timezone' => 'UTC',
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => 1,
|
||||
'schedule_time' => '04:00',
|
||||
'schedule_weekdays_csv' => null,
|
||||
'catchup_once' => 0,
|
||||
'next_run_at' => null,
|
||||
]
|
||||
);
|
||||
$registry->expects($this->once())->method('get')->with('user_lifecycle_run')->willReturn([
|
||||
'label' => 'User lifecycle run',
|
||||
'description' => 'Runs lifecycle',
|
||||
'allowed_schedule_types' => ['hourly', 'daily', 'weekly'],
|
||||
]);
|
||||
$capturedUpdate = null;
|
||||
$jobRepository->expects($this->once())
|
||||
->method('updateJobMeta')
|
||||
->with(11, $this->anything())
|
||||
->willReturnCallback(function (int $id, array $data) use (&$capturedUpdate): bool {
|
||||
$capturedUpdate = $data;
|
||||
return true;
|
||||
});
|
||||
|
||||
$service = new ScheduledJobService($jobRepository, $runRepository, $registry, $calculator);
|
||||
$result = $service->updateFromAdmin(11, [
|
||||
'timezone' => 'UTC',
|
||||
'schedule_type' => 'daily',
|
||||
'schedule_interval' => '1',
|
||||
'schedule_time' => '04:00',
|
||||
]);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(11, (int) (($result['job'] ?? [])['id'] ?? 0));
|
||||
$this->assertSame(0, (int) (($result['job'] ?? [])['enabled'] ?? 1));
|
||||
$this->assertIsArray($capturedUpdate);
|
||||
$this->assertSame(0, (int) ($capturedUpdate['enabled'] ?? -1));
|
||||
$this->assertSame('UTC', (string) ($capturedUpdate['timezone'] ?? ''));
|
||||
$this->assertSame('daily', (string) ($capturedUpdate['schedule_type'] ?? ''));
|
||||
$this->assertSame(1, (int) ($capturedUpdate['schedule_interval'] ?? -1));
|
||||
$this->assertSame('04:00', (string) ($capturedUpdate['schedule_time'] ?? ''));
|
||||
$this->assertSame(0, (int) ($capturedUpdate['catchup_once'] ?? -1));
|
||||
$this->assertNull($capturedUpdate['next_run_at'] ?? null);
|
||||
}
|
||||
}
|
||||
35
tests/Service/Scheduler/SchedulerRunServiceTest.php
Normal file
35
tests/Service/Scheduler/SchedulerRunServiceTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Scheduler;
|
||||
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRepository;
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRunRepository;
|
||||
use MintyPHP\Repository\Scheduler\SchedulerRuntimeRepository;
|
||||
use MintyPHP\Service\Scheduler\ScheduleCalculator;
|
||||
use MintyPHP\Service\Scheduler\ScheduledJobRegistry;
|
||||
use MintyPHP\Service\Scheduler\ScheduledJobService;
|
||||
use MintyPHP\Service\Scheduler\SchedulerRunService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SchedulerRunServiceTest extends TestCase
|
||||
{
|
||||
public function testRunJobNowRejectsInvalidRequest(): void
|
||||
{
|
||||
$scheduledJobService = $this->createMock(ScheduledJobService::class);
|
||||
$scheduledJobService->expects($this->once())->method('ensureSystemJobs');
|
||||
|
||||
$service = new SchedulerRunService(
|
||||
$scheduledJobService,
|
||||
$this->createMock(ScheduledJobRepository::class),
|
||||
$this->createMock(ScheduledJobRunRepository::class),
|
||||
$this->createMock(SchedulerRuntimeRepository::class),
|
||||
$this->createMock(ScheduledJobRegistry::class),
|
||||
new ScheduleCalculator()
|
||||
);
|
||||
|
||||
$result = $service->runJobNow(0, 0);
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertSame('invalid_request', $result['error']);
|
||||
}
|
||||
}
|
||||
33
tests/Service/Scheduler/SchedulerServicesFactoryTest.php
Normal file
33
tests/Service/Scheduler/SchedulerServicesFactoryTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Scheduler;
|
||||
|
||||
use MintyPHP\Service\Scheduler\ScheduledJobService;
|
||||
use MintyPHP\Service\Scheduler\SchedulerRunService;
|
||||
use MintyPHP\Service\Scheduler\SchedulerServicesFactory;
|
||||
use MintyPHP\Service\User\UserLifecycleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SchedulerServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new SchedulerServicesFactory();
|
||||
|
||||
$scheduledJobServiceA = $factory->createScheduledJobService();
|
||||
$scheduledJobServiceB = $factory->createScheduledJobService();
|
||||
$this->assertInstanceOf(ScheduledJobService::class, $scheduledJobServiceA);
|
||||
$this->assertSame($scheduledJobServiceA, $scheduledJobServiceB);
|
||||
|
||||
$schedulerRunServiceA = $factory->createSchedulerRunService();
|
||||
$schedulerRunServiceB = $factory->createSchedulerRunService();
|
||||
$this->assertInstanceOf(SchedulerRunService::class, $schedulerRunServiceA);
|
||||
$this->assertSame($schedulerRunServiceA, $schedulerRunServiceB);
|
||||
|
||||
$userLifecycleServiceA = $factory->createUserLifecycleService();
|
||||
$userLifecycleServiceB = $factory->createUserLifecycleService();
|
||||
$this->assertInstanceOf(UserLifecycleService::class, $userLifecycleServiceA);
|
||||
$this->assertSame($userLifecycleServiceA, $userLifecycleServiceB);
|
||||
}
|
||||
}
|
||||
|
||||
56
tests/Service/Scheduler/UserLifecycleJobHandlerTest.php
Normal file
56
tests/Service/Scheduler/UserLifecycleJobHandlerTest.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Scheduler;
|
||||
|
||||
use MintyPHP\Service\Scheduler\Handler\UserLifecycleJobHandler;
|
||||
use MintyPHP\Service\User\UserLifecycleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserLifecycleJobHandlerTest extends TestCase
|
||||
{
|
||||
public function testExecuteReturnsSuccessWithMappedResult(): void
|
||||
{
|
||||
$lifecycleService = $this->createMock(UserLifecycleService::class);
|
||||
$lifecycleService->expects($this->once())
|
||||
->method('run')
|
||||
->with(42)
|
||||
->willReturn([
|
||||
'ok' => true,
|
||||
'run_uuid' => 'run-1',
|
||||
'deactivated_count' => 3,
|
||||
'deleted_count' => 2,
|
||||
'skipped_count' => 1,
|
||||
'duration_ms' => 99,
|
||||
]);
|
||||
|
||||
$handler = new UserLifecycleJobHandler($lifecycleService);
|
||||
$result = $handler->execute(42);
|
||||
|
||||
$this->assertSame('success', $result['status']);
|
||||
$this->assertNull($result['error_code']);
|
||||
$this->assertSame('run-1', $result['result']['run_uuid']);
|
||||
$this->assertSame(3, $result['result']['deactivated_count']);
|
||||
$this->assertSame(2, $result['result']['deleted_count']);
|
||||
$this->assertSame(1, $result['result']['skipped_count']);
|
||||
$this->assertSame(99, $result['result']['duration_ms']);
|
||||
}
|
||||
|
||||
public function testExecuteReturnsSkippedWhenLifecycleLockIsNotAcquired(): void
|
||||
{
|
||||
$lifecycleService = $this->createMock(UserLifecycleService::class);
|
||||
$lifecycleService->expects($this->once())
|
||||
->method('run')
|
||||
->with(null)
|
||||
->willReturn([
|
||||
'ok' => false,
|
||||
'error' => 'lock_not_acquired',
|
||||
]);
|
||||
|
||||
$handler = new UserLifecycleJobHandler($lifecycleService);
|
||||
$result = $handler->execute(null);
|
||||
|
||||
$this->assertSame('skipped', $result['status']);
|
||||
$this->assertSame('lock_not_acquired', $result['error_code']);
|
||||
}
|
||||
}
|
||||
|
||||
22
tests/Service/Security/SecurityServicesFactoryTest.php
Normal file
22
tests/Service/Security/SecurityServicesFactoryTest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Security;
|
||||
|
||||
use MintyPHP\Repository\Security\RateLimitRepository;
|
||||
use MintyPHP\Service\Security\RateLimiterService;
|
||||
use MintyPHP\Service\Security\SecurityServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SecurityServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new SecurityServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(RateLimitRepository::class, $factory->createRateLimitRepository());
|
||||
$this->assertSame($factory->createRateLimitRepository(), $factory->createRateLimitRepository());
|
||||
|
||||
$this->assertInstanceOf(RateLimiterService::class, $factory->createRateLimiterService());
|
||||
$this->assertSame($factory->createRateLimiterService(), $factory->createRateLimiterService());
|
||||
}
|
||||
}
|
||||
45
tests/Service/Settings/SettingCacheServiceTest.php
Normal file
45
tests/Service/Settings/SettingCacheServiceTest.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Settings;
|
||||
|
||||
use MintyPHP\Service\Settings\SettingCacheService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingCacheServiceTest extends TestCase
|
||||
{
|
||||
public function testGetReturnsNullForMissingKey(): void
|
||||
{
|
||||
$file = $this->tempFilePath();
|
||||
file_put_contents($file, "<?php\nreturn ['app_title' => 'Demo'];\n");
|
||||
|
||||
$service = new SettingCacheService($file);
|
||||
$this->assertNull($service->get('missing'));
|
||||
}
|
||||
|
||||
public function testUpdateWritesAndNormalizesValues(): void
|
||||
{
|
||||
$file = $this->tempFilePath();
|
||||
file_put_contents($file, "<?php\nreturn ['app_title' => 'Old'];\n");
|
||||
|
||||
$service = new SettingCacheService($file);
|
||||
$this->assertTrue($service->update([
|
||||
'app_title' => 'New',
|
||||
'app_locale' => null,
|
||||
]));
|
||||
|
||||
$this->assertSame('New', $service->get('app_title'));
|
||||
$this->assertNull($service->get('app_locale'));
|
||||
}
|
||||
|
||||
private function tempFilePath(): string
|
||||
{
|
||||
$dir = sys_get_temp_dir() . '/mintyphp_settings_cache_test_' . bin2hex(random_bytes(6));
|
||||
mkdir($dir, 0775, true);
|
||||
$file = $dir . '/settings.php';
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
$this->assertDirectoryExists($dir);
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
32
tests/Service/Settings/SettingGatewayTest.php
Normal file
32
tests/Service/Settings/SettingGatewayTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Settings;
|
||||
|
||||
use MintyPHP\Service\Settings\SettingGateway;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingGatewayTest extends TestCase
|
||||
{
|
||||
public function testGetDefaultTenantIdDelegatesToService(): void
|
||||
{
|
||||
$settingService = $this->createMock('MintyPHP\\Service\\Settings\\SettingService');
|
||||
$settingService->expects($this->once())
|
||||
->method('getDefaultTenantId')
|
||||
->willReturn(42);
|
||||
|
||||
$gateway = new SettingGateway($settingService);
|
||||
$this->assertSame(42, $gateway->getDefaultTenantId());
|
||||
}
|
||||
|
||||
public function testSetDefaultRoleIdDelegatesToService(): void
|
||||
{
|
||||
$settingService = $this->createMock('MintyPHP\\Service\\Settings\\SettingService');
|
||||
$settingService->expects($this->once())
|
||||
->method('setDefaultRoleId')
|
||||
->with(7, null)
|
||||
->willReturn(true);
|
||||
|
||||
$gateway = new SettingGateway($settingService);
|
||||
$this->assertTrue($gateway->setDefaultRoleId(7));
|
||||
}
|
||||
}
|
||||
144
tests/Service/Settings/SettingServiceTest.php
Normal file
144
tests/Service/Settings/SettingServiceTest.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Settings;
|
||||
|
||||
use MintyPHP\Repository\Access\RoleRepository;
|
||||
use MintyPHP\Repository\Org\DepartmentRepository;
|
||||
use MintyPHP\Repository\Settings\SettingRepository;
|
||||
use MintyPHP\Repository\Tenant\TenantRepository;
|
||||
use MintyPHP\Service\Settings\SettingService;
|
||||
use MintyPHP\Service\Settings\ThemeConfigService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingServiceTest extends TestCase
|
||||
{
|
||||
public function testSetDefaultTenantIdRejectsUnknownTenant(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->expects($this->never())->method('set');
|
||||
|
||||
$tenantRepository = $this->createMock(TenantRepository::class);
|
||||
$tenantRepository->expects($this->once())->method('find')->with(99)->willReturn(null);
|
||||
|
||||
$service = $this->newService($settingRepository, $tenantRepository);
|
||||
$this->assertFalse($service->setDefaultTenantId(99));
|
||||
}
|
||||
|
||||
public function testSetApiTokenDefaultTtlDaysRejectsAboveCurrentMax(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->method('getValue')->willReturnCallback(
|
||||
static function (string $key): ?string {
|
||||
if ($key === SettingService::API_TOKEN_MAX_TTL_DAYS_KEY) {
|
||||
return '30';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
$settingRepository->expects($this->never())->method('set');
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertFalse($service->setApiTokenDefaultTtlDays(31));
|
||||
}
|
||||
|
||||
public function testSetApiTokenMaxTtlDaysUpdatesDefaultWhenNeeded(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->method('getValue')->willReturnCallback(
|
||||
static function (string $key): ?string {
|
||||
if ($key === SettingService::API_TOKEN_DEFAULT_TTL_DAYS_KEY) {
|
||||
return '365';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
$calls = [];
|
||||
$settingRepository->expects($this->exactly(2))
|
||||
->method('set')
|
||||
->willReturnCallback(
|
||||
static function (string $key, ?string $value, ?string $description) use (&$calls): bool {
|
||||
$calls[] = [$key, $value, $description];
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertTrue($service->setApiTokenMaxTtlDays(120));
|
||||
$this->assertSame(
|
||||
[
|
||||
[SettingService::API_TOKEN_DEFAULT_TTL_DAYS_KEY, '120', 'setting.api_token_default_ttl_days'],
|
||||
[SettingService::API_TOKEN_MAX_TTL_DAYS_KEY, '120', 'setting.api_token_max_ttl_days'],
|
||||
],
|
||||
$calls
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetUserInactivityDeleteDaysRequiresDeactivatePolicy(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->method('getValue')->willReturnCallback(
|
||||
static function (string $key): ?string {
|
||||
if ($key === SettingService::USER_INACTIVITY_DEACTIVATE_DAYS_KEY) {
|
||||
return '0';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
$settingRepository->expects($this->never())->method('set');
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertFalse($service->setUserInactivityDeleteDays(30));
|
||||
}
|
||||
|
||||
public function testSetApiCorsAllowedOriginsRejectsInvalidOrigins(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->expects($this->never())->method('set');
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertFalse($service->setApiCorsAllowedOrigins("https://ok.example\njavascript:alert(1)"));
|
||||
}
|
||||
|
||||
public function testSetMicrosoftAuthorityRequiresHttps(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->expects($this->never())->method('set');
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertFalse($service->setMicrosoftAuthority('http://login.microsoftonline.com/common/v2.0'));
|
||||
}
|
||||
|
||||
public function testSetAppPrimaryColorNormalizesHexWithoutHash(): void
|
||||
{
|
||||
$settingRepository = $this->createMock(SettingRepository::class);
|
||||
$settingRepository->expects($this->once())
|
||||
->method('set')
|
||||
->with(SettingService::APP_PRIMARY_COLOR_KEY, '#abc', 'setting.app_primary_color')
|
||||
->willReturn(true);
|
||||
|
||||
$service = $this->newService($settingRepository);
|
||||
$this->assertTrue($service->setAppPrimaryColor('abc'));
|
||||
}
|
||||
|
||||
private function newService(
|
||||
SettingRepository $settingRepository,
|
||||
?TenantRepository $tenantRepository = null,
|
||||
?RoleRepository $roleRepository = null,
|
||||
?DepartmentRepository $departmentRepository = null
|
||||
): SettingService {
|
||||
$tenantRepository = $tenantRepository ?? $this->createMock(TenantRepository::class);
|
||||
$roleRepository = $roleRepository ?? $this->createMock(RoleRepository::class);
|
||||
$departmentRepository = $departmentRepository ?? $this->createMock(DepartmentRepository::class);
|
||||
$themeConfigService = $this->createMock(ThemeConfigService::class);
|
||||
$themeConfigService->method('all')->willReturn(['light' => 'Light', 'dark' => 'Dark']);
|
||||
|
||||
return new SettingService(
|
||||
$settingRepository,
|
||||
$tenantRepository,
|
||||
$roleRepository,
|
||||
$departmentRepository,
|
||||
$themeConfigService
|
||||
);
|
||||
}
|
||||
}
|
||||
33
tests/Service/Settings/SettingServicesFactoryTest.php
Normal file
33
tests/Service/Settings/SettingServicesFactoryTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Settings;
|
||||
|
||||
use MintyPHP\Repository\Settings\SettingRepository;
|
||||
use MintyPHP\Service\Settings\SettingCacheService;
|
||||
use MintyPHP\Service\Settings\SettingGateway;
|
||||
use MintyPHP\Service\Settings\SettingServicesFactory;
|
||||
use MintyPHP\Service\Settings\ThemeConfigService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new SettingServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(SettingRepository::class, $factory->createSettingRepository());
|
||||
$this->assertSame($factory->createSettingRepository(), $factory->createSettingRepository());
|
||||
|
||||
$this->assertInstanceOf('MintyPHP\\Service\\Settings\\SettingService', $factory->createSettingService());
|
||||
$this->assertSame($factory->createSettingService(), $factory->createSettingService());
|
||||
|
||||
$this->assertInstanceOf(SettingCacheService::class, $factory->createSettingCacheService());
|
||||
$this->assertSame($factory->createSettingCacheService(), $factory->createSettingCacheService());
|
||||
|
||||
$this->assertInstanceOf(ThemeConfigService::class, $factory->createThemeConfigService());
|
||||
$this->assertSame($factory->createThemeConfigService(), $factory->createThemeConfigService());
|
||||
|
||||
$this->assertInstanceOf(SettingGateway::class, $factory->createSettingGateway());
|
||||
$this->assertSame($factory->createSettingGateway(), $factory->createSettingGateway());
|
||||
}
|
||||
}
|
||||
42
tests/Service/Tenant/TenantServicesFactoryTest.php
Normal file
42
tests/Service/Tenant/TenantServicesFactoryTest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Tenant;
|
||||
|
||||
use MintyPHP\Repository\Org\DepartmentRepository;
|
||||
use MintyPHP\Repository\Tenant\TenantRepository;
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepository;
|
||||
use MintyPHP\Service\Access\PermissionGateway;
|
||||
use MintyPHP\Service\Tenant\TenantAvatarService;
|
||||
use MintyPHP\Service\Tenant\TenantFaviconService;
|
||||
use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
use MintyPHP\Service\Tenant\TenantServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TenantServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryReturnsStableInstancesPerFactoryObject(): void
|
||||
{
|
||||
$factory = new TenantServicesFactory();
|
||||
|
||||
$this->assertInstanceOf(TenantRepository::class, $factory->createTenantRepository());
|
||||
$this->assertSame($factory->createTenantRepository(), $factory->createTenantRepository());
|
||||
|
||||
$this->assertInstanceOf(DepartmentRepository::class, $factory->createDepartmentRepository());
|
||||
$this->assertSame($factory->createDepartmentRepository(), $factory->createDepartmentRepository());
|
||||
|
||||
$this->assertInstanceOf(UserTenantRepository::class, $factory->createUserTenantRepository());
|
||||
$this->assertSame($factory->createUserTenantRepository(), $factory->createUserTenantRepository());
|
||||
|
||||
$this->assertInstanceOf(PermissionGateway::class, $factory->createPermissionGateway());
|
||||
$this->assertSame($factory->createPermissionGateway(), $factory->createPermissionGateway());
|
||||
|
||||
$this->assertInstanceOf(TenantScopeService::class, $factory->createTenantScopeService());
|
||||
$this->assertSame($factory->createTenantScopeService(), $factory->createTenantScopeService());
|
||||
|
||||
$this->assertInstanceOf(TenantAvatarService::class, $factory->createTenantAvatarService());
|
||||
$this->assertSame($factory->createTenantAvatarService(), $factory->createTenantAvatarService());
|
||||
|
||||
$this->assertInstanceOf(TenantFaviconService::class, $factory->createTenantFaviconService());
|
||||
$this->assertSame($factory->createTenantFaviconService(), $factory->createTenantFaviconService());
|
||||
}
|
||||
}
|
||||
27
tests/Service/User/UserPasswordPolicyServiceTest.php
Normal file
27
tests/Service/User/UserPasswordPolicyServiceTest.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\User;
|
||||
|
||||
use MintyPHP\Service\User\UserPasswordPolicyService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserPasswordPolicyServiceTest extends TestCase
|
||||
{
|
||||
public function testMinLengthMatchesHints(): void
|
||||
{
|
||||
$service = new UserPasswordPolicyService();
|
||||
$this->assertSame(12, $service->minLength());
|
||||
|
||||
$hints = $service->hints();
|
||||
$this->assertNotEmpty($hints);
|
||||
$this->assertSame('min', (string) ($hints[0]['rule'] ?? ''));
|
||||
}
|
||||
|
||||
public function testValidateRejectsWeakPassword(): void
|
||||
{
|
||||
$service = new UserPasswordPolicyService();
|
||||
$errors = $service->validate('abc', 'abc', true, 'foo@example.com');
|
||||
|
||||
$this->assertNotEmpty($errors);
|
||||
}
|
||||
}
|
||||
37
tests/Service/User/UserPasswordServiceTest.php
Normal file
37
tests/Service/User/UserPasswordServiceTest.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\User;
|
||||
|
||||
use MintyPHP\Repository\User\UserWriteRepository;
|
||||
use MintyPHP\Service\User\UserPasswordPolicyService;
|
||||
use MintyPHP\Service\User\UserPasswordService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserPasswordServiceTest extends TestCase
|
||||
{
|
||||
public function testResetPasswordRejectsWeakPassword(): void
|
||||
{
|
||||
$writeRepository = $this->createMock(UserWriteRepository::class);
|
||||
$writeRepository->expects($this->never())->method('setPassword');
|
||||
|
||||
$service = new UserPasswordService(new UserPasswordPolicyService(), $writeRepository);
|
||||
$result = $service->resetPassword(10, 'abc', 'abc');
|
||||
|
||||
$this->assertFalse($result['ok']);
|
||||
$this->assertNotEmpty($result['errors']);
|
||||
}
|
||||
|
||||
public function testResetPasswordUpdatesRepositoryOnValidInput(): void
|
||||
{
|
||||
$writeRepository = $this->createMock(UserWriteRepository::class);
|
||||
$writeRepository->expects($this->once())
|
||||
->method('setPassword')
|
||||
->with(11, 'StrongPass1!')
|
||||
->willReturn(true);
|
||||
|
||||
$service = new UserPasswordService(new UserPasswordPolicyService(), $writeRepository);
|
||||
$result = $service->resetPassword(11, 'StrongPass1!', 'StrongPass1!');
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
}
|
||||
}
|
||||
29
tests/Service/User/UserServicesFactoryTest.php
Normal file
29
tests/Service/User/UserServicesFactoryTest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\User;
|
||||
|
||||
use MintyPHP\Service\User\UserServicesFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserServicesFactoryTest extends TestCase
|
||||
{
|
||||
public function testFactoryCachesCoreInstances(): void
|
||||
{
|
||||
$factory = new UserServicesFactory();
|
||||
|
||||
$this->assertSame($factory->createUserAccountService(), $factory->createUserAccountService());
|
||||
$this->assertSame($factory->createUserAssignmentService(), $factory->createUserAssignmentService());
|
||||
$this->assertSame($factory->createUserTenantContextService(), $factory->createUserTenantContextService());
|
||||
$this->assertSame($factory->createUserPasswordService(), $factory->createUserPasswordService());
|
||||
$this->assertSame($factory->createUserReadRepository(), $factory->createUserReadRepository());
|
||||
$this->assertSame($factory->createUserWriteRepository(), $factory->createUserWriteRepository());
|
||||
$this->assertSame($factory->createUserListQueryRepository(), $factory->createUserListQueryRepository());
|
||||
$this->assertSame($factory->createUserSavedFilterRepository(), $factory->createUserSavedFilterRepository());
|
||||
$this->assertSame($factory->createUserTenantRepository(), $factory->createUserTenantRepository());
|
||||
$this->assertSame($factory->createUserRoleRepository(), $factory->createUserRoleRepository());
|
||||
$this->assertSame($factory->createUserDepartmentRepository(), $factory->createUserDepartmentRepository());
|
||||
$this->assertSame($factory->createUserPasswordPolicyService(), $factory->createUserPasswordPolicyService());
|
||||
$this->assertSame($factory->createUserAvatarService(), $factory->createUserAvatarService());
|
||||
$this->assertSame($factory->createUserSavedFilterService(), $factory->createUserSavedFilterService());
|
||||
}
|
||||
}
|
||||
@@ -2,32 +2,39 @@
|
||||
|
||||
namespace MintyPHP\Tests\Service;
|
||||
|
||||
use MintyPHP\Service\User\UserService;
|
||||
use MintyPHP\Service\User\UserPasswordPolicyService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserServicePasswordTest extends TestCase
|
||||
{
|
||||
private UserPasswordPolicyService $policy;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->policy = new UserPasswordPolicyService();
|
||||
}
|
||||
|
||||
public function testRequiresPasswordWhenRequired(): void
|
||||
{
|
||||
$errors = UserService::validatePassword('', '', true, 'test@example.com');
|
||||
$errors = $this->policy->validate('', '', true, 'test@example.com');
|
||||
$this->assertNotEmpty($errors);
|
||||
}
|
||||
|
||||
public function testRejectsMismatch(): void
|
||||
{
|
||||
$errors = UserService::validatePassword('Abcd1234$efg', 'Abcd1234$xxx', true, 'test@example.com');
|
||||
$errors = $this->policy->validate('Abcd1234$efg', 'Abcd1234$xxx', true, 'test@example.com');
|
||||
$this->assertNotEmpty($errors);
|
||||
}
|
||||
|
||||
public function testAcceptsStrongPassword(): void
|
||||
{
|
||||
$errors = UserService::validatePassword('StrongPass1!', 'StrongPass1!', true, 'test@example.com');
|
||||
$errors = $this->policy->validate('StrongPass1!', 'StrongPass1!', true, 'test@example.com');
|
||||
$this->assertSame([], $errors);
|
||||
}
|
||||
|
||||
public function testRejectsPasswordContainingEmail(): void
|
||||
{
|
||||
$errors = UserService::validatePassword('test@example.comA1!', 'test@example.comA1!', true, 'test@example.com');
|
||||
$errors = $this->policy->validate('test@example.comA1!', 'test@example.comA1!', true, 'test@example.com');
|
||||
$this->assertNotEmpty($errors);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user