blob: 7ba0e153418b0543652bc3755dc2ff34df3824c3 [file] [log] [blame]
<?php
namespace LdapRecord\Testing;
use Exception;
use LdapRecord\DetailedError;
use LdapRecord\DetectsErrors;
use LdapRecord\HandlesConnection;
use LdapRecord\LdapInterface;
use LdapRecord\Support\Arr;
use PHPUnit\Framework\Assert as PHPUnit;
use PHPUnit\Framework\Constraint\Constraint;
class LdapFake implements LdapInterface
{
use HandlesConnection, DetectsErrors;
/**
* The expectations of the LDAP fake.
*
* @var array
*/
protected $expectations = [];
/**
* The default fake error number.
*
* @var int
*/
protected $errNo = 1;
/**
* The default fake last error string.
*
* @var string
*/
protected $lastError = '';
/**
* The default fake diagnostic message string.
*
* @var string
*/
protected $diagnosticMessage = '';
/**
* Create a new expected operation.
*
* @param string $method
*
* @return LdapExpectation
*/
public static function operation($method)
{
return new LdapExpectation($method);
}
/**
* Set the user that will pass binding.
*
* @param string $dn
*
* @return $this
*/
public function shouldAuthenticateWith($dn)
{
return $this->expect(
static::operation('bind')->with($dn, PHPUnit::anything())->andReturn(true)
);
}
/**
* Add an LDAP method expectation.
*
* @param LdapExpectation|array $expectations
*
* @return $this
*/
public function expect($expectations = [])
{
$expectations = Arr::wrap($expectations);
foreach ($expectations as $key => $expectation) {
// If the key is non-numeric, we will assume
// that the string is the method name and
// the expectation is the return value.
if (! is_numeric($key)) {
$expectation = static::operation($key)->andReturn($expectation);
}
if (! $expectation instanceof LdapExpectation) {
$expectation = static::operation($expectation);
}
$this->expectations[$expectation->getMethod()][] = $expectation;
}
return $this;
}
/**
* Determine if the method has any expectations.
*
* @param string $method
*
* @return bool
*/
public function hasExpectations($method)
{
return count($this->getExpectations($method)) > 0;
}
/**
* Get expectations by method.
*
* @param string $method
*
* @return LdapExpectation[]|mixed
*/
public function getExpectations($method)
{
return $this->expectations[$method] ?? [];
}
/**
* Remove an expectation by method and key.
*
* @param string $method
* @param int $key
*
* @return void
*/
public function removeExpectation($method, $key)
{
unset($this->expectations[$method][$key]);
}
/**
* Set the error number of a failed bind attempt.
*
* @param int $number
*
* @return $this
*/
public function shouldReturnErrorNumber($number = 1)
{
$this->errNo = $number;
return $this;
}
/**
* Set the last error of a failed bind attempt.
*
* @param string $message
*
* @return $this
*/
public function shouldReturnError($message = '')
{
$this->lastError = $message;
return $this;
}
/**
* Set the diagnostic message of a failed bind attempt.
*
* @param string $message
*
* @return $this
*/
public function shouldReturnDiagnosticMessage($message = '')
{
$this->diagnosticMessage = $message;
return $this;
}
/**
* Return a fake error number.
*
* @return int
*/
public function errNo()
{
return $this->errNo;
}
/**
* Return a fake error.
*
* @return string
*/
public function getLastError()
{
return $this->lastError;
}
/**
* @inheritdoc
*/
public function getDiagnosticMessage()
{
return $this->diagnosticMessage;
}
/**
* Return a fake detailed error.
*
* @return DetailedError
*/
public function getDetailedError()
{
return new DetailedError(
$this->errNo(),
$this->getLastError(),
$this->getDiagnosticMessage()
);
}
/**
* @inheritdoc
*/
public function getEntries($searchResults)
{
return $searchResults;
}
/**
* @inheritdoc
*/
public function isUsingSSL()
{
return $this->hasExpectations('isUsingSSL')
? $this->resolveExpectation('isUsingSSL')
: $this->useSSL;
}
/**
* @inheritdoc
*/
public function isUsingTLS()
{
return $this->hasExpectations('isUsingTLS')
? $this->resolveExpectation('isUsingTLS')
: $this->useTLS;
}
/**
* @inheritdoc
*/
public function isBound()
{
return $this->hasExpectations('isBound')
? $this->resolveExpectation('isBound')
: $this->bound;
}
/**
* @inheritdoc
*/
public function setOption($option, $value)
{
return $this->hasExpectations('setOption')
? $this->resolveExpectation('setOption', func_get_args())
: true;
}
/**
* @inheritdoc
*/
public function getOption($option, &$value = null)
{
return $this->resolveExpectation('getOption', func_get_args());
}
/**
* @inheritdoc
*/
public function startTLS()
{
return $this->resolveExpectation('startTLS', func_get_args());
}
/**
* @inheritdoc
*/
public function connect($hosts = [], $port = 389)
{
$this->bound = false;
$this->host = $this->makeConnectionUris($hosts, $port);
return $this->connection = $this->hasExpectations('connect')
? $this->resolveExpectation('connect', func_get_args())
: true;
}
/**
* @inheritdoc
*/
public function close()
{
$this->connection = null;
$this->bound = false;
$this->host = null;
return $this->hasExpectations('close')
? $this->resolveExpectation('close')
: true;
}
/**
* @inheritdoc
*/
public function bind($username, $password)
{
return $this->bound = $this->resolveExpectation('bind', func_get_args());
}
/**
* @inheritdoc
*/
public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = null, $serverControls = [])
{
return $this->resolveExpectation('search', func_get_args());
}
/**
* @inheritdoc
*/
public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = null, $serverControls = [])
{
return $this->resolveExpectation('listing', func_get_args());
}
/**
* @inheritdoc
*/
public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = null, $serverControls = [])
{
return $this->resolveExpectation('read', func_get_args());
}
/**
* @inheritdoc
*/
public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = [])
{
return $this->resolveExpectation('parseResult', func_get_args());
}
/**
* @inheritdoc
*/
public function add($dn, array $entry)
{
return $this->resolveExpectation('add', func_get_args());
}
/**
* @inheritdoc
*/
public function delete($dn)
{
return $this->resolveExpectation('delete', func_get_args());
}
/**
* @inheritdoc
*/
public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false)
{
return $this->resolveExpectation('rename', func_get_args());
}
/**
* @inheritdoc
*/
public function modify($dn, array $entry)
{
return $this->resolveExpectation('modify', func_get_args());
}
/**
* @inheritdoc
*/
public function modifyBatch($dn, array $values)
{
return $this->resolveExpectation('modifyBatch', func_get_args());
}
/**
* @inheritdoc
*/
public function modAdd($dn, array $entry)
{
return $this->resolveExpectation('modAdd', func_get_args());
}
/**
* @inheritdoc
*/
public function modReplace($dn, array $entry)
{
return $this->resolveExpectation('modReplace', func_get_args());
}
/**
* @inheritdoc
*/
public function modDelete($dn, array $entry)
{
return $this->resolveExpectation('modDelete', func_get_args());
}
/**
* @inheritdoc
*/
public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '')
{
return $this->resolveExpectation('controlPagedResult', func_get_args());
}
/**
* @inheritdoc
*/
public function controlPagedResultResponse($result, &$cookie)
{
return $this->resolveExpectation('controlPagedResultResponse', func_get_args());
}
/**
* @inheritdoc
*/
public function freeResult($result)
{
return $this->resolveExpectation('freeResult', func_get_args());
}
/**
* @inheritdoc
*/
public function err2Str($number)
{
return $this->resolveExpectation('err2Str', func_get_args());
}
/**
* Resolve the methods expectations.
*
* @param string $method
* @param array $args
*
* @throws Exception
*
* @return mixed
*/
protected function resolveExpectation($method, array $args = [])
{
foreach ($this->getExpectations($method) as $key => $expectation) {
$this->assertMethodArgumentsMatch($method, $expectation->getExpectedArgs(), $args);
$expectation->decrementCallCount();
if ($expectation->getExpectedCount() === 0) {
$this->removeExpectation($method, $key);
}
if (! is_null($exception = $expectation->getExpectedException())) {
throw $exception;
}
if ($expectation->isReturningError()) {
$this->applyExpectationError($expectation);
}
return $expectation->getExpectedValue();
}
throw new Exception("LDAP method [$method] was unexpected.");
}
/**
* Apply the expectation error to the fake.
*
* @param LdapExpectation $expectation
*
* @return void
*/
protected function applyExpectationError(LdapExpectation $expectation)
{
$this->shouldReturnError($expectation->getExpectedErrorMessage());
$this->shouldReturnErrorNumber($expectation->getExpectedErrorCode());
$this->shouldReturnDiagnosticMessage($expectation->getExpectedErrorDiagnosticMessage());
}
/**
* Assert that the expected arguments match the operations arguments.
*
* @param string $method
* @param Constraint[] $expectedArgs
* @param array $methodArgs
*
* @return void
*/
protected function assertMethodArgumentsMatch($method, array $expectedArgs = [], array $methodArgs = [])
{
foreach ($expectedArgs as $key => $constraint) {
$argNumber = $key + 1;
PHPUnit::assertArrayHasKey(
$key,
$methodArgs,
"LDAP method [$method] argument #{$argNumber} does not exist."
);
$constraint->evaluate(
$methodArgs[$key],
"LDAP method [$method] expectation failed."
);
}
}
}