CaiWuXiTong/tests/Unit/Auth/FinanceRosterTest.php

86 lines
5.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Unit\Auth;
use App\Services\Auth\FinanceRoster;
use Mockery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class FinanceRosterTest extends TestCase
{
public function test_directory_search_is_bounded_parameterized_and_escapes_wildcards(): void
{
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->once()->with(Mockery::on(function ($sql): bool {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
return str_contains($sql, 'TOP (12)') && str_contains($sql, 'flowerNameId') && ! str_contains($sql, "张'_%");
}), ["%张'!_!%![%", "%张'!_!%![%"])->andReturn([]);
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->once()->with('operations_replica')->andReturn($query);
$roster = new FinanceRoster($factory);
$this->assertSame([], $roster->search(''));
$this->assertSame([], $roster->search("张'_%["));
}
#[DataProvider('rosterCases')]
public function test_roster_authorization(array $overrides, bool $expected): void
{
config(['dingtalk.finance_department_ids' => ['22848291', '994080505']]);
config(['dingtalk.super_admin_user_ids' => $overrides['designated_ids'] ?? []]);
$data = array_replace([
'flowers' => [['Id' => 123, 'name' => '测试员工', 'flowerName' => '测试花名', 'deptId' => '55248182', 'status' => '', 'empStatus' => '正式']],
'employees' => [['active' => 1, 'dept_id_list' => '[55248182]']],
'registrations' => [['employment_status' => '3', 'department_ids' => '55248182', 'main_department_id' => '55248182']],
'resignation' => null,
'ancestors' => [['Id' => '55248182', 'parent_id' => '22848291', 'depth' => 0], ['Id' => '22848291', 'parent_id' => '1', 'depth' => 1]],
], $overrides);
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->andReturnUsing(function ($sql, $bindings) use ($data) {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
if (str_contains($sql, 'WITH lineage')) {
$this->assertContains('55248182', $bindings);
return $data['ancestors'];
}
$this->assertSame(['ding-fixture-id'], $bindings);
return match (true) {
str_contains($sql, 'hr_flowerName_book') => $data['flowers'],
str_contains($sql, 'hr_employee_register') => $data['registrations'],
str_contains($sql, 'hr_employee') => $data['employees'],
};
});
$query->shouldReceive('first')->with(Mockery::on(function ($sql) {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
return str_contains($sql, 'hr_employee_dimission');
}), ['ding-fixture-id'])->andReturn($data['resignation']);
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->with('operations_replica')->andReturn($query);
$result = (new FinanceRoster($factory))->findEligible('ding-fixture-id');
$this->assertSame($expected, $result !== null);
}
public static function rosterCases(): array
{
return [
'designated active administrator outside finance' => [['designated_ids' => ['ding-fixture-id'], 'ancestors' => [['Id' => '55248182', 'parent_id' => '1', 'depth' => 0]]], true],
'designation does not cover another identity' => [['designated_ids' => ['another-id'], 'ancestors' => [['Id' => '55248182', 'parent_id' => '1', 'depth' => 0]]], false],
'designated administrator still requires active employment' => [['designated_ids' => ['ding-fixture-id'], 'employees' => [['active' => 0]]], false],
'designated administrator cannot bypass resignation' => [['designated_ids' => ['ding-fixture-id'], 'resignation' => ['userid' => 'ding-fixture-id']], false],
'designated administrator needs roster identity' => [['designated_ids' => ['ding-fixture-id'], 'flowers' => []], false],
'finance descendant' => [[], true],
'second finance root' => [['ancestors' => [['Id' => '994080505', 'parent_id' => '994121400', 'depth' => 1]]], true],
'concurrent finance membership' => [['employees' => [['active' => 1, 'dept_id_list' => '[100,55248182]']]], true],
'not in roster' => [['flowers' => []], false],
'same department name outside allowed roots' => [['ancestors' => [['Id' => '123456', 'parent_id' => '1', 'depth' => 0]]], false],
'resignation beats active status' => [['resignation' => ['userid' => 'ding-fixture-id']], false],
'numeric resigned status' => [['registrations' => [['employment_status' => '5']]], false],
'disabled employee' => [['employees' => [['active' => 0]]], false],
'flower-only member' => [['employees' => [], 'registrations' => []], true],
'ambiguous roster identity' => [['flowers' => [['Id' => 1], ['Id' => 2]]], false],
'broken department cycle' => [['ancestors' => [['Id' => '22848291', 'parent_id' => '22848291', 'depth' => 20]]], false],
];
}
}