Files
ADconsle/tests/Unit/OuTreeServiceTest.php
T
kzbkowskiandClaude Sonnet 5 3dae779724
CI / PHP 8.4 (push) Successful in 1m5s
Release image / image (push) Successful in 1m39s
test: stop OuTreeServiceTest hitting a real LDAP server
it_limits_tree_to_default_user_and_computer_ous only mocked
OrganizationalUnit::query(); attachCounts() then called the real
User/Computer query via countObjectsInOu(), which threw "Can't contact
LDAP server" on any checkout whose .env points LDAP_HOSTS at an
unreachable DC (i.e. every CI run since v1.0.0). Stub countObjectsInOu()
on a partial mock, like the sibling test already does — the test only
asserts tree shape, not counts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JKT9p3bfP8RZesExdYCS52
2026-09-10 09:24:15 +02:00

202 lines
6.9 KiB
PHP

<?php
namespace Tests\Unit;
use App\Services\OuTreeService;
use Mockery;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class OuTreeServiceTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// Drzewo jest cache'owane w pliku - czyścimy przed każdym testem,
// żeby mock LDAP faktycznie był odpytywany.
OuTreeService::flush();
}
#[Test]
public function it_limits_tree_to_default_user_and_computer_ous(): void
{
config([
'ldap.defaults.user_ou' => 'OU=oddzial,DC=example,DC=com',
'ldap.defaults.computer_ou' => 'OU=Komputery,DC=example,DC=com',
]);
$userOu = new class('OU=oddzial,DC=example,DC=com', 'oddzial')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$nestedUserOu = new class('OU=Users,OU=oddzial,DC=example,DC=com', 'Users')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$computerOu = new class('OU=Komputery,DC=example,DC=com', 'Komputery')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$computerChild = new class('OU=Workstations,OU=Komputery,DC=example,DC=com', 'Workstations')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$externalOu = new class('OU=External,DC=example,DC=com', 'External')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$query = Mockery::mock();
$query->shouldReceive('select')->with(['ou', 'distinguishedname'])->andReturnSelf();
$query->shouldReceive('get')->twice()->andReturn([
$userOu,
$nestedUserOu,
$computerOu,
$computerChild,
$externalOu,
]);
Mockery::mock('alias:\LdapRecord\Models\ActiveDirectory\OrganizationalUnit')
->shouldReceive('query')->twice()->andReturn($query);
// Ten test sprawdza wyłącznie kształt drzewa (ograniczenie do rootów +
// zagnieżdżenie), nie liczniki - a countObjectsInOu() odpytałoby realny
// LDAP. Podstawiamy 0.
$service = Mockery::mock(OuTreeService::class)->makePartial()->shouldAllowMockingProtectedMethods();
$service->shouldReceive('countObjectsInOu')->andReturn(0);
$userTree = $service->tree('users');
$computerTree = $service->tree('computers');
$this->assertCount(1, $userTree);
$this->assertSame('oddzial', $userTree[0]['name']);
$this->assertCount(1, $userTree[0]['children']);
$this->assertSame('Users', $userTree[0]['children'][0]['name']);
$this->assertCount(1, $computerTree);
$this->assertSame('Komputery', $computerTree[0]['name']);
$this->assertCount(1, $computerTree[0]['children']);
$this->assertSame('Workstations', $computerTree[0]['children'][0]['name']);
Mockery::close();
}
#[Test]
public function it_counts_actual_users_inside_each_ou_subtree(): void
{
config([
'ldap.defaults.user_ou' => 'OU=Users,DC=example,DC=com',
'ldap.defaults.computer_ou' => 'OU=Komputery,DC=example,DC=com',
'ldap.defaults.groups_ou' => 'OU=Grupy,DC=example,DC=com',
]);
$rootOu = new class('OU=Users,DC=example,DC=com', 'Users')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$childOu = new class('OU=Developers,OU=Users,DC=example,DC=com', 'Developers')
{
public function __construct(public string $dn, public string $name) {}
public function getDn(): string
{
return $this->dn;
}
public function getFirstAttribute(string $attribute): ?string
{
return $attribute === 'ou' ? $this->name : null;
}
};
$ouQuery = Mockery::mock();
$ouQuery->shouldReceive('select')->with(['ou', 'distinguishedname'])->andReturnSelf();
$ouQuery->shouldReceive('get')->once()->andReturn([$rootOu, $childOu]);
Mockery::mock('alias:\LdapRecord\Models\ActiveDirectory\OrganizationalUnit')
->shouldReceive('query')->once()->andReturn($ouQuery);
// countObjectsInOu() zwraca liczbę obiektów w CAŁYM poddrzewie danego OU
// (zapytanie rekursywne). Liczba przy OU nadrzędnym NIE może być sumą
// z dziećmi - inaczej te same obiekty są liczone wielokrotnie
// (błąd: root pokazywał 313 zamiast 121).
$service = Mockery::mock(OuTreeService::class)->makePartial()->shouldAllowMockingProtectedMethods();
$service->shouldReceive('countObjectsInOu')->with('OU=Users,DC=example,DC=com', 'users')->once()->andReturn(5);
$service->shouldReceive('countObjectsInOu')->with('OU=Developers,OU=Users,DC=example,DC=com', 'users')->once()->andReturn(2);
$tree = $service->tree('users');
$this->assertCount(1, $tree);
$this->assertSame(5, $tree[0]['count'], 'Licznik OU nadrzędnego = liczba w całym poddrzewie, bez podwójnego sumowania.');
$this->assertSame(2, $tree[0]['children'][0]['count']);
Mockery::close();
}
}