CaiWuXiTong/tests/Unit/Auth/SupplierPortalAccessTest.php
苏木 d04ed7c94d feat: 支持供应商联系人绑定多个厂家 (#922)
同一钉钉联系人可绑定多家供应商,并提供厂家选择、切换和逐厂家授权校验。
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
2026-11-06 10:39:13 +08:00

168 lines
7.4 KiB
PHP

<?php
namespace Tests\Unit\Auth;
use App\Models\Auth\SupplierPortalIdentity;
use App\Models\Auth\SupplierPortalSupplier;
use App\Services\Auth\SupplierPortalAccess;
use Mockery;
use Modules\EcommerceFinance\Repositories\SupplierManagementMirrorRepository;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use Tests\TestCase;
class SupplierPortalAccessTest extends TestCase
{
public function test_exact_supplier_group_resolves_to_one_supplier(): void
{
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->twice()->andReturnUsing(function (string $sql, array $bindings): array {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
if (str_contains($sql, 'hr_employee')) {
$this->assertSame(['ding-supplier'], $bindings);
return [[
'userid' => 'ding-supplier',
'unionid' => 'union-supplier',
'active' => 1,
'dept_id_list' => '55248182',
]];
}
$this->assertContains('55248182', $bindings);
return [
['Id' => 55248182, 'parent_id' => 900, 'name' => '晨丰', 'depth' => 0],
['Id' => 900, 'parent_id' => 1, 'name' => '供应商', 'depth' => 1],
];
});
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->once()->with('operations_replica')->andReturn($query);
$suppliers = Mockery::mock(SupplierManagementMirrorRepository::class);
$suppliers->shouldReceive('suppliersByNames')->once()
->with(['晨丰', '供应商'])
->andReturn([(object) ['Id' => 9, 'scmcode' => 'CF-001', 'scmname' => '晨丰']]);
$resolved = (new SupplierPortalAccess($factory, $suppliers))->resolve('ding-supplier', 'union-supplier');
$this->assertSame('晨丰', $resolved['supplier_name']);
$this->assertSame('CF-001', $resolved['supplier_code']);
$this->assertSame(55248182, $resolved['dingtalk_group_id']);
}
public function test_inactive_or_union_mismatched_employee_is_denied(): void
{
foreach ([
[['userid' => 'ding-supplier', 'unionid' => 'union-supplier', 'active' => 0, 'dept_id_list' => '1']],
[['userid' => 'ding-supplier', 'unionid' => 'different-union', 'active' => 1, 'dept_id_list' => '1']],
] as $employees) {
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->once()->with(Mockery::on(function (string $sql): bool {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
return str_contains($sql, 'hr_employee');
}), ['ding-supplier'])->andReturn($employees);
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->andReturn($query);
$suppliers = Mockery::mock(SupplierManagementMirrorRepository::class);
$suppliers->shouldNotReceive('suppliersByNames');
$this->assertNull((new SupplierPortalAccess($factory, $suppliers))->resolve('ding-supplier', 'union-supplier'));
}
}
public function test_multiple_supplier_ancestors_resolve_to_all_suppliers(): void
{
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->twice()->andReturnUsing(function (string $sql): array {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
if (str_contains($sql, 'hr_employee')) {
return [['userid' => 'ding-supplier', 'unionid' => 'union-supplier', 'active' => 1, 'dept_id_list' => '10,20']];
}
return [
['Id' => 10, 'parent_id' => 1, 'name' => '晨丰', 'depth' => 0],
['Id' => 20, 'parent_id' => 1, 'name' => '鸣飞家具', 'depth' => 0],
];
});
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->once()->andReturn($query);
$suppliers = Mockery::mock(SupplierManagementMirrorRepository::class);
$suppliers->shouldReceive('suppliersByNames')->once()->andReturn([
(object) ['Id' => 1, 'scmcode' => 'CF', 'scmname' => '晨丰'],
(object) ['Id' => 2, 'scmcode' => 'MF', 'scmname' => '鸣飞家具'],
]);
$access = new SupplierPortalAccess($factory, $suppliers);
$this->assertSame(
['晨丰', '鸣飞家具'],
array_column($access->resolveAll('ding-supplier', 'union-supplier'), 'supplier_name'),
);
}
public function test_organization_cycle_is_rejected(): void
{
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')->twice()->andReturnUsing(function (string $sql): array {
UpstreamReadOnlyQuery::assertReadOnlySql($sql);
if (str_contains($sql, 'hr_employee')) {
return [['userid' => 'ding-supplier', 'unionid' => 'union-supplier', 'active' => 1, 'dept_id_list' => '30']];
}
return [['Id' => 30, 'parent_id' => 30, 'name' => '晨丰', 'depth' => 20]];
});
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSource')->once()->andReturn($query);
$suppliers = Mockery::mock(SupplierManagementMirrorRepository::class);
$suppliers->shouldNotReceive('suppliersByNames');
$this->assertNull((new SupplierPortalAccess($factory, $suppliers))->resolve('ding-supplier', 'union-supplier'));
}
public function test_binding_multiple_suppliers_keeps_all_and_prunes_only_removed_ones(): void
{
$user = user_model_class()::query()->firstOrFail();
$companyId = (int) $user->companies()->firstOrFail()->id;
$access = new SupplierPortalAccess(
Mockery::mock(UpstreamReadOnlyQueryFactory::class),
Mockery::mock(SupplierManagementMirrorRepository::class),
);
$access->bindMany($user, $companyId, [
[
'supplier_name' => '晨丰',
'supplier_code' => 'CF-001',
'dingtalk_group_id' => 10,
'dingtalk_group_name' => '晨丰',
],
[
'supplier_name' => '鸣飞家具',
'supplier_code' => 'MF-001',
'dingtalk_group_id' => 20,
'dingtalk_group_name' => '鸣飞家具',
],
]);
$this->assertSame(2, SupplierPortalSupplier::query()->where('user_id', $user->id)->count());
$this->assertSame(1, SupplierPortalIdentity::query()->where('user_id', $user->id)->count());
$access->bindMany($user, $companyId, [[
'supplier_name' => '鸣飞家具',
'supplier_code' => 'MF-001',
'dingtalk_group_id' => 20,
'dingtalk_group_name' => '鸣飞家具',
]]);
$this->assertDatabaseMissing('supplier_portal_suppliers', [
'company_id' => $companyId,
'user_id' => $user->id,
'supplier_name' => '晨丰',
]);
$this->assertDatabaseHas('supplier_portal_suppliers', [
'company_id' => $companyId,
'user_id' => $user->id,
'supplier_name' => '鸣飞家具',
]);
}
}