From d46250dab70c7c1e69811b0f65a5bb319fd8dfcf Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sun, 16 Nov 2025 13:56:55 -0500 Subject: [PATCH 01/12] Fix implicit nulls which have been deprecated. --- src/FreeDSx/Ldap/Control/Vlv/VlvControl.php | 2 +- src/FreeDSx/Ldap/Controls.php | 2 +- src/FreeDSx/Ldap/Exception/OperationException.php | 2 +- .../Ldap/Exception/UnsolicitedNotificationException.php | 2 +- src/FreeDSx/Ldap/LdapClient.php | 2 +- .../ClientProtocolHandler/ClientExtendedOperationHandler.php | 2 +- src/FreeDSx/Ldap/Protocol/LdapQueue.php | 2 +- src/FreeDSx/Ldap/Protocol/Queue/ClientQueue.php | 2 +- src/FreeDSx/Ldap/Search/RangeRetrieval.php | 2 +- src/FreeDSx/Ldap/Server/RequestHistory.php | 2 +- src/FreeDSx/Ldap/Sync/SyncRepl.php | 4 ++-- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/FreeDSx/Ldap/Control/Vlv/VlvControl.php b/src/FreeDSx/Ldap/Control/Vlv/VlvControl.php index eccf9485..9864d203 100644 --- a/src/FreeDSx/Ldap/Control/Vlv/VlvControl.php +++ b/src/FreeDSx/Ldap/Control/Vlv/VlvControl.php @@ -103,7 +103,7 @@ public function getFilter(): ?GreaterThanOrEqualFilter return $this->filter; } - public function setFilter(GreaterThanOrEqualFilter $filter = null): self + public function setFilter(?GreaterThanOrEqualFilter $filter = null): self { $this->filter = $filter; diff --git a/src/FreeDSx/Ldap/Controls.php b/src/FreeDSx/Ldap/Controls.php index 7855d2da..378a2e2c 100644 --- a/src/FreeDSx/Ldap/Controls.php +++ b/src/FreeDSx/Ldap/Controls.php @@ -43,7 +43,7 @@ class Controls public static function create( string $oid, bool $criticality = false, - ProtocolElementInterface|AbstractType $value = null + ProtocolElementInterface|AbstractType|null $value = null ): Control { return new Control( controlType: $oid, diff --git a/src/FreeDSx/Ldap/Exception/OperationException.php b/src/FreeDSx/Ldap/Exception/OperationException.php index fe4359ba..55722a1e 100644 --- a/src/FreeDSx/Ldap/Exception/OperationException.php +++ b/src/FreeDSx/Ldap/Exception/OperationException.php @@ -29,7 +29,7 @@ class OperationException extends Exception public function __construct( string $message = '', int $code = ResultCode::OPERATIONS_ERROR, - Throwable $previous = null + ?Throwable $previous = null ) { $message = empty($message) ? $this->generateMessage($code) diff --git a/src/FreeDSx/Ldap/Exception/UnsolicitedNotificationException.php b/src/FreeDSx/Ldap/Exception/UnsolicitedNotificationException.php index b6078e27..b2a9e2d6 100644 --- a/src/FreeDSx/Ldap/Exception/UnsolicitedNotificationException.php +++ b/src/FreeDSx/Ldap/Exception/UnsolicitedNotificationException.php @@ -28,7 +28,7 @@ class UnsolicitedNotificationException extends ProtocolException public function __construct( string $message = "", int $code = 0, - Throwable $previous = null, + ?Throwable $previous = null, string $oid = "" ) { $this->oid = $oid; diff --git a/src/FreeDSx/Ldap/LdapClient.php b/src/FreeDSx/Ldap/LdapClient.php index f546cc0d..72b96b72 100644 --- a/src/FreeDSx/Ldap/LdapClient.php +++ b/src/FreeDSx/Ldap/LdapClient.php @@ -320,7 +320,7 @@ public function vlv( */ public function dirSync( ?string $rootNc = null, - FilterInterface $filter = null, + ?FilterInterface $filter = null, Attribute|string ...$attributes ): DirSync { return new DirSync( diff --git a/src/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandler.php b/src/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandler.php index e839ecac..e86b07d7 100644 --- a/src/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandler.php +++ b/src/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandler.php @@ -38,7 +38,7 @@ class ClientExtendedOperationHandler extends ClientBasicHandler public function __construct( ClientQueue $queue, - ExtendedResponseFactory $extendedResponseFactory = null + ?ExtendedResponseFactory $extendedResponseFactory = null ) { $this->extendedResponseFactory = $extendedResponseFactory ?? new ExtendedResponseFactory(); diff --git a/src/FreeDSx/Ldap/Protocol/LdapQueue.php b/src/FreeDSx/Ldap/Protocol/LdapQueue.php index 5a870e96..286606a1 100644 --- a/src/FreeDSx/Ldap/Protocol/LdapQueue.php +++ b/src/FreeDSx/Ldap/Protocol/LdapQueue.php @@ -41,7 +41,7 @@ class LdapQueue extends Asn1MessageQueue public function __construct( Socket $socket, - EncoderInterface $encoder = null + ?EncoderInterface $encoder = null ) { parent::__construct( $socket, diff --git a/src/FreeDSx/Ldap/Protocol/Queue/ClientQueue.php b/src/FreeDSx/Ldap/Protocol/Queue/ClientQueue.php index 06e052da..5765fc86 100644 --- a/src/FreeDSx/Ldap/Protocol/Queue/ClientQueue.php +++ b/src/FreeDSx/Ldap/Protocol/Queue/ClientQueue.php @@ -42,7 +42,7 @@ class ClientQueue extends LdapQueue */ public function __construct( private readonly SocketPool $socketPool, - EncoderInterface $encoder = null + ?EncoderInterface $encoder = null ) { parent::__construct( $socketPool->connect(), diff --git a/src/FreeDSx/Ldap/Search/RangeRetrieval.php b/src/FreeDSx/Ldap/Search/RangeRetrieval.php index eba8265a..e90c6473 100644 --- a/src/FreeDSx/Ldap/Search/RangeRetrieval.php +++ b/src/FreeDSx/Ldap/Search/RangeRetrieval.php @@ -86,7 +86,7 @@ public function getAllRanged(Entry $entry): array */ public function hasRanged( Entry $entry, - Attribute|string $attribute = null + Attribute|string|null $attribute = null ): bool { return $attribute !== null ? (bool) $this->getRanged($entry, $attribute) diff --git a/src/FreeDSx/Ldap/Server/RequestHistory.php b/src/FreeDSx/Ldap/Server/RequestHistory.php index e501b47f..1b9d9cfe 100644 --- a/src/FreeDSx/Ldap/Server/RequestHistory.php +++ b/src/FreeDSx/Ldap/Server/RequestHistory.php @@ -30,7 +30,7 @@ final class RequestHistory private PagingRequests $pagingRequests; - public function __construct(PagingRequests $pagingRequests = null) + public function __construct(?PagingRequests $pagingRequests = null) { $this->pagingRequests = $pagingRequests ?? new PagingRequests(); } diff --git a/src/FreeDSx/Ldap/Sync/SyncRepl.php b/src/FreeDSx/Ldap/Sync/SyncRepl.php index 5d9f05e1..de469b99 100644 --- a/src/FreeDSx/Ldap/Sync/SyncRepl.php +++ b/src/FreeDSx/Ldap/Sync/SyncRepl.php @@ -156,7 +156,7 @@ public function request(): SyncRequest * **Note**: The LdapClient should be instantiated with no timeout via {@see ClientOptions::setTimeoutRead(-1)}. * Otherwise, the listen operation will terminate due to a network timeout. */ - public function listen(Closure $entryHandler = null): void + public function listen(?Closure $entryHandler = null): void { $this->sync( mode: SyncRequestControl::MODE_REFRESH_AND_PERSIST, @@ -170,7 +170,7 @@ public function listen(Closure $entryHandler = null): void * If a cookie is provided, then it is a poll for content update. If no cookie is provided, then it is a poll for * content update. To provide a cookie from a previous poll {@see self::useCookie()}. */ - public function poll(Closure $entryHandler = null): void + public function poll(?Closure $entryHandler = null): void { $this->sync( mode: SyncRequestControl::MODE_REFRESH_ONLY, From 08f956c32bb61e3463a389d41ef5bdf69fe44ed0 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sun, 16 Nov 2025 13:57:55 -0500 Subject: [PATCH 02/12] Move from PHPSpec to PHPUnit. --- .github/workflows/build.yml | 4 +- composer.json | 25 +- phpstan.tests.neon | 5 +- phpunit.xml.dist | 29 +- src/FreeDSx/Ldap/Server/RequestHistory.php | 8 + .../{FreeDSx/Ldap => }/LdapClientTest.php | 18 +- .../{FreeDSx/Ldap => }/LdapProxyTest.php | 10 +- .../{FreeDSx/Ldap => }/LdapServerTest.php | 2 +- .../{FreeDSx/Ldap => }/LdapTestCase.php | 2 +- .../{FreeDSx/Ldap => }/Search/DirSyncTest.php | 4 +- .../{FreeDSx/Ldap => }/Search/PagingTest.php | 20 +- .../Ldap => }/Search/RangeRetrievalTest.php | 8 +- .../{FreeDSx/Ldap => }/Search/VlvTest.php | 36 +- .../{FreeDSx/Ldap => }/ServerTestCase.php | 2 +- .../{FreeDSx/Ldap => }/Sync/SyncReplTest.php | 4 +- tests/spec/FreeDSx/Ldap/ContainerSpec.php | 121 ---- .../Control/Ad/DirSyncRequestControlSpec.php | 91 --- .../Control/Ad/DirSyncResponseControlSpec.php | 89 --- .../Ad/ExpectedEntryCountControlSpec.php | 81 --- .../Ldap/Control/Ad/ExtendedDnControlSpec.php | 72 --- .../Control/Ad/PolicyHintsControlSpec.php | 64 --- .../Ldap/Control/Ad/SdFlagsControlSpec.php | 56 -- .../Ldap/Control/Ad/SetOwnerControlSpec.php | 65 --- .../FreeDSx/Ldap/Control/ControlBagSpec.php | 122 ---- .../spec/FreeDSx/Ldap/Control/ControlSpec.php | 73 --- .../Ldap/Control/PagingControlSpec.php | 82 --- .../Control/PwdPolicyResponseControlSpec.php | 84 --- .../Ldap/Control/Sorting/SortKeySpec.php | 76 --- .../Control/Sorting/SortingControlSpec.php | 129 ----- .../Sorting/SortingResponseControlSpec.php | 72 --- .../Ldap/Control/SubentriesControlSpec.php | 59 -- .../Ldap/Control/Sync/SyncDoneControlSpec.php | 77 --- .../Control/Sync/SyncRequestControlSpec.php | 83 --- .../Control/Sync/SyncStateControlSpec.php | 115 ---- .../Ldap/Control/Vlv/VlvControlSpec.php | 73 --- .../Control/Vlv/VlvResponseControlSpec.php | 69 --- tests/spec/FreeDSx/Ldap/ControlsSpec.php | 208 ------- .../spec/FreeDSx/Ldap/Entry/AttributeSpec.php | 191 ------- tests/spec/FreeDSx/Ldap/Entry/ChangeSpec.php | 115 ---- tests/spec/FreeDSx/Ldap/Entry/ChangesSpec.php | 75 --- tests/spec/FreeDSx/Ldap/Entry/DnSpec.php | 77 --- tests/spec/FreeDSx/Ldap/Entry/EntriesSpec.php | 123 ---- tests/spec/FreeDSx/Ldap/Entry/EntrySpec.php | 267 --------- tests/spec/FreeDSx/Ldap/Entry/OptionSpec.php | 110 ---- tests/spec/FreeDSx/Ldap/Entry/OptionsSpec.php | 119 ---- tests/spec/FreeDSx/Ldap/Entry/RdnSpec.php | 84 --- .../Ldap/Exception/BindExceptionSpec.php | 30 - .../Exception/ConnectionExceptionSpec.php | 30 - .../Exception/FilterParseExceptionSpec.php | 30 - .../InvalidArgumentExceptionSpec.php | 30 - .../Ldap/Exception/OperationExceptionSpec.php | 46 -- .../Ldap/Exception/ProtocolExceptionSpec.php | 30 - .../Ldap/Exception/ReferralExceptionSpec.php | 55 -- .../Ldap/Exception/RuntimeExceptionSpec.php | 30 - .../Exception/SkipReferralExceptionSpec.php | 30 - .../UnexpectedValueExceptionSpec.php | 30 - .../UnsolicitedNotificationExceptionSpec.php | 41 -- .../Ldap/Exception/UrlParseExceptionSpec.php | 30 - tests/spec/FreeDSx/Ldap/LdapClientSpec.php | 453 --------------- tests/spec/FreeDSx/Ldap/LdapServerSpec.php | 146 ----- .../FreeDSx/Ldap/LdapUrlExtensionSpec.php | 126 ---- tests/spec/FreeDSx/Ldap/LdapUrlSpec.php | 266 --------- .../FreeDSx/Ldap/Operation/LdapResultSpec.php | 92 --- .../Operation/Request/AbandonRequestSpec.php | 63 -- .../Ldap/Operation/Request/AddRequestSpec.php | 111 ---- .../Operation/Request/AnonBindRequestSpec.php | 86 --- .../Operation/Request/CancelRequestSpec.php | 69 --- .../Operation/Request/CompareRequestSpec.php | 76 --- .../Operation/Request/DeleteRequestSpec.php | 69 --- .../Operation/Request/ExtendedRequestSpec.php | 78 --- .../Operation/Request/ModifyDnRequestSpec.php | 104 ---- .../Operation/Request/ModifyRequestSpec.php | 184 ------ .../Request/PasswordModifyRequestSpec.php | 108 ---- .../Operation/Request/SearchRequestSpec.php | 183 ------ .../Request/SimpleBindRequestSpec.php | 109 ---- .../Operation/Request/SyncRequestSpec.php | 28 - .../Operation/Request/UnbindRequestSpec.php | 43 -- .../Operation/Response/AddResponseSpec.php | 36 -- .../Operation/Response/BindResponseSpec.php | 70 --- .../Response/CompareResponseSpec.php | 36 -- .../Operation/Response/DeleteResponseSpec.php | 36 -- .../Response/ExtendedResponseSpec.php | 63 -- .../Response/IntermediateResponseSpec.php | 60 -- .../Response/ModifyDnResponseSpec.php | 36 -- .../Operation/Response/ModifyResponseSpec.php | 36 -- .../Response/SearchResultDoneSpec.php | 36 -- .../Response/SearchResultEntrySpec.php | 76 --- .../Response/SearchResultReferenceSpec.php | 70 --- .../Response/SyncInfo/SyncIdSetSpec.php | 106 ---- .../Response/SyncInfo/SyncNewCookieSpec.php | 63 -- .../SyncInfo/SyncRefreshDeleteSpec.php | 74 --- .../SyncInfo/SyncRefreshPresentSpec.php | 72 --- tests/spec/FreeDSx/Ldap/OperationsSpec.php | 253 -------- .../Ldap/Protocol/AuthenticatorSpec.php | 87 --- .../Ldap/Protocol/Bind/AnonymousBindSpec.php | 88 --- .../Ldap/Protocol/Bind/SimpleBindSpec.php | 111 ---- .../ClientBasicHandlerSpec.php | 134 ----- .../ClientExtendedOperationHandlerSpec.php | 81 --- .../ClientReferralHandlerSpec.php | 257 --------- .../ClientSaslBindHandlerSpec.php | 245 -------- .../ClientSearchHandlerSpec.php | 176 ------ .../ClientSyncHandlerSpec.php | 336 ----------- .../ClientUnbindHandlerSpec.php | 49 -- .../MockCancelResponseProcessor.php | 28 - .../RequestCancelerSpec.php | 126 ---- .../Protocol/ClientProtocolHandlerSpec.php | 150 ----- .../ClientProtocolHandlerFactorySpec.php | 146 ----- .../Factory/ExtendedResponseFactorySpec.php | 65 --- .../Protocol/Factory/FilterFactorySpec.php | 44 -- .../Protocol/Factory/ResponseFactorySpec.php | 106 ---- .../ServerProtocolHandlerFactorySpec.php | 136 ----- .../Ldap/Protocol/LdapMessageRequestSpec.php | 63 -- .../FreeDSx/Ldap/Protocol/LdapQueueSpec.php | 53 -- .../Queue/ClientQueueInstantiatorSpec.php | 96 ---- .../Ldap/Protocol/Queue/ClientQueueSpec.php | 132 ----- .../MessageWrapper/SaslMessageWrapperSpec.php | 62 -- .../Ldap/Protocol/Queue/ServerQueueSpec.php | 100 ---- .../Ldap/Protocol/ReferralContextSpec.php | 57 -- .../Ldap/Protocol/RootDseLoaderSpec.php | 89 --- .../Ldap/Protocol/ServerAuthorizationSpec.php | 151 ----- .../ServerDispatchHandlerSpec.php | 125 ---- .../ServerPagingUnsupportedHandlerSpec.php | 156 ----- .../ServerRootDseHandlerSpec.php | 239 -------- .../ServerSearchHandlerSpec.php | 111 ---- .../ServerStartTlsHandlerSpec.php | 120 ---- .../ServerUnbindHandlerSpec.php | 46 -- .../ServerWhoAmIHandlerSpec.php | 83 --- .../Protocol/ServerProtocolHandlerSpec.php | 261 --------- .../spec/FreeDSx/Ldap/Search/DirSyncSpec.php | 197 ------- .../Ldap/Search/Filter/AndFilterSpec.php | 143 ----- .../Search/Filter/ApproximateFilterSpec.php | 76 --- .../Ldap/Search/Filter/EqualityFilterSpec.php | 77 --- .../Filter/GreaterThanOrEqualFilterSpec.php | 76 --- .../Filter/LessThanOrEqualFilterSpec.php | 76 --- .../Search/Filter/MatchingRuleFilterSpec.php | 137 ----- .../Ldap/Search/Filter/NotFilterSpec.php | 67 --- .../Ldap/Search/Filter/OrFilterSpec.php | 143 ----- .../Ldap/Search/Filter/PresentFilterSpec.php | 62 -- .../Search/Filter/SubstringFilterSpec.php | 172 ------ .../FreeDSx/Ldap/Search/FilterParserSpec.php | 202 ------- .../spec/FreeDSx/Ldap/Search/FiltersSpec.php | 117 ---- tests/spec/FreeDSx/Ldap/Search/PagingSpec.php | 158 ----- .../Ldap/Search/RangeRetrievalSpec.php | 106 ---- .../Ldap/Search/Result/EntryResultSpec.php | 80 --- .../Ldap/Search/Result/ReferralResultSpec.php | 85 --- tests/spec/FreeDSx/Ldap/Search/VlvSpec.php | 285 --------- .../FreeDSx/Ldap/Server/ChildProcessSpec.php | 48 -- .../Ldap/Server/Paging/PagingRequestSpec.php | 144 ----- .../Ldap/Server/Paging/PagingRequestsSpec.php | 82 --- .../Ldap/Server/Paging/PagingResponseSpec.php | 54 -- .../Ldap/Server/RequestContextSpec.php | 42 -- .../GenericRequestHandlerSpec.php | 82 --- .../RequestHandler/HandlerFactorySpec.php | 73 --- .../RequestHandler/ProxyHandlerSpec.php | 83 --- .../RequestHandler/ProxyPagingHandlerSpec.php | 113 ---- .../ProxyRequestHandlerSpec.php | 135 ----- .../Ldap/Server/RequestHistorySpec.php | 48 -- .../Ldap/Server/ServerProtocolFactorySpec.php | 48 -- .../Ldap/Server/SocketServerFactorySpec.php | 66 --- .../Ldap/Server/Token/AnonTokenSpec.php | 51 -- .../Ldap/Server/Token/BindTokenSpec.php | 51 -- .../Ldap/Sync/MockSyncEntryHandler.php | 31 - .../Ldap/Sync/MockSyncIdSetHandler.php | 31 - .../Ldap/Sync/MockSyncReferralHandler.php | 31 - .../Ldap/Sync/Result/SyncEntryResultSpec.php | 176 ------ .../Ldap/Sync/Result/SyncIdSetResultSpec.php | 130 ----- tests/spec/FreeDSx/Ldap/Sync/SessionSpec.php | 56 -- tests/spec/FreeDSx/Ldap/Sync/SyncReplSpec.php | 213 ------- tests/unit/ContainerTest.php | 91 +++ .../Control/Ad/DirSyncRequestControlTest.php | 119 ++++ .../Control/Ad/DirSyncResponseControlTest.php | 102 ++++ .../Ad/ExpectedEntryCountControlTest.php | 103 ++++ .../unit/Control/Ad/ExtendedDnControlTest.php | 85 +++ .../Control/Ad/PolicyHintsControlTest.php | 74 +++ tests/unit/Control/Ad/SdFlagsControlTest.php | 62 ++ tests/unit/Control/Ad/SetOwnerControlTest.php | 76 +++ tests/unit/Control/ControlBagTest.php | 132 +++++ tests/unit/Control/ControlTest.php | 92 +++ tests/unit/Control/PagingControlTest.php | 103 ++++ .../Control/PwdPolicyResponseControlTest.php | 109 ++++ tests/unit/Control/Sorting/SortKeyTest.php | 88 +++ .../Control/Sorting/SortingControlTest.php | 161 ++++++ .../Sorting/SortingResponseControlTest.php | 83 +++ tests/unit/Control/SubentriesControlTest.php | 72 +++ .../unit/Control/Sync/SyncDoneControlTest.php | 89 +++ .../Control/Sync/SyncRequestControlTest.php | 102 ++++ .../Control/Sync/SyncStateControlTest.php | 148 +++++ tests/unit/Control/Vlv/VlvControlTest.php | 94 +++ .../Control/Vlv/VlvResponseControlTest.php | 82 +++ tests/unit/ControlsTest.php | 219 +++++++ tests/unit/Entry/AttributeTest.php | 249 ++++++++ tests/unit/Entry/ChangeTest.php | 164 ++++++ tests/unit/Entry/ChangesSpec.php | 88 +++ tests/unit/Entry/DnTest.php | 98 ++++ tests/unit/Entry/EntriesTest.php | 147 +++++ tests/unit/Entry/EntryTest.php | 386 +++++++++++++ tests/unit/Entry/OptionTest.php | 128 +++++ tests/unit/Entry/OptionsSpec.php | 138 +++++ tests/unit/Entry/RdnTest.php | 113 ++++ .../unit/Exception/OperationExceptionTest.php | 60 ++ .../unit/Exception/ReferralExceptionTest.php | 60 ++ .../UnsolicitedNotificationExceptionTest.php | 40 ++ tests/unit/LdapClientTest.php | 539 ++++++++++++++++++ tests/unit/LdapServerTest.php | 172 ++++++ tests/unit/LdapUrlExtensionTest.php | 146 +++++ tests/unit/LdapUrlTest.php | 356 ++++++++++++ tests/unit/Operation/LdapResultTest.php | 118 ++++ .../Operation/Request/AbandonRequestTest.php | 66 +++ .../unit/Operation/Request/AddRequestTest.php | 142 +++++ .../Operation/Request/AnonBindRequestTest.php | 119 ++++ .../Operation/Request/CancelRequestTest.php | 94 +++ .../Operation/Request/CompareRequestTest.php | 109 ++++ .../Operation/Request/DeleteRequestTest.php | 91 +++ .../Operation/Request/ExtendedRequestTest.php | 117 ++++ .../Operation/Request/ModifyDnRequestTest.php | 164 ++++++ .../Operation/Request/ModifyRequestTest.php | 207 +++++++ .../Request/PasswordModifyRequestTest.php | 168 ++++++ .../Operation/Request/SearchRequestTest.php | 298 ++++++++++ .../Request/SimpleBindRequestTest.php | 170 ++++++ .../Operation/Request/SyncRequestTest.php | 40 ++ .../Operation/Request/UnbindRequestTest.php | 50 ++ .../Operation/Response/AddResponseTest.php | 39 ++ .../Operation/Response/BindResponseTest.php | 106 ++++ .../Response/CompareResponseTest.php | 35 ++ .../Operation/Response/DeleteResponseTest.php | 40 ++ .../Response/ExtendedResponseTest.php | 81 +++ .../Response/IntermediateResponseTest.php | 67 +++ .../Response/ModifyDnResponseTest.php | 36 ++ .../Operation/Response/ModifyResponseTest.php | 35 ++ .../Response/PasswordModifyResponseTest.php} | 70 ++- .../Response/SearchResponseTest.php} | 72 ++- .../Response/SearchResultDoneTest.php | 35 ++ .../Response/SearchResultEntryTest.php | 85 +++ .../Response/SearchResultReferenceTest.php | 81 +++ .../Response/SyncInfo/SyncIdSetTest.php | 129 +++++ .../Response/SyncInfo/SyncNewCookieTest.php | 72 +++ .../SyncInfo/SyncRefreshDeleteTest.php | 86 +++ .../SyncInfo/SyncRefreshPresentTest.php | 86 +++ tests/unit/OperationsTest.php | 289 ++++++++++ tests/unit/Protocol/AuthenticatorTest.php | 90 +++ .../unit/Protocol/Bind/AnonymousBindTest.php | 99 ++++ tests/unit/Protocol/Bind/SimpleBindTest.php | 137 +++++ .../ClientBasicHandlerTest.php | 142 +++++ .../ClientExtendedOperationHandlerTest.php | 97 ++++ .../ClientReferralHandlerTest.php | 299 ++++++++++ .../ClientSaslBindHandlerTest.php | 284 +++++++++ .../ClientSearchHandlerTest.php | 199 +++++++ .../ClientStartTlsHandlerTest.php} | 53 +- .../ClientSyncHandlerTest.php | 341 +++++++++++ .../ClientUnbindHandlerTest.php | 49 ++ .../RequestCancelerTest.php | 138 +++++ .../Protocol/ClientProtocolHandlerTest.php | 187 ++++++ .../ClientProtocolHandlerFactoryTest.php | 211 +++++++ .../Factory/ExtendedResponseFactoryTest.php | 73 +++ .../Protocol/Factory/FilterFactoryTest.php | 42 ++ .../Protocol/Factory/ResponseFactoryTest.php | 208 +++++++ .../ServerProtocolHandlerFactoryTest.php | 194 +++++++ .../unit/Protocol/LdapMessageRequestTest.php | 67 +++ .../Protocol/LdapMessageResponseTest.php} | 59 +- tests/unit/Protocol/LdapQueueTest.php | 73 +++ .../Queue/ClientQueueInstantiatorTest.php | 100 ++++ tests/unit/Protocol/Queue/ClientQueueTest.php | 209 +++++++ .../MessageWrapper/SaslMessageWrapperTest.php | 84 +++ tests/unit/Protocol/Queue/ServerQueueTest.php | 157 +++++ tests/unit/Protocol/ReferralContextTest.php | 63 ++ tests/unit/Protocol/RootDseLoaderTest.php | 91 +++ .../unit/Protocol/ServerAuthorizationTest.php | 166 ++++++ .../ServerDispatchHandlerTest.php | 163 ++++++ .../ServerPagingHandlerTest.php} | 238 ++++---- .../ServerPagingUnsupportedHandlerTest.php | 167 ++++++ .../ServerRootDseHandlerTest.php | 250 ++++++++ .../ServerSearchHandlerTest.php | 126 ++++ .../ServerStartTlsHandlerTest.php | 141 +++++ .../ServerUnbindHandlerTest.php | 51 ++ .../ServerWhoAmIHandlerTest.php | 101 ++++ .../Protocol/ServerProtocolHandlerTest.php | 359 ++++++++++++ tests/unit/Search/DirSyncTest.php | 291 ++++++++++ tests/unit/Search/Filter/AndFilterTest.php | 175 ++++++ .../Search/Filter/ApproximateFilterTest.php | 92 +++ .../unit/Search/Filter/EqualityFilterTest.php | 100 ++++ .../Filter/GreaterThanOrEqualFilterTest.php | 95 +++ .../Filter/LessThanOrEqualFilterTest.php | 95 +++ .../Search/Filter/MatchingRuleFilterTest.php | 177 ++++++ tests/unit/Search/Filter/NotFilterTest.php | 93 +++ tests/unit/Search/Filter/OrFilterTest.php | 181 ++++++ .../unit/Search/Filter/PresentFilterTest.php | 69 +++ .../Search/Filter/SubstringFilterTest.php | 271 +++++++++ tests/unit/Search/FilterParserTest.php | 326 +++++++++++ tests/unit/Search/FiltersTest.php | 166 ++++++ tests/unit/Search/PagingTest.php | 200 +++++++ tests/unit/Search/RangeRetrievalTest.php | 182 ++++++ tests/unit/Search/Result/EntryResultTest.php | 89 +++ .../unit/Search/Result/ReferralResultTest.php | 95 +++ tests/unit/Search/VlvTest.php | 371 ++++++++++++ tests/unit/Server/ChildProcessTest.php | 61 ++ .../Paging/PagingRequestComparatorTest.php} | 42 +- .../unit/Server/Paging/PagingRequestTest.php | 183 ++++++ .../unit/Server/Paging/PagingRequestsTest.php | 92 +++ .../unit/Server/Paging/PagingResponseTest.php | 63 ++ tests/unit/Server/RequestContextTest.php | 48 ++ .../GenericRequestHandlerTest.php | 81 +++ .../RequestHandler/HandlerFactoryTest.php | 80 +++ .../RequestHandler/ProxyHandlerTest.php | 85 +++ .../RequestHandler/ProxyPagingHandlerTest.php | 129 +++++ .../ProxyRequestHandlerTest.php | 228 ++++++++ tests/unit/Server/RequestHistoryTest.php | 63 ++ .../unit/Server/ServerProtocolFactoryTest.php | 60 ++ tests/unit/Server/SocketServerFactoryTest.php | 95 +++ tests/unit/Server/Token/AnonTokenTest.php | 48 ++ tests/unit/Server/Token/BindTokenTest.php | 54 ++ .../unit/Sync/Result/SyncEntryResultTest.php | 195 +++++++ .../unit/Sync/Result/SyncIdSetResultTest.php | 140 +++++ .../Sync/Result/SyncReferralResultTest.php} | 61 +- tests/unit/Sync/SessionTest.php | 60 ++ tests/unit/Sync/SyncReplTest.php | 238 ++++++++ .../Ldap => unit}/TestFactoryTrait.php | 2 +- 316 files changed, 19082 insertions(+), 15753 deletions(-) rename tests/integration/{FreeDSx/Ldap => }/LdapClientTest.php (98%) rename tests/integration/{FreeDSx/Ldap => }/LdapProxyTest.php (91%) rename tests/integration/{FreeDSx/Ldap => }/LdapServerTest.php (99%) rename tests/integration/{FreeDSx/Ldap => }/LdapTestCase.php (97%) rename tests/integration/{FreeDSx/Ldap => }/Search/DirSyncTest.php (94%) rename tests/integration/{FreeDSx/Ldap => }/Search/PagingTest.php (88%) rename tests/integration/{FreeDSx/Ldap => }/Search/RangeRetrievalTest.php (95%) rename tests/integration/{FreeDSx/Ldap => }/Search/VlvTest.php (84%) rename tests/integration/{FreeDSx/Ldap => }/ServerTestCase.php (98%) rename tests/integration/{FreeDSx/Ldap => }/Sync/SyncReplTest.php (93%) delete mode 100644 tests/spec/FreeDSx/Ldap/ContainerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncRequestControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncResponseControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/ExpectedEntryCountControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/ExtendedDnControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/PolicyHintsControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/SdFlagsControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Ad/SetOwnerControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/ControlBagSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/ControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/PagingControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/PwdPolicyResponseControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sorting/SortKeySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sorting/SortingControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sorting/SortingResponseControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/SubentriesControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sync/SyncDoneControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sync/SyncRequestControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Sync/SyncStateControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Vlv/VlvControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Control/Vlv/VlvResponseControlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/ControlsSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/AttributeSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/ChangeSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/ChangesSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/DnSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/EntriesSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/EntrySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/OptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/OptionsSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Entry/RdnSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/BindExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/ConnectionExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/FilterParseExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/InvalidArgumentExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/OperationExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/ProtocolExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/ReferralExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/RuntimeExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/SkipReferralExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/UnexpectedValueExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/UnsolicitedNotificationExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Exception/UrlParseExceptionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/LdapClientSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/LdapServerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/LdapUrlExtensionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/LdapUrlSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/LdapResultSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/AbandonRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/AddRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/AnonBindRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/CancelRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/CompareRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/DeleteRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/ExtendedRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/ModifyDnRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/ModifyRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/PasswordModifyRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/SearchRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/SimpleBindRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/SyncRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Request/UnbindRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/AddResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/BindResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/CompareResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/DeleteResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/ExtendedResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/IntermediateResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/ModifyDnResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/ModifyResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultDoneSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultEntrySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultReferenceSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncIdSetSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncNewCookieSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshDeleteSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshPresentSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/OperationsSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/AuthenticatorSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Bind/AnonymousBindSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Bind/SimpleBindSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientBasicHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientReferralHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSaslBindHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSearchHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSyncHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientUnbindHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/MockCancelResponseProcessor.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/RequestCancelerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Factory/ClientProtocolHandlerFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Factory/ExtendedResponseFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Factory/FilterFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Factory/ResponseFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Factory/ServerProtocolHandlerFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/LdapMessageRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/LdapQueueSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueInstantiatorSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Queue/MessageWrapper/SaslMessageWrapperSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/Queue/ServerQueueSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ReferralContextSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/RootDseLoaderSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerAuthorizationSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerDispatchHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerRootDseHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerSearchHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerStartTlsHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerUnbindHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/DirSyncSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/AndFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/ApproximateFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/EqualityFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/GreaterThanOrEqualFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/LessThanOrEqualFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/MatchingRuleFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/NotFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/OrFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/PresentFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Filter/SubstringFilterSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/FilterParserSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/FiltersSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/PagingSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/RangeRetrievalSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Result/EntryResultSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/Result/ReferralResultSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Search/VlvSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/ChildProcessSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestsSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/Paging/PagingResponseSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestContextSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHandler/GenericRequestHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHandler/HandlerFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyPagingHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyRequestHandlerSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/RequestHistorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/ServerProtocolFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/SocketServerFactorySpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/Token/AnonTokenSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Server/Token/BindTokenSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/MockSyncEntryHandler.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/MockSyncIdSetHandler.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/MockSyncReferralHandler.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/Result/SyncEntryResultSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/Result/SyncIdSetResultSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/SessionSpec.php delete mode 100644 tests/spec/FreeDSx/Ldap/Sync/SyncReplSpec.php create mode 100644 tests/unit/ContainerTest.php create mode 100644 tests/unit/Control/Ad/DirSyncRequestControlTest.php create mode 100644 tests/unit/Control/Ad/DirSyncResponseControlTest.php create mode 100644 tests/unit/Control/Ad/ExpectedEntryCountControlTest.php create mode 100644 tests/unit/Control/Ad/ExtendedDnControlTest.php create mode 100644 tests/unit/Control/Ad/PolicyHintsControlTest.php create mode 100644 tests/unit/Control/Ad/SdFlagsControlTest.php create mode 100644 tests/unit/Control/Ad/SetOwnerControlTest.php create mode 100644 tests/unit/Control/ControlBagTest.php create mode 100644 tests/unit/Control/ControlTest.php create mode 100644 tests/unit/Control/PagingControlTest.php create mode 100644 tests/unit/Control/PwdPolicyResponseControlTest.php create mode 100644 tests/unit/Control/Sorting/SortKeyTest.php create mode 100644 tests/unit/Control/Sorting/SortingControlTest.php create mode 100644 tests/unit/Control/Sorting/SortingResponseControlTest.php create mode 100644 tests/unit/Control/SubentriesControlTest.php create mode 100644 tests/unit/Control/Sync/SyncDoneControlTest.php create mode 100644 tests/unit/Control/Sync/SyncRequestControlTest.php create mode 100644 tests/unit/Control/Sync/SyncStateControlTest.php create mode 100644 tests/unit/Control/Vlv/VlvControlTest.php create mode 100644 tests/unit/Control/Vlv/VlvResponseControlTest.php create mode 100644 tests/unit/ControlsTest.php create mode 100644 tests/unit/Entry/AttributeTest.php create mode 100644 tests/unit/Entry/ChangeTest.php create mode 100644 tests/unit/Entry/ChangesSpec.php create mode 100644 tests/unit/Entry/DnTest.php create mode 100644 tests/unit/Entry/EntriesTest.php create mode 100644 tests/unit/Entry/EntryTest.php create mode 100644 tests/unit/Entry/OptionTest.php create mode 100644 tests/unit/Entry/OptionsSpec.php create mode 100644 tests/unit/Entry/RdnTest.php create mode 100644 tests/unit/Exception/OperationExceptionTest.php create mode 100644 tests/unit/Exception/ReferralExceptionTest.php create mode 100644 tests/unit/Exception/UnsolicitedNotificationExceptionTest.php create mode 100644 tests/unit/LdapClientTest.php create mode 100644 tests/unit/LdapServerTest.php create mode 100644 tests/unit/LdapUrlExtensionTest.php create mode 100644 tests/unit/LdapUrlTest.php create mode 100644 tests/unit/Operation/LdapResultTest.php create mode 100644 tests/unit/Operation/Request/AbandonRequestTest.php create mode 100644 tests/unit/Operation/Request/AddRequestTest.php create mode 100644 tests/unit/Operation/Request/AnonBindRequestTest.php create mode 100644 tests/unit/Operation/Request/CancelRequestTest.php create mode 100644 tests/unit/Operation/Request/CompareRequestTest.php create mode 100644 tests/unit/Operation/Request/DeleteRequestTest.php create mode 100644 tests/unit/Operation/Request/ExtendedRequestTest.php create mode 100644 tests/unit/Operation/Request/ModifyDnRequestTest.php create mode 100644 tests/unit/Operation/Request/ModifyRequestTest.php create mode 100644 tests/unit/Operation/Request/PasswordModifyRequestTest.php create mode 100644 tests/unit/Operation/Request/SearchRequestTest.php create mode 100644 tests/unit/Operation/Request/SimpleBindRequestTest.php create mode 100644 tests/unit/Operation/Request/SyncRequestTest.php create mode 100644 tests/unit/Operation/Request/UnbindRequestTest.php create mode 100644 tests/unit/Operation/Response/AddResponseTest.php create mode 100644 tests/unit/Operation/Response/BindResponseTest.php create mode 100644 tests/unit/Operation/Response/CompareResponseTest.php create mode 100644 tests/unit/Operation/Response/DeleteResponseTest.php create mode 100644 tests/unit/Operation/Response/ExtendedResponseTest.php create mode 100644 tests/unit/Operation/Response/IntermediateResponseTest.php create mode 100644 tests/unit/Operation/Response/ModifyDnResponseTest.php create mode 100644 tests/unit/Operation/Response/ModifyResponseTest.php rename tests/{spec/FreeDSx/Ldap/Operation/Response/PasswordModifyResponseSpec.php => unit/Operation/Response/PasswordModifyResponseTest.php} (50%) rename tests/{spec/FreeDSx/Ldap/Operation/Response/SearchResponseSpec.php => unit/Operation/Response/SearchResponseTest.php} (59%) create mode 100644 tests/unit/Operation/Response/SearchResultDoneTest.php create mode 100644 tests/unit/Operation/Response/SearchResultEntryTest.php create mode 100644 tests/unit/Operation/Response/SearchResultReferenceTest.php create mode 100644 tests/unit/Operation/Response/SyncInfo/SyncIdSetTest.php create mode 100644 tests/unit/Operation/Response/SyncInfo/SyncNewCookieTest.php create mode 100644 tests/unit/Operation/Response/SyncInfo/SyncRefreshDeleteTest.php create mode 100644 tests/unit/Operation/Response/SyncInfo/SyncRefreshPresentTest.php create mode 100644 tests/unit/OperationsTest.php create mode 100644 tests/unit/Protocol/AuthenticatorTest.php create mode 100644 tests/unit/Protocol/Bind/AnonymousBindTest.php create mode 100644 tests/unit/Protocol/Bind/SimpleBindTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientBasicHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientReferralHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientSaslBindHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientSearchHandlerTest.php rename tests/{spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientStartTlsHandlerSpec.php => unit/Protocol/ClientProtocolHandler/ClientStartTlsHandlerTest.php} (50%) create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientSyncHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/ClientUnbindHandlerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandler/RequestCancelerTest.php create mode 100644 tests/unit/Protocol/ClientProtocolHandlerTest.php create mode 100644 tests/unit/Protocol/Factory/ClientProtocolHandlerFactoryTest.php create mode 100644 tests/unit/Protocol/Factory/ExtendedResponseFactoryTest.php create mode 100644 tests/unit/Protocol/Factory/FilterFactoryTest.php create mode 100644 tests/unit/Protocol/Factory/ResponseFactoryTest.php create mode 100644 tests/unit/Protocol/Factory/ServerProtocolHandlerFactoryTest.php create mode 100644 tests/unit/Protocol/LdapMessageRequestTest.php rename tests/{spec/FreeDSx/Ldap/Protocol/LdapMessageResponseSpec.php => unit/Protocol/LdapMessageResponseTest.php} (50%) create mode 100644 tests/unit/Protocol/LdapQueueTest.php create mode 100644 tests/unit/Protocol/Queue/ClientQueueInstantiatorTest.php create mode 100644 tests/unit/Protocol/Queue/ClientQueueTest.php create mode 100644 tests/unit/Protocol/Queue/MessageWrapper/SaslMessageWrapperTest.php create mode 100644 tests/unit/Protocol/Queue/ServerQueueTest.php create mode 100644 tests/unit/Protocol/ReferralContextTest.php create mode 100644 tests/unit/Protocol/RootDseLoaderTest.php create mode 100644 tests/unit/Protocol/ServerAuthorizationTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerDispatchHandlerTest.php rename tests/{spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingHandlerSpec.php => unit/Protocol/ServerProtocolHandler/ServerPagingHandlerTest.php} (50%) create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerRootDseHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerSearchHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerStartTlsHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerUnbindHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php create mode 100644 tests/unit/Protocol/ServerProtocolHandlerTest.php create mode 100644 tests/unit/Search/DirSyncTest.php create mode 100644 tests/unit/Search/Filter/AndFilterTest.php create mode 100644 tests/unit/Search/Filter/ApproximateFilterTest.php create mode 100644 tests/unit/Search/Filter/EqualityFilterTest.php create mode 100644 tests/unit/Search/Filter/GreaterThanOrEqualFilterTest.php create mode 100644 tests/unit/Search/Filter/LessThanOrEqualFilterTest.php create mode 100644 tests/unit/Search/Filter/MatchingRuleFilterTest.php create mode 100644 tests/unit/Search/Filter/NotFilterTest.php create mode 100644 tests/unit/Search/Filter/OrFilterTest.php create mode 100644 tests/unit/Search/Filter/PresentFilterTest.php create mode 100644 tests/unit/Search/Filter/SubstringFilterTest.php create mode 100644 tests/unit/Search/FilterParserTest.php create mode 100644 tests/unit/Search/FiltersTest.php create mode 100644 tests/unit/Search/PagingTest.php create mode 100644 tests/unit/Search/RangeRetrievalTest.php create mode 100644 tests/unit/Search/Result/EntryResultTest.php create mode 100644 tests/unit/Search/Result/ReferralResultTest.php create mode 100644 tests/unit/Search/VlvTest.php create mode 100644 tests/unit/Server/ChildProcessTest.php rename tests/{spec/FreeDSx/Ldap/Server/Paging/PagingRequestComparatorSpec.php => unit/Server/Paging/PagingRequestComparatorTest.php} (73%) create mode 100644 tests/unit/Server/Paging/PagingRequestTest.php create mode 100644 tests/unit/Server/Paging/PagingRequestsTest.php create mode 100644 tests/unit/Server/Paging/PagingResponseTest.php create mode 100644 tests/unit/Server/RequestContextTest.php create mode 100644 tests/unit/Server/RequestHandler/GenericRequestHandlerTest.php create mode 100644 tests/unit/Server/RequestHandler/HandlerFactoryTest.php create mode 100644 tests/unit/Server/RequestHandler/ProxyHandlerTest.php create mode 100644 tests/unit/Server/RequestHandler/ProxyPagingHandlerTest.php create mode 100644 tests/unit/Server/RequestHandler/ProxyRequestHandlerTest.php create mode 100644 tests/unit/Server/RequestHistoryTest.php create mode 100644 tests/unit/Server/ServerProtocolFactoryTest.php create mode 100644 tests/unit/Server/SocketServerFactoryTest.php create mode 100644 tests/unit/Server/Token/AnonTokenTest.php create mode 100644 tests/unit/Server/Token/BindTokenTest.php create mode 100644 tests/unit/Sync/Result/SyncEntryResultTest.php create mode 100644 tests/unit/Sync/Result/SyncIdSetResultTest.php rename tests/{spec/FreeDSx/Ldap/Sync/Result/SyncReferralResultSpec.php => unit/Sync/Result/SyncReferralResultTest.php} (62%) create mode 100644 tests/unit/Sync/SessionTest.php create mode 100644 tests/unit/Sync/SyncReplTest.php rename tests/{spec/FreeDSx/Ldap => unit}/TestFactoryTrait.php (98%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb4080e1..f019d704 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,8 +39,8 @@ jobs: key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- - - name: Run Specs - run: composer run-script test-spec + - name: Run Unit Tests + run: composer run-script test-unit # Not ideal, but we run as root here because the LDAP server needs access to certs / keys system directories. # For tests this seems fine, but in a real scenario this would be bad... diff --git a/composer.json b/composer.json index 754bb504..d7daa268 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,14 @@ "psr/log": "^3" }, "require-dev": { - "phpspec/phpspec": "^7.2", - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^10.5", "symplify/easy-coding-standard": "^9.4", - "friends-of-phpspec/phpspec-code-coverage": "^6.3", - "phpstan/phpstan": "^1.11", + "phpstan/phpstan": "^2.1", "symfony/process": "^5.4", "squizlabs/php_codesniffer": "^3.7", - "slevomat/coding-standard": "^7.2" + "slevomat/coding-standard": "^7.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/extension-installer": "^1.4" }, "suggest": { "ext-openssl": "For SSL/TLS support and some SASL mechanisms.", @@ -40,13 +40,14 @@ }, "autoload-dev": { "psr-4": { - "integration\\FreeDSx\\Ldap\\": "tests/integration/FreeDSx/Ldap", - "spec\\FreeDSx\\Ldap\\": "tests/spec/FreeDSx/Ldap" + "Tests\\Integration\\FreeDSx\\Ldap\\": "tests/integration", + "Tests\\Unit\\FreeDSx\\Ldap\\": "tests/unit" } }, "config": { "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true } }, "scripts": { @@ -54,17 +55,17 @@ "phpspec run --no-interaction -c phpspec.cov.yml", "phpunit --coverage-clover=coverage-integration.xml" ], - "test-spec": [ - "phpspec run --no-interaction" + "test-unit": [ + "phpunit --testsuite unit" ], "test-integration": [ - "phpunit" + "phpunit --testsuite integration" ], "analyse": [ "phpstan --memory-limit=-1 analyse" ], "analyse-tests": [ - "phpstan analyse -c phpstan.tests.neon" + "phpstan --memory-limit=-1 analyse -c phpstan.tests.neon" ], "cs-fix": [ "phpcbf --standard=ruleset.xml --extensions=php --tab-width=4 -sp src", diff --git a/phpstan.tests.neon b/phpstan.tests.neon index 3399513b..3a7550e8 100644 --- a/phpstan.tests.neon +++ b/phpstan.tests.neon @@ -1,7 +1,4 @@ parameters: - level: 1 + level: 3 paths: - %currentWorkingDirectory%/tests - ignoreErrors: - # Does not seem to be a great extension for phpspec :( - - '#Call to an undefined (static )?method spec\\FreeDSx\\Ldap\\.*Spec::#' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index aab8a119..eb16731e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,29 @@ - - - - src - - + + + - + ./tests/integration + + ./tests/unit + diff --git a/src/FreeDSx/Ldap/Server/RequestHistory.php b/src/FreeDSx/Ldap/Server/RequestHistory.php index 1b9d9cfe..3422edee 100644 --- a/src/FreeDSx/Ldap/Server/RequestHistory.php +++ b/src/FreeDSx/Ldap/Server/RequestHistory.php @@ -59,4 +59,12 @@ public function pagingRequest(): PagingRequests { return $this->pagingRequests; } + + /** + * @return int[] + */ + public function getIds(): array + { + return $this->ids; + } } diff --git a/tests/integration/FreeDSx/Ldap/LdapClientTest.php b/tests/integration/LdapClientTest.php similarity index 98% rename from tests/integration/FreeDSx/Ldap/LdapClientTest.php rename to tests/integration/LdapClientTest.php index 7cf842e7..7502db6f 100644 --- a/tests/integration/FreeDSx/Ldap/LdapClientTest.php +++ b/tests/integration/LdapClientTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap; +namespace Tests\Integration\FreeDSx\Ldap; use FreeDSx\Ldap\ClientOptions; use FreeDSx\Ldap\Entry\Entries; @@ -379,9 +379,9 @@ public function testSearchOperationWithEntryHandler(): void $this->client->search($op); - $this->assertSame( + $this->assertCount( 843, - $entries->count() + $entries ); } @@ -424,9 +424,9 @@ public function testSubSearchOperation(): void Entries::class, $entries ); - $this->assertSame( + $this->assertCount( 843, - $entries->count() + $entries, ); } @@ -439,13 +439,9 @@ public function testListSearchOperation(): void 'ou=Payroll,ou=FreeDSx-Test,dc=example,dc=com' )); - $this->assertInstanceOf( - Entries::class, - $entries - ); - $this->assertSame( + $this->assertCount( 100, - $entries->count() + $entries, ); foreach ($entries->toArray() as $entry) { diff --git a/tests/integration/FreeDSx/Ldap/LdapProxyTest.php b/tests/integration/LdapProxyTest.php similarity index 91% rename from tests/integration/FreeDSx/Ldap/LdapProxyTest.php rename to tests/integration/LdapProxyTest.php index 9d65c643..dbb9ef40 100644 --- a/tests/integration/FreeDSx/Ldap/LdapProxyTest.php +++ b/tests/integration/LdapProxyTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap; +namespace Tests\Integration\FreeDSx\Ldap; use FreeDSx\Ldap\Operations; use FreeDSx\Ldap\Search\Filters; @@ -58,18 +58,18 @@ public function testItCanHandlePaging(): void $entries = $paging->getEntries(); - $this->assertSame( + $this->assertCount( 1000, - $entries->count() + $entries ); while ($paging->hasEntries()) { $entries->add(...$paging->getEntries()->toArray()); } - $this->assertSame( + $this->assertCount( 10001, - $entries->count() + $entries ); } diff --git a/tests/integration/FreeDSx/Ldap/LdapServerTest.php b/tests/integration/LdapServerTest.php similarity index 99% rename from tests/integration/FreeDSx/Ldap/LdapServerTest.php rename to tests/integration/LdapServerTest.php index ac30dc5b..7efeb249 100644 --- a/tests/integration/FreeDSx/Ldap/LdapServerTest.php +++ b/tests/integration/LdapServerTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap; +namespace Tests\Integration\FreeDSx\Ldap; use FreeDSx\Ldap\Entry\Entry; use FreeDSx\Ldap\Exception\BindException; diff --git a/tests/integration/FreeDSx/Ldap/LdapTestCase.php b/tests/integration/LdapTestCase.php similarity index 97% rename from tests/integration/FreeDSx/Ldap/LdapTestCase.php rename to tests/integration/LdapTestCase.php index 8f50d619..09eb679d 100644 --- a/tests/integration/FreeDSx/Ldap/LdapTestCase.php +++ b/tests/integration/LdapTestCase.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap; +namespace Tests\Integration\FreeDSx\Ldap; use FreeDSx\Ldap\ClientOptions; use FreeDSx\Ldap\LdapClient; diff --git a/tests/integration/FreeDSx/Ldap/Search/DirSyncTest.php b/tests/integration/Search/DirSyncTest.php similarity index 94% rename from tests/integration/FreeDSx/Ldap/Search/DirSyncTest.php rename to tests/integration/Search/DirSyncTest.php index 2cb5854f..be177b10 100644 --- a/tests/integration/FreeDSx/Ldap/Search/DirSyncTest.php +++ b/tests/integration/Search/DirSyncTest.php @@ -11,13 +11,13 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap\Search; +namespace Tests\Integration\FreeDSx\Ldap\Search; use FreeDSx\Ldap\LdapClient; use FreeDSx\Ldap\Search\DirSync; use FreeDSx\Ldap\Search\Filter\FilterInterface; use FreeDSx\Ldap\Search\Filters; -use integration\FreeDSx\Ldap\LdapTestCase; +use Tests\Integration\FreeDSx\Ldap\LdapTestCase; class DirSyncTest extends LdapTestCase { diff --git a/tests/integration/FreeDSx/Ldap/Search/PagingTest.php b/tests/integration/Search/PagingTest.php similarity index 88% rename from tests/integration/FreeDSx/Ldap/Search/PagingTest.php rename to tests/integration/Search/PagingTest.php index f3f78678..e9ae853a 100644 --- a/tests/integration/FreeDSx/Ldap/Search/PagingTest.php +++ b/tests/integration/Search/PagingTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap\Search; +namespace Tests\Integration\FreeDSx\Ldap\Search; use FreeDSx\Ldap\Entry\Entries; use FreeDSx\Ldap\LdapClient; @@ -20,7 +20,7 @@ use FreeDSx\Ldap\Search\Filters; use FreeDSx\Ldap\Search\Paging; use FreeDSx\Ldap\Search\Result\EntryResult; -use integration\FreeDSx\Ldap\LdapTestCase; +use Tests\Integration\FreeDSx\Ldap\LdapTestCase; use Throwable; class PagingTest extends LdapTestCase @@ -62,18 +62,18 @@ public function testPagingAll(): void { $entries = $this->paging->getEntries(); - $this->assertSame( + $this->assertCount( 1000, - $entries->count() + $entries ); while ($this->paging->hasEntries()) { $entries->add(...$this->paging->getEntries()); } - $this->assertSame( + $this->assertCount( 10001, - $entries->count() + $entries ); } @@ -96,9 +96,9 @@ public function testPagingAllWhenEntryHandlerIsUsed(): void $this->paging->getEntries(); } - $this->assertSame( + $this->assertCount( 10001, - $entries->count() + $entries ); } @@ -106,9 +106,9 @@ public function testPagingSpecificSize(): void { $entries = $this->paging->getEntries(100); - $this->assertSame( + $this->assertCount( 100, - $entries->count() + $entries ); $this->assertTrue($this->paging->hasEntries()); diff --git a/tests/integration/FreeDSx/Ldap/Search/RangeRetrievalTest.php b/tests/integration/Search/RangeRetrievalTest.php similarity index 95% rename from tests/integration/FreeDSx/Ldap/Search/RangeRetrievalTest.php rename to tests/integration/Search/RangeRetrievalTest.php index ab19d9b4..d2694cb2 100644 --- a/tests/integration/FreeDSx/Ldap/Search/RangeRetrievalTest.php +++ b/tests/integration/Search/RangeRetrievalTest.php @@ -11,12 +11,12 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap\Search; +namespace Tests\Integration\FreeDSx\Ldap\Search; use FreeDSx\Ldap\Entry\Attribute; use FreeDSx\Ldap\LdapClient; use FreeDSx\Ldap\Search\RangeRetrieval; -use integration\FreeDSx\Ldap\LdapTestCase; +use Tests\Integration\FreeDSx\Ldap\LdapTestCase; class RangeRetrievalTest extends LdapTestCase { @@ -50,9 +50,9 @@ public function testRetrieveAll(): void 'member' ); - $this->assertSame( + $this->assertCount( 10001, - count($result->getValues()) + $result->getValues() ); } diff --git a/tests/integration/FreeDSx/Ldap/Search/VlvTest.php b/tests/integration/Search/VlvTest.php similarity index 84% rename from tests/integration/FreeDSx/Ldap/Search/VlvTest.php rename to tests/integration/Search/VlvTest.php index fba268e5..55bfd5c0 100644 --- a/tests/integration/FreeDSx/Ldap/Search/VlvTest.php +++ b/tests/integration/Search/VlvTest.php @@ -11,14 +11,14 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap\Search; +namespace Tests\Integration\FreeDSx\Ldap\Search; use FreeDSx\Ldap\LdapClient; use FreeDSx\Ldap\Operation\Request\SearchRequest; use FreeDSx\Ldap\Operations; use FreeDSx\Ldap\Search\Filters; use FreeDSx\Ldap\Search\Vlv; -use integration\FreeDSx\Ldap\LdapTestCase; +use Tests\Integration\FreeDSx\Ldap\LdapTestCase; use Throwable; class VlvTest extends LdapTestCase @@ -56,9 +56,9 @@ protected function tearDown(): void public function testVlv(): void { - $this->assertSame( + $this->assertCount( 101, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertSame( 453, @@ -72,9 +72,9 @@ public function testVlv(): void $this->vlv->moveForward(100); - $this->assertSame( + $this->assertCount( 101, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertSame( 101, @@ -83,9 +83,9 @@ public function testVlv(): void $this->vlv->moveTo(300); - $this->assertSame( + $this->assertCount( 101, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertSame( 300, @@ -94,9 +94,9 @@ public function testVlv(): void $this->vlv->moveBackward(100); - $this->assertSame( + $this->assertCount( 101, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertSame( 200, @@ -105,9 +105,9 @@ public function testVlv(): void $this->vlv->moveTo((int) $this->vlv->listSize()); - $this->assertSame( + $this->assertCount( 1, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertTrue($this->vlv->isAtEndOfList()); } @@ -128,9 +128,9 @@ public function testVlvAsPercentage(): void $this->vlv->beforePosition(100); $this->vlv->moveTo(50); - $this->assertSame( + $this->assertCount( 201, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertGreaterThan( 215, @@ -143,9 +143,9 @@ public function testVlvAsPercentage(): void $this->vlv->moveForward(25); - $this->assertSame( + $this->assertCount( 201, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertGreaterThan( 325, @@ -158,9 +158,9 @@ public function testVlvAsPercentage(): void $this->vlv->moveBackward(50); - $this->assertSame( + $this->assertCount( 201, - $this->vlv->getEntries()->count() + $this->vlv->getEntries() ); $this->assertGreaterThan( 105, diff --git a/tests/integration/FreeDSx/Ldap/ServerTestCase.php b/tests/integration/ServerTestCase.php similarity index 98% rename from tests/integration/FreeDSx/Ldap/ServerTestCase.php rename to tests/integration/ServerTestCase.php index 1a63d657..dfa52de0 100644 --- a/tests/integration/FreeDSx/Ldap/ServerTestCase.php +++ b/tests/integration/ServerTestCase.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap; +namespace Tests\Integration\FreeDSx\Ldap; use Exception; use FreeDSx\Ldap\LdapClient; diff --git a/tests/integration/FreeDSx/Ldap/Sync/SyncReplTest.php b/tests/integration/Sync/SyncReplTest.php similarity index 93% rename from tests/integration/FreeDSx/Ldap/Sync/SyncReplTest.php rename to tests/integration/Sync/SyncReplTest.php index e51ec795..2f163177 100644 --- a/tests/integration/FreeDSx/Ldap/Sync/SyncReplTest.php +++ b/tests/integration/Sync/SyncReplTest.php @@ -11,11 +11,11 @@ * file that was distributed with this source code. */ -namespace integration\FreeDSx\Ldap\Sync; +namespace Tests\Integration\FreeDSx\Ldap\Sync; use FreeDSx\Ldap\Exception\CancelRequestException; use FreeDSx\Ldap\Sync\Result\SyncEntryResult; -use integration\FreeDSx\Ldap\LdapTestCase; +use Tests\Integration\FreeDSx\Ldap\LdapTestCase; class SyncReplTest extends LdapTestCase { diff --git a/tests/spec/FreeDSx/Ldap/ContainerSpec.php b/tests/spec/FreeDSx/Ldap/ContainerSpec.php deleted file mode 100644 index dfd01bbf..00000000 --- a/tests/spec/FreeDSx/Ldap/ContainerSpec.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Container; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler; -use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; -use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; -use FreeDSx\Ldap\Protocol\RootDseLoader; -use FreeDSx\Ldap\Protocol\ServerAuthorization; -use FreeDSx\Ldap\Server\HandlerFactoryInterface; -use FreeDSx\Ldap\Server\ServerProtocolFactory; -use FreeDSx\Ldap\Server\ServerRunner\ServerRunnerInterface; -use FreeDSx\Ldap\Server\SocketServerFactory; -use FreeDSx\Ldap\ServerOptions; -use FreeDSx\Socket\SocketPool; -use PhpSpec\Exception\Example\SkippingException; -use PhpSpec\ObjectBehavior; - -class ContainerSpec extends ObjectBehavior -{ - public function let(): void - { - $client = new LdapClient(); - $serverOptions = new ServerOptions(); - - $this->beConstructedWith([ - LdapClient::class => $client, - ClientOptions::class => $client->getOptions(), - ServerOptions::class => $serverOptions, - ]); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Container::class); - } - - public function it_should_get_the_client(LdapClient $client): void - { - $this->get(LdapClient::class) - ->shouldBeAnInstanceOf(LdapClient::class); - } - - public function it_should_make_the_client_protocol_handler(): void - { - $this->get(ClientProtocolHandler::class) - ->shouldBeAnInstanceOf(ClientProtocolHandler::class); - } - - public function it_should_make_the_ClientQueueInstantiator(): void - { - $this->get(ClientQueueInstantiator::class) - ->shouldBeAnInstanceOf(ClientQueueInstantiator::class); - } - - public function it_shoulld_make_the_ClientProtocolHandlerFactory(): void - { - $this->get(ClientProtocolHandlerFactory::class) - ->shouldBeAnInstanceOf(ClientProtocolHandlerFactory::class); - } - - public function it_should_make_the_SocketPool(): void - { - $this->get(SocketPool::class) - ->shouldBeAnInstanceOf(SocketPool::class); - } - - public function it_should_make_the_RootDseLoader(): void - { - $this->get(RootDseLoader::class) - ->shouldBeAnInstanceOf(RootDseLoader::class); - } - - public function it_should_make_the_ServerProtocolFactory(): void - { - $this->get(ServerProtocolFactory::class) - ->shouldBeAnInstanceOf(ServerProtocolFactory::class); - } - - public function it_should_make_the_default_ServerRunner(): void - { - if (str_starts_with(strtoupper(PHP_OS), 'WIN')) { - throw new SkippingException('Cannot construct the default PCNTL runner on Windows.'); - } - - $this->get(ServerRunnerInterface::class) - ->shouldBeAnInstanceOf(ServerRunnerInterface::class); - } - - public function it_should_make_the_default_HandlerFactory(): void - { - $this->get(HandlerFactoryInterface::class) - ->shouldBeAnInstanceOf(HandlerFactoryInterface::class); - } - - public function it_should_make_the_ServerAuthorization(): void - { - $this->get(ServerAuthorization::class) - ->shouldBeAnInstanceOf(ServerAuthorization::class); - } - - public function it_should_make_the_ServerSocketFactory(): void - { - $this->get(SocketServerFactory::class) - ->shouldBeAnInstanceOf(SocketServerFactory::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncRequestControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncRequestControlSpec.php deleted file mode 100644 index 5a6a85bd..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncRequestControlSpec.php +++ /dev/null @@ -1,91 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class DirSyncRequestControlSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(DirSyncRequestControl::class); - } - - public function it_should_set_the_flags(): void - { - $this->setFlags(DirSyncRequestControl::FLAG_PUBLIC_DATA_ONLY); - $this->getFlags()->shouldBeEqualTo(DirSyncRequestControl::FLAG_PUBLIC_DATA_ONLY); - } - - public function it_should_have_incremental_values_as_the_default_flags(): void - { - $this->getFlags()->shouldBeEqualTo((int) DirSyncRequestControl::FLAG_INCREMENTAL_VALUES); - } - - public function it_should_set_the_cookie(): void - { - $this->setCookie('foo'); - $this->getCookie()->shouldBeEqualTo('foo'); - } - - public function it_should_have_an_empty_cookie_by_default(): void - { - $this->getCookie()->shouldBeEqualTo(''); - } - - public function it_should_set_the_max_bytes(): void - { - $this->setMaxBytes(2000); - $this->getMaxBytes()->shouldBeEqualTo(2000); - } - - public function it_should_have_the_max_value_for_max_bytes_by_default(): void - { - $this->getMaxBytes()->shouldBeEqualTo(2147483647); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_DIR_SYNC), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(-0x80000000), - Asn1::integer(2147483647), - Asn1::octetString('') - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_DIR_SYNC), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(-0x80000000), - Asn1::integer(2147483647), - Asn1::octetString('') - ))) - ))->setValue(null)->shouldBeLike(new DirSyncRequestControl()); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncResponseControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncResponseControlSpec.php deleted file mode 100644 index ec8a9dd0..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/DirSyncResponseControlSpec.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\DirSyncResponseControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class DirSyncResponseControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(DirSyncResponseControl::class); - } - - public function it_should_get_the_more_results_value(): void - { - $this->getMoreResults()->shouldBeEqualTo(0); - } - - public function it_should_return_false_for_has_more_results_when_more_results_is_0(): void - { - $this->hasMoreResults()->shouldBeEqualTo(false); - } - - public function it_should_return_false_for_has_more_results_when_more_results_is_not_0(): void - { - $this->beConstructedWith(1); - $this->hasMoreResults()->shouldBeEqualTo(true); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo(''); - } - - public function it_should_get_the_unused_value(): void - { - $this->getUnused(0); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_DIR_SYNC), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(0), - Asn1::integer(0), - Asn1::octetString('') - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_DIR_SYNC), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(0), - Asn1::integer(0), - Asn1::octetString('') - ))) - ))->setValue(null)->shouldBeLike(new DirSyncResponseControl(0)); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/ExpectedEntryCountControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/ExpectedEntryCountControlSpec.php deleted file mode 100644 index 504c44ec..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/ExpectedEntryCountControlSpec.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\ExpectedEntryCountControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class ExpectedEntryCountControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1, 50); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ExpectedEntryCountControl::class); - } - - public function it_should_set_the_maximum(): void - { - $this->setMaximum(100)->getMaximum()->shouldBeEqualTo(100); - } - - public function it_should_get_the_maximum(): void - { - $this->getMaximum()->shouldBeEqualTo(50); - } - - public function it_should_set_the_minimum(): void - { - $this->setMinimum(100)->getMinimum()->shouldBeEqualTo(100); - } - - public function it_should_get_the_minimum(): void - { - $this->getMinimum()->shouldBeEqualTo(1); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_EXPECTED_ENTRY_COUNT), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1), - Asn1::integer(50) - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_EXPECTED_ENTRY_COUNT), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1), - Asn1::integer(50) - ))) - ))->setValue(null)->shouldBeLike(new ExpectedEntryCountControl(1, 50)); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/ExtendedDnControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/ExtendedDnControlSpec.php deleted file mode 100644 index cd64755e..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/ExtendedDnControlSpec.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\ExtendedDnControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class ExtendedDnControlSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(ExtendedDnControl::class); - } - - public function it_should_set_whether_or_not_to_use_hex_format(): void - { - $this->setUseHexFormat(true)->getUseHexFormat()->shouldBeEqualTo(true); - } - - public function it_should_not_use_hex_format_by_default(): void - { - $this->getUseHexFormat()->shouldBeEqualTo(false); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_EXTENDED_DN), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1) - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_EXTENDED_DN), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1) - ))) - ))->setValue(null)->shouldBeLike(new ExtendedDnControl()); - } - - public function it_should_be_constructed_from_asn1_when_the_empty_value_form_is_used(): void - { - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_EXTENDED_DN), - Asn1::boolean(false) - ))->setValue(null)->shouldBeLike(new ExtendedDnControl()); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/PolicyHintsControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/PolicyHintsControlSpec.php deleted file mode 100644 index 47e0cdfc..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/PolicyHintsControlSpec.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\PolicyHintsControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class PolicyHintsControlSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(PolicyHintsControl::class); - } - - public function it_should_be_enabled_by_default(): void - { - $this->getIsEnabled()->shouldBeEqualTo(true); - } - - public function it_should_set_whether_or_not_it_is_enabled(): void - { - $this->setIsEnabled(false)->getIsEnabled()->shouldBeEqualTo(false); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_POLICY_HINTS), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1) - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_POLICY_HINTS), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1) - ))) - ))->setValue(null)->shouldBeLike(new PolicyHintsControl()); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/SdFlagsControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/SdFlagsControlSpec.php deleted file mode 100644 index 28e15d6d..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/SdFlagsControlSpec.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\SdFlagsControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SdFlagsControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(SdFlagsControl::DACL_SECURITY_INFORMATION); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SdFlagsControl::class); - } - - public function it_should_get_the_flags(): void - { - $this->getFlags()->shouldBeEqualTo(SdFlagsControl::DACL_SECURITY_INFORMATION); - } - - public function it_should_set_the_flags(): void - { - $this->setFlags(SdFlagsControl::SACL_SECURITY_INFORMATION)->getFlags()->shouldBeEqualTo(SdFlagsControl::SACL_SECURITY_INFORMATION); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SD_FLAGS), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(4) - ))) - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Ad/SetOwnerControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Ad/SetOwnerControlSpec.php deleted file mode 100644 index cc3fc16b..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Ad/SetOwnerControlSpec.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Ad; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Ad\SetOwnerControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SetOwnerControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SetOwnerControl::class); - } - - public function it_should_get_the_sid(): void - { - $this->getSid()->shouldBeEqualTo('foo'); - } - - public function it_should_set_the_sid(): void - { - $this->setSid('bar')->getSid()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SET_OWNER), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::octetString('foo'))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_SET_OWNER), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::octetString('foo'))) - ))->setValue(null)->shouldBeLike(new SetOwnerControl('foo')); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/ControlBagSpec.php b/tests/spec/FreeDSx/Ldap/Control/ControlBagSpec.php deleted file mode 100644 index 99a24a8b..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/ControlBagSpec.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control; - -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Control\Sync\SyncRequestControl; -use PhpSpec\ObjectBehavior; - -class ControlBagSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new Control('foo'), new Control('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ControlBag::class); - } - - public function it_should_implement_iterator_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_get_the_control_count(): void - { - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_get_a_control_by_string(): void - { - $this->get('foo')->shouldBeLike(new Control('foo')); - } - - public function it_should_return_null_on_a_control_that_doesnt_exist(): void - { - $this->get('foobar')->shouldBeNull(); - } - - public function it_should_add_a_control(): void - { - $this->add(new Control('foobar')); - - $this->has('foobar')->shouldBeEqualTo(true); - } - - public function it_should_check_if_a_control_exists_with_has(): void - { - $this->has('bar')->shouldBeEqualTo(true); - $this->has('foobar')->shouldBeEqualTo(false); - } - - public function it_should_check_if_a_control_exists_by_an_object_check(): void - { - $foobar = new Control('foobar'); - - $this->has($foobar)->shouldBeEqualTo(false); - $this->add($foobar); - $this->has($foobar)->shouldBeEqualTo(true); - } - - public function it_should_remove_a_control_by_string(): void - { - $this->remove('foo'); - - $this->has('foo')->shouldBeEqualTo(false); - } - - public function it_should_remove_a_control_by_object(): void - { - $foobar = new Control('foobar'); - $this->add($foobar); - $this->has($foobar)->shouldBeEqualTo(true); - - $this->remove($foobar); - $this->has($foobar)->shouldBeEqualTo(false); - } - - public function it_should_get_the_controls_as_an_array(): void - { - $this->toArray()->shouldBeLike([new Control('foo'), new Control('bar')]); - } - - public function it_should_reset_the_controls(): void - { - $this->reset(); - - $this->toArray()->shouldBeEqualTo([]); - } - - public function it_should_get_a_control_by_its_class_name(): void - { - $this->add(new SyncRequestControl()); - - $this->getByClass(SyncRequestControl::class) - ->shouldBeAnInstanceOf(SyncRequestControl::class); - } - - public function it_should_return_null_if_a_control_by_its_class_name_does_not_exist(): void - { - $this->getByClass(SyncRequestControl::class) - ->shouldBeNull(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/ControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/ControlSpec.php deleted file mode 100644 index 4e183138..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/ControlSpec.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use PhpSpec\ObjectBehavior; - -class ControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Control::class); - } - - public function it_should_get_the_control_type(): void - { - $this->getTypeOid()->shouldBeEqualTo('foo'); - $this->setTypeOid('bar')->getTypeOid()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_control_value(): void - { - $this->getValue()->shouldBeEqualTo(null); - $this->setValue('bar')->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_criticality(): void - { - $this->getCriticality()->shouldBeEqualTo(false); - $this->setCriticality(true)->getCriticality()->shouldBeEqualTo(true); - } - - public function it_should_have_a_string_representation_of_the_oid_type(): void - { - $this->__toString()->shouldBeEqualTo('foo'); - } - - public function it_should_generate_correct_ASN1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString('foo'), - Asn1::boolean(false) - )); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString('foobar'), - Asn1::boolean(true) - )]); - - $this->getTypeOid()->shouldBeEqualTo('foobar'); - $this->getCriticality()->shouldBeEqualTo(true); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/PagingControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/PagingControlSpec.php deleted file mode 100644 index 0d738704..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/PagingControlSpec.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class PagingControlSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(PagingControl::class); - } - public function let(): void - { - $this->beConstructedWith(0, 'foo'); - } - - public function it_should_default_to_an_empty_cookie_on_construction(): void - { - $this->beConstructedWith(10); - - $this->getCookie()->shouldBeEqualTo(''); - } - - public function it_should_set_the_size(): void - { - $this->getSize(0); - $this->setSize(1)->getSize()->shouldBeEqualTo(1); - } - - public function it_should_set_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('foo'); - $this->setCookie('bar')->getCookie()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_PAGING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(0), - Asn1::octetString('foo') - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_PAGING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1), - Asn1::octetString('foobar') - ))) - )]); - - $this->getSize()->shouldBeEqualTo(1); - $this->getCookie()->shouldBeEqualTo('foobar'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/PwdPolicyResponseControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/PwdPolicyResponseControlSpec.php deleted file mode 100644 index 2c3e29ef..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/PwdPolicyResponseControlSpec.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\PwdPolicyResponseControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class PwdPolicyResponseControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1, 2, 3); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(PwdPolicyResponseControl::class); - } - - public function it_should_get_the_error(): void - { - $this->getError()->shouldBeEqualTo(3); - } - - public function it_should_get_the_time_before_expiration(): void - { - $this->getTimeBeforeExpiration()->shouldBeEqualTo(1); - } - - public function it_should_get_the_grace_attempts_remaining(): void - { - $this->getGraceAttemptsRemaining()->shouldBeEqualTo(2); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_PWD_POLICY), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(0, Asn1::sequence(Asn1::context(0, Asn1::integer(100)))), - Asn1::context(1, Asn1::enumerated(2)) - ))) - )]); - - $this->getTimeBeforeExpiration()->shouldBeEqualTo(100); - $this->getError()->shouldBeEqualTo(2); - $this->getTypeOid()->shouldBeEqualTo(Control::OID_PWD_POLICY); - $this->getCriticality()->shouldBeEqualTo(false); - } - - public function it_should_generate_correct_asn1(): void - { - $this->beConstructedWith(100, null, 2); - - $encoder = new LdapEncoder(); - $this->toAsn1()->shouldBeLike( - Asn1::sequence( - Asn1::octetString(Control::OID_PWD_POLICY), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(0, Asn1::sequence(Asn1::context(0, Asn1::integer(100)))), - Asn1::context(1, Asn1::enumerated(2)) - ))) - ) - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortKeySpec.php b/tests/spec/FreeDSx/Ldap/Control/Sorting/SortKeySpec.php deleted file mode 100644 index b66eac4e..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortKeySpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sorting; - -use FreeDSx\Ldap\Control\Sorting\SortKey; -use PhpSpec\ObjectBehavior; - -class SortKeySpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SortKey::class); - } - - public function it_should_be_constructed_via_reverse_order(): void - { - $this->beConstructedWith('cn', true); - - $this->getUseReverseOrder()->shouldBeEqualTo(true); - } - - public function it_should_be_constructed_ascending(): void - { - $this->beConstructedThrough('ascending', ['foo']); - - $this->getUseReverseOrder()->shouldBeEqualTo(false); - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_be_constructed_descending(): void - { - $this->beConstructedThrough('descending', ['foo']); - - $this->getUseReverseOrder()->shouldBeEqualTo(true); - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_not_use_reverse_order_by_default(): void - { - $this->getUseReverseOrder()->shouldBeEqualTo(false); - } - - public function it_should_set_the_attribute_to_use(): void - { - $this->getAttribute()->shouldBeEqualTo('cn'); - $this->setAttribute('foo')->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_set_the_ordering_rule(): void - { - $this->getOrderingRule()->shouldBeNull(); - $this->setOrderingRule('foo')->getOrderingRule()->shouldBeEqualTo('foo'); - } - - public function it_should_set_whether_to_use_reverse_order(): void - { - $this->setUseReverseOrder(true); - $this->getUseReverseOrder()->shouldBeEqualTo(true); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingControlSpec.php deleted file mode 100644 index 8a905322..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingControlSpec.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sorting; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sorting\SortingControl; -use FreeDSx\Ldap\Control\Sorting\SortKey; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SortingControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new SortKey('foo'), new SortKey('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SortingControl::class); - } - - public function it_should_have_the_sorting_oid(): void - { - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SORTING); - } - - public function it_should_get_the_sort_keys(): void - { - $this->getSortKeys()->shouldBeLike([ - new SortKey('foo'), - new SortKey('bar') - ]); - } - - public function it_should_set_sort_keys(): void - { - $this->setSortKeys(new SortKey('foobar')); - - $this->getSortKeys()->shouldBeLike([new SortKey('foobar')]); - } - - public function it_should_add_sort_keys(): void - { - $key = new SortKey('foobar'); - $this->addSortKeys($key); - - $this->getSortKeys()->shouldContain($key); - } - - public function it_should_generate_correct_asn1(): void - { - $this->addSortKeys(new SortKey('foobar', true, 'bleh')); - - $encoder = new LdapEncoder(); - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SORTING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequenceOf( - Asn1::sequence(Asn1::octetString('foo')), - Asn1::sequence(Asn1::octetString('bar')), - Asn1::sequence( - Asn1::octetString('foobar'), - Asn1::context(0, Asn1::octetString('bleh')), - Asn1::context(1, Asn1::boolean(true)) - ) - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_SORTING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequenceOf( - Asn1::sequence(Asn1::octetString('foo')), - Asn1::sequence(Asn1::octetString('bar')), - Asn1::sequence( - Asn1::octetString('foobar'), - Asn1::context(0, Asn1::octetString('bleh')), - Asn1::context(1, Asn1::boolean(true)) - ) - ))) - ))->setValue(null)->shouldBeLike(new SortingControl( - new SortKey('foo'), - new SortKey('bar'), - new SortKey('foobar', true, 'bleh') - )); - } - - public function it_should_throw_an_error_parsing_sorting_keys_with_no_attribute(): void - { - $encoder = new LdapEncoder(); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SORTING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequenceOf( - Asn1::sequence(Asn1::octetString('')) - ))) - )]); - } - - public function it_should_throw_an_error_parsing_sorting_keys_with_unexpected_values(): void - { - $encoder = new LdapEncoder(); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SORTING), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequenceOf( - Asn1::sequence(Asn1::octetString('foo'), Asn1::enumerated(1)) - ))) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingResponseControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingResponseControlSpec.php deleted file mode 100644 index 95d40e73..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sorting/SortingResponseControlSpec.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sorting; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sorting\SortingResponseControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SortingResponseControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'cn'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SortingResponseControl::class); - } - - public function it_should_get_the_result(): void - { - $this->getResult()->shouldBeEqualTo(0); - } - - public function it_should_get_the_attribute(): void - { - $this->getAttribute()->shouldBeEqualTo('cn'); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_SORTING_RESPONSE), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('cn') - ))) - ))->setValue(null)->shouldBeLike(new SortingResponseControl(0, 'cn')); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - $this->toAsn1()->shouldBeLike( - Asn1::sequence( - Asn1::octetString(Control::OID_SORTING_RESPONSE), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(0), - Asn1::context(0, Asn1::octetString('cn')) - ))) - ) - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/SubentriesControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/SubentriesControlSpec.php deleted file mode 100644 index 3978bb8a..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/SubentriesControlSpec.php +++ /dev/null @@ -1,59 +0,0 @@ -shouldHaveType(SubentriesControl::class); - } - - public function it_should_have_a_default_visibility_of_true(): void - { - $this->getIsVisible()->shouldBeEqualTo(true); - } - - public function it_should_set_the_visibility(): void - { - $this->setIsVisible(false)->getIsVisible()->shouldBeEqualTo(false); - } - - public function it_should_have_the_subentries_oid(): void - { - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SUBENTRIES); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SUBENTRIES), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::boolean(true))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SUBENTRIES), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::boolean(true))) - )]); - - $this->getIsVisible()->shouldBeEqualTo(true); - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SUBENTRIES); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncDoneControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Sync/SyncDoneControlSpec.php deleted file mode 100644 index e0068f3f..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncDoneControlSpec.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sync; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sync\SyncDoneControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncDoneControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('omnomnom', false); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncDoneControl::class); - } - - public function it_should_get_refresh_deletes(): void - { - $this->getRefreshDeletes()->shouldBeEqualTo(false); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_DONE), - Asn1::boolean(true), - Asn1::octetString( - $encoder->encode(Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - )) - ) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_DONE), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))) - )]); - - $this->getRefreshDeletes()->shouldBeEqualTo(false); - $this->getCookie()->shouldBeEqualTo('omnomnom'); - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SYNC_DONE); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncRequestControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Sync/SyncRequestControlSpec.php deleted file mode 100644 index 184bd271..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncRequestControlSpec.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sync; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sync\SyncRequestControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncRequestControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1, 'omnomnom'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncRequestControl::class); - } - - public function it_should_get_the_mode(): void - { - $this->getMode()->shouldBeEqualTo(1); - } - - public function it_should_get_the_reload_hint(): void - { - $this->getReloadHint()->shouldBeEqualTo(false); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_REQUEST), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(1), - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_REQUEST), - Asn1::boolean(true), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(1), - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))) - )]); - - $this->getMode()->shouldBeEqualTo(1); - $this->getReloadHint()->shouldBeEqualTo(false); - $this->getCookie()->shouldBeEqualTo('omnomnom'); - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SYNC_REQUEST); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncStateControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Sync/SyncStateControlSpec.php deleted file mode 100644 index d0b854cd..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Sync/SyncStateControlSpec.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Sync; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sync\SyncStateControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncStateControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'omnomnom'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncStateControl::class); - } - - public function it_should_get_the_state(): void - { - $this->getState()->shouldBeEqualTo(0); - } - - public function it_should_get_the_entry_uuid(): void - { - $this->getEntryUuid()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_tell_if_it_is_for_a_present_state(): void - { - $this->beConstructedWith(SyncStateControl::STATE_PRESENT, 'foo'); - - $this->isPresent() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_add_state(): void - { - $this->beConstructedWith(SyncStateControl::STATE_ADD, 'foo'); - - $this->isAdd() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_modify_state(): void - { - $this->beConstructedWith(SyncStateControl::STATE_MODIFY, 'foo'); - - $this->isModify() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_delete_state(): void - { - $this->beConstructedWith(SyncStateControl::STATE_DELETE, 'foo'); - - $this->isDelete() - ->shouldBeEqualTo(true); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_STATE), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('foo'), - Asn1::octetString('omnomnom') - ))) - )); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::octetString(Control::OID_SYNC_STATE), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('foo'), - Asn1::octetString('omnomnom') - ))) - )]); - - $this->getState()->shouldBeEqualTo(0); - $this->getEntryUuid()->shouldBeEqualTo('foo'); - $this->getCookie()->shouldBeEqualTo('omnomnom'); - $this->getTypeOid()->shouldBeEqualTo(Control::OID_SYNC_STATE); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvControlSpec.php deleted file mode 100644 index 961e4d57..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvControlSpec.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Vlv; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Vlv\VlvControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class VlvControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(10, 9, 8, 0); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(VlvControl::class); - } - - public function it_should_have_a_count_of_zero_by_default(): void - { - $this->getCount()->shouldBeEqualTo(0); - } - - public function it_should_get_and_set_the_value_for_after(): void - { - $this->getAfter()->shouldBeEqualTo(9); - $this->setBefore(10)->getBefore()->shouldBeEqualTo(10); - } - - public function it_should_get_and_set_the_value_for_before(): void - { - $this->getBefore()->shouldBeEqualTo(10); - $this->setBefore(20)->getBefore()->shouldBeEqualTo(20); - } - - public function it_should_get_and_set_the_value_for_the_offset(): void - { - $this->getOffset()->shouldBeEqualTo(8); - $this->setOffset(16)->getOffset()->shouldBeEqualTo(16); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::octetString(Control::OID_VLV), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(10), - Asn1::integer(9), - Asn1::context(0, Asn1::sequence( - Asn1::integer(8), - Asn1::integer(0) - )) - ))) - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvResponseControlSpec.php b/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvResponseControlSpec.php deleted file mode 100644 index 89895d36..00000000 --- a/tests/spec/FreeDSx/Ldap/Control/Vlv/VlvResponseControlSpec.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Control\Vlv; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Vlv\VlvResponseControl; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class VlvResponseControlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(10, 9, 0); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(VlvResponseControl::class); - } - - public function it_should_get_the_offset(): void - { - $this->getOffset()->shouldBeEqualTo(10); - } - - public function it_should_get_the_count(): void - { - $this->getCount()->shouldBeEqualTo(9); - } - - public function it_should_get_the_context_id(): void - { - $this->getContextId()->shouldBeNull(); - } - - public function it_should_get_the_result(): void - { - $this->getResult()->shouldBeEqualTo(0); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::sequence( - Asn1::octetString(Control::OID_VLV_RESPONSE), - Asn1::boolean(false), - Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1), - Asn1::integer(300), - Asn1::enumerated(0), - Asn1::octetString('foo') - ))) - ))->setValue(null)->shouldBeLike(new VlvResponseControl(1, 300, 0, 'foo')); - } -} diff --git a/tests/spec/FreeDSx/Ldap/ControlsSpec.php b/tests/spec/FreeDSx/Ldap/ControlsSpec.php deleted file mode 100644 index 901ce61d..00000000 --- a/tests/spec/FreeDSx/Ldap/ControlsSpec.php +++ /dev/null @@ -1,208 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; -use FreeDSx\Ldap\Control\Ad\ExpectedEntryCountControl; -use FreeDSx\Ldap\Control\Ad\ExtendedDnControl; -use FreeDSx\Ldap\Control\Ad\PolicyHintsControl; -use FreeDSx\Ldap\Control\Ad\SdFlagsControl; -use FreeDSx\Ldap\Control\Ad\SetOwnerControl; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Control\Sorting\SortingControl; -use FreeDSx\Ldap\Control\Sorting\SortKey; -use FreeDSx\Ldap\Control\SubentriesControl; -use FreeDSx\Ldap\Control\Sync\SyncRequestControl; -use FreeDSx\Ldap\Control\Vlv\VlvControl; -use FreeDSx\Ldap\Controls; -use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class ControlsSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(Controls::class); - } - - public function it_should_create_a_paging_control(): void - { - $this->paging(100) - ->shouldBeLike(new PagingControl( - 100, - '' - )); - } - - public function it_should_create_a_vlv_offset_control(): void - { - $this->vlv( - 10, - 12 - )->shouldBeLike(new VlvControl( - 10, - 12, - 1, - 0 - )); - } - - public function it_should_create_a_vlv_filter_control(): void - { - $this->vlvFilter( - 10, - 12, - Filters::gte( - 'foo', - 'bar' - ) - )->shouldBeLike(new VlvControl( - 10, - 12, - null, - null, - new GreaterThanOrEqualFilter( - 'foo', - 'bar' - ) - )); - } - - public function it_should_create_an_sd_flags_control(): void - { - $this->sdFlags(7) - ->shouldBeLike(new SdFlagsControl(7)); - } - - public function it_should_create_a_password_policy_control(): void - { - $this->pwdPolicy() - ->shouldBeLike(new Control( - Control::OID_PWD_POLICY, - true - )); - } - - public function it_should_create_a_subtree_delete_control(): void - { - $this->subtreeDelete() - ->shouldBeLike(new Control(Control::OID_SUBTREE_DELETE)); - } - - public function it_should_create_a_sorting_control_using_a_string(): void - { - $this->sort('cn') - ->shouldBeLike(new SortingControl(new SortKey('cn'))); - } - - public function it_should_create_a_sorting_control_using_a_sort_key(): void - { - $this->sort(new SortKey('foo')) - ->shouldBeLike(new SortingControl(new SortKey('foo'))); - } - - public function it_should_create_an_extended_dn_control(): void - { - $this->extendedDn() - ->shouldBeLike(new ExtendedDnControl()); - } - - public function it_should_create_a_dir_sync_control(): void - { - $this->dirSync() - ->shouldBeLike(new DirSyncRequestControl()); - } - - public function it_should_create_a_dir_sync_control_with_options(): void - { - $this->dirSync( - DirSyncRequestControl::FLAG_INCREMENTAL_VALUES - | DirSyncRequestControl::FLAG_OBJECT_SECURITY, - 'foo' - )->shouldBeLike( - new DirSyncRequestControl( - DirSyncRequestControl::FLAG_INCREMENTAL_VALUES - | DirSyncRequestControl::FLAG_OBJECT_SECURITY, - 'foo' - ) - ); - } - - public function it_should_create_an_expected_entry_count_control(): void - { - $this->expectedEntryCount(1, 100) - ->shouldBeLike(new ExpectedEntryCountControl(1, 100)); - } - - public function it_should_create_a_policy_hints_control(): void - { - $this::policyHints() - ->shouldBeLike(new PolicyHintsControl()); - } - - public function it_should_create_a_set_owners_control(): void - { - $this::setOwner('foo') - ->shouldBeLike(new SetOwnerControl('foo')); - } - - public function it_should_create_a_show_deleted_control(): void - { - $this::showDeleted() - ->shouldBeLike(new Control( - Control::OID_SHOW_DELETED, - true - )); - } - - public function it_should_create_a_show_recycled_control(): void - { - $this::showRecycled() - ->shouldBeLike(new Control( - Control::OID_SHOW_RECYCLED, - true - )); - } - - public function it_should_create_a_subentries_control(): void - { - $this::subentries() - ->shouldBeLike(new SubentriesControl(true)); - } - - public function it_should_create_a_manageDsaIt_control(): void - { - $this::manageDsaIt() - ->shouldBeLike(new Control( - Control::OID_MANAGE_DSA_IT, - true - )); - } - - public function it_should_get_a_sync_request_control(): void - { - $this::syncRequest() - ->shouldBeLike(new SyncRequestControl()); - - $this::syncRequest( - 'foo', - SyncRequestControl::MODE_REFRESH_AND_PERSIST - )->shouldBeLike(new SyncRequestControl( - SyncRequestControl::MODE_REFRESH_AND_PERSIST, - 'foo', - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/AttributeSpec.php b/tests/spec/FreeDSx/Ldap/Entry/AttributeSpec.php deleted file mode 100644 index 967aafcf..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/AttributeSpec.php +++ /dev/null @@ -1,191 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Option; -use FreeDSx\Ldap\Entry\Options; -use PhpSpec\ObjectBehavior; - -class AttributeSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn', 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Attribute::class); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_implement_iterator_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_get_the_name(): void - { - $this->getName()->shouldBeEqualTo('cn'); - $this->getOptions()->add('foo'); - $this->getName()->shouldBeEqualTo('cn'); - } - - public function it_should_get_the_complete_attribute_description(): void - { - $this->getDescription()->shouldBeEqualTo('cn'); - $this->getOptions()->add('foo'); - $this->getDescription()->shouldBeEqualTo('cn;foo'); - } - - public function it_should_return_false_for_hasOptions_when_there_are_none(): void - { - $this->hasOptions()->shouldBeEqualTo(false); - } - - public function it_should_get_options(): void - { - $this->getOptions()->shouldBeLike(new Options()); - } - - public function it_should_get_the_values(): void - { - $this->getValues()->shouldBeEqualTo(['foo', 'bar']); - } - - public function it_should_get_the_first_value_if_it_exists(): void - { - $this->firstValue()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_last_value_if_it_exists(): void - { - $this->lastValue()->shouldBeEqualTo('bar'); - } - - public function it_should_get_null_if_the_first_value_does_not_exist(): void - { - $this->beConstructedWith('foo'); - - $this->firstValue()->shouldBeNull(); - } - - public function it_should_get_null_if_the_last_value_does_not_exist(): void - { - $this->beConstructedWith('foo'); - - $this->lastValue()->shouldBeNull(); - } - - public function it_should_have_a_string_representation(): void - { - $this->__toString()->shouldBeEqualTo('foo, bar'); - } - - public function it_should_get_a_count_of_values(): void - { - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_add_values(): void - { - $this->add('foobar', 'meh'); - - $this->getValues()->shouldBeEqualTo(['foo', 'bar', 'foobar', 'meh']); - } - - public function it_should_remove_values(): void - { - $this->remove('bar'); - - $this->getValues()->shouldBeEqualTo(['foo']); - } - - public function it_should_set_values(): void - { - $this->set('foo')->getValues()->shouldBeEqualTo(['foo']); - } - - public function it_should_reset_values(): void - { - $this->reset()->getValues()->shouldBeEqualTo([]); - } - - public function it_should_check_if_a_value_exists(): void - { - $this->has('foo')->shouldBeEqualTo(true); - $this->has('bleh')->shouldBeEqualTo(false); - } - - public function it_should_check_if_it_equals_another_attribute(): void - { - $this->equals(new Attribute('cn'))->shouldBeEqualTo(true); - $this->equals(new Attribute('CN'))->shouldBeEqualTo(true); - $this->equals(new Attribute('foo'))->shouldBeEqualTo(false); - } - - public function it_should_check_if_it_equals_another_attribute_with_options(): void - { - $this->equals(new Attribute('cn;foo'))->shouldBeEqualTo(false); - $this->getOptions()->add('foo'); - $this->equals(new Attribute('cn;foo'))->shouldBeEqualTo(true); - } - - public function it_should_be_check_equality_with_the_name_only_by_default(): void - { - $this->getOptions()->add('foo'); - - $this->equals(new Attribute('cn'))->shouldBeEqualTo(true); - } - - public function it_should_be_check_equality_with_name_and_options_when_strict_is_set(): void - { - $this->getOptions()->add('foo'); - - $this->equals(new Attribute('cn'), true)->shouldBeEqualTo(false); - } - - public function it_should_escape_a_value(): void - { - $this::escape("(foo=*\bar)\x00")->shouldBeEqualTo('\28foo=\2a\5cbar\29\00'); - } - - public function it_should_escape_a_value_to_complete_hex(): void - { - $this::escapeAll("foobar")->shouldBeEqualTo('\66\6f\6f\62\61\72'); - } - - public function it_should_ignore_an_empty_value_when_escaping(): void - { - $this::escape('')->shouldBeLike(''); - } - - public function it_should_not_escape_a_string_that_is_already_hex_encoded(): void - { - $this::escape('\66\6f\6f\62\61\72')->shouldBeEqualTo('\66\6f\6f\62\61\72'); - } - - public function it_should_parse_options_in_the_attribute(): void - { - $this->beConstructedWith('foo;lang-en-us', 'bar'); - - $this->getName()->shouldBeEqualTo('foo'); - $this->getOptions()->shouldBeLike(new Options(new Option('lang-en-us'))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/ChangeSpec.php b/tests/spec/FreeDSx/Ldap/Entry/ChangeSpec.php deleted file mode 100644 index 6cca1cb7..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/ChangeSpec.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Change; -use PhpSpec\ObjectBehavior; - -class ChangeSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Change::TYPE_REPLACE, new Attribute('foo', 'bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Change::class); - } - - public function it_should_get_the_mod_type(): void - { - $this->getType()->shouldBeEqualTo(Change::TYPE_REPLACE); - } - - public function it_should_set_the_mod_type(): void - { - $this->setType(Change::TYPE_ADD)->getType()->shouldBeEqualTo(Change::TYPE_ADD); - } - - public function it_should_get_the_attribute(): void - { - $this->getAttribute()->shouldBeLike(new Attribute('foo', 'bar')); - } - - public function it_should_set_the_attribute(): void - { - $this->setAttribute(new Attribute('cn', 'foo'))->getAttribute()->shouldBeLike(new Attribute('cn', 'foo')); - } - - public function it_should_construct_a_reset_change(): void - { - $this->beConstructedThrough('reset', ['foo']); - - $this->getAttribute()->shouldBeLike(new Attribute('foo')); - $this->getType()->shouldBeEqualTo(Change::TYPE_DELETE); - } - - public function it_should_construct_an_add_change(): void - { - $this->beConstructedThrough('add', ['foo', 'bar']); - - $this->getAttribute()->shouldBeLike(new Attribute('foo', 'bar')); - $this->getType()->shouldBeEqualTo(Change::TYPE_ADD); - } - - public function it_should_construct_a_delete_change(): void - { - $this->beConstructedThrough('delete', ['foo', 'bar', 'foobar']); - - $this->getAttribute()->shouldBeLike(new Attribute('foo', 'bar', 'foobar')); - $this->getType()->shouldBeEqualTo(Change::TYPE_DELETE); - } - - public function it_should_construct_a_replace_change(): void - { - $this->beConstructedThrough('replace', ['foo', 'bar']); - - $this->getAttribute()->shouldBeLike(new Attribute('foo', 'bar')); - $this->getType()->shouldBeEqualTo(Change::TYPE_REPLACE); - } - - public function it_should_check_whether_it_is_an_add(): void - { - $this->isAdd()->shouldBeEqualTo(false); - $this->setType(Change::TYPE_ADD); - $this->isAdd()->shouldBeEqualTo(true); - } - - public function it_should_check_whether_it_is_a_delete(): void - { - $this->isDelete()->shouldBeEqualTo(false); - $this->setType(Change::TYPE_DELETE); - $this->isDelete()->shouldBeEqualTo(true); - $this->setAttribute(new Attribute('foo')); - $this->isDelete()->shouldBeEqualTo(false); - } - - public function it_should_check_whether_it_is_a_replace(): void - { - $this->isReplace()->shouldBeEqualTo(true); - $this->setType(Change::TYPE_ADD); - $this->isReplace()->shouldBeEqualTo(false); - } - - public function it_should_check_whether_it_is_a_reset(): void - { - $this->isReset()->shouldBeEqualTo(false); - $this->setType(Change::TYPE_DELETE); - $this->isReset()->shouldBeEqualTo(false); - $this->setAttribute(new Attribute('foo')); - $this->isReset()->shouldBeEqualTo(true); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/ChangesSpec.php b/tests/spec/FreeDSx/Ldap/Entry/ChangesSpec.php deleted file mode 100644 index f56c7301..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/ChangesSpec.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Changes; -use PhpSpec\ObjectBehavior; - -class ChangesSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Change::replace('foo', 'bar'), Change::delete('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Changes::class); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_implement_iterable_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_add_changes(): void - { - $change = Change::add('sn', 'foo'); - $this->add($change); - $this->toArray()->shouldContain($change); - } - - public function it_should_remove_changes(): void - { - $change = Change::add('sn', 'foo'); - $this->add($change); - $this->remove($change); - $this->toArray()->shouldNotContain($change); - } - - public function it_should_reset_changes(): void - { - $this->reset(); - $this->toArray()->shouldBeEqualTo([]); - } - - public function it_should_get_the_count_of_changes(): void - { - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_get_the_changes_as_an_array(): void - { - $this->toArray()->shouldBeLike([ - Change::replace('foo', 'bar'), - Change::delete('bar') - ]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/DnSpec.php b/tests/spec/FreeDSx/Ldap/Entry/DnSpec.php deleted file mode 100644 index 39065cf0..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/DnSpec.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Entry\Rdn; -use PhpSpec\ObjectBehavior; - -class DnSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn=fo\,o, dc=local,dc=example'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Dn::class); - } - - public function it_should_get_all_pieces_as_an_array_of_RDNs(): void - { - $this->toArray()->shouldBeLike([ - new Rdn("cn", "fo\,o"), - new Rdn("dc", "local"), - new Rdn("dc", "example"), - ]); - } - - public function it_should_get_the_parent_dn(): void - { - $this->getParent()->shouldBeLike(new Dn('dc=local,dc=example')); - } - - public function it_should_get_the_rdn(): void - { - $this->getRdn()->shouldBeLike(new Rdn('cn', 'fo\,o')); - } - - public function it_should_return_a_count(): void - { - $this->count()->shouldBeEqualTo(3); - } - - public function it_should_get_the_string_representation(): void - { - $this->toString()->shouldBeEqualTo('cn=fo\,o, dc=local,dc=example'); - } - - public function it_should_check_if_it_is_a_valid_dn(): void - { - $this::isValid('cn=foo,dc=bar=dc=foo')->shouldBeEqualTo(true); - $this::isValid('')->shouldBeEqualTo(true); - $this::isValid('foo')->shouldBeEqualTo(false); - } - - public function it_should_handle_a_rootdse_as_a_dn(): void - { - $this->beConstructedWith(''); - - $this->toString()->shouldBeEqualTo(''); - $this->toArray()->shouldBeEqualTo([]); - $this->count()->shouldBeEqualTo(0); - $this->getParent()->shouldBeNull(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/EntriesSpec.php b/tests/spec/FreeDSx/Ldap/Entry/EntriesSpec.php deleted file mode 100644 index 0314e33d..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/EntriesSpec.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use PhpSpec\ObjectBehavior; - -class EntriesSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new Entry('foo'), new Entry('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Entries::class); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_implement_iterator_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_get_the_count(): void - { - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_get_the_first_entry(): void - { - $this->first()->shouldBeLike(new Entry('foo')); - } - - public function it_should_return_null_if_the_first_entry_does_not_exist(): void - { - $this->beConstructedWith(...[]); - - $this->first()->shouldBeNull(); - } - - public function it_should_get_the_last_entry(): void - { - $this->last()->shouldBeLike(new Entry('bar')); - } - - public function it_should_return_null_if_the_last_entry_does_not_exist(): void - { - $this->beConstructedWith(...[]); - - $this->last()->shouldBeNull(); - } - - public function it_should_add_entries(): void - { - $this->add(new Entry('cn=new'), new Entry('cn=another')); - $this->count()->shouldBeEqualTo(4); - } - - public function it_should_remove_entries(): void - { - $entry1 = new Entry('cn=new'); - $entry2 = new Entry('cn=another'); - $this->add($entry1, $entry2); - $this->remove($entry1, $entry2); - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_not_remove_entries_if_they_dont_exist(): void - { - $this->remove(new Entry('cn=meh')); - $this->count()->shouldBeEqualTo(2); - } - - public function it_should_check_if_an_entry_object_is_in_the_collection(): void - { - $entry = new Entry('cn=meh'); - $this->has($entry)->shouldBeEqualTo(false); - $this->add($entry); - $this->has($entry)->shouldBeEqualTo(true); - } - - public function it_should_check_if_an_entry_is_in_the_collection_by_dn(): void - { - $this->has('foo')->shouldBeEqualTo(true); - $this->has('cn=meh')->shouldBeEqualTo(false); - } - - public function it_should_get_an_entry_by_dn(): void - { - $this->get('foo')->shouldBeLike(new Entry('foo')); - } - - public function it_should_return_null_when_trying_to_get_an_entry_that_doesnt_exist(): void - { - $this->get('meh')->shouldBeNull(); - } - - public function it_should_get_the_array_of_entries(): void - { - $this->toArray()->shouldBeLike([ - new Entry('foo'), - new Entry('bar') - ]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/EntrySpec.php b/tests/spec/FreeDSx/Ldap/Entry/EntrySpec.php deleted file mode 100644 index 1098f48b..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/EntrySpec.php +++ /dev/null @@ -1,267 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Changes; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Entry\Entry; -use PhpSpec\ObjectBehavior; - -class EntrySpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - new Dn('cn=foo,dc=example,dc=local'), - new Attribute('cn', 'foo'), - new Attribute('telephoneNumber', '123', '456'), - new Attribute('cn;lang-en-us', 'bar'), - new Attribute('member;range=0-*', 'dc=foo') - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Entry::class); - } - - public function it_should_get_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('cn=foo,dc=example,dc=local')); - } - - public function it_should_allow_being_constructed_with_a_string_dn(): void - { - $this->beConstructedWith('cn=foo,dc=example,dc=local'); - - $this->getDn()->shouldBeLike(new Dn('cn=foo,dc=example,dc=local')); - } - - public function it_should_be_constructed_from_an_array_using_create(): void - { - $this->beConstructedThrough('create', ['cn=foobar,dc=example,dc=local', [ - 'cn' => 'foobar', - 'telephoneNumber' => ['123', '456'], - ]]); - - $this->getDn()->shouldBeLike(new Dn('cn=foobar,dc=example,dc=local')); - $this->getAttributes()->shouldBeLike([ - new Attribute('cn', 'foobar'), - new Attribute('telephoneNumber', '123', '456') - ]); - } - - public function it_should_be_constructed_from_an_array_using_fromArray(): void - { - $this->beConstructedThrough('fromArray', ['cn=foobar,dc=example,dc=local', [ - 'cn' => 'foobar', - 'telephoneNumber' => ['123', '456'], - ]]); - - $this->getDn()->shouldBeLike(new Dn('cn=foobar,dc=example,dc=local')); - $this->getAttributes()->shouldBeLike([ - new Attribute('cn', 'foobar'), - new Attribute('telephoneNumber', '123', '456') - ]); - } - - public function it_should_be_constructed_from_an_array_using_fromArray_when_not_all_data_are_strings(): void - { - $this->beConstructedThrough('fromArray', ['cn=foobar,dc=example,dc=local', [ - 'cn' => 'foobar', - 'telephoneNumber' => [123, 456], - 'is_bad_idea' => true, - ]]); - - $this->getDn() - ->shouldBeLike(new Dn('cn=foobar,dc=example,dc=local')); - $this->getAttributes() - ->shouldBeLike([ - new Attribute('cn', 'foobar'), - new Attribute('telephoneNumber', '123', '456'), - new Attribute('is_bad_idea', '1') - ]); - } - - public function it_should_get_the_entry_as_an_associative_array(): void - { - $this->toArray()->shouldBeEqualTo([ - 'cn' => ['foo'], - 'telephoneNumber' => [ '123', '456'], - 'cn;lang-en-us' => ['bar'], - 'member;range=0-*' => ['dc=foo'] - ]); - } - - public function it_should_get_the_count_as_the_amount_of_attributes_in_the_entry(): void - { - $this->count()->shouldBeEqualTo(4); - } - - public function it_should_have_a_string_representation_of_the_dn(): void - { - $this->__toString()->shouldBeEqualTo('cn=foo,dc=example,dc=local'); - } - - public function it_should_return_null_for_an_attribute_that_doesnt_exist(): void - { - $this->get('foobar')->shouldBeNull(); - } - - public function it_should_get_an_attribute_using_a_string(): void - { - $this->get('cN')->getName()->shouldBeEqualTo('cn'); - } - - public function it_should_get_an_attribute_using_an_attribute(): void - { - $this->get(new Attribute('cn'))->getName()->shouldBeEqualTo('cn'); - } - - public function it_should_get_an_attribute_with_options_using_only_the_name(): void - { - $this->get('cn')->getValues()->shouldBeEqualTo(['foo']); - } - - public function it_should_get_an_attribute_with_options_using_the_options(): void - { - $this->get('cn;lang-en-us')->getValues()->shouldBeLike(['bar']); - } - - public function it_should_not_get_an_attribute_with_the_same_name_if_the_requested_options_are_not_the_same(): void - { - $this->get('member;foo')->shouldBeNull(); - } - - public function it_should_respect_the_strict_option_for_getting_an_attribute(): void - { - $this->get('member', true)->shouldBeNull(); - $this->get('member')->shouldBeAnInstanceOf(Attribute::class); - } - - public function it_should_reset_an_attribute_using_a_string(): void - { - $this->reset('cn')->get('cn', true)->shouldBeNull(); - } - - public function it_should_remove_an_attribute_using_an_attribute(): void - { - $this->reset(new Attribute('cn'))->get('cn', true)->shouldBeNull(); - } - - public function if_should_check_if_it_has_an_attribute_using_a_string() - { - $this->has('Cn')->shouldBeEqualTo(true); - $this->has('bleh')->shouldBeEqualTo(false); - } - - public function it_should_check_if_it_has_an_attribute_using_an_attribute(): void - { - $this->has(new Attribute('cn'))->shouldBeEqualTo(true); - $this->has(new Attribute('bleh'))->shouldBeEqualTo(false); - } - - public function it_should_get_an_attribute_through_the_magic_get(): void - { - $this->__get('CN')->getName()->shouldBeEqualTo('cn'); - } - - public function it_should_get_null_on_an_attribute_that_doesnt_exist_through_the_magic_get(): void - { - $this->__get('foo')->shouldBeNull(); - } - - public function it_should_set_an_attribute_through_the_magic_set(): void - { - $this->__set('foo', 'bar'); - $this->__set('bar', ['foo', 'bar']); - - $this->get('foo')->getValues()->shouldBeEqualTo(['bar']); - $this->get('bar')->getValues()->shouldBeEqualTo(['foo', 'bar']); - } - - public function it_should_check_if_an_attribute_exists_through_the_magic_isset(): void - { - $this->__isset('CN')->shouldBeEqualTo(true); - $this->__isset('foo')->shouldBeEqualTo(false); - } - - public function it_should_remove_a_variable_through_the_magic_unset(): void - { - $this->__unset('cn'); - - $this->get('cn', true)->shouldBeNull(); - } - - public function it_should_add_to_an_attributes_values_if_it_already_exists_while_adding(): void - { - $this->add('cn', 'bar'); - $this->get('cn')->getValues()->shouldBeEqualTo(['foo', 'bar']); - - $this->add(new Attribute('telephonenumber', '789')); - $this->get('telephoneNumber')->getValues()->shouldBeEqualTo(['123', '456', '789']); - - $this->add('sn', 'smith'); - $this->get('sn')->getValues()->shouldBeLike(['smith']); - } - - public function it_should_remove_an_attributes_values_if_it_already_exists_when_deleting(): void - { - $this->remove('telephonenumber', '123'); - $this->get('telephoneNumber')->getValues()->shouldNotContain(['123']); - - $this->remove(new Attribute('telephonenumber', '456')); - $this->get('telephoneNumber')->getValues()->shouldBeEqualTo([]); - } - - public function it_should_not_generate_a_delete_change_when_no_values_are_provided(): void - { - $this->remove('telephonenumber'); - $this->changes()->toArray()->shouldBeEqualTo([]); - } - - public function it_should_generate_a_delete_change_when_unsetting_an_attribute(): void - { - $this->__unset('telephonenumber'); - - $this->changes()->toArray()[0]->getAttribute()->getName()->shouldBeEqualTo('telephonenumber'); - $this->changes()->toArray()[0]->getAttribute()->getValues()->shouldBeEqualTo([]); - $this->changes()->toArray()[0]->getType()->shouldBeEqualTo(Change::TYPE_DELETE); - } - - public function it_should_generate_a_replace_change_when_setting_an_attribute(): void - { - $this->set('cn', 'foo'); - - $this->changes()->toArray()[0]->getAttribute()->getName()->shouldBeEqualTo('cn'); - $this->changes()->toArray()[0]->getAttribute()->getValues()->shouldBeEqualTo(['foo']); - $this->changes()->toArray()[0]->getType()->shouldBeEqualTo(Change::TYPE_REPLACE); - } - - public function it_should_generate_an_add_change_when_adding_an_attribute(): void - { - $this->add('sn', 'Smith'); - - $this->changes()->toArray()[0]->getAttribute()->getName()->shouldBeEqualTo('sn'); - $this->changes()->toArray()[0]->getAttribute()->getValues()->shouldBeEqualTo(['Smith']); - $this->changes()->toArray()[0]->getType()->shouldBeEqualTo(Change::TYPE_ADD); - } - - public function it_should_get_the_changes(): void - { - $this->changes()->shouldBeAnInstanceOf(Changes::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/OptionSpec.php b/tests/spec/FreeDSx/Ldap/Entry/OptionSpec.php deleted file mode 100644 index d47cf12b..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/OptionSpec.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Option; -use PhpSpec\ObjectBehavior; - -class OptionSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Option::class); - } - - public function it_should_detect_if_it_is_not_a_language_option(): void - { - $this->isLanguageTag()->shouldBeEqualTo(false); - } - - public function it_should_detect_if_it_is_a_language_option(): void - { - $this->beConstructedWith('lang-en'); - - $this->isLanguageTag()->shouldBeEqualTo(true); - } - - public function it_should_detect_if_it_is_not_a_range_option(): void - { - $this->isRange()->shouldBeEqualTo(false); - } - - public function it_should_detect_if_it_is_a_range_option(): void - { - $this->beConstructedWith('range=0-1500'); - - $this->isRange()->shouldBeEqualTo(true); - } - - public function it_should_get_the_high_range_value_of_an_option(): void - { - $this->beConstructedWith('range=0-1500'); - - $this->getHighRange()->shouldBeEqualTo('1500'); - } - - public function it_should_return_an_empty_string_if_the_high_range_cannot_be_parsed(): void - { - $this->getHighRange()->shouldBeEqualTo(''); - } - - public function it_should_get_the_low_range_value_of_an_option(): void - { - $this->beConstructedWith('range=0-1500'); - - $this->getLowRange()->shouldBeEqualTo('0'); - } - - public function it_should_return_null_if_the_low_range_cannot_be_parsed(): void - { - $this->getLowRange()->shouldBeNull(); - } - - public function it_should_have_a_factory_method_for_a_range(): void - { - $this->beConstructedThrough('fromRange', ['0']); - - $this->isRange()->shouldBeEqualTo(true); - $this->getLowRange()->shouldBeEqualTo('0'); - $this->getHighRange()->shouldBeEqualTo('*'); - } - - public function it_should_return_whether_the_option_starts_with_a_string(): void - { - $this->startsWith('fo')->shouldBeEqualTo(true); - $this->startsWith('bar')->shouldBeEqualTo(false); - } - - public function it_should_get_the_string_option_with_toString(): void - { - $this->toString()->shouldBeEqualTo('foo'); - } - - public function it_should_have_a_string_representation(): void - { - $this->__toString()->shouldBeEqualTo('foo'); - } - - public function it_should_check_for_equality_with_another_option(): void - { - $this->equals(new Option('FOO'))->shouldBeEqualTo(true); - $this->equals(new Option('foo'))->shouldBeEqualTo(true); - $this->equals(new Option('bar'))->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/OptionsSpec.php b/tests/spec/FreeDSx/Ldap/Entry/OptionsSpec.php deleted file mode 100644 index 06d8ad5d..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/OptionsSpec.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Option; -use FreeDSx\Ldap\Entry\Options; -use PhpSpec\ObjectBehavior; - -class OptionsSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'Bar', 'lang-en', 'range=1500-*'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Options::class); - } - - public function it_should_be_countable(): void - { - $this->shouldImplement(\Countable::class); - } - - public function it_should_be_iterable(): void - { - $this->shouldImplement(\IteratorAggregate::class); - } - - public function it_should_get_the_first_option(): void - { - $this->first()->shouldBeLike(new Option('foo')); - } - - public function it_should_get_the_last_option(): void - { - $this->last()->shouldBeLike(new Option('range=1500-*')); - } - - public function it_should_return_null_for_the_first_option_when_there_are_none(): void - { - $this->beConstructedWith(...[]); - - $this->first()->shouldBeNull(); - } - - public function it_should_return_null_for_the_last_option_when_there_are_none(): void - { - $this->beConstructedWith(...[]); - - $this->last()->shouldBeNull(); - } - - public function it_should_get_a_semi_colon_separated_string_representation_calling_toString(): void - { - $this->toString()->shouldBeEqualTo('foo;Bar;lang-en;range=1500-*'); - } - - public function it_should_sort_and_lowercase_the_string_representation_if_requested(): void - { - $this->toString(true)->shouldBeEqualTo('bar;foo;lang-en;range=1500-*'); - } - - public function it_should_have_a_string_representation(): void - { - $this->__toString()->shouldBeEqualTo('foo;Bar;lang-en;range=1500-*'); - } - - public function it_should_get_the_count(): void - { - $this->count()->shouldBeEqualTo(4); - } - - public function it_should_get_an_array_of_options_if_requested(): void - { - $this->toArray()->shouldBeLike([ - new Option('foo'), - new Option('Bar'), - new Option('lang-en'), - new Option('range=1500-*') - ]); - } - - public function it_should_add_an_option(): void - { - $this->add('x-bar'); - $this->has('x-bar')->shouldBeEqualTo(true); - } - - public function it_should_remove_an_option(): void - { - $this->remove('foo'); - $this->has('foo')->shouldBeEqualTo(false); - } - - public function it_should_set_the_options(): void - { - $this->set('foo'); - $this->count()->shouldBeEqualTo(1); - } - - public function it_should_check_for_an_option(): void - { - $this->has('bar')->shouldBeEqualTo(true); - $this->has('x-foo')->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Entry/RdnSpec.php b/tests/spec/FreeDSx/Ldap/Entry/RdnSpec.php deleted file mode 100644 index e50b2eda..00000000 --- a/tests/spec/FreeDSx/Ldap/Entry/RdnSpec.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Entry; - -use FreeDSx\Ldap\Entry\Rdn; -use FreeDSx\Ldap\Exception\InvalidArgumentException; -use PhpSpec\ObjectBehavior; - -class RdnSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn', 'foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Rdn::class); - } - - public function it_should_get_the_name(): void - { - $this->getName()->shouldBeEqualTo('cn'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_string_representation(): void - { - $this->toString()->shouldBeEqualTo('cn=foo'); - } - - public function it_should_get_whether_it_is_multivalued(): void - { - $this->isMultivalued()->shouldBeEqualTo(false); - } - - public function it_should_be_created_from_a_string_rdn(): void - { - $this->beConstructedThrough('create', ['cn=foobar']); - - $this->getName()->shouldBeEqualTo('cn'); - $this->getValue()->shouldBeEqualTo('foobar'); - } - - public function it_should_error_when_constructing_an_rdn_that_is_invalid(): void - { - $this->shouldThrow(InvalidArgumentException::class)->during('create', ['foobar']); - } - - public function it_should_escape_an_rdn_value_with_leading_and_trailing_spaces(): void - { - $this::escape(' foo,= bar ')->shouldBeEqualTo('\20foo\2c= bar\20'); - } - - public function it_should_escape_an_rdn_value_with_a_leading_pound_sign(): void - { - $this::escape('# foo ')->shouldBeEqualTo('\23 foo\20'); - } - - public function it_should_escape_required_values(): void - { - $this::escape('\foo + "bar", > bar < foo;')->shouldBeEqualTo('\5cfoo \2b \22bar\22\2c \3e bar \3c foo\3b'); - } - - public function it_should_escape_all_characters(): void - { - $this::escapeAll('# foo ')->shouldBeEqualTo('\23\20\66\6f\6f\20'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/BindExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/BindExceptionSpec.php deleted file mode 100644 index 7cf6ec8f..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/BindExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\BindException; -use PhpSpec\ObjectBehavior; - -class BindExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(BindException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/ConnectionExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/ConnectionExceptionSpec.php deleted file mode 100644 index a635688f..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/ConnectionExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\ConnectionException; -use PhpSpec\ObjectBehavior; - -class ConnectionExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(ConnectionException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/FilterParseExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/FilterParseExceptionSpec.php deleted file mode 100644 index c9b99a53..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/FilterParseExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\FilterParseException; -use PhpSpec\ObjectBehavior; - -class FilterParseExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(FilterParseException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/InvalidArgumentExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/InvalidArgumentExceptionSpec.php deleted file mode 100644 index 8a1db111..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/InvalidArgumentExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\InvalidArgumentException; -use PhpSpec\ObjectBehavior; - -class InvalidArgumentExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(InvalidArgumentException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/OperationExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/OperationExceptionSpec.php deleted file mode 100644 index a776f018..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/OperationExceptionSpec.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\ResultCode; -use PhpSpec\ObjectBehavior; - -class OperationExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(OperationException::class); - } - - public function it_should_have_a_default_code_of_operations_error(): void - { - $this->getCode()->shouldBeEqualTo(ResultCode::OPERATIONS_ERROR); - } - - public function it_should_get_the_code_short_string(): void - { - $this->getCodeShort()->shouldBeEqualTo('operationsError'); - } - - public function it_should_get_the_code_description(): void - { - $this->getCodeDescription()->shouldBeEqualTo(ResultCode::MEANING_DESCRIPTION[ResultCode::OPERATIONS_ERROR]); - } - - public function it_should_generate_a_message_if_none_was_provided(): void - { - $this->getMessage()->shouldBeEqualTo('The result code 1 was thrown (operationsError). Indicates that the operation is not properly sequenced with relation to other operations (of same or different type).'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/ProtocolExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/ProtocolExceptionSpec.php deleted file mode 100644 index 8e0c3df1..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/ProtocolExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\ProtocolException; -use PhpSpec\ObjectBehavior; - -class ProtocolExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(ProtocolException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/ReferralExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/ReferralExceptionSpec.php deleted file mode 100644 index e5a537e1..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/ReferralExceptionSpec.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\ReferralException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\ResultCode; -use PhpSpec\ObjectBehavior; - -class ReferralExceptionSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', new LdapUrl('foo'), new LdapUrl('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ReferralException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } - - public function it_should_get_the_referrals(): void - { - $this->getReferrals()->shouldBeLike([ - new LdapUrl('foo'), - new LdapUrl('bar') - ]); - } - - public function it_should_set_the_message(): void - { - $this->getMessage()->shouldBeEqualTo('foo'); - } - - public function it_should_have_a_code_of_the_referral_result_code(): void - { - $this->getCode()->shouldBeEqualTo(ResultCode::REFERRAL); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/RuntimeExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/RuntimeExceptionSpec.php deleted file mode 100644 index b0248055..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/RuntimeExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\RuntimeException; -use PhpSpec\ObjectBehavior; - -class RuntimeExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(RuntimeException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/SkipReferralExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/SkipReferralExceptionSpec.php deleted file mode 100644 index 91fca57a..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/SkipReferralExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\SkipReferralException; -use PhpSpec\ObjectBehavior; - -class SkipReferralExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(SkipReferralException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/UnexpectedValueExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/UnexpectedValueExceptionSpec.php deleted file mode 100644 index 1d70adcf..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/UnexpectedValueExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\UnexpectedValueException; -use PhpSpec\ObjectBehavior; - -class UnexpectedValueExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(UnexpectedValueException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/UnsolicitedNotificationExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/UnsolicitedNotificationExceptionSpec.php deleted file mode 100644 index 3acae501..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/UnsolicitedNotificationExceptionSpec.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; -use PhpSpec\ObjectBehavior; - -class UnsolicitedNotificationExceptionSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 0, null, 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(UnsolicitedNotificationException::class); - } - - public function it_should_extend_protocol_exception(): void - { - $this->shouldBeAnInstanceOf(ProtocolException::class); - } - - public function it_should_get_the_name_oid(): void - { - $this->getOid()->shouldBeEqualTo('bar'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Exception/UrlParseExceptionSpec.php b/tests/spec/FreeDSx/Ldap/Exception/UrlParseExceptionSpec.php deleted file mode 100644 index a287ad93..00000000 --- a/tests/spec/FreeDSx/Ldap/Exception/UrlParseExceptionSpec.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Exception; - -use FreeDSx\Ldap\Exception\UrlParseException; -use PhpSpec\ObjectBehavior; - -class UrlParseExceptionSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(UrlParseException::class); - } - - public function it_should_extend_exception(): void - { - $this->shouldBeAnInstanceOf('\Exception'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/LdapClientSpec.php b/tests/spec/FreeDSx/Ldap/LdapClientSpec.php deleted file mode 100644 index 6fbe91a6..00000000 --- a/tests/spec/FreeDSx/Ldap/LdapClientSpec.php +++ /dev/null @@ -1,453 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Container; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use FreeDSx\Ldap\Operation\Response\AddResponse; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\CompareResponse; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; -use FreeDSx\Ldap\Operation\Response\ModifyResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; -use FreeDSx\Ldap\Search\DirSync; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Search\Paging; -use FreeDSx\Ldap\Search\RangeRetrieval; -use FreeDSx\Ldap\Search\Vlv; -use FreeDSx\Ldap\Sync\SyncRepl; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class LdapClientSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let( - ClientProtocolHandler $handler, - Container $mockContainer, - ClientQueueInstantiator $queueInstantiator, - ): void { - $mockContainer - ->get(ClientProtocolHandler::class) - ->willReturn($handler); - - $mockContainer - ->get(ClientQueueInstantiator::class) - ->willReturn($queueInstantiator); - - $queueInstantiator - ->isInstantiatedAndConnected() - ->willReturn(false); - - $this->beConstructedWith( - new ClientOptions(), - $mockContainer, - ); - } - - public function it_should_send_a_message_and_throw_an_exception_if_no_response_is_received_on_sendAndReceive(ClientProtocolHandler $handler): void - { - $handler->send(Argument::any()) - ->shouldBeCalled() - ->willReturn(null); - - $this->shouldThrow(OperationException::class) - ->during( - 'sendAndReceive', - [Operations::read('')] - ); - } - - public function it_should_send_a_message_and_return_the_response_on_sendAndReceive( - ClientProtocolHandler $handler, - LdapMessageResponse $response, - ): void { - $handler->send(Argument::any()) - ->shouldBeCalled() - ->willReturn($response); - - $this->sendAndReceive(Operations::read('')) - ->shouldBeEqualTo($response); - } - - public function it_should_send_a_search_and_get_entries_back(ClientProtocolHandler $handler): void - { - $search = Operations::search(Filters::equal( - 'foo', - 'bar' - )); - - $handler->send($search)->shouldBeCalled() - ->willReturn( - $this::makeSearchResponseFromEntries(new Entries( - Entry::create('dc=foo,dc=bar') - )) - ); - - $this->search($search) - ->shouldBeLike(new Entries(Entry::create('dc=foo,dc=bar'))); - } - - public function it_should_bind(ClientProtocolHandler $handler): void - { - $response = new LdapMessageResponse( - 1, - new BindResponse(new LdapResult( - 0, - '' - )) - ); - $handler->send(new SimpleBindRequest( - 'foo', - 'bar', - 3 - ))->shouldBeCalled() - ->willReturn($response); - - $this->bind( - 'foo', - 'bar' - )->shouldBeEqualTo($response); - } - - public function it_should_construct_a_pager_helper(): void - { - $this->paging(Operations::search( - Filters::equal( - 'foo', - 'bar' - ) - ))->shouldBeAnInstanceOf(Paging::class); - } - - public function it_should_construct_a_vlv_helper(): void - { - $this->vlv( - Operations::search(Filters::equal( - 'foo', - 'bar' - )), - 'cn', - 100 - )->shouldBeAnInstanceOf(Vlv::class); - } - - public function it_should_construct_a_dirsync_helper(): void - { - $this->dirSync() - ->shouldBeAnInstanceOf(DirSync::class); - } - - public function it_should_construct_a_range_retrieval_helper(): void - { - $this->range() - ->shouldBeAnInstanceOf(RangeRetrieval::class); - } - - public function it_should_start_tls(ClientProtocolHandler $handler): void - { - $handler->send(Operations::extended(ExtendedRequest::OID_START_TLS)) - ->shouldBeCalled() - ->willReturn(null); - - $this->startTls(); - } - - public function it_should_unbind_if_requested(ClientProtocolHandler $handler): void - { - $handler->send(new UnbindRequest()) - ->shouldBeCalled() - ->willReturn(null); - - $this->unbind(); - } - - public function it_should_return_a_whoami(ClientProtocolHandler $handler): void - { - $handler->send(Operations::extended(ExtendedRequest::OID_WHOAMI)) - ->willReturn(new LdapMessageResponse( - 1, - new ExtendedResponse( - new LdapResult(0, ''), - null, - 'foo' - ) - )); - - $this->whoami() - ->shouldBeEqualTo('foo'); - } - - public function it_should_return_a_correct_compare_response_on_a_match(ClientProtocolHandler $handler): void - { - $handler->send(Operations::compare( - 'cn=foo', - 'foo', - 'bar' - ))->willReturn(new LdapMessageResponse( - 1, - new CompareResponse(ResultCode::COMPARE_TRUE) - )); - - $this->compare( - 'cn=foo', - 'foo', - 'bar' - )->shouldBeEqualTo(true); - } - - public function it_should_return_a_correct_compare_response_on_a_non_match(ClientProtocolHandler $handler): void - { - $handler->send(Operations::compare( - 'cn=foo', - 'foo', - 'bar' - ))->willReturn(new LdapMessageResponse( - 1, - new CompareResponse(ResultCode::COMPARE_FALSE) - )); - - $this->compare( - 'cn=foo', - 'foo', - 'bar' - )->shouldBeEqualTo(false); - } - - public function it_should_send_a_modify_operation_on_update(ClientProtocolHandler $handler): void - { - $entry = Entry::create('cn=foo,dc=local', ['cn' => 'foo']); - $entry->set('sn', 'bar'); - - $handler->send( - Operations::modify( - $entry->getDn(), - ...$entry->changes() - ) - )->shouldBeCalled() - ->willReturn(new LdapMessageResponse( - 1, - new ModifyResponse(ResultCode::SUCCESS) - )); - - $this->update($entry); - } - - public function it_should_send_an_add_operation_on_create(ClientProtocolHandler $handler): void - { - $entry = Entry::create('cn=foo,dc=local', ['cn' => 'foo']); - - $handler->send(Operations::add($entry)) - ->shouldBeCalled() - ->willReturn(new LdapMessageResponse( - 1, - new AddResponse(ResultCode::SUCCESS) - )); - - $this->create($entry); - } - - public function it_should_send_a_delete_operation_on_delete(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - - $handler->send(Operations::delete('cn=foo,dc=local'))->shouldBeCalled() - ->willReturn(new LdapMessageResponse( - 1, - new DeleteResponse(ResultCode::SUCCESS) - )); - - $this->delete($entry->getDn()->toString()); - } - - public function it_should_send_a_modify_dn_operation_on_move(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - $parent = new Entry('cn=bar,dc=local'); - - $handler->send(Operations::move('cn=foo,dc=local', 'cn=bar,dc=local')) - ->shouldBeCalled() - ->willReturn(new LdapMessageResponse( - 1, - new ModifyDnResponse(ResultCode::SUCCESS) - )); - - $this->move( - $entry, - $parent, - ); - } - - public function it_should_send_a_modify_dn_operation_on_rename(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - $newRdn = 'cn=bar'; - - $handler->send(Operations::rename('cn=foo,dc=local', 'cn=bar')) - ->shouldBeCalled() - ->willReturn(new LdapMessageResponse( - 1, - new ModifyDnResponse(ResultCode::SUCCESS) - )); - - $this->rename( - $entry, - $newRdn, - ); - } - - public function it_should_send_a_base_search_on_a_read_and_return_an_entry(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - $handler->send(Operations::read('cn=foo,dc=local')) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); - - $this->read($entry->getDn()->toString()) - ->shouldBeEqualTo($entry); - } - - public function it_should_send_a_read_to_the_RootDSE_if_it_is_called_with_no_arguments(ClientProtocolHandler $handler): void - { - $entry = new Entry(''); - $handler->send(Operations::read('')) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); - - $this->read() - ->shouldBeEqualTo($entry); - } - - public function it_should_send_a_base_search_on_a_read_and_return_null_if_it_does_not_exist(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - - $handler->send(Operations::read('cn=foo,dc=local')) - ->shouldBeCalled() - ->willThrow(new OperationException( - '', - ResultCode::NO_SUCH_OBJECT - )); - - $this->read($entry->getDn()->toString()) - ->shouldBeNull(); - } - - public function it_should_throw_an_exception_on_read_or_fail_if_the_entry_does_not_exist(ClientProtocolHandler $handler): void - { - $exception = new OperationException('', ResultCode::NO_SUCH_OBJECT); - $entry = new Entry('cn=foo,dc=local'); - $handler->send(Operations::read('cn=foo,dc=local'))->shouldBeCalled() - ->willThrow($exception); - - $this->getOptions(); - - $this->shouldThrow($exception) - ->during('readOrFail', [$entry]); - } - - public function it_should_return_an_entry_on_a_readOrFail_if_it_exists(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - $handler->send(Operations::read('cn=foo,dc=local'))->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); - - $this->readOrFail($entry->getDn()->toString()) - ->shouldBeEqualTo($entry); - } - - public function it_should_send_a_base_search_on_a_read_and_throw_an_unrelated_operation_exception(ClientProtocolHandler $handler): void - { - $entry = new Entry('cn=foo,dc=local'); - - $handler->send(Operations::read('cn=foo,dc=local')) - ->shouldBeCalled() - ->willThrow(new OperationException( - '', - ResultCode::INSUFFICIENT_ACCESS_RIGHTS - )); - - $this->shouldThrow(OperationException::class) - ->during( - 'read', - [$entry] - ); - } - - public function it_should_get_the_default_options(): void - { - $this->getOptions() - ->toArray() - ->shouldBeEqualTo([ - 'version' => 3, - 'servers' => [], - 'port' => 389, - 'transport' => 'tcp', - 'base_dn' => null, - 'page_size' => 1000, - 'use_ssl' => false, - 'ssl_validate_cert' => true, - 'ssl_allow_self_signed' => false, - 'ssl_ca_cert' => null, - 'ssl_peer_name' => null, - 'timeout_connect' => 3, - 'timeout_read' => 10, - 'referral' => 'throw', - 'referral_chaser' => null, - 'referral_limit' => 10, - ]); - } - - public function it_should_set_the_options(): void - { - $options = (new ClientOptions()) - ->setServers([ - 'foo', - 'bar', - ]); - - $this->setOptions($options); - - $this->getOptions() - ->shouldBeEqualTo($options); - } - - public function it_should_construct_a_syncrepl_helper(): void - { - $syncRequest = Filters::present('foo'); - - $this->syncRepl($syncRequest) - ->shouldBeLike(new SyncRepl( - $this->getWrappedObject(), - $syncRequest - )); - - $this->syncRepl() - ->shouldBeLike(new SyncRepl($this->getWrappedObject())); - } -} diff --git a/tests/spec/FreeDSx/Ldap/LdapServerSpec.php b/tests/spec/FreeDSx/Ldap/LdapServerSpec.php deleted file mode 100644 index 745ab091..00000000 --- a/tests/spec/FreeDSx/Ldap/LdapServerSpec.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\LdapServer; -use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; -use FreeDSx\Ldap\Server\RequestHandler\ProxyPagingHandler; -use FreeDSx\Ldap\Server\RequestHandler\ProxyRequestHandler; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface; -use FreeDSx\Ldap\Server\ServerRunner\ServerRunnerInterface; -use FreeDSx\Ldap\ServerOptions; -use FreeDSx\Socket\SocketServer; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use Psr\Log\LoggerInterface; - -class LdapServerSpec extends ObjectBehavior -{ - public function let(ServerRunnerInterface $serverRunner): void - { - $this->beConstructedWith( - (new ServerOptions()) - ->setPort(33389) - ->setServerRunner($serverRunner->getWrappedObject()) - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapServer::class); - } - - public function it_should_run_the_server(ServerRunnerInterface $serverRunner): void - { - $serverRunner->run(Argument::type(SocketServer::class)) - ->shouldBeCalled(); - - $this->run(); - } - - public function it_should_use_the_request_handler_specified(RequestHandlerInterface $requestHandler): void - { - $this->useRequestHandler($requestHandler); - - $this->getOptions() - ->getRequestHandler() - ->shouldBeEqualTo($requestHandler); - } - - public function it_should_use_the_rootdse_handler_specified(RootDseHandlerInterface $rootDseHandler): void - { - $this->useRootDseHandler($rootDseHandler); - - $this->getOptions() - ->getRootDseHandler() - ->shouldBeEqualTo($rootDseHandler); - } - - public function it_should_use_the_paging_handler_specified(PagingHandlerInterface $pagingHandler): void - { - $this->usePagingHandler($pagingHandler); - - $this->getOptions() - ->getPagingHandler() - ->shouldBeEqualTo($pagingHandler); - } - - public function it_should_use_the_logger_specified(LoggerInterface $logger): void - { - $this->useLogger($logger); - - $this->getOptions() - ->getLogger() - ->shouldBeEqualTo($logger); - } - - public function it_should_get_the_default_options(): void - { - $this->getOptions() - ->toArray() - ->shouldBeEqualTo([ - 'ip' => "0.0.0.0", - 'port' => 33389, - 'unix_socket' => "/var/run/ldap.socket", - 'transport' => "tcp", - 'idle_timeout' => 600, - 'require_authentication' => true, - 'allow_anonymous' => false, - 'request_handler' => null, - 'rootdse_handler' => null, - 'paging_handler' => null, - 'logger' => null, - 'use_ssl' => false, - 'ssl_cert' => null, - 'ssl_cert_key' => null, - 'ssl_cert_passphrase' => null, - 'dse_alt_server' => null, - 'dse_naming_contexts' => [ - "dc=FreeDSx,dc=local" - ], - 'dse_vendor_name' => "FreeDSx", - 'dse_vendor_version' => null, - ]); - } - - public function it_should_make_a_proxy_server(): void - { - $client = new LdapClient( - (new ClientOptions()) - ->setServers(['localhost']) - ); - $serverOptions = new ServerOptions(); - $proxyRequestHandler = new ProxyHandler($client); - $server = new LdapServer($serverOptions); - $server->useRequestHandler($proxyRequestHandler); - $server->useRootDseHandler($proxyRequestHandler); - $server->usePagingHandler(new ProxyPagingHandler($client)); - - $proxyOptions = $this::makeProxy('localhost') - ->getOptions(); - - $proxyOptions->getPagingHandler() - ->shouldBeAnInstanceOf(ProxyPagingHandler::class); - $proxyOptions - ->getRequestHandler() - ->shouldBeAnInstanceOf(ProxyRequestHandler::class); - $proxyOptions - ->getRootDseHandler() - ->shouldBeAnInstanceOf(ProxyRequestHandler::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/LdapUrlExtensionSpec.php b/tests/spec/FreeDSx/Ldap/LdapUrlExtensionSpec.php deleted file mode 100644 index a98fd41c..00000000 --- a/tests/spec/FreeDSx/Ldap/LdapUrlExtensionSpec.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\LdapUrlExtension; -use PhpSpec\ObjectBehavior; - -class LdapUrlExtensionSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapUrlExtension::class); - } - - public function it_should_get_the_extension_name(): void - { - $this->getName() - ->shouldBeEqualTo('foo'); - - $this->setName('bar') - ->getName() - ->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_extension_value(): void - { - $this->getValue()->shouldBeNull(); - - $this->setValue('bar') - ->getValue() - ->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_criticality(): void - { - $this->getIsCritical() - ->shouldBeEqualTo(false); - - $this->setIsCritical(true) - ->getIsCritical() - ->shouldBeEqualTo(true); - } - - public function it_should_parse_an_extension_with_only_a_name(): void - { - $this::parse('foo') - ->shouldBeLike(new LdapUrlExtension('foo')); - } - - public function it_should_generate_a_string_extension_with_only_a_name(): void - { - $this->toString() - ->shouldBeEqualTo('foo'); - } - - public function it_should_parse_an_extension_with_a_criticality(): void - { - $this::parse('!foo') - ->shouldBeLike(new LdapUrlExtension( - 'foo', - null, - true - )); - } - - public function it_should_generate_a_string_extension_with_a_criticality(): void - { - $this->setIsCritical(true); - - $this->toString() - ->shouldBeEqualTo('!foo'); - } - - public function it_should_parse_an_extension_with_a_value(): void - { - $this::parse('foo=bar') - ->shouldBeLike(new LdapUrlExtension( - 'foo', - 'bar' - )); - } - - public function it_should_generate_a_string_extension_with_a_value(): void - { - $this->setValue('bar'); - - $this->toString() - ->shouldBeEqualTo('foo=bar'); - } - - public function it_should_parse_an_extension_and_decode_it_if_needed(): void - { - $this::parse('e-bindname=cn=Manager%2cdc=example%2cdc=com') - ->shouldBeLike(new LdapUrlExtension( - 'e-bindname', - 'cn=Manager,dc=example,dc=com' - )); - } - - public function it_should_generate_a_string_extension_and_encode_it_if_needed(): void - { - $this->beConstructedWith( - 'e-bindname', - 'cn=Manager,dc=example,dc=com' - ); - - $this->toString() - ->shouldBeEqualTo('e-bindname=cn=Manager%2cdc=example%2cdc=com'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/LdapUrlSpec.php b/tests/spec/FreeDSx/Ldap/LdapUrlSpec.php deleted file mode 100644 index 7fc4d268..00000000 --- a/tests/spec/FreeDSx/Ldap/LdapUrlSpec.php +++ /dev/null @@ -1,266 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\InvalidArgumentException; -use FreeDSx\Ldap\Exception\UrlParseException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\LdapUrlExtension; -use PhpSpec\ObjectBehavior; - -class LdapUrlSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapUrl::class); - } - - public function it_should_have_a_string_representation(): void - { - $this->toString()->shouldBeEqualTo('ldap://foo/'); - } - - public function it_should_parse_a_url_with_no_host_but_a_path(): void - { - $this::parse('ldap:///o=University%20of%20Michigan,c=US')->shouldBeLike( - (new LdapUrl())->setDn('o=University of Michigan,c=US') - ); - } - - public function it_should_generate_a_url_with_no_host_but_a_path(): void - { - $this->beConstructedWith(null); - $this->setDn('o=University of Michigan,c=US'); - - $this->toString()->shouldBeEqualTo('ldap:///o=University%20of%20Michigan,c=US'); - } - - public function it_should_parse_a_url_with_a_host_and_path_but_no_query_elements(): void - { - $this::parse('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US')->shouldBeLike( - (new LdapUrl('ldap1.example.net'))->setDn('o=University of Michigan,c=US') - ); - } - - public function it_should_generate_a_url_with_a_host_and_path_but_no_query_elements(): void - { - $this->beConstructedWith('ldap1.example.net'); - $this->setDn('o=University of Michigan,c=US'); - - $this->toString()->shouldBeEqualTo('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US'); - } - - public function it_should_parse_a_url_with_a_host_path_and_attribute(): void - { - $this::parse('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US?postalAddress')->shouldBeLike( - (new LdapUrl('ldap1.example.net')) - ->setDn('o=University of Michigan,c=US') - ->setAttributes('postalAddress') - ); - } - - public function it_should_generate_a_url_with_a_host_path_and_attribute(): void - { - $this->beConstructedWith('ldap1.example.net'); - $this->setDn('o=University of Michigan,c=US'); - $this->setAttributes('postalAddress'); - - $this->toString()->shouldBeEqualTo('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US?postalAddress'); - } - - public function it_should_parse_a_url_with_a_host_port_path_scope_and_filter(): void - { - $this::parse('ldap://ldap1.example.net:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)')->shouldBeLike( - (new LdapUrl('ldap1.example.net')) - ->setPort(6666) - ->setDn('o=University of Michigan,c=US') - ->setFilter('(cn=Babs Jensen)') - ->setScope('sub') - ); - } - - public function it_should_generate_a_url_with_a_host_port_path_scope_and_filter(): void - { - $this->beConstructedWith('ldap1.example.net'); - $this->setDn('o=University of Michigan,c=US'); - $this->setPort(6666); - $this->setScope('sub'); - $this->setFilter('(cn=Babs Jensen)'); - - $this->toString()->shouldBeEqualTo('ldap://ldap1.example.net:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)'); - } - - public function it_should_parse_a_url_with_a_host_path_single_scope_and_attribute(): void - { - $this::parse('LDAP://ldap1.example.com/c=GB?objectClass?ONE')->shouldBeLike( - (new LdapUrl('ldap1.example.com')) - ->setDn('c=GB') - ->setAttributes('objectClass') - ->setScope('one') - ); - } - - public function it_should_generate_a_url_with_a_host_path_single_scope_and_attribute(): void - { - $this->beConstructedWith('ldap1.example.com'); - $this->setDn('c=GB'); - $this->setAttributes('objectClass'); - $this->setScope('ONE'); - - $this->toString()->shouldBeEqualTo('ldap://ldap1.example.com/c=GB?objectClass?one'); - } - - public function it_should_parse_a_url_with_a_percent_encoded_question_mark_in_the_path(): void - { - $this::parse('ldap://ldap2.example.com/o=Question%3f,c=US?mail')->shouldBeLike( - (new LdapUrl('ldap2.example.com')) - ->setDn('o=Question?,c=US') - ->setAttributes('mail') - ); - } - - public function it_should_generate_a_url_with_a_percent_encoded_question_mark_in_the_path(): void - { - $this->beConstructedWith('ldap2.example.com'); - $this->setDn('o=Question?,c=US'); - $this->setAttributes('mail'); - - $this->toString()->shouldBeEqualTo('ldap://ldap2.example.com/o=Question%3f,c=US?mail'); - } - - public function it_should_parse_a_url_with_percent_encoded_filter_that_was_hex_escaped(): void - { - $this::parse('ldap://ldap3.example.com/o=Babsco,c=US???(four-octet=%5c00%5c00%5c00%5c04)')->shouldBeLike( - (new LdapUrl('ldap3.example.com')) - ->setDn('o=Babsco,c=US') - ->setFilter('(four-octet=\00\00\00\04)') - ); - } - - public function it_should_generate_a_url_with_percent_encoded_filter_that_was_hex_escaped(): void - { - $this->beConstructedWith('ldap3.example.com'); - $this->setDn('o=Babsco,c=US'); - $this->setFilter('(four-octet=\00\00\00\04)'); - - $this->toString()->shouldBeEqualTo('ldap://ldap3.example.com/o=Babsco,c=US???(four-octet=%5c00%5c00%5c00%5c04)'); - } - - public function it_should_parse_a_url_with_extensions(): void - { - $this::parse('ldap:///??sub??e-bindname=cn=Manager%2cdc=example%2cdc=com')->shouldBeLike( - (new LdapUrl(null)) - ->setScope('sub') - ->setExtensions(new LdapUrlExtension('e-bindname', 'cn=Manager,dc=example,dc=com')) - ); - } - - public function it_should_generate_a_url_with_extensions(): void - { - $this->beConstructedWith(null); - $this->setScope('sub'); - $this->setExtensions(new LdapUrlExtension('e-bindname', 'cn=Manager,dc=example,dc=com')); - - $this->toString()->shouldBeEqualTo('ldap:///??sub??e-bindname=cn=Manager%2cdc=example%2cdc=com'); - } - - public function it_should_parse_a_url_with_all_default_query_fields(): void - { - $this->parse('ldap://foo/????')->shouldBeLike(new LdapUrl('foo')); - $this->parse('ldap:///????')->shouldBeLike(new LdapUrl()); - } - - public function it_should_set_the_port(): void - { - $this->getPort()->shouldBeNull(); - $this->setPort(9001)->getPort()->shouldBeEqualTo(9001); - } - - public function it_should_set_the_scope(): void - { - $this->getScope()->shouldBeNull(); - $this->setScope('base')->getScope()->shouldBeEqualTo('base'); - $this->setScope('one')->getScope()->shouldBeEqualTo('one'); - $this->setScope('sub')->getScope()->shouldBeEqualTo('sub'); - } - - public function it_should_reject_an_invalid_scope(): void - { - $this->shouldThrow(InvalidArgumentException::class)->during('setScope', ['foo']); - } - - public function it_should_set_the_filter(): void - { - $this->getFilter()->shouldBeNull(); - $this->setFilter('foo=null')->getFilter()->shouldBeEqualTo('foo=null'); - } - - public function it_should_set_the_dn(): void - { - $this->getDn()->shouldBeNull(); - $this->setDn('dc=foo')->getDn()->shouldBeLike(new Dn('dc=foo')); - } - - public function it_should_set_the_attributes(): void - { - $this->getAttributes()->shouldBeEqualTo([]); - $this->setAttributes('foo', 'bar') - ->getAttributes() - ->shouldBeLike([ - new Attribute('foo'), - new Attribute('bar') - ]); - } - - public function it_should_set_the_host(): void - { - $this->getHost()->shouldBeEqualTo('foo'); - $this->setHost('bar')->getHost()->shouldBeEqualTo('bar'); - } - - public function it_should_set_whether_or_not_ssl_is_used(): void - { - $this->getUseSsl()->shouldBeEqualTo(false); - $this->setUseSsl(true) - ->getUseSsl() - ->shouldBeEqualTo(true); - } - - public function it_should_throw_an_error_if_the_scheme_is_not_ldap(): void - { - $this->shouldThrow(UrlParseException::class)->during( - 'parse', - ['https://foo/?'] - ); - $this->shouldThrow(UrlParseException::class)->during( - 'parse', - ['https:///?'] - ); - } - - public function it_should_throw_an_error_on_a_malformed_url(): void - { - $this->shouldThrow(UrlParseException::class)->during( - 'parse', - ['ldap'] - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/LdapResultSpec.php b/tests/spec/FreeDSx/Ldap/Operation/LdapResultSpec.php deleted file mode 100644 index f86e376d..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/LdapResultSpec.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class LdapResultSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapResult::class); - } - - public function it_should_get_the_diagnostic_message(): void - { - $this->getDiagnosticMessage()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_result_code(): void - { - $this->getResultCode()->shouldBeEqualTo(0); - } - - public function it_should_get_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('foo')); - } - - public function it_shouod_get_the_referrals(): void - { - $this->getReferrals()->shouldBeEqualTo([]); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(3, (new IncompleteType( - $encoder->encode(Asn1::octetString('ldap://foo')) - . $encoder->encode(Asn1::octetString('ldap://bar')) - ))->setIsConstructed(true)) - )]); - - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); - $this->getResultCode()->shouldBeEqualTo(0); - $this->getDiagnosticMessage()->shouldBeEqualTo('foo'); - $this->getReferrals()->shouldBeLike([ - new LdapUrl('foo'), - new LdapUrl('bar') - ]); - } - - public function it_should_throw_a_protocol_exception_if_the_referral_cannot_be_parsed(): void - { - $encoder = new LdapEncoder(); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(3, (new IncompleteType( - $encoder->encode(Asn1::octetString('ldap://foo')) - . $encoder->encode(Asn1::octetString('bar')) - ))->setIsConstructed(true)) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/AbandonRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/AbandonRequestSpec.php deleted file mode 100644 index 8ed8c2b4..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/AbandonRequestSpec.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\AbandonRequest; -use PhpSpec\ObjectBehavior; - -class AbandonRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AbandonRequest::class); - } - - public function it_should_get_the_message_id(): void - { - $this->getMessageId()->shouldBeEqualTo(1); - $this->setMessageId(2)->getMessageId()->shouldBeEqualTo(2); - } - - public function it_should_generate_correct_ASN1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(16, Asn1::integer(1))); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $this::fromAsn1(Asn1::application(16, Asn1::integer(1))) - ->shouldBeLike(new AbandonRequest(1)); - } - - public function it_should_not_allow_non_integers_from_ASN1(): void - { - $this->shouldThrow(ProtocolException::class) - ->during( - 'fromAsn1', - [ - Asn1::application( - 16, - Asn1::octetString('foo') - ) - ] - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/AddRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/AddRequestSpec.php deleted file mode 100644 index 00e8b202..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/AddRequestSpec.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\AddRequest; -use PhpSpec\ObjectBehavior; - -class AddRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo'])); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AddRequest::class); - } - - public function it_should_set_entry(): void - { - $entry = Entry::create('cn=foobar,dc=foo,dc=bar', ['cn' => 'foobar']); - $this->getEntry()->shouldBeLike(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo'])); - $this->setEntry($entry)->getEntry()->shouldBeEqualTo($entry); - } - - public function it_should_generate_correct_asn1(): void - { - $this->beConstructedWith(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo', 'sn' => ['foo', 'bar']])); - - $this->toAsn1()->shouldBeLike(Asn1::application(8, Asn1::sequence( - Asn1::octetString('cn=foo,dc=foo,dc=bar'), - Asn1::sequenceOf( - Asn1::sequence( - Asn1::octetString('cn'), - Asn1::setOf( - Asn1::octetString('foo') - ) - ), - Asn1::sequence( - Asn1::octetString('sn'), - Asn1::setOf( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ) - ) - ) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $add = new AddRequest(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo', 'sn' => ['foo', 'bar']])); - - $this::fromAsn1($add->toAsn1())->shouldBeLike(new AddRequest(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo', 'sn' => ['foo', 'bar']]))); - } - - public function it_should_detect_a_malformed_asn1_request(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::integer(2) - )]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence(), - Asn1::octetString('bar') - )]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence(), - Asn1::octetString('bar') - )]); - } - - public function it_should_detect_a_malformed_asn1_request_partial_attribute(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::sequence( - Asn1::octetString('foo') - ) - ) - )]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence() - ) - ) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/AnonBindRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/AnonBindRequestSpec.php deleted file mode 100644 index 55ffa258..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/AnonBindRequestSpec.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\AnonBindRequest; -use FreeDSx\Ldap\Operation\Request\BindRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use PhpSpec\ObjectBehavior; - -class AnonBindRequestSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(AnonBindRequest::class); - } - - public function it_should_extend_bind_request(): void - { - $this->shouldBeAnInstanceOf(BindRequest::class); - } - - public function it_should_default_to_ldap_v3(): void - { - $this->getVersion()->shouldBeEqualTo(3); - } - - public function it_should_have_an_empty_username_by_default(): void - { - $this->getUsername()->shouldBeEqualTo(''); - } - - public function it_should_set_the_username(): void - { - $this->setUsername('foo'); - $this->getUsername()->shouldBeEqualTo('foo'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(0, Asn1::sequence( - Asn1::integer(3), - Asn1::octetString(''), - Asn1::context(0, Asn1::octetString('')) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $anon = new AnonBindRequest('foo', 2); - - $this::fromAsn1($anon->toAsn1())->shouldBeLike($anon); - } - - public function it_should_check_that_a_password_is_empty_properly(): void - { - $this::fromAsn1((new SimpleBindRequest('foo', '0'))->toAsn1())->shouldNotBeAnInstanceOf(AnonBindRequest::class); - } - - public function it_should_detect_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::integer(2)]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::integer(2) - )]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::integer(3), - Asn1::octetString('foo'), - Asn1::context(3, Asn1::octetString('foo')) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/CancelRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/CancelRequestSpec.php deleted file mode 100644 index 72af7baf..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/CancelRequestSpec.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\CancelRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class CancelRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(CancelRequest::class); - } - - public function it_should_set_the_message_id(): void - { - $this->getMessageId()->shouldBeEqualTo(1); - $this->setMessageId(2)->getMessageId()->shouldBeEqualTo(2); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_CANCEL)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::integer(1) - )))) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new CancelRequest(2))->toAsn1())->shouldBeLike(new CancelRequest(2)); - } - - public function it_should_detect_invalid_asn1_from_asn1(): void - { - $req = new ExtendedRequest('foo', Asn1::octetString('foo')); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [$req->toAsn1()]); - - $req->setValue(Asn1::sequence()); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [$req->toAsn1()]); - - $req->setValue(Asn1::sequence(Asn1::octetString('bar'))); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [$req->toAsn1()]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/CompareRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/CompareRequestSpec.php deleted file mode 100644 index ac4523fe..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/CompareRequestSpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\AbstractType; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DnRequestInterface; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use PhpSpec\ObjectBehavior; - -class CompareRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new Dn('dc=foo,dc=bar'), new EqualityFilter('foo', 'bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(CompareRequest::class); - } - - public function it_should_implement_the_DnRequestInterface(): void - { - $this->shouldImplement(DnRequestInterface::class); - } - - public function it_should_set_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); - $this->setDn('dc=foobar')->getDn()->shouldBeLike(new Dn('dc=foobar')); - } - - public function it_should_set_the_filter(): void - { - $this->getFilter()->shouldBeLike(new EqualityFilter('foo', 'bar')); - $this->setFilter(new EqualityFilter('cn', 'foo'))->getFilter()->shouldBeLike(new EqualityFilter('cn', 'foo')); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(14, Asn1::sequence( - Asn1::octetString('dc=foo,dc=bar'), - Asn1::universal(AbstractType::TAG_TYPE_SEQUENCE, (new EqualityFilter('foo', 'bar'))->toAsn1()) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $req = new CompareRequest('foo', new EqualityFilter('foo', 'bar')); - - $this::fromAsn1($req->toAsn1())->shouldBeLike($req); - } - - public function it_should_detect_invalid_asn1_from_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence(Asn1::octetString('foo'))]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence(Asn1::octetString('foo'), Asn1::sequence())]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/DeleteRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/DeleteRequestSpec.php deleted file mode 100644 index f05d648f..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/DeleteRequestSpec.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\DnRequestInterface; -use PhpSpec\ObjectBehavior; - -class DeleteRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn=foo,dc=foo,dc=bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(DeleteRequest::class); - } - - public function it_should_implement_the_DnRequestInterface(): void - { - $this->shouldImplement(DnRequestInterface::class); - } - - public function it_should_set_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('cn=foo,dc=foo,dc=bar')); - $this->setDn(new Dn('foo'))->getDn()->shouldBeLike(new Dn('foo')); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(10, Asn1::octetString('cn=foo,dc=foo,dc=bar'))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this->beConstructedThrough('fromAsn1', [Asn1::application(10, Asn1::octetString( - 'dc=foo,dc=bar' - ))]); - - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::application(11, Asn1::octetString( - 'dc=foo,dc=bar' - ))]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::application(11, Asn1::integer( - 2 - ))]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/ExtendedRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/ExtendedRequestSpec.php deleted file mode 100644 index b3a0474b..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/ExtendedRequestSpec.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use PhpSpec\ObjectBehavior; - -class ExtendedRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(ExtendedRequest::OID_START_TLS); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ExtendedRequest::class); - } - - public function it_should_get_the_extended_request_name(): void - { - $this->getName()->shouldBeEqualTo('1.3.6.1.4.1.1466.20037'); - $this->setName('foo')->getName()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_extended_request_value(): void - { - $this->setValue('foo')->getValue()->shouldBeEqualTo('foo'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_START_TLS)) - ))); - - $this->setValue('foo'); - - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_START_TLS)), - Asn1::context(1, Asn1::octetString('foo')) - ))); - } - - public function it_should_be_constructed_from_asn1_with_no_value(): void - { - $request = new ExtendedRequest('foo'); - - $this::fromAsn1($request->toAsn1())->shouldBeLike($request); - } - - public function it_should_be_constructed_from_asn1_with_a_value(): void - { - $request = new ExtendedRequest('foo', 'bar'); - - $this::fromAsn1($request->toAsn1())->shouldBeLike($request); - } - - public function it_should_detect_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence(Asn1::octetString('foo'))]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyDnRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyDnRequestSpec.php deleted file mode 100644 index efd9b5dd..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyDnRequestSpec.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Entry\Rdn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\DnRequestInterface; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use PhpSpec\ObjectBehavior; - -class ModifyDnRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('cn=foo,dc=foo,dc=bar', 'cn=bar', true); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ModifyDnRequest::class); - } - - public function it_should_implement_the_DnRequestInterface(): void - { - $this->shouldImplement(DnRequestInterface::class); - } - - public function it_should_set_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('cn=foo,dc=foo,dc=bar')); - $this->setDn(new Dn('foo'))->getDn()->shouldBeLike(new Dn('foo')); - } - - public function it_should_set_the_new_rdn(): void - { - $this->getNewRdn()->shouldBeLike(Rdn::create('cn=bar')); - $this->setNewRdn(Rdn::create('cn=foo'))->getNewRdn()->shouldBeLike(Rdn::create('cn=foo')); - } - - public function it_should_set_whether_to_delete_the_old_rdn(): void - { - $this->getDeleteOldRdn()->shouldBeEqualTo(true); - $this->setDeleteOldRdn(false)->getDeleteOldRdn()->shouldBeEqualTo(false); - } - - public function it_should_set_the_new_parent_dn(): void - { - $this->getNewParentDn()->shouldBeNull(); - $this->setNewParentDn(new Dn('foo'))->getNewParentDn()->shouldBeLike(new Dn('foo')); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(12, Asn1::sequence( - Asn1::octetString('cn=foo,dc=foo,dc=bar'), - Asn1::octetString('cn=bar'), - Asn1::boolean(true) - ))); - - $this->setNewParentDn('dc=foobar'); - - $this->toAsn1()->shouldBeLike(Asn1::application(12, Asn1::sequence( - Asn1::octetString('cn=foo,dc=foo,dc=bar'), - Asn1::octetString('cn=bar'), - Asn1::boolean(true), - Asn1::context(0, Asn1::octetString('dc=foobar')) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $req = new ModifyDnRequest('foo', 'cn=bar', false, 'foobar'); - $this::fromAsn1($req->toAsn1())->shouldBeLike($req); - - $req = new ModifyDnRequest('foo', 'cn=bar', false); - $this::fromAsn1($req->toAsn1())->shouldBeLike($req); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence(Asn1::integer(1))]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::octetString('cn=foo'), - Asn1::boolean(true), - Asn1::octetString('foobar') - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyRequestSpec.php deleted file mode 100644 index 29ccfc9e..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/ModifyRequestSpec.php +++ /dev/null @@ -1,184 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\DnRequestInterface; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use PhpSpec\ObjectBehavior; - -class ModifyRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - 'cn=foo,dc=foo,dc=bar', - Change::replace('foo', 'bar'), - Change::add('sn', 'bleep', 'blorp') - ); - } - - public function it_should_implement_the_DnRequestInterface(): void - { - $this->shouldImplement(DnRequestInterface::class); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ModifyRequest::class); - } - - public function it_should_set_the_dn(): void - { - $this->getDn()->shouldBeLike(new Dn('cn=foo,dc=foo,dc=bar')); - - $this->setDn(new Dn('foo'))->getDn()->shouldBeLike(new Dn('foo')); - } - - public function it_should_set_the_changes(): void - { - $this->getChanges()->shouldBeLike([ - Change::replace('foo', 'bar'), - Change::add('sn', 'bleep', 'blorp') - ]); - - $this->setChanges(Change::delete('foo', 'bar'))->getChanges()->shouldBeLike([ - Change::delete('foo', 'bar') - ]); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(6, Asn1::sequence( - Asn1::octetString('cn=foo,dc=foo,dc=bar'), - Asn1::sequenceOf( - Asn1::sequence( - Asn1::enumerated(2), - Asn1::sequence( - Asn1::octetString('foo'), - Asn1::setOf( - Asn1::octetString('bar') - ) - ) - ), - Asn1::sequence( - Asn1::enumerated(0), - Asn1::sequence( - Asn1::octetString('sn'), - Asn1::setOf( - Asn1::octetString('bleep'), - Asn1::octetString('blorp') - ) - ) - ) - ) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $req = new ModifyRequest( - 'foo', - Change::add('foo', 'bar'), - Change::delete('bar', 'foo'), - Change::replace('foobar', 'foo') - ); - - $this::fromAsn1($req->toAsn1())->shouldBeLike(new ModifyRequest( - 'foo', - Change::add('foo', 'bar'), - Change::delete('bar', 'foo'), - Change::replace('foobar', 'foo') - )); - } - - public function it_should_not_be_constructed_from_asn1_with_an_invalid_dn_type(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::integer(1), - Asn1::sequence() - )]); - } - - public function it_should_not_be_constructed_from_asn1_with_an_invalid_changelist(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::set( - Asn1::octetString('dc=foo'), - Asn1::sequence() - )]); - } - - public function it_should_not_be_constructed_from_asn1_with_an_invalid_changelist_type(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::integer(1), - Asn1::sequence() - )]); - } - - public function it_should_not_be_constructed_from_asn1_with_invalid_attribute_values(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::sequence( - Asn1::enumerated(2), - Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::octetString('bar') - ) - ) - ) - ) - )]); - } - - public function it_should_not_be_constructed_from_asn1_without_a_partial_attribute_description(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::sequence( - Asn1::enumerated(2), - Asn1::sequence( - Asn1::setOf( - Asn1::octetString('bar') - ) - ) - ) - ) - )]); - } - - public function it_should_not_be_constructed_from_asn1_without_a_change_type(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequence( - Asn1::sequence( - Asn1::integer(999), - Asn1::sequence( - Asn1::setOf( - Asn1::octetString('bar') - ) - ) - ) - ) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/PasswordModifyRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/PasswordModifyRequestSpec.php deleted file mode 100644 index 388c1348..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/PasswordModifyRequestSpec.php +++ /dev/null @@ -1,108 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\PasswordModifyRequest; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class PasswordModifyRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar', '12345'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(PasswordModifyRequest::class); - } - - public function it_should_get_the_new_password(): void - { - $this->getNewPassword()->shouldBeEqualTo('12345'); - $this->setNewPassword('foo')->getNewPassword()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_old_password(): void - { - $this->getOldPassword()->shouldBeEqualTo('bar'); - $this->setOldPassword('foo')->getOldPassword()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_username(): void - { - $this->getUsername()->shouldBeEqualTo('foo'); - $this->setUsername('bar')->getUsername()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(0, Asn1::octetString('foo')), - Asn1::context(1, Asn1::octetString('bar')), - Asn1::context(2, Asn1::octetString('12345')) - )))) - ))); - - $this->setUsername(null); - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(1, Asn1::octetString('bar')), - Asn1::context(2, Asn1::octetString('12345')) - )))) - ))); - - $this->setOldPassword(null); - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(2, Asn1::octetString('12345')) - )))) - ))); - - $this->setNewPassword(null); - $this->toAsn1()->shouldBeLike(Asn1::application(23, Asn1::sequence( - Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence()))) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $passwdMod = new PasswordModifyRequest('foo', 'bar', '12345'); - $this::fromAsn1($passwdMod->toAsn1())->shouldBeLike($passwdMod->setValue(null)); - - $passwdMod = new PasswordModifyRequest(null, 'bar', '12345'); - $this::fromAsn1($passwdMod->toAsn1())->shouldBeLike($passwdMod->setValue(null)); - - $passwdMod = new PasswordModifyRequest('foo', null, '12345'); - $this::fromAsn1($passwdMod->toAsn1())->shouldBeLike($passwdMod->setValue(null)); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [ - (new ExtendedRequest('foo'))->setValue(Asn1::set())->toAsn1() - ]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/SearchRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/SearchRequestSpec.php deleted file mode 100644 index 20f5d79c..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/SearchRequestSpec.php +++ /dev/null @@ -1,183 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Exception\UnexpectedValueException; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Result\EntryResult; -use FreeDSx\Ldap\Search\Result\ReferralResult; -use PhpSpec\ObjectBehavior; - -class SearchRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new EqualityFilter('cn', 'foo')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SearchRequest::class); - } - - public function it_should_set_the_attributes(): void - { - $this->getAttributes()->shouldBeEqualTo([]); - $this->setAttributes(new Attribute('foo'))->getAttributes()->shouldBeLike([new Attribute('foo')]); - } - - public function it_should_set_the_attributes_using_simple_string_values(): void - { - $this->setAttributes('foo', 'bar'); - $this->getAttributes()->shouldBeLike([new Attribute('foo'), new Attribute('bar')]); - } - - public function it_should_set_the_base_dn(): void - { - $this->getBaseDn()->shouldBeNull(); - $this->setBaseDn('dc=foo')->getBaseDn()->shouldBeLike(new Dn('dc=foo')); - } - - public function it_should_set_the_scope(): void - { - $this->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_WHOLE_SUBTREE); - $this->setScope(SearchRequest::SCOPE_BASE_OBJECT)->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_BASE_OBJECT); - } - - public function it_should_set_whether_or_not_to_dereference_aliases(): void - { - $this->getDereferenceAliases()->shouldBeEqualTo(SearchRequest::DEREF_NEVER); - $this->setDereferenceAliases(SearchRequest::DEREF_ALWAYS)->getDereferenceAliases()->shouldBeEqualTo(SearchRequest::DEREF_ALWAYS); - } - - public function it_should_set_a_size_limit(): void - { - $this->getSizeLimit()->shouldBeEqualTo(0); - $this->setSizeLimit(100)->getSizeLimit()->shouldBeEqualTo(100); - } - - public function it_should_set_a_time_limit(): void - { - $this->getTimeLimit()->shouldBeEqualTo(0); - $this->setTimeLimit(10)->getTimeLimit()->shouldBeEqualTo(10); - } - - public function it_should_set_whether_or_not_to_get_attributes_only(): void - { - $this->getAttributesOnly()->shouldBeEqualTo(false); - $this->setAttributesOnly(true)->getAttributesOnly()->shouldBeEqualTo(true); - } - - public function it_should_have_an_alias_for_set_attributes_called_select(): void - { - $this->select('foo', 'bar')->getAttributes()->shouldBeLike([new Attribute('foo'), new Attribute('bar')]); - } - - public function it_should_have_an_alias_for_setBaseDn_called_base(): void - { - $this->base('dc=foo')->getBaseDn()->shouldBeLike(new Dn('dc=foo')); - } - - public function it_should_have_a_method_to_set_the_scopes(): void - { - $this->useSubtreeScope()->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_WHOLE_SUBTREE); - $this->useBaseScope()->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_BASE_OBJECT); - $this->useSingleLevelScope()->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_SINGLE_LEVEL); - } - - public function it_should_set_and_get_an_entry_handler(): void - { - $handler = fn (EntryResult $result) => $result->getEntry(); - - $this->useEntryHandler($handler) - ->shouldReturn($this); - $this->getEntryHandler() - ->shouldReturn($handler); - } - - public function it_should_set_and_get_a_referral_handler(): void - { - $handler = fn (ReferralResult $result) => $result->getReferrals(); - - $this->useReferralHandler($handler) - ->shouldReturn($this); - $this->getReferralHandler() - ->shouldBe($handler); - } - - public function it_should_set_and_get_the_cancel_strategy(): void - { - $this->useCancelStrategy(SearchRequest::CANCEL_CONTINUE); - - $this->getCancelStrategy() - ->shouldBe(SearchRequest::CANCEL_CONTINUE); - } - - public function it_should_not_allow_invalid_cancel_stragegies(): void - { - $this->shouldThrow(UnexpectedValueException::class) - ->during('useCancelStrategy', ['foo']); - } - - public function it_should_generate_correct_asn1(): void - { - $this->setBaseDn('dc=foo,dc=bar'); - - $this->toAsn1()->shouldBeLike(Asn1::application(3, Asn1::sequence( - Asn1::octetString('dc=foo,dc=bar'), - Asn1::enumerated(2), - Asn1::enumerated(0), - Asn1::integer(0), - Asn1::integer(0), - Asn1::boolean(false), - (new EqualityFilter('cn', 'foo'))->toAsn1(), - Asn1::sequenceOf() - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $search = (new SearchRequest(new EqualityFilter('foo', 'bar'), 'cn')) - ->base('dc,=foo,dc=bar') - ->timeLimit(10) - ->sizeLimit(5) - ->useBaseScope() - ->setAttributesOnly(true) - ->setDereferenceAliases(2); - $this->beConstructedThrough('fromAsn1', [$search->toAsn1()]); - - $this->getBaseDn()->shouldBeLike(new Dn('dc,=foo,dc=bar')); - $this->getSizeLimit()->shouldBeEqualTo(5); - $this->getTimeLimit()->shouldBeEqualTo(10); - $this->getScope()->shouldBeEqualTo(SearchRequest::SCOPE_BASE_OBJECT); - $this->getAttributesOnly()->shouldBeEqualTo(true); - $this->getDereferenceAliases()->shouldBeEqualTo(2); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::set()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::integer(5), - Asn1::octetString('foo') - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/SimpleBindRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/SimpleBindRequestSpec.php deleted file mode 100644 index c8a250d8..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/SimpleBindRequestSpec.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\BindException; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\BindRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use PhpSpec\ObjectBehavior; - -class SimpleBindRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('', ''); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SimpleBindRequest::class); - } - - public function it_should_extend_bind_request(): void - { - $this->shouldBeAnInstanceOf(BindRequest::class); - } - - public function it_should_default_to_ldap_v3(): void - { - $this->getVersion()->shouldBeEqualTo(3); - } - - public function it_should_set_the_username(): void - { - $this->setUsername('foo'); - $this->getUsername()->shouldBeEqualTo('foo'); - } - - public function it_should_set_the_password(): void - { - $this->setPassword('bar'); - $this->getPassword()->shouldBeEqualTo('bar'); - } - - public function it_should_not_allow_an_empty_username_or_password(): void - { - $this->shouldThrow(BindException::class)->duringToAsn1(); - - $this->setUsername('foo'); - $this->shouldThrow(BindException::class)->duringToAsn1(); - - $this->setUsername(''); - $this->setPassword('bar'); - $this->shouldThrow(BindException::class)->duringToAsn1(); - - $this->setUsername('foo')->setPassword('bar'); - $this->shouldNotThrow(BindException::class)->duringToAsn1(); - } - - public function it_should_correctly_detect_a_zero_string_as_non_empty(): void - { - $this->setUsername('foo'); - $this->setPassword('0'); - - $this->shouldNotThrow(BindException::class)->duringToAsn1(); - - $this->setUsername('0'); - $this->shouldNotThrow(BindException::class)->duringToAsn1(); - } - - public function it_should_generate_correct_asn1(): void - { - $this->setUsername('foo')->setPassword('bar'); - - $this->toAsn1()->shouldBeLike(Asn1::application(0, Asn1::sequence( - Asn1::integer(3), - Asn1::octetString('foo'), - Asn1::context(0, Asn1::octetString('bar')) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $bind = new SimpleBindRequest('foo', 'bar'); - - $this::fromAsn1($bind->toAsn1())->shouldBeLike($bind); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence( - Asn1::octetString('foo'), - Asn1::integer(3) - )]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/SyncRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/SyncRequestSpec.php deleted file mode 100644 index 61a2f08c..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/SyncRequestSpec.php +++ /dev/null @@ -1,28 +0,0 @@ -getFilter() - ->shouldBeLike(Filters::present('objectClass')); - } - - public function it_should_set_and_get_the_setId_handler(): void - { - $handler = fn (SyncIdSetResult $result) => $result->getEntryUuids(); - - $this->useIdSetHandler($handler) - ->shouldBeEqualTo($this); - $this->getIdSetHandler() - ->shouldBeEqualTo($handler); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Request/UnbindRequestSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Request/UnbindRequestSpec.php deleted file mode 100644 index c7654192..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Request/UnbindRequestSpec.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Request; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\NullType; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use PhpSpec\ObjectBehavior; - -class UnbindRequestSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(UnbindRequest::class); - } - - public function it_should_form_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike((new NullType())->setTagClass(NullType::TAG_CLASS_APPLICATION)->setTagNumber(2)); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1(Asn1::null())->shouldBeLike(new UnbindRequest()); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/AddResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/AddResponseSpec.php deleted file mode 100644 index 1789d234..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/AddResponseSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\AddResponse; -use PhpSpec\ObjectBehavior; - -class AddResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AddResponse::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/BindResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/BindResponseSpec.php deleted file mode 100644 index 3f12fc0c..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/BindResponseSpec.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class BindResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new LdapResult(0, 'foo', 'bar', new LdapUrl('foo')), 'foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(BindResponse::class); - } - - public function it_should_get_the_sasl_creds(): void - { - $this->getSaslCredentials()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_ldap_result_data(): void - { - $this->getResultCode()->shouldBeEqualTo(0); - $this->getDn()->shouldBeLike(new Dn('foo')); - $this->getDiagnosticMessage()->shouldBeLike('bar'); - $this->getReferrals()->shouldBeLike([new LdapUrl('foo')]); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::application(1, Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(3, (new IncompleteType($encoder->encode( - Asn1::octetString('ldap://foo') - )) - )->setIsConstructed(true)), - Asn1::context(7, Asn1::octetString('foo')) - ))]); - - $this->getSaslCredentials()->shouldBeEqualTo('foo'); - $this->getResultCode()->shouldBeEqualTo(0); - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); - $this->getDiagnosticMessage()->shouldBeEqualTo('foo'); - $this->getReferrals()->shouldBeLike([new LdapUrl('foo')]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/CompareResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/CompareResponseSpec.php deleted file mode 100644 index 38dacb14..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/CompareResponseSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\CompareResponse; -use PhpSpec\ObjectBehavior; - -class CompareResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(CompareResponse::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/DeleteResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/DeleteResponseSpec.php deleted file mode 100644 index a367d6f2..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/DeleteResponseSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use PhpSpec\ObjectBehavior; - -class DeleteResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(DeleteResponse::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/ExtendedResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/ExtendedResponseSpec.php deleted file mode 100644 index 9945ebda..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/ExtendedResponseSpec.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class ExtendedResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new LdapResult(0, 'dc=foo,dc=bar', 'foo'), 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ExtendedResponse::class); - } - - public function it_should_get_the_name(): void - { - $this->getName()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_be_constructed_from_asn1(): void - { - $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::application(24, Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(3, (new IncompleteType( - $encoder->encode(Asn1::octetString('ldap://foo')) - . $encoder->encode(Asn1::octetString('ldap://bar')) - )))->setIsConstructed(true), - Asn1::context(10, Asn1::octetString('foo')), - Asn1::context(11, Asn1::octetString('bar')) - ))]); - - $this->getName()->shouldBeEqualTo('foo'); - $this->getValue()->shouldBeEqualTo('bar'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/IntermediateResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/IntermediateResponseSpec.php deleted file mode 100644 index 327bc35a..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/IntermediateResponseSpec.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Operation\Response\IntermediateResponse; -use PhpSpec\ObjectBehavior; - -class IntermediateResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(IntermediateResponse::class); - } - - public function it_should_get_the_name(): void - { - $this->getName()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this->beConstructedThrough('fromAsn1', [Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString('foo')), - Asn1::context(1, Asn1::octetString('bar')) - ))]); - - $this->getName()->shouldBeEqualTo('foo'); - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString('foo')), - Asn1::context(1, Asn1::octetString('bar')) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyDnResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyDnResponseSpec.php deleted file mode 100644 index c323fce2..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyDnResponseSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; -use PhpSpec\ObjectBehavior; - -class ModifyDnResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ModifyDnResponse::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyResponseSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyResponseSpec.php deleted file mode 100644 index 21c0b4b9..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/ModifyResponseSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\ModifyResponse; -use PhpSpec\ObjectBehavior; - -class ModifyResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ModifyResponse::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultDoneSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultDoneSpec.php deleted file mode 100644 index 76c47361..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultDoneSpec.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use PhpSpec\ObjectBehavior; - -class SearchResultDoneSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(0, 'foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SearchResultDone::class); - } - - public function it_should_extend_ldap_result(): void - { - $this->shouldBeAnInstanceOf(LdapResult::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultEntrySpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultEntrySpec.php deleted file mode 100644 index 49c106db..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultEntrySpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use PhpSpec\ObjectBehavior; - -class SearchResultEntrySpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo'])); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SearchResultEntry::class); - } - - public function it_should_get_the_entry(): void - { - $this->getEntry()->shouldBeLike(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo'])); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this->beConstructedThrough('fromAsn1', [Asn1::application(4, Asn1::sequence( - Asn1::octetString('dc=foo,dc=bar'), - Asn1::sequenceOf( - Asn1::sequence( - Asn1::octetString('cn'), - Asn1::sequenceOf( - Asn1::octetString('foo') - ) - ), - Asn1::sequence( - Asn1::octetString('sn'), - Asn1::sequenceOf( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ) - ) - ) - ))]); - - $this->getEntry()->shouldBeLike(Entry::create('dc=foo,dc=bar', ['cn' => ['foo'], 'sn' => ['foo', 'bar']])); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::application(4, Asn1::sequence( - Asn1::octetString('cn=foo,dc=foo,dc=bar'), - Asn1::sequenceOf( - Asn1::sequence( - Asn1::octetString('cn'), - Asn1::setOf( - Asn1::octetString('foo') - ) - ) - ) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultReferenceSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultReferenceSpec.php deleted file mode 100644 index 562e1933..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResultReferenceSpec.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use PhpSpec\ObjectBehavior; - -class SearchResultReferenceSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new LdapUrl('foo'), new LdapUrl('bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SearchResultReference::class); - } - - public function it_should_get_the_referrals(): void - { - $this->getReferrals()->shouldBeLike([ - new LdapUrl('foo'), - new LdapUrl('bar') - ]); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this->beConstructedThrough('fromAsn1', [Asn1::application(19, Asn1::sequence( - Asn1::octetString('ldap://foo'), - Asn1::octetString('ldap://bar') - ))]); - - $this->getReferrals()->shouldBeLike([ - new LdapUrl('foo'), - new LdapUrl('bar') - ]); - } - - public function it_should_generate_correct_asn1(): void - { - $this::toAsn1()->shouldBeLike(Asn1::application(19, Asn1::sequence( - Asn1::octetString('ldap://foo/'), - Asn1::octetString('ldap://bar/') - ))); - } - - public function it_should_throw_a_protocol_exception_if_the_referral_cannot_be_parsed(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::application(19, Asn1::sequence( - Asn1::octetString('ldap://foo/'), - Asn1::octetString('?bar') - ))]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncIdSetSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncIdSetSpec.php deleted file mode 100644 index 77110e82..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncIdSetSpec.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response\SyncInfo; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Operation\Response\IntermediateResponse; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncIdSet; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncIdSetSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - ['foo', 'bar'], - false, - 'omnomnom' - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncIdSet::class); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_get_whether_to_refresh_deletes(): void - { - $this->getRefreshDeletes()->shouldBeEqualTo(false); - } - - public function it_should_get_the_entry_uuids(): void - { - $this->getEntryUuids()->shouldBeEqualTo(['foo', 'bar']); - } - - public function it_should_have_the_correct_response_name(): void - { - $this->getName()->shouldBeEqualTo(IntermediateResponse::OID_SYNC_INFO); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false), - Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) - ))))) - )))->shouldBeLike(new SyncIdSet(['foo', 'bar'], false, 'omnomnom')); - } - - public function it_should_be_constructed_through_the_intermediate_response_factory_methd(): void - { - $encoder = new LdapEncoder(); - - $this->beConstructedThrough([IntermediateResponse::class, 'fromAsn1'], [Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false), - Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) - ))))) - ))]); - - $this->getCookie()->shouldBeEqualTo('omnomnom'); - $this->getRefreshDeletes()->shouldBeEqualTo(false); - $this->getEntryUuids()->shouldBeEqualTo([ - 'foo', - 'bar', - ]); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false), - Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) - ))))) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncNewCookieSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncNewCookieSpec.php deleted file mode 100644 index 6f5588dd..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncNewCookieSpec.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response\SyncInfo; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Operation\Response\IntermediateResponse; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncNewCookie; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncNewCookieSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('omnomnom'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncNewCookie::class); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_have_the_correct_response_name(): void - { - $this->getName()->shouldBeEqualTo(IntermediateResponse::OID_SYNC_INFO); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(0, Asn1::octetString('omnomnom'))))) - )))->shouldBeLike(new SyncNewCookie('omnomnom')); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(0, Asn1::octetString('omnomnom'))))) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshDeleteSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshDeleteSpec.php deleted file mode 100644 index e1ed7096..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshDeleteSpec.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response\SyncInfo; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Operation\Response\IntermediateResponse; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshDelete; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncRefreshDeleteSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(false, 'omnomnom'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncRefreshDelete::class); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_get_whether_the_refresh_is_done(): void - { - $this->getRefreshDone()->shouldBeEqualTo(false); - } - - public function it_should_have_the_correct_response_name(): void - { - $this->getName()->shouldBeEqualTo(IntermediateResponse::OID_SYNC_INFO); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(1, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))))) - )))->shouldBeLike(new SyncRefreshDelete(false, 'omnomnom')); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(1, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))))) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshPresentSpec.php b/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshPresentSpec.php deleted file mode 100644 index 5127f046..00000000 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SyncInfo/SyncRefreshPresentSpec.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Operation\Response\SyncInfo; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Operation\Response\IntermediateResponse; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshPresent; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class SyncRefreshPresentSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(false, 'omnomnom'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncRefreshPresent::class); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo('omnomnom'); - } - - public function it_should_get_whether_the_refresh_is_done(): void - { - $this->getRefreshDone()->shouldBeEqualTo(false); - } - - public function it_should_have_the_correct_response_name(): void - { - $this->getName()->shouldBeEqualTo(IntermediateResponse::OID_SYNC_INFO); - } - - public function it_should_be_constructed_from_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this::fromAsn1(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(2, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))))) - )))->shouldBeLike(new SyncRefreshPresent(false, 'omnomnom')); - } - - public function it_should_generate_correct_ASN1(): void - { - $encoder = new LdapEncoder(); - - $this->toAsn1()->shouldBeLike(Asn1::application(25, Asn1::sequence( - Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), - Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(2, Asn1::sequence( - Asn1::octetString('omnomnom'), - Asn1::boolean(false) - ))))) - ))); - } -} diff --git a/tests/spec/FreeDSx/Ldap/OperationsSpec.php b/tests/spec/FreeDSx/Ldap/OperationsSpec.php deleted file mode 100644 index 73208474..00000000 --- a/tests/spec/FreeDSx/Ldap/OperationsSpec.php +++ /dev/null @@ -1,253 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap; - -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Dn; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\Request\AbandonRequest; -use FreeDSx\Ldap\Operation\Request\AddRequest; -use FreeDSx\Ldap\Operation\Request\AnonBindRequest; -use FreeDSx\Ldap\Operation\Request\CancelRequest; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use FreeDSx\Ldap\Operation\Request\PasswordModifyRequest; -use FreeDSx\Ldap\Operation\Request\SaslBindRequest; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class OperationsSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(Operations::class); - } - - public function it_should_create_a_sasl_bind(): void - { - $this::bindSasl([ - 'username' => 'foo', - 'password' => 'bar', - ])->shouldBeLike(new SaslBindRequest( - '', - null, - [ - 'username' => 'foo', - 'password' => 'bar' - ] - )); - $this::bindSasl( - [ - 'username' => 'foo', - 'password' => 'bar', - ], - 'DIGEST-MD5' - )->shouldBeLike( - new SaslBindRequest( - 'DIGEST-MD5', - null, - [ - 'username' => 'foo', - 'password' => 'bar', - ] - ) - ); - } - - public function it_should_create_an_add_operation(): void - { - $this::add(new Entry('foo')) - ->shouldBeLike(new AddRequest(new Entry('foo'))); - } - - public function it_should_create_a_modify_operation(): void - { - $this::modify( - 'foo', - Change::replace( - 'foo', - 'bar' - ) - )->shouldBeLike(new ModifyRequest( - 'foo', - Change::replace( - 'foo', - 'bar' - ) - )); - } - - public function it_should_create_a_rename_operation(): void - { - $this::rename( - 'cn=foo,dc=bar,dc=foo', - 'foo=bar' - )->shouldBeLike(new ModifyDnRequest( - 'cn=foo,dc=bar,dc=foo', - 'foo=bar', - true - )); - } - - public function it_should_create_a_move_operation(): void - { - // Calling getRdn triggers a cache of RDN pieces on the object. Need this for the check.. - $dn = new Dn('cn=foo,dc=example,dc=local'); - $dn->getRdn(); - - $this::move( - new Dn('cn=foo,dc=example,dc=local'), - new Dn('ou=foo,dc=example,dc=local') - )->shouldBeLike(new ModifyDnRequest( - 'cn=foo,dc=example,dc=local', - 'cn=foo', - true, - 'ou=foo,dc=example,dc=local' - )); - } - - public function it_should_create_a_delete_operation(): void - { - $this::delete('cn=foo,dc=example,dc=local') - ->shouldBeLike(new DeleteRequest('cn=foo,dc=example,dc=local')); - } - - public function it_should_create_a_search_operation(): void - { - $this::search( - new EqualityFilter( - 'foo', - 'bar' - ), - 'cn' - )->shouldBeLike(new SearchRequest( - new EqualityFilter( - 'foo', - 'bar' - ), - 'cn' - )); - } - - public function it_should_create_a_compare_operation(): void - { - $this::compare( - 'cn=foo,dc=example,dc=local', - 'foo', - 'bar' - )->shouldBeLike(new CompareRequest( - 'cn=foo,dc=example,dc=local', - new EqualityFilter( - 'foo', - 'bar' - ) - )); - } - - public function it_should_create_an_unbind_operation(): void - { - $this::unbind() - ->shouldBeLike(new UnbindRequest()); - } - - public function it_should_create_an_anonymous_bind_operation(): void - { - $this::bindAnonymously() - ->shouldBeLike(new AnonBindRequest()); - } - - public function it_should_create_a_username_password_bind_operation(): void - { - $this::bind( - 'foo', - 'bar' - )->shouldBeLike(new SimpleBindRequest( - 'foo', - 'bar' - )); - } - - public function it_should_create_an_abandon_request(): void - { - $this::abandon(9) - ->shouldBeLike(new AbandonRequest(9)); - } - - public function it_should_create_a_whoami_request(): void - { - $this::whoami() - ->shouldBeLike(new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); - } - - public function it_should_create_a_cancel_request(): void - { - $this->cancel(1) - ->shouldBeLike(new CancelRequest(1)); - } - - public function it_should_create_a_base_object_search(): void - { - $this::read('dc=foo,dc=bar') - ->shouldBeLike( - (new SearchRequest(Filters::present('objectClass'))) - ->useBaseScope() - ->base('dc=foo,dc=bar') - ); - - $this::read('dc=foo,dc=bar', 'foo', 'bar') - ->shouldBeLike( - (new SearchRequest(Filters::present('objectClass'))) - ->useBaseScope() - ->base('dc=foo,dc=bar') - ->select('foo', 'bar') - ); - } - - public function it_should_create_a_single_level_search(): void - { - $this::list( - Filters::equal( - 'foo', - 'bar' - ), - 'dc=foo,dc=bar', - 'cn' - )->shouldBeLike( - (new SearchRequest(Filters::equal('foo', 'bar'), 'cn')) - ->base('dc=foo,dc=bar') - ->useSingleLevelScope() - ); - } - - public function it_should_create_a_password_modify_request(): void - { - $this::passwordModify( - 'foo', - '12345', - '6789' - )->shouldBeLike(new PasswordModifyRequest( - 'foo', - '12345', - '6789' - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/AuthenticatorSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/AuthenticatorSpec.php deleted file mode 100644 index 6db688f7..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/AuthenticatorSpec.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Protocol\Authenticator; -use FreeDSx\Ldap\Protocol\Bind\BindInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Server\Token\BindToken; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class AuthenticatorSpec extends ObjectBehavior -{ - public function let( - BindInterface $authOne, - BindInterface $authTwo, - ): void { - $this->beConstructedWith([ - $authOne, - $authTwo, - ]); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Authenticator::class); - } - - public function it_should_throw_an_exception_on_an_unknown_bind_type( - BindInterface $authOne, - BindInterface $authTwo, - ): void { - $authOne - ->supports(Argument::any()) - ->willReturn(false); - $authTwo - ->supports(Argument::any()) - ->willReturn(false); - - $this->shouldThrow(OperationException::class) - ->during( - 'bind', - [new LdapMessageRequest(1, new DeleteRequest('foo'))] - ); - } - - public function it_should_return_the_token(BindInterface $authOne): void - { - $bindReq = new SimpleBindRequest( - 'foo', - 'bar', - ); - $message = new LdapMessageRequest( - 1, - $bindReq, - ); - - $token = new BindToken( - 'foo', - 'bar', - ); - - $authOne - ->supports($message) - ->willReturn(true); - $authOne - ->bind($message) - ->willReturn($token); - - $this->bind($message) - ->shouldBe($token); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Bind/AnonymousBindSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Bind/AnonymousBindSpec.php deleted file mode 100644 index 575c6ebd..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Bind/AnonymousBindSpec.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Bind; - -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\AnonBindRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\Bind\AnonymousBind; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\AnonToken; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class AnonymousBindSpec extends ObjectBehavior -{ - public function let(ServerQueue $queue): void - { - $this->beConstructedWith($queue); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AnonymousBind::class); - } - - public function it_should_validate_the_version(ServerQueue $queue, RequestHandlerInterface $dispatcher): void - { - $bind = new LdapMessageRequest( - 1, - Operations::bindAnonymously()->setVersion(4) - ); - - $queue->sendMessage(Argument::any())->shouldNotBeCalled(); - - $this->shouldThrow(OperationException::class)->during( - 'bind', - [$bind, $dispatcher, $queue] - ); - } - - public function it_should_return_an_anon_token_with_the_supplied_username(ServerQueue $queue, RequestHandlerInterface $dispatcher): void - { - $bind = new LdapMessageRequest( - 1, - Operations::bindAnonymously('foo') - ); - - $queue->sendMessage(new LdapMessageResponse(1, new BindResponse( - new LdapResult(0) - )))->shouldBeCalled()->willReturn($queue); - - $this->bind($bind)->shouldBeLike( - new AnonToken('foo') - ); - } - - public function it_should_only_support_anonymous_binds(): void - { - $this->supports(new LdapMessageRequest( - 1, - new AnonBindRequest() - ))->shouldBe(true); - $this->supports(new LdapMessageRequest( - 1, - new SimpleBindRequest( - 'foo', - 'bar', - ) - ))->shouldBe(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Bind/SimpleBindSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Bind/SimpleBindSpec.php deleted file mode 100644 index d8281209..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Bind/SimpleBindSpec.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Bind; - -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\AnonBindRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\Bind\SimpleBind; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\BindToken; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class SimpleBindSpec extends ObjectBehavior -{ - public function let( - ServerQueue $queue, - RequestHandlerInterface $dispatcher, - ): void { - $this->beConstructedWith( - $queue, - $dispatcher, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SimpleBind::class); - } - - public function it_should_return_a_token_on_success( - ServerQueue $queue, - RequestHandlerInterface $dispatcher, - ): void { - $bind = new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar')); - - $dispatcher->bind('foo@bar', 'bar') - ->shouldBeCalled() - ->willReturn(true); - $queue->sendMessage(new LdapMessageResponse(1, new BindResponse( - new LdapResult(0) - )))->shouldBeCalled()->willReturn($queue); - - $this->bind($bind)->shouldBeLike( - new BindToken('foo@bar', 'bar') - ); - } - - public function it_should_throw_an_operations_exception_with_invalid_credentials_if_they_are_wrong( - ServerQueue $queue, - RequestHandlerInterface $dispatcher, - ): void { - $bind = new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar')); - - $dispatcher->bind('foo@bar', 'bar') - ->shouldBeCalled() - ->willReturn(false); - $queue->sendMessage(Argument::any())->shouldNotBeCalled(); - - $this->shouldThrow(new OperationException('Invalid credentials.', ResultCode::INVALID_CREDENTIALS)) - ->during( - 'bind', - [$bind] - ); - } - - public function it_should_validate_the_version(ServerQueue $queue): void - { - $bind = new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar', 5)); - - $queue->sendMessage(Argument::any())->shouldNotBeCalled(); - - $this->shouldThrow(OperationException::class) - ->during( - 'bind', - [$bind] - ); - } - - public function it_should_only_support_simple_binds(): void - { - $this->supports(new LdapMessageRequest( - 1, - new AnonBindRequest() - ))->shouldBe(false); - $this->supports(new LdapMessageRequest( - 1, - new SimpleBindRequest( - 'foo', - 'bar', - ) - ))->shouldBe(true); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientBasicHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientBasicHandlerSpec.php deleted file mode 100644 index 68434c1d..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientBasicHandlerSpec.php +++ /dev/null @@ -1,134 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\Exception\BindException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\CompareResponse; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientBasicHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientBasicHandlerSpec extends ObjectBehavior -{ - public function let(ClientQueue $queue): void - { - $this->beConstructedWith($queue); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientBasicHandler::class); - } - - public function it_should_implement_ResponseHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(ResponseHandlerInterface::class); - } - - public function it_should_implement_RequestHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(RequestHandlerInterface::class); - } - - public function it_should_handle_a_request_and_return_a_response(ClientQueue $queue, ): void - { - $queue->sendMessage(Argument::type(LdapMessageRequest::class))->shouldBeCalledOnce(); - $queue->getMessage(1)->willReturn( - new LdapMessageResponse(1, new DeleteResponse(0)) - ); - - $this->handleRequest( - new LdapMessageRequest(1, new DeleteRequest('cn=foo')) - )->shouldBeAnInstanceOf( - LdapMessageResponse::class - ); - } - - public function it_should_handle_a_response(ClientQueue $queue): void - { - $messageRequest = new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')); - $messageFrom = new LdapMessageResponse(1, new BindResponse(new LdapResult(0))); - - $this->handleResponse( - $messageRequest, - $messageFrom, - )->shouldBeEqualTo($messageFrom); - } - - public function it_should_handle_a_response_with_non_error_codes(): void - { - $messageRequest = new LdapMessageRequest(1, new CompareRequest('foo', new EqualityFilter('foo', 'bar'))); - $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_FALSE)); - - $this->handleResponse( - $messageRequest, - $messageFrom - )->shouldBeEqualTo($messageFrom); - - $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_TRUE)); - - $this->handleResponse( - $messageRequest, - $messageFrom - )->shouldBeEqualTo($messageFrom); - - $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::REFERRAL)); - - $this->handleResponse( - $messageRequest, - $messageFrom - )->shouldBeEqualTo($messageFrom); - } - - public function it_should_throw_an_operation_exception_on_errors(): void - { - $messageRequest = new LdapMessageRequest(1, new CompareRequest('foo', new EqualityFilter('foo', 'bar'))); - $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_FALSE)); - - $this->handleResponse( - $messageRequest, - $messageFrom, - )->shouldBeEqualTo($messageFrom); - } - - public function it_should_throw_a_specific_bind_exception_for_a_bind_response(): void - { - $messageRequest = new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')); - $messageFrom = new LdapMessageResponse(1, new BindResponse(new LdapResult(ResultCode::INVALID_CREDENTIALS, 'foo', 'message'))); - - $this->shouldThrow(new BindException( - 'Unable to bind to LDAP. message', - ResultCode::INVALID_CREDENTIALS - ))->during( - 'handleResponse', - [ - $messageRequest, - $messageFrom, - ] - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerSpec.php deleted file mode 100644 index 63ef8dff..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerSpec.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\PasswordModifyResponse; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientExtendedOperationHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; -use FreeDSx\Ldap\Protocol\Factory\ExtendedResponseFactory; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientExtendedOperationHandlerSpec extends ObjectBehavior -{ - public function let( - ClientQueue $queue, - ExtendedResponseFactory $responseFactory - ): void { - $this->beConstructedWith( - $queue, - $responseFactory - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientExtendedOperationHandler::class); - } - - public function it_should_implement_ClientResponseHandler(): void - { - $this->shouldBeAnInstanceOf(ResponseHandlerInterface::class); - } - - public function it_should_handle_a_response(ExtendedResponseFactory $responseFactory, ClientQueue $queue): void - { - $responseFactory->has(Argument::any())->willReturn(false); - $responseFactory->get(Argument::any(), Argument::any())->shouldNotBeCalled(); - - $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0), 'bar', 'foo')); - $this->handleResponse( - new LdapMessageRequest(1, new ExtendedRequest('foo', 'bar')), - $response, - )->shouldBeEqualTo($response); - } - - public function it_should_handle_an_extended_response_that_has_a_mapped_class( - ExtendedResponseFactory $responseFactory, - ClientQueue $queue - ): void { - $extendedResponse = new PasswordModifyResponse(new LdapResult(0)); - $responseFactory->has(Argument::any())->willReturn(true); - $responseFactory->get(Argument::any(), 'foo')->shouldBeCalled()->willReturn($extendedResponse); - - $request = new ExtendedRequest('foo', 'bar'); - $extendedRequest = new LdapMessageRequest(1, $request); - $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0), 'bar')); - $queue->getMessage(Argument::any())->willReturn($response); - $queue->sendMessage($extendedRequest)->shouldBeCalled(); - - $this->handleRequest($extendedRequest) - ->getResponse() - ->shouldBeAnInstanceOf(PasswordModifyResponse::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientReferralHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientReferralHandlerSpec.php deleted file mode 100644 index f6f9700e..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientReferralHandlerSpec.php +++ /dev/null @@ -1,257 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Exception\ConnectionException; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Exception\ReferralException; -use FreeDSx\Ldap\Exception\SkipReferralException; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientReferralHandler; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\ReferralChaserInterface; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientReferralHandlerSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new ClientOptions()); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientReferralHandler::class); - } - - public function it_should_throw_an_exception_on_referrals(ClientQueue $queue): void - { - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('throw') - ); - - $response = new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', 'foo', new LdapUrl('foo'))); - $request = new LdapMessageRequest(1, new DeleteRequest('cn=foo')); - - $this->shouldThrow(ReferralException::class) - ->during( - 'handleResponse', - [ - $request, - $response, - $queue, - ] - ); - } - - public function it_should_follow_referrals_with_a_referral_chaser_if_specified( - ReferralChaserInterface $chaser, - ClientQueue $queue, - LdapClient $ldapClient - ): void { - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(10) - ->setReferralChaser($chaser->getWrappedObject()) - ); - - $chaser->client(Argument::any())->willReturn($ldapClient); - $bind = new SimpleBindRequest('foo', 'bar'); - $chaser->chase(Argument::any(), Argument::any(), Argument::any())->willReturn($bind); - $ldapClient->send($bind)->shouldBeCalled()->willReturn(null); - - $message = new LdapMessageResponse(2, new DeleteResponse(0)); - $ldapClient->send(new DeleteRequest('foo'))->shouldBeCalled()->willReturn($message); - - $this->handleResponse( - new LdapMessageRequest(2, new DeleteRequest('foo')), - new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'))) - )->shouldBeLike($message); - } - - public function it_should_throw_an_exception_if_the_referral_limit_is_reached( - ReferralChaserInterface $chaser, - ClientQueue $queue - ): void { - $clientOptions = (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(-1) - ->setReferralChaser($chaser->getWrappedObject()); - - $this->beConstructedWith($clientOptions); - - $chaser->client(Argument::any()) - ->willReturn(new LdapClient($clientOptions)); - - $this->shouldThrow(new OperationException( - 'The referral limit of -1 has been reached.' - ))->during( - 'handleResponse', - [ - new LdapMessageRequest( - 2, - new DeleteRequest('foo') - ), - new LdapMessageResponse( - 1, - new DeleteResponse( - ResultCode::REFERRAL, - '', - '', - new LdapUrl('foo') - ) - ), - $queue, - $clientOptions, - ] - ); - } - - public function it_should_throw_an_exception_if_all_referrals_have_been_tried_and_follow_is_specified( - ReferralChaserInterface $chaser, - ClientQueue $queue - ): void { - $chaser->chase( - Argument::any(), - Argument::any(), - Argument::any(), - )->willThrow(new SkipReferralException()); - - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(10) - ->setReferralChaser($chaser->getWrappedObject()) - ); - - $this->shouldThrow(new OperationException( - 'All referral attempts have been exhausted. ', - ResultCode::REFERRAL - ))->during( - 'handleResponse', - [ - new LdapMessageRequest( - 2, - new DeleteRequest('foo') - ), - new LdapMessageResponse( - 1, - new DeleteResponse( - ResultCode::REFERRAL, - '', - '', - new LdapUrl('foo') - ) - ), - $queue, - ] - ); - } - - public function it_should_continue_to_the_next_referral_if_a_connection_exception_is_thrown( - ReferralChaserInterface $chaser, - ClientQueue $queue, - LdapClient $ldapClient - ): void { - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(10) - ->setReferralChaser($chaser->getWrappedObject()) - ); - - $chaser->client(Argument::any())->willReturn($ldapClient); - $bind = new SimpleBindRequest('foo', 'bar'); - - $chaser->chase(Argument::any(), Argument::any(), Argument::any())->willReturn($bind); - - $ldapClient->send($bind)->shouldBeCalled()->willThrow(new ConnectionException(), 1); - $ldapClient->send($bind)->shouldBeCalled()->willReturn(null, 2); - - $queue->getMessage(1)->willReturn(new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'), new LdapUrl('bar')))); - $message = new LdapMessageResponse(2, new DeleteResponse(0)); - $ldapClient->send(new DeleteRequest('foo'))->shouldBeCalled()->willReturn($message); - - $this->handleResponse( - new LdapMessageRequest(1, new DeleteRequest('foo')), - new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'))) - )->shouldBeLike($message); - } - - public function it_should_continue_to_the_next_referral_if_an_operation_exception_with_a_referral_result_code_is_thrown( - ReferralChaserInterface $chaser, - ClientQueue $queue, - LdapClient $ldapClient - ): void { - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(10) - ->setReferralChaser($chaser->getWrappedObject()) - ); - - $chaser->client(Argument::any())->willReturn($ldapClient); - $bind = new SimpleBindRequest('foo', 'bar'); - $chaser->chase(Argument::any(), Argument::any(), Argument::any())->willReturn($bind); - $ldapClient->send($bind)->shouldBeCalled()->willReturn(null); - - $queue->getMessage(1)->willReturn(new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'), new LdapUrl('bar')))); - $message = new LdapMessageResponse(2, new DeleteResponse(0)); - $ldapClient->send(new DeleteRequest('foo'))->shouldBeCalled()->willThrow(new OperationException('fail', ResultCode::REFERRAL), 1); - $ldapClient->send(new DeleteRequest('foo'))->shouldBeCalled()->willReturn($message, 2); - - $this->handleResponse( - new LdapMessageRequest(1, new DeleteRequest('foo')), - new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'))) - )->shouldBeLike($message); - } - - public function it_should_not_bind_on_the_referral_client_initially_if_the_referral_is_for_a_bind_request( - ReferralChaserInterface $chaser, - ClientQueue $queue, - LdapClient $ldapClient - ): void { - $this->beConstructedWith( - (new ClientOptions()) - ->setReferral('follow') - ->setReferralLimit(10) - ->setReferralChaser($chaser->getWrappedObject()) - ); - - $chaser->client(Argument::any())->willReturn($ldapClient); - $chaser->chase(Argument::any(), Argument::any(), Argument::any())->willReturn(new SimpleBindRequest('foo', 'bar')); - $ldapClient->send(Argument::any())->shouldBeCalledTimes(1); - - $message = new LdapMessageResponse(1, new BindResponse(new LdapResult(0))); - $ldapClient->send(new SimpleBindRequest('foo', 'bar'))->shouldBeCalled()->willReturn($message); - - $this->handleResponse( - new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')), - new LdapMessageResponse(1, new BindResponse(new LdapResult(ResultCode::REFERRAL, '', '', new LdapUrl('foo')))) - )->shouldBeLike($message); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSaslBindHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSaslBindHandlerSpec.php deleted file mode 100644 index b1c3cfde..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSaslBindHandlerSpec.php +++ /dev/null @@ -1,245 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\BindException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSaslBindHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Protocol\Queue\MessageWrapper\SaslMessageWrapper; -use FreeDSx\Ldap\Protocol\RootDseLoader; -use FreeDSx\Sasl\Challenge\ChallengeInterface; -use FreeDSx\Sasl\Mechanism\MechanismInterface; -use FreeDSx\Sasl\Sasl; -use FreeDSx\Sasl\SaslContext; -use FreeDSx\Sasl\Security\SecurityLayerInterface; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientSaslBindHandlerSpec extends ObjectBehavior -{ - private LdapMessageResponse $saslChallenge; - - private LdapMessageResponse $saslComplete; - - public function let( - Sasl $sasl, - ClientQueue $queue, - RootDseLoader $rootDseLoader, - ): void { - $queue - ->sendMessage(Argument::any()) - ->willReturn($queue); - - $rootDseLoader - ->load() - ->willReturn( - Entry::fromArray( - '', - ['supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5'], ] - ) - ); - $queue - ->generateId() - ->willReturn(2, 3, 4, 5, 6); - - $this->saslChallenge = new LdapMessageResponse( - 1, - new BindResponse(new LdapResult(ResultCode::SASL_BIND_IN_PROGRESS)) - ); - $this->saslComplete = new LdapMessageResponse( - 2, - new BindResponse(new LdapResult(ResultCode::SUCCESS), 'foo') - ); - - $this->beConstructedWith( - $queue, - $rootDseLoader, - $sasl, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientSaslBindHandler::class); - } - - public function it_should_implement_RequestHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(RequestHandlerInterface::class); - } - - public function it_should_handle_a_sasl_bind_request( - ChallengeInterface $challenge, - MechanismInterface $mech, - Sasl $sasl, - ClientQueue $queue, - RootDseLoader $rootDseLoader, - ): void { - $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar']); - $messageRequest = new LdapMessageRequest(1, $saslBind); - - $queue->getMessage(1)->willReturn($this->saslChallenge); - $queue->getMessage(2)->willReturn($this->saslComplete); - - $sasl->select(['DIGEST-MD5', 'CRAM-MD5'], ['username' => 'foo', 'password' => 'bar']) - ->shouldBeCalled() - ->willReturn($mech); - $mech->getName()->willReturn('DIGEST-MD5'); - $mech->challenge()->willReturn($challenge); - - $challenge->challenge(null, ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo') - ); - $challenge->challenge(Argument::type('string'), ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo')->setIsComplete(true) - ); - - $rootDseLoader - ->load(true) - ->willReturn( - Entry::fromArray('', [ - 'supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5'], - ]) - ); - - $this->handleRequest($messageRequest) - ->shouldBeEqualTo($this->saslComplete); - } - - public function it_should_detect_a_downgrade_attack( - ChallengeInterface $challenge, - MechanismInterface $mech, - Sasl $sasl, - ClientQueue $queue, - RootDseLoader $rootDseLoader, - ): void { - $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar']); - $messageRequest = new LdapMessageRequest(1, $saslBind); - - $queue->getMessage(1)->willReturn($this->saslChallenge); - $queue->getMessage(2)->willReturn($this->saslComplete); - - $sasl->select(['PLAIN'], ['username' => 'foo', 'password' => 'bar']) - ->shouldBeCalled() - ->willReturn($mech); - $mech->getName()->willReturn('PLAIN'); - $mech->challenge()->willReturn($challenge); - - $challenge->challenge(null, ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo') - ); - $challenge->challenge(Argument::type('string'), ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo')->setIsComplete(true) - ); - - $rootDseLoader - ->load() - ->willReturn( - Entry::fromArray('', [ - 'supportedSaslMechanisms' => ['PLAIN'], - ]) - ); - $rootDseLoader - ->load(true) - ->willReturn( - Entry::fromArray('', [ - 'supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5'], - ]) - ); - - $this->shouldThrow( - new BindException( - 'Possible SASL downgrade attack detected. The advertised SASL mechanisms have changed.' - ) - )->during('handleRequest', [$messageRequest]); - } - - public function it_should_not_query_the_rootdse_if_the_mechanism_was_explicitly_specified( - ChallengeInterface $challenge, - MechanismInterface $mech, - Sasl $sasl, - ClientQueue $queue, - RootDseLoader $rootDseLoader, - ): void { - $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar'], 'DIGEST-MD5'); - $messageRequest = new LdapMessageRequest(1, $saslBind); - - $queue->getMessage(1)->willReturn($this->saslChallenge); - $queue->getMessage(2)->willReturn($this->saslComplete); - - $sasl->get('DIGEST-MD5') - ->shouldBeCalled() - ->willReturn($mech); - $mech->getName()->willReturn('DIGEST-MD5'); - $mech->challenge()->willReturn($challenge); - - $challenge->challenge(null, ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo') - ); - $challenge->challenge(Argument::type('string'), ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo')->setIsComplete(true) - ); - - $rootDseLoader - ->load(Argument::any()) - ->shouldNotBeCalled(); - - $this->handleRequest($messageRequest) - ->shouldBeEqualTo($this->saslComplete); - } - - public function it_should_set_the_set_the_security_layer_on_the_queue_if_one_was_negotiated( - SecurityLayerInterface $securityLayer, - ChallengeInterface $challenge, - MechanismInterface $mech, - Sasl $sasl, - ClientQueue $queue - ): void { - $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar'], 'DIGEST-MD5'); - $messageRequest = new LdapMessageRequest(1, $saslBind); - - $queue->getMessage(1)->willReturn($this->saslChallenge); - $queue->getMessage(2)->willReturn($this->saslComplete); - - $sasl->get('DIGEST-MD5') - ->shouldBeCalled() - ->willReturn($mech); - $mech->getName()->willReturn('DIGEST-MD5'); - $mech->challenge()->willReturn($challenge); - - $challenge->challenge(null, ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo') - ); - $challenge->challenge(Argument::type('string'), ["username" => "foo", "password" => "bar"])->willReturn( - (new SaslContext())->setResponse('foo') - ->setHasSecurityLayer(true) - ->setIsAuthenticated(true) - ->setIsComplete(true) - ); - $mech->securityLayer()->shouldBeCalled()->willReturn($securityLayer); - - $queue->setMessageWrapper(Argument::type(SaslMessageWrapper::class))->shouldBeCalled()->willReturn($queue); - - $this->handleRequest($messageRequest) - ->shouldBeEqualTo($this->saslComplete); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSearchHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSearchHandlerSpec.php deleted file mode 100644 index 00f2c6ba..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSearchHandlerSpec.php +++ /dev/null @@ -1,176 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Response\SearchResponse; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSearchHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class ClientSearchHandlerSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let(ClientQueue $queue): void - { - $this->beConstructedWith( - $queue, - new ClientOptions(), - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientSearchHandler::class); - } - - public function it_should_implement_ResponseHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(ResponseHandlerInterface::class); - } - - public function it_should_implement_RequestHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(RequestHandlerInterface::class); - } - - public function it_should_send_a_request_and_get_a_response( - ClientQueue $queue, - LdapMessageResponse $response - ): void { - $request = Operations::search(new EqualityFilter('foo', 'bar')); - $message = new LdapMessageRequest(1, $request); - - $queue->sendMessage($message)->shouldBeCalledOnce(); - $queue->getMessage(1)->shouldBeCalledOnce()->willReturn($response); - - $this->handleRequest($message) - ->shouldBeEqualTo($response); - } - - public function it_should_set_a_default_DN_for_a_request_that_has_none( - LdapMessageResponse $response, - ClientQueue $queue, - LdapMessageRequest $message, - SearchRequest $request - ): void { - $this->beConstructedWith( - $queue, - (new ClientOptions()) - ->setBaseDn('cn=foo') - ); - $queue->getMessage(1)->shouldBeCalled()->willReturn($response); - $queue->sendMessage($message)->shouldBeCalledOnce(); - - $message->getMessageId()->willReturn(1); - $message->getRequest()->willReturn($request); - $request->getBaseDn()->willReturn(null); - - $request->setBaseDn('cn=foo') - ->shouldBeCalledOnce(); - - $this->handleRequest($message); - } - - public function it_should_not_keep_getting_messages_when_the_first_result_is_search_done(ClientQueue $queue): void - { - $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); - $response = new LdapMessageResponse(1, new SearchResultDone(0)); - - $queue->getMessage(Argument::any()) - ->shouldNotBeCalled(); - - $this->handleResponse( - $messageTo, - $response, - )->getResponse() - ->shouldBeAnInstanceOf(SearchResponse::class); - } - - public function it_should_retrieve_results_until_it_receives_a_search_done_and_return_all_results(ClientQueue $queue): void - { - $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); - $response = new LdapMessageResponse(1, new SearchResultEntry(new Entry('bar'))); - - $entries = [ - new SearchResultEntry(new Entry('foo')), - new SearchResultEntry(new Entry('foo')), - new SearchResultEntry(new Entry('foo')), - ]; - $referrals = [ - new SearchResultReference(new LdapUrl('ldap://foo')), - ]; - - $queue->getMessage(1)->willReturn( - new LdapMessageResponse(1, $entries[0]), - new LdapMessageResponse(1, $entries[1]), - new LdapMessageResponse(1, $referrals[0]), - new LdapMessageResponse(1, $entries[2]), - new LdapMessageResponse(1, new SearchResultDone( - 0, - 'cn=foo', - 'bar' - )) - ); - - $queue->getMessage(1)->shouldBeCalledTimes(5); - $this->handleResponse( - $messageTo, - $response, - )->shouldBeLike( - $this::makeSearchResponseFromEntries( - dn: 'cn=foo', - diagnostic: 'bar', - searchEntryResults: [ - new SearchResultEntry(new Entry('bar')), - ...$entries, - ], - searchReferralResults: $referrals, - ) - ); - } - - public function it_should_throw_an_exception_if_the_result_code_is_not_success(ClientQueue $queue): void - { - $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); - $response = new LdapMessageResponse(1, new SearchResultDone(ResultCode::SIZE_LIMIT_EXCEEDED)); - - $this->shouldThrow(OperationException::class)->during( - 'handleResponse', - [ - $messageTo, - $response, - $queue, - new ClientOptions() - ] - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSyncHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSyncHandlerSpec.php deleted file mode 100644 index b2057f81..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientSyncHandlerSpec.php +++ /dev/null @@ -1,336 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use Closure; -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Control\Sync\SyncDoneControl; -use FreeDSx\Ldap\Control\Sync\SyncRequestControl; -use FreeDSx\Ldap\Control\Sync\SyncStateControl; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\RuntimeException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\Request\SyncRequest; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncIdSet; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSyncHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Sync\Result\SyncEntryResult; -use FreeDSx\Ldap\Sync\Result\SyncIdSetResult; -use FreeDSx\Ldap\Sync\Result\SyncReferralResult; -use FreeDSx\Ldap\Sync\Session; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use spec\FreeDSx\Ldap\Sync\MockSyncEntryHandler; -use spec\FreeDSx\Ldap\Sync\MockSyncIdSetHandler; -use spec\FreeDSx\Ldap\Sync\MockSyncReferralHandler; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class ClientSyncHandlerSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let(ClientQueue $queue): void - { - $this->beConstructedWith( - $queue, - new ClientOptions(), - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientSyncHandler::class); - } - - - public function it_should_implement_ResponseHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(ResponseHandlerInterface::class); - } - - public function it_should_implement_RequestHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(RequestHandlerInterface::class); - } - - public function it_should_set_a_default_DN_for_a_request_that_has_none( - LdapMessageResponse $response, - ClientQueue $queue, - LdapMessageRequest $message, - SyncRequest $request - ): void { - $this->beConstructedWith( - $queue, - (new ClientOptions()) - ->setBaseDn('cn=foo') - ); - - $queue->getMessage(1)->shouldBeCalled()->willReturn($response); - $queue->sendMessage($message)->shouldBeCalledOnce(); - - $message->getMessageId()->willReturn(1); - $message->getRequest()->willReturn($request); - $request->getBaseDn()->willReturn(null); - - $request->setBaseDn('cn=foo') - ->shouldBeCalledOnce(); - - $this->handleRequest($message); - } - - - public function it_should_retrieve_results_until_it_receives_a_search_done_with_a_sync_done_control( - ClientQueue $queue, - ): void { - $messageTo = new LdapMessageRequest( - 1, - new SyncRequest(), - new SyncRequestControl(), - ); - $response = new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('bar')) - ); - - $entries = [ - new SearchResultEntry(new Entry('foo')), - new SearchResultEntry(new Entry('foo')), - new SearchResultEntry(new Entry('foo')), - ]; - $referrals = [ - new SearchResultReference(new LdapUrl('ldap://foo')), - ]; - - $queue->getMessage(1)->willReturn( - new LdapMessageResponse(1, $entries[0]), - new LdapMessageResponse(1, $entries[1]), - new LdapMessageResponse(1, $referrals[0]), - new LdapMessageResponse(1, $entries[2]), - new LdapMessageResponse( - 1, - new SearchResultDone( - 0, - 'cn=foo', - 'bar' - ), - new SyncDoneControl('foo') - ) - ); - - $queue->getMessage(1) - ->shouldBeCalledTimes(5); - - $this->handleResponse( - $messageTo, - $response, - )->shouldBeLike( - $this::makeSearchResponseFromEntries( - dn: 'cn=foo', - diagnostic: 'bar', - controls: [ - new SyncDoneControl('foo'), - ] - ) - ); - } - - public function it_should_throw_an_exception_if_a_sync_request_control_was_not_provided(ClientQueue $queue, ): void - { - $this->shouldThrow(new RuntimeException(sprintf( - 'Expected a "%s", but there is none.', - SyncRequestControl::class, - )))->during( - 'handleResponse', - [ - new LdapMessageRequest( - 1, - new SyncRequest(), - ), - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('bar')) - ), - $queue, - new ClientOptions(), - ] - ); - } - - public function it_should_process_a_sync_entry( - ClientQueue $queue, - MockSyncEntryHandler $syncEntryHandler, - ): void { - $entry = new Entry('bar'); - $syncState = new SyncStateControl( - SyncStateControl::STATE_ADD, - 'foo', - 'bar' - ); - - $messageTo = new LdapMessageRequest( - 1, - (new SyncRequest()) - ->useEntryHandler(Closure::fromCallable($syncEntryHandler->getWrappedObject())), - new SyncRequestControl(), - ); - $response = new LdapMessageResponse( - 1, - new SearchResultEntry($entry), - $syncState, - ); - - $queue->getMessage(1)->willReturn( - new LdapMessageResponse( - 1, - new SearchResultDone( - 0, - 'cn=foo', - 'bar' - ), - new SyncDoneControl('foo') - ) - ); - - $syncEntryHandler->__invoke( - Argument::that(function (SyncEntryResult $result) use ($response) { - return $result->getMessage() === $response; - }), - Argument::type(Session::class), - )->shouldBeCalledOnce(); - - $this->handleResponse( - $messageTo, - $response, - )->shouldBeLike( - $this::makeSearchResponseFromEntries( - dn: 'cn=foo', - diagnostic: 'bar', - controls: [ - new SyncDoneControl('foo'), - ] - ) - ); - } - - public function it_should_process_a_sync_id_set( - ClientQueue $queue, - MockSyncIdSetHandler $mockSyncReferralHandler, - ): void { - $messageTo = new LdapMessageRequest( - 1, - (new SyncRequest()) - ->useIdSetHandler(Closure::fromCallable($mockSyncReferralHandler->getWrappedObject())), - new SyncRequestControl(), - ); - $response = new LdapMessageResponse( - 1, - new SyncIdSet(['bar']), - ); - - $queue->getMessage(1)->willReturn( - new LdapMessageResponse( - 1, - new SearchResultDone( - 0, - 'cn=foo', - 'bar' - ), - new SyncDoneControl('foo') - ) - ); - - $mockSyncReferralHandler->__invoke( - Argument::that(function (SyncIdSetResult $result) use ($response) { - return $result->getMessage() === $response; - }), - Argument::type(Session::class), - )->shouldBeCalledOnce(); - - $this->handleResponse( - $messageTo, - $response, - )->shouldBeLike( - $this::makeSearchResponseFromEntries( - dn: 'cn=foo', - diagnostic: 'bar', - controls: [ - new SyncDoneControl('foo'), - ] - ) - ); - } - - public function it_should_process_a_sync_referral( - ClientQueue $queue, - MockSyncReferralHandler $mockSyncReferralHandler, - ): void { - $referral = new LdapUrl('bar'); - $syncState = new SyncStateControl( - SyncStateControl::STATE_ADD, - 'foo', - 'bar' - ); - - $messageTo = new LdapMessageRequest( - 1, - (new SyncRequest()) - ->useReferralHandler(Closure::fromCallable($mockSyncReferralHandler->getWrappedObject())), - new SyncRequestControl(), - ); - $response = new LdapMessageResponse( - 1, - new SearchResultReference($referral), - $syncState, - ); - - $queue->getMessage(1)->willReturn( - new LdapMessageResponse( - 1, - new SearchResultDone( - 0, - 'cn=foo', - 'bar' - ), - new SyncDoneControl('foo') - ) - ); - - $mockSyncReferralHandler->__invoke( - Argument::that(function (SyncReferralResult $result) use ($response) { - return $result->getMessage() === $response; - }), - Argument::type(Session::class), - )->shouldBeCalledOnce(); - - $this->handleResponse( - $messageTo, - $response, - )->shouldBeLike( - $this::makeSearchResponseFromEntries( - dn: 'cn=foo', - diagnostic: 'bar', - controls: [ - new SyncDoneControl('foo'), - ] - ) - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientUnbindHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientUnbindHandlerSpec.php deleted file mode 100644 index 2863a7cf..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientUnbindHandlerSpec.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientUnbindHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use PhpSpec\ObjectBehavior; - -class ClientUnbindHandlerSpec extends ObjectBehavior -{ - public function let(ClientQueue $queue): void - { - $this->beConstructedWith($queue); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientUnbindHandler::class); - } - - public function it_should_implement_RequestHandlerInterface(): void - { - $this->shouldBeAnInstanceOf(RequestHandlerInterface::class); - } - - public function it_should_send_the_message_and_close_the_queue(ClientQueue $queue): void - { - $unbind = new LdapMessageRequest(1, new UnbindRequest()); - $queue->sendMessage($unbind)->shouldBeCalledOnce(); - $queue->close()->shouldBeCalledOnce(); - - $this->handleRequest($unbind) - ->shouldBeNull(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/MockCancelResponseProcessor.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/MockCancelResponseProcessor.php deleted file mode 100644 index c9109c0f..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/MockCancelResponseProcessor.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use FreeDSx\Ldap\Protocol\LdapMessageResponse; - -/** - * Only used to test message handler processing during cancellations. - * - * @internal - */ -class MockCancelResponseProcessor -{ - public function __invoke(LdapMessageResponse $messageResponse): void - { - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/RequestCancelerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/RequestCancelerSpec.php deleted file mode 100644 index 2f40e0fd..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/RequestCancelerSpec.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; - -use Closure; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\CancelRequest; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class RequestCancelerSpec extends ObjectBehavior -{ - public function it_should_return_the_cancel_response(ClientQueue $queue): void - { - $this->beConstructedWith($queue); - - $cancelResponse = new ExtendedResponse(new LdapResult(ResultCode::CANCELED)); - $queue - ->getMessage() - ->willReturn( - new LdapMessageResponse(1, new SearchResultEntry(Entry::create(''))), - new LdapMessageResponse(1, new SearchResultReference()), - new LdapMessageResponse(2, $cancelResponse), - ) - ; - $queue - ->generateId() - ->willReturn(2) - ; - - $queue - ->sendMessage(Argument::that( - fn (LdapMessageRequest $request) => - $request->getRequest() instanceof CancelRequest - )) - ->shouldBeCalledOnce(); - - $this->cancel(1) - ->shouldBe($cancelResponse); - } - - public function it_should_keep_processing_on_the_continue_strategy( - ClientQueue $queue, - MockCancelResponseProcessor $mockResponseProcessor, - ): void { - $this->beConstructedWith( - $queue, - SearchRequest::CANCEL_CONTINUE, - Closure::fromCallable($mockResponseProcessor->getWrappedObject()), - ); - - $queue - ->sendMessage(Argument::any()) - ->willReturn($queue); - $queue - ->getMessage() - ->willReturn( - new LdapMessageResponse(1, new SearchResultEntry(Entry::create(''))), - new LdapMessageResponse(1, new SearchResultReference()), - new LdapMessageResponse(2, new ExtendedResponse(new LdapResult(ResultCode::CANCELED))), - ) - ; - $queue - ->generateId() - ->willReturn(2) - ; - - $this->cancel(1); - - $mockResponseProcessor - ->__invoke(Argument::any()) - ->shouldHaveBeenCalledTimes(2); - } - - public function it_should_throw_an_operation_error_if_the_cancel_result_code_was_not_success(ClientQueue $queue): void - { - $this->beConstructedWith($queue); - - $cancelResponse = new ExtendedResponse(new LdapResult( - ResultCode::TOO_LATE, - '', - 'Fail' - )); - $queue - ->getMessage() - ->willReturn(new LdapMessageResponse(2, $cancelResponse)) - ; - $queue - ->generateId() - ->willReturn(2) - ; - - $queue - ->sendMessage(Argument::any()) - ->willReturn($queue); - - $this - ->shouldThrow(new OperationException( - $cancelResponse->getDiagnosticMessage(), - $cancelResponse->getResultCode() - )) - ->during('cancel', [1]) - ; - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandlerSpec.php deleted file mode 100644 index 56f6ce07..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandlerSpec.php +++ /dev/null @@ -1,150 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Exception\ConnectionException; -use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; -use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class ClientProtocolHandlerSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let( - ClientQueue $queue, - ClientQueueInstantiator $clientQueueInstantiator, - ClientProtocolHandlerFactory $protocolHandlerFactory, - ResponseHandlerInterface $responseHandler, - RequestHandlerInterface $requestHandler - ): void { - $protocolHandlerFactory - ->forResponse( - Argument::any(), - Argument::any() - )->willReturn($responseHandler); - - $protocolHandlerFactory - ->forRequest(Argument::any()) - ->willReturn($requestHandler); - - $clientQueueInstantiator - ->make() - ->willReturn($queue); - - $queue->generateId() - ->willReturn(1); - - $this->beConstructedWith( - new ClientOptions(), - $clientQueueInstantiator, - $protocolHandlerFactory, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientProtocolHandler::class); - } - - public function it_should_close_the_queue_on_a_disconnect_notice_and_throw_a_connection_exception(RequestHandlerInterface $requestHandler, ClientQueue $queue): void - { - $requestHandler - ->handleRequest(Argument::any()) - ->willThrow(new UnsolicitedNotificationException('foo', 0, null, ExtendedResponse::OID_NOTICE_OF_DISCONNECTION)); - - $queue->close()->shouldBeCalledOnce(); - $this->shouldThrow(ConnectionException::class)->during('send', [new DeleteRequest('foo')]); - } - - public function it_should_throw_a_ldap_specific_connection_exception_on_socket_issues(RequestHandlerInterface $requestHandler, ClientQueue $queue): void - { - $requestHandler - ->handleRequest(Argument::any()) - ->willThrow(new \FreeDSx\Socket\Exception\ConnectionException('foo')); - - $this->shouldThrow(ConnectionException::class)->during('send', [new DeleteRequest('foo')]); - } - - public function it_should_send_a_request_and_handle_a_response(RequestHandlerInterface $requestHandler, ResponseHandlerInterface $responseHandler, ClientQueue $queue): void - { - $request = new DeleteRequest('cn=foo'); - $messageResponse = new LdapMessageResponse(1, new DeleteResponse(0)); - $messageRequest = new LdapMessageRequest(1, $request); - - $requestHandler->handleRequest(Argument::that(function (LdapMessageRequest $messageRequest) use ($request) { - return $messageRequest->getRequest() === $request; - }))->shouldBeCalledOnce() - ->willReturn($messageResponse); - $responseHandler->handleResponse( - $messageRequest, - $messageResponse, - )->shouldBeCalledOnce() - ->willReturn($messageResponse); - - $this->send($request)->shouldBeLike($messageResponse); - } - - public function it_should_return_null_if_no_response_was_returned(ResponseHandlerInterface $responseHandler, RequestHandlerInterface $requestHandler, ClientQueue $queue): void - { - $request = new UnbindRequest(); - $messageRequest = new LdapMessageRequest(1, $request); - - $requestHandler->handleRequest(Argument::that(function (LdapMessageRequest $messageRequest) use ($request) { - return $messageRequest->getRequest() === $request; - }))->shouldBeCalledOnce() - ->willReturn(null); - $responseHandler->handleResponse( - Argument::any(), - Argument::any(), - )->shouldNotBeCalled(); - - $this->send($request)->shouldBeEqualTo(null); - } - - public function it_should_throw_a_LDAP_specific_connection_exception_if_the_response_handler_throws_a_socket_exception(ResponseHandlerInterface $responseHandler, RequestHandlerInterface $requestHandler, ClientQueue $queue): void - { - $request = new DeleteRequest('cn=foo'); - $messageResponse = new LdapMessageResponse(1, new DeleteResponse(0)); - $messageRequest = new LdapMessageRequest(1, $request); - - $requestHandler->handleRequest( - Argument::that(function (LdapMessageRequest $messageRequest) use ($request) { - return $request === $messageRequest->getRequest(); - }) - )->shouldBeCalledOnce() - ->willReturn($messageResponse); - $responseHandler->handleResponse( - $messageRequest, - $messageResponse, - )->shouldBeCalledOnce() - ->willThrow(new \FreeDSx\Socket\Exception\ConnectionException('foo')); - - $this->shouldThrow(ConnectionException::class)->during('send', [$request]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ClientProtocolHandlerFactorySpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Factory/ClientProtocolHandlerFactorySpec.php deleted file mode 100644 index a6f0aa36..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ClientProtocolHandlerFactorySpec.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Factory; - -use FreeDSx\Ldap\ClientOptions; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\RequestInterface; -use FreeDSx\Ldap\Operation\Request\SaslBindRequest; -use FreeDSx\Ldap\Operation\Request\SyncRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshDelete; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientBasicHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientExtendedOperationHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientReferralHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSaslBindHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSearchHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientStartTlsHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSyncHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientUnbindHandler; -use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; -use FreeDSx\Ldap\Protocol\RootDseLoader; -use PhpSpec\ObjectBehavior; - -class ClientProtocolHandlerFactorySpec extends ObjectBehavior -{ - public function let( - ClientQueueInstantiator $queueInstantiator, - ClientQueue $queue, - RootDseLoader $rootDseLoader, - ): void { - $queueInstantiator - ->make() - ->willReturn($queue); - - $this->beConstructedWith( - new ClientOptions(), - $queueInstantiator, - $rootDseLoader, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientProtocolHandlerFactory::class); - } - - public function it_should_get_a_search_response_handler(RequestInterface $request): void - { - $this->forResponse($request, new SearchResultEntry(new Entry('')))->shouldBeAnInstanceOf(ClientSearchHandler::class); - $this->forResponse($request, new SearchResultDone(0))->shouldBeAnInstanceOf(ClientSearchHandler::class); - } - - public function it_should_get_an_unbind_request_handler(): void - { - $this->forRequest(Operations::unbind())->shouldBeAnInstanceOf(ClientUnbindHandler::class); - } - - public function it_should_get_a_basic_request_handler(): void - { - $this->forRequest(Operations::delete('cn=foo'))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::bind('foo', 'bar'))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::add(new Entry('')))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::modify(new Entry('')))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::move('cn=foo', 'cn=bar'))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::cancel(1))->shouldBeAnInstanceOf(ClientBasicHandler::class); - $this->forRequest(Operations::whoami())->shouldBeAnInstanceOf(ClientBasicHandler::class); - } - - public function it_should_get_a_referral_handler(RequestInterface $request): void - { - $this->forResponse($request, new DeleteResponse(ResultCode::REFERRAL))->shouldBeAnInstanceOf( - ClientReferralHandler::class - ); - } - - public function it_should_get_an_extended_response_handler(RequestInterface $request): void - { - $this->forResponse($request, new ExtendedResponse(new LdapResult(0)))->shouldBeAnInstanceOf( - ClientExtendedOperationHandler::class - ); - } - - public function it_should_get_a_start_tls_handler(): void - { - $this->forResponse(new ExtendedRequest(ExtendedRequest::OID_START_TLS), new ExtendedResponse(new LdapResult(0), ExtendedRequest::OID_START_TLS))->shouldBeAnInstanceOf( - ClientStartTlsHandler::class - ); - } - - public function it_should_get_a_basic_response_handler(RequestInterface $request): void - { - $this->forResponse($request, new BindResponse(new LdapResult(0)))->shouldBeAnInstanceOf( - ClientBasicHandler::class - ); - } - - public function it_should_get_a_sasl_bind_handler(): void - { - $this->forRequest(new SaslBindRequest('DIGEST-MD5'))->shouldBeAnInstanceOf( - ClientSaslBindHandler::class - ); - } - - public function it_should_get_a_sync_handler_for_a_request(): void - { - $this->forRequest(new SyncRequest()) - ->shouldBeAnInstanceOf(ClientSyncHandler::class); - } - - public function it_should_get_a_sync_handler_for_a_response(): void - { - $this->forResponse( - new SyncRequest(), - new SearchResultDone(0) - )->shouldBeAnInstanceOf(ClientSyncHandler::class); - } - - public function it_should_get_a_sync_handler_for_an_sync_info_response(): void - { - $this->forResponse( - new SyncRequest(), - new SyncRefreshDelete(), - )->shouldBeAnInstanceOf(ClientSyncHandler::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ExtendedResponseFactorySpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Factory/ExtendedResponseFactorySpec.php deleted file mode 100644 index 078cd0c8..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ExtendedResponseFactorySpec.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Factory; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Response\PasswordModifyResponse; -use FreeDSx\Ldap\Protocol\Factory\ExtendedResponseFactory; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; - -class ExtendedResponseFactorySpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(ExtendedResponseFactory::class); - } - - public function it_should_check_if_a_mapping_exists_for_a_specific_request_oid(): void - { - $this->has(ExtendedRequest::OID_PWD_MODIFY)->shouldBeEqualTo(true); - $this->has('foo')->shouldBeEqualTo(false); - } - - public function it_should_add_a_mapping_for_a_specific_oid(): void - { - $this->set('foo', PasswordModifyResponse::class); - $this->has('foo')->shouldBeEqualTo(true); - } - - public function it_should_get_a_mapping_based_on_an_oid_and_asn1(): void - { - $encoder = new LdapEncoder(); - - $this->get(Asn1::application(24, Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(3, (new IncompleteType( - $encoder->encode(Asn1::octetString('ldap://foo')) - . $encoder->encode(Asn1::octetString('ldap://bar')) - ))->setIsConstructed(true)), - Asn1::context(11, Asn1::octetString($encoder->encode(Asn1::sequence( - Asn1::context(0, Asn1::octetString('bleep-blorp')) - )))) - )), ExtendedRequest::OID_PWD_MODIFY)->shouldBeLike(new PasswordModifyResponse( - new LdapResult(0, 'dc=foo,dc=bar', 'foo', new LdapUrl('foo'), new LdapUrl('bar')), - 'bleep-blorp' - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Factory/FilterFactorySpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Factory/FilterFactorySpec.php deleted file mode 100644 index de7d59d2..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Factory/FilterFactorySpec.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Factory; - -use FreeDSx\Ldap\Protocol\Factory\FilterFactory; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use PhpSpec\ObjectBehavior; - -class FilterFactorySpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(FilterFactory::class); - } - - public function it_should_check_if_a_mapping_exists(): void - { - $this::has(0)->shouldBeEqualTo(true); - $this::has(99)->shouldBeEqualTo(false); - } - - public function it_should_set_a_mapping(): void - { - $this::set(99, EqualityFilter::class); - - $this::has(99)->shouldBeEqualTo(true); - } - - public function it_should_get_a_mapping(): void - { - $this::get((new EqualityFilter('foo', 'bar'))->toAsn1())->shouldBeLike(new EqualityFilter('foo', 'bar')); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ResponseFactorySpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Factory/ResponseFactorySpec.php deleted file mode 100644 index 07de6c71..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ResponseFactorySpec.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Factory; - -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\AddRequest; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\AddResponse; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\CompareResponse; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; -use FreeDSx\Ldap\Operation\Response\ModifyResponse; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\Factory\ResponseFactory; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class ResponseFactorySpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(ResponseFactory::class); - } - - public function it_should_get_a_bind_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new BindResponse(new LdapResult(0, '', 'foo')))); - } - - public function it_should_get_an_add_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new AddRequest(Entry::create('foo'))), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new AddResponse(0, 'foo', 'foo'))); - } - - public function it_should_get_a_compare_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new CompareRequest('foo', Filters::equal('foo', 'bar'))), ResultCode::COMPARE_TRUE, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_TRUE, 'foo', 'foo'))); - } - - public function it_should_get_a_modify_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new ModifyRequest('foo', Change::add('foo', 'bar'))), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new ModifyResponse(0, 'foo', 'foo'))); - } - - public function it_should_get_a_modify_dn_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new ModifyDnRequest('foo', 'cn=bar', true)), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new ModifyDnResponse(0, 'foo', 'foo'))); - } - - public function it_should_get_an_extended_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new ExtendedRequest('foo', 'bar')), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0, '', 'foo')))); - } - - public function it_should_get_a_delete_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new DeleteRequest('foo')), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new DeleteResponse(0, 'foo', 'foo'))); - } - - public function it_should_get_a_search_response(): void - { - $this->getStandardResponse(new LdapMessageRequest(1, new SearchRequest(Filters::present('objectClass'))), 0, 'foo') - ->shouldBeLike(new LdapMessageResponse(1, new SearchResultDone(0, '', 'foo'))); - } - - public function it_should_get_an_extended_error_response(): void - { - $this->getExtendedError('foo', ResultCode::PROTOCOL_ERROR, ExtendedRequest::OID_START_TLS)->shouldBeLike( - new LdapMessageResponse(0, new ExtendedResponse( - new LdapResult(ResultCode::PROTOCOL_ERROR, '', 'foo'), - ExtendedRequest::OID_START_TLS - )) - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ServerProtocolHandlerFactorySpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Factory/ServerProtocolHandlerFactorySpec.php deleted file mode 100644 index d6c0e1b9..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Factory/ServerProtocolHandlerFactorySpec.php +++ /dev/null @@ -1,136 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Factory; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\Factory\ServerProtocolHandlerFactory; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerDispatchHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingUnsupportedHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerRootDseHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerSearchHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerStartTlsHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerUnbindHandler; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerWhoAmIHandler; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Server\HandlerFactoryInterface; -use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; -use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHistory; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; - -class ServerProtocolHandlerFactorySpec extends ObjectBehavior -{ - public function let( - HandlerFactoryInterface $handlerFactory, - ServerQueue $queue, - ): void { - $this->beConstructedWith( - $handlerFactory, - new ServerOptions(), - new RequestHistory(), - $queue, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerProtocolHandlerFactory::class); - } - - public function it_should_get_a_start_tls_hanlder(ServerQueue $queue): void - { - $this->get(Operations::extended(ExtendedRequest::OID_START_TLS), new ControlBag()) - ->shouldBeAnInstanceOf(ServerStartTlsHandler::class); - } - - public function it_should_get_a_whoami_handler(ServerQueue $queue): void - { - $this->get(Operations::whoami(), new ControlBag()) - ->shouldBeAnInstanceOf(ServerWhoAmIHandler::class); - } - - public function it_should_get_a_search_handler(HandlerFactoryInterface $handlerFactory): void - { - $handlerFactory->makeRequestHandler() - ->willReturn(new GenericRequestHandler()); - - $this->get(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerSearchHandler::class); - } - - public function it_should_get_a_paging_handler_when_supported( - HandlerFactoryInterface $handlerFactory, - PagingHandlerInterface $pagingHandler - ): void { - $controls = new ControlBag(new PagingControl(10)); - - $handlerFactory->makePagingHandler() - ->shouldBeCalled() - ->willReturn($pagingHandler); - - $this->get(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), $controls)->shouldBeAnInstanceOf(ServerPagingHandler::class); - } - - public function it_should_get_a_paging_unsupported_handler_when_no_paging_handler_exists(HandlerFactoryInterface $handlerFactory): void - { - $controls = new ControlBag(new PagingControl(10)); - - $handlerFactory->makePagingHandler() - ->shouldBeCalled() - ->willReturn(null); - - $handlerFactory->makeRequestHandler() - ->willReturn(new GenericRequestHandler()); - - $this->get(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), $controls)->shouldBeAnInstanceOf(ServerPagingUnsupportedHandler::class); - } - - public function it_should_get_a_root_dse_handler(ServerQueue $queue): void - { - $this->get(Operations::read(''), new ControlBag()) - ->shouldBeAnInstanceOf(ServerRootDseHandler::class); - } - - public function it_should_get_an_unbind_handler(ServerQueue $queue): void - { - $this->get(Operations::unbind(), new ControlBag()) - ->shouldBeAnInstanceOf(ServerUnbindHandler::class); - } - - public function it_should_get_the_dispatch_handler_for_common_requests(HandlerFactoryInterface $handlerFactory, ): void - { - $handlerFactory->makeRequestHandler() - ->willReturn(new GenericRequestHandler()); - - $this->get(Operations::add(Entry::fromArray('cn=foo')), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - $this->get(Operations::delete('cn=foo'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - $this->get(Operations::compare('cn=foo', 'foo', 'bar'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - $this->get(Operations::modify('cn=foo'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - $this->get(Operations::move('cn=foo', 'foo=bar'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - $this->get(Operations::rename('cn=foo', 'cn=foo'), new ControlBag()) - ->shouldBeAnInstanceOf(ServerDispatchHandler::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/LdapMessageRequestSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/LdapMessageRequestSpec.php deleted file mode 100644 index 10b091d6..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/LdapMessageRequestSpec.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Protocol\LdapMessage; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use PhpSpec\ObjectBehavior; - -class LdapMessageRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(1, new DeleteRequest('dc=foo,dc=bar'), new Control('foo')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapMessageRequest::class); - } - - public function it_should_extend_ldap_message(): void - { - $this->shouldBeAnInstanceOf(LdapMessage::class); - } - - public function it_should_get_the_response(): void - { - $this->getRequest()->shouldBeAnInstanceOf(DeleteRequest::class); - } - - public function it_should_get_the_controls(): void - { - $this->controls()->has('foo')->shouldBeEqualTo(true); - } - - public function it_should_get_the_message_id(): void - { - $this->getMessageId()->shouldBeEqualTo(1); - } - - public function it_should_generate_correct_ASN1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::sequence( - Asn1::integer(1), - Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), - Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())) - )); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/LdapQueueSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/LdapQueueSpec.php deleted file mode 100644 index 9c9967d3..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/LdapQueueSpec.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Asn1\Encoder\EncoderInterface; -use FreeDSx\Ldap\Protocol\LdapQueue; -use FreeDSx\Socket\Queue\Asn1MessageQueue; -use FreeDSx\Socket\Socket; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class LdapQueueSpec extends ObjectBehavior -{ - public function let(Socket $socket, EncoderInterface $encoder): void - { - $socket->read(Argument::any())->willReturn('foo', false); - $encoder->getLastPosition()->willReturn(3); - - $this->beConstructedWith($socket, $encoder); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LdapQueue::class); - } - - public function it_should_extend_the_Asn1MessageQueue(): void - { - $this->shouldBeAnInstanceOf(Asn1MessageQueue::class); - } - - public function it_should_get_the_current_id(): void - { - $this->currentId()->shouldBeEqualTo(0); - } - - public function it_should_generate_an_id(): void - { - $this->generateId()->shouldBeEqualTo(1); - $this->generateId()->shouldBeEqualTo(2); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueInstantiatorSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueInstantiatorSpec.php deleted file mode 100644 index f5ddec6c..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueInstantiatorSpec.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Queue; - -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; -use FreeDSx\Socket\Socket; -use FreeDSx\Socket\SocketPool; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientQueueInstantiatorSpec extends ObjectBehavior -{ - public function let(SocketPool $socketPool): void - { - $this->beConstructedWith($socketPool); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientQueueInstantiator::class); - } - - public function it_should_return_false_if_not_instantiated_for_isConnectedAndInstantiated(): void - { - $this->isInstantiatedAndConnected() - ->shouldBe(false); - } - - public function it_should_return_false_if_instantiated_but_not_connected_for_isConnectedAndInstantiated( - SocketPool $socketPool, - Socket $socket, - ): void { - $socketPool->connect(Argument::any()) - ->willReturn($socket); - - $socket->isConnected() - ->willReturn(false); - - $this->make(); - - $this->isInstantiatedAndConnected() - ->shouldBe(false); - } - - public function it_should_return_true_if_instantiated_and_connected_for_isConnectedAndInstantiated( - SocketPool $socketPool, - Socket $socket, - ): void { - $socketPool->connect(Argument::any()) - ->willReturn($socket); - - $socket->isConnected() - ->willReturn(true); - - $this->make(); - - $this->isInstantiatedAndConnected() - ->shouldBe(true); - } - - public function it_should_return_an_instantiated_socket_on_make( - SocketPool $socketPool, - Socket $socket, - ): void { - $socketPool->connect(Argument::any()) - ->willReturn($socket); - - $this->make() - ->shouldBeAnInstanceOf(ClientQueue::class); - } - - public function it_should_return_the_same_queue_when_it_was_already_instantiated( - SocketPool $socketPool, - Socket $socket, - ): void { - $socketPool->connect(Argument::any()) - ->willReturn($socket); - - $queue = $this->make(); - - $this->make() - ->shouldBe($queue->getWrappedObject()); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueSpec.php deleted file mode 100644 index 00b0f4f5..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ClientQueueSpec.php +++ /dev/null @@ -1,132 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Queue; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Encoder\EncoderInterface; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use FreeDSx\Ldap\Protocol\Queue\MessageWrapperInterface; -use FreeDSx\Socket\Queue\Buffer; -use FreeDSx\Socket\Socket; -use FreeDSx\Socket\SocketPool; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ClientQueueSpec extends ObjectBehavior -{ - public function let(SocketPool $socketPool, Socket $socket, EncoderInterface $encoder): void - { - $socket->read(Argument::any())->willReturn('foo', false); - $encoder->getLastPosition()->willReturn(3); - $socketPool->connect(Argument::any())->shouldBeCalled()->willReturn($socket); - - $this->beConstructedWith($socketPool, $encoder); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientQueue::class); - } - - public function it_should_send_a_message(Socket $socket, EncoderInterface $encoder): void - { - $encoder->encode(Argument::any())->shouldBeCalledTimes(1)->willReturn('foo'); - $socket->write(Argument::any())->shouldBeCalledTimes(1); - - $this->sendMessage(new LdapMessageRequest(1, Operations::whoami())); - } - - public function it_should_get_a_response_message(EncoderInterface $encoder): void - { - $encoder->decode(Argument::any())->willReturn(Asn1::sequence( - Asn1::integer(3), - Asn1::application(11, Asn1::sequence( - Asn1::integer(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('') - )), - Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) - )); - - $this->getMessage()->shouldBeAnInstanceOf(LdapMessageResponse::class); - } - - public function it_should_throw_an_unsolicited_notification_exception_when_one_is_received(EncoderInterface $encoder, Socket $socket): void - { - $encoder->decode(Argument::any())->willReturn(Asn1::sequence( - Asn1::integer(0), - Asn1::application(24, Asn1::sequence( - Asn1::enumerated(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('foo'), - Asn1::context(10, Asn1::octetString(ExtendedResponse::OID_NOTICE_OF_DISCONNECTION)) - )) - )); - - $this->shouldThrow(UnsolicitedNotificationException::class)->during('getMessage'); - } - - public function it_should_throw_a_protocol_exception_if_the_message_id_was_unexpected(EncoderInterface $encoder): void - { - $encoder->decode(Argument::any())->willReturn(Asn1::sequence( - Asn1::integer(3), - Asn1::application(11, Asn1::sequence( - Asn1::integer(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('') - )), - Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) - )); - - $this->shouldThrow(ProtocolException::class)->during('getMessage', [2]); - } - - - public function it_should_set_a_message_wrapper_and_use_it_when_sending_messages(Socket $socket, EncoderInterface $encoder, MessageWrapperInterface $messageWrapper): void - { - $encoder->encode(Argument::any())->shouldBeCalledTimes(1)->willReturn('foo'); - $socket->write(Argument::any())->shouldBeCalledTimes(1); - - $messageWrapper->wrap('foo')->shouldBeCalled()->willReturn('bar'); - $this->setMessageWrapper($messageWrapper); - $this->sendMessage(new LdapMessageRequest(1, Operations::whoami())); - } - - public function it_should_set_a_message_wrapper_and_use_it_when_receiving_messages(Socket $socket, EncoderInterface $encoder, MessageWrapperInterface $messageWrapper): void - { - $asn1 = Asn1::sequence( - Asn1::integer(3), - Asn1::application(11, Asn1::sequence( - Asn1::integer(0), - Asn1::octetString('dc=foo,dc=bar'), - Asn1::octetString('') - )), - Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) - ); - $encoder->decode('bar')->willReturn($asn1); - $messageWrapper->unwrap('foo')->shouldBeCalled()->willReturn(new Buffer('bar', 3)); - - $this->setMessageWrapper($messageWrapper); - $this->getMessage()->shouldBeAnInstanceOf(LdapMessageResponse::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Queue/MessageWrapper/SaslMessageWrapperSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Queue/MessageWrapper/SaslMessageWrapperSpec.php deleted file mode 100644 index 3a561b11..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Queue/MessageWrapper/SaslMessageWrapperSpec.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Queue\MessageWrapper; - -use FreeDSx\Ldap\Protocol\Queue\MessageWrapper\SaslMessageWrapper; -use FreeDSx\Sasl\SaslContext; -use FreeDSx\Sasl\Security\SecurityLayerInterface; -use FreeDSx\Socket\Exception\PartialMessageException; -use FreeDSx\Socket\Queue\Buffer; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class SaslMessageWrapperSpec extends ObjectBehavior -{ - public function let(SecurityLayerInterface $securityLayer): void - { - $context = new SaslContext(); - $context->setResponse('foo'); - $this->beConstructedWith($securityLayer, $context); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SaslMessageWrapper::class); - } - - public function it_should_wrap_the_message(SecurityLayerInterface $securityLayer): void - { - $securityLayer->wrap('bar', Argument::type(SaslContext::class)) - ->shouldBeCalled() - ->willReturn('foobar'); - - $this->wrap('bar')->shouldBeEqualTo("\x00\x00\x00\x06foobar"); - } - - public function it_should_unwrap_the_message(SecurityLayerInterface $securityLayer): void - { - $securityLayer->unwrap('foobar', Argument::type(SaslContext::class)) - ->shouldBeCalled() - ->willReturn('foobar'); - - $this->unwrap("\x00\x00\x00\x06foobar")->shouldBeLike(new Buffer("foobar", 10)); - } - - public function it_should_throw_a_partial_message_exception_when_there_is_not_enough_data_to_unwrap(SecurityLayerInterface $securityLayer): void - { - $securityLayer->unwrap(Argument::any(), Argument::any())->shouldNotBeCalled(); - - $this->shouldThrow(PartialMessageException::class)->during('unwrap', ["\x00\x00\x00\x06foo"]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ServerQueueSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/Queue/ServerQueueSpec.php deleted file mode 100644 index 3ab4e58f..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/Queue/ServerQueueSpec.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\Queue; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Asn1\Encoder\EncoderInterface; -use FreeDSx\Asn1\Type\IncompleteType; -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Operation\Response\DeleteResponse; -use FreeDSx\Ldap\Protocol\LdapEncoder; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\MessageWrapperInterface; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Socket\Queue\Buffer; -use FreeDSx\Socket\Socket; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerQueueSpec extends ObjectBehavior -{ - public function let(Socket $socket, EncoderInterface $encoder): void - { - $socket->read(Argument::any())->willReturn('foo', false); - $encoder->getLastPosition()->willReturn(3); - $this->beConstructedWith($socket, $encoder); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerQueue::class); - } - - public function it_should_send_a_message(Socket $socket, EncoderInterface $encoder): void - { - $encoder->encode(Argument::any())->shouldBeCalledTimes(1)->willReturn('foo'); - $socket->write(Argument::any())->shouldBeCalledTimes(1); - - $this->sendMessage(new LdapMessageResponse(1, new DeleteResponse(0))); - } - - public function it_should_get_a_request_message(EncoderInterface $encoder, Socket $socket): void - { - $encoder->decode(Argument::any())->willReturn(Asn1::sequence( - Asn1::integer(1), - Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), - new IncompleteType((new LdapEncoder())->encode(Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())))) - )); - - $this->getMessage()->shouldBeAnInstanceOf(LdapMessageRequest::class); - } - - public function it_should_send_multiple_messages_with_write_and_respect_the_buffer_size($socket, $encoder): void - { - $encoder->encode(Argument::any())->shouldBeCalledTimes(2)->willReturn(str_repeat('f', 8000)); - $socket->write(Argument::any())->shouldBeCalledTimes(2); - - $this->sendMessage( - new LdapMessageResponse(1, new DeleteResponse(0)), - new LdapMessageResponse(2, new DeleteResponse(0)) - ); - } - - - public function it_should_set_a_message_wrapper_and_use_it_when_sending_messages(Socket $socket, EncoderInterface $encoder, MessageWrapperInterface $messageWrapper): void - { - $encoder->encode(Argument::any())->shouldBeCalledTimes(1)->willReturn('foo'); - $socket->write(Argument::any())->shouldBeCalledTimes(1); - - $messageWrapper->wrap('foo')->shouldBeCalled()->willReturn('bar'); - $this->setMessageWrapper($messageWrapper); - $this->sendMessage(new LdapMessageResponse(1, new DeleteResponse(0))); - } - - public function it_should_set_a_message_wrapper_and_use_it_when_receiving_messages(Socket $socket, EncoderInterface $encoder, MessageWrapperInterface $messageWrapper): void - { - $asn1 = Asn1::sequence( - Asn1::integer(1), - Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), - new IncompleteType((new LdapEncoder())->encode(Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())))) - ); - $encoder->decode(Argument::any())->willReturn($asn1); - - $messageWrapper->unwrap('foo')->shouldBeCalled()->willReturn(new Buffer('bar', 3)); - - $this->setMessageWrapper($messageWrapper); - $this->getMessage()->shouldBeAnInstanceOf(LdapMessageRequest::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ReferralContextSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ReferralContextSpec.php deleted file mode 100644 index af17fba4..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ReferralContextSpec.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Protocol\ReferralContext; -use PhpSpec\ObjectBehavior; - -class ReferralContextSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new LdapUrl('foo')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ReferralContext::class); - } - - public function it_should_get_the_referrals(): void - { - $this->getReferrals()->shouldBeLike([new LdapUrl('foo')]); - } - - public function it_should_check_if_it_has_a_specific_referral(): void - { - $this->hasReferral(new LdapUrl('Foo'))->shouldBeLike(true); - $this->hasReferral(new LdapUrl('bar'))->shouldBeLike(false); - } - - public function it_should_add_a_referral(): void - { - $this->addReferral(new LdapUrl('bar')); - - $this->getReferrals()->shouldBeLike([ - new LdapUrl('foo'), - new LdapUrl('bar') - ]); - } - - public function it_should_get_the_referral_count(): void - { - $this->count()->shouldBeEqualTo(1); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/RootDseLoaderSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/RootDseLoaderSpec.php deleted file mode 100644 index 47f2bdf2..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/RootDseLoaderSpec.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Protocol\RootDseLoader; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class RootDseLoaderSpec extends ObjectBehavior -{ - public function let(LdapClient $ldapClient): void - { - $this->beConstructedWith($ldapClient); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(RootDseLoader::class); - } - - public function it_should_load_the_root_dse(LdapClient $ldapClient): void - { - $entry = Entry::fromArray('', []); - - $ldapClient - ->read( - '', - Argument::any(), - ) - ->willReturn($entry); - - $this->load() - ->shouldBe($entry); - } - - public function it_should_use_the_cached_root_dse_on_a_second_load_call(LdapClient $ldapClient): void - { - $entry = Entry::fromArray('', []); - - $ldapClient - ->read( - '', - Argument::any(), - ) - ->shouldBeCalledOnce() - ->willReturn($entry); - - $this->load() - ->shouldBe($entry); - } - - public function it_should_not_use_the_cached_root_if_the_reload_param_is_used(LdapClient $ldapClient): void - { - $entry = Entry::fromArray('', []); - - $ldapClient - ->read( - '', - Argument::any(), - ) - ->shouldBeCalledTimes(2) - ->willReturn($entry); - - $this->load(); - - $this->load(reload: true) - ->shouldBe($entry); - } - - public function it_should_throw_an_exception_if_no_root_dse_is_returned(): void - { - $this->shouldThrow(OperationException::class) - ->during('load'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerAuthorizationSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerAuthorizationSpec.php deleted file mode 100644 index fb793de6..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerAuthorizationSpec.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\ServerAuthorization; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Server\Token\AnonToken; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; - -class ServerAuthorizationSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new ServerOptions()); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerAuthorization::class); - } - - public function it_should_have_an_anonymous_token_by_default(): void - { - $this->getToken()->shouldBeAnInstanceOf(AnonToken::class); - } - - public function it_should_set_the_token(): void - { - $token = new AnonToken('foo'); - - $this->setToken($token); - $this->getToken()->shouldBeEqualTo($token); - } - - public function it_should_not_require_authentication_for_a_start_tls_request(): void - { - $this->isAuthenticationRequired(Operations::extended(ExtendedRequest::OID_START_TLS))->shouldBeEqualTo(false); - } - - public function it_should_not_require_authentication_for_a_whoami_request(): void - { - $this->isAuthenticationRequired(Operations::extended(ExtendedRequest::OID_WHOAMI))->shouldBeEqualTo(false); - } - - public function it_should_not_require_authentication_for_a_bind_request(): void - { - $this->isAuthenticationRequired(Operations::bind('foo', 'bar'))->shouldBeEqualTo(false); - } - - public function it_should_not_require_authentication_for_an_unbind_request(): void - { - $this->isAuthenticationRequired(Operations::unbind())->shouldBeEqualTo(false); - } - - public function it_should_not_require_authentication_for_a_rootdse_request(): void - { - $this->isAuthenticationRequired(Operations::read(''))->shouldBeEqualTo(false); - } - - public function it_should_require_authentication_for_all_other_operations(): void - { - $this->isAuthenticationRequired(Operations::read('cn=bar'))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::search(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::add(Entry::fromArray('', [])))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::delete('cn=foo'))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::rename('cn=foo', 'cn=foo'))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::abandon(1))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::passwordModify('', '', ''))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::modify('cn=foo', Change::reset('foo')))->shouldBeEqualTo(true); - $this->isAuthenticationRequired(Operations::compare('cn=foo', 'foo', 'bar'))->shouldBeEqualTo(true); - } - - public function it_should_not_require_authentication_if_it_has_been_explicitly_disabled(): void - { - $this->beConstructedWith( - (new ServerOptions()) - ->setAllowAnonymous(false) - ->setRequireAuthentication(false), - new AnonToken() - ); - - $this->isAuthenticationRequired(Operations::read('cn=bar'))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::search(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::add(Entry::fromArray('', [])))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::delete('cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::rename('cn=foo', 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::abandon(1))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::passwordModify('', '', ''))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::modify('cn=foo', Change::reset('foo')))->shouldBeEqualTo(false); - $this->isAuthenticationRequired(Operations::compare('cn=foo', 'foo', 'bar'))->shouldBeEqualTo(false); - } - - public function it_should_not_allow_anonymous_authentication_by_default(): void - { - $this->isAuthenticationTypeSupported(Operations::bindAnonymously())->shouldBeEqualTo(false); - } - - public function it_should_respect_the_option_for_whether_anon_binds_are_allowed(): void - { - $this->beConstructedWith( - (new ServerOptions()) - ->setAllowAnonymous(true), - new AnonToken() - ); - - $this->isAuthenticationTypeSupported(Operations::bindAnonymously())->shouldBeEqualTo(true); - } - - public function it_should_allow_simple_bind_types(): void - { - $this->isAuthenticationTypeSupported(Operations::bind('foo', 'bar'))->shouldBeEqualTo(true); - } - - public function it_should_tell_if_a_request_is_an_authentication_type(): void - { - $this->isAuthenticationRequest(Operations::bind('foo', 'bar'))->shouldBeEqualTo(true); - $this->isAuthenticationRequest(Operations::bindAnonymously())->shouldBeEqualTo(true); - } - - public function it_should_tell_if_a_request_is_not_an_authentication_type(): void - { - $this->isAuthenticationRequest(Operations::read('cn=bar'))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::search(new EqualityFilter('foo', 'bar'), 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::add(Entry::fromArray('', [])))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::delete('cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::rename('cn=foo', 'cn=foo'))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::abandon(1))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::passwordModify('', '', ''))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::modify('cn=foo', Change::reset('foo')))->shouldBeEqualTo(false); - $this->isAuthenticationRequest(Operations::compare('cn=foo', 'foo', 'bar'))->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerDispatchHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerDispatchHandlerSpec.php deleted file mode 100644 index 65da6db8..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerDispatchHandlerSpec.php +++ /dev/null @@ -1,125 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\Request\AbandonRequest; -use FreeDSx\Ldap\Operation\Request\AddRequest; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerDispatchHandler; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerDispatchHandlerSpec extends ObjectBehavior -{ - public function let( - ServerQueue $queue, - RequestHandlerInterface $handler, - ): void { - $queue->sendMessage(Argument::any()) - ->willReturn($queue); - - $this->beConstructedWith( - $queue, - $handler, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerDispatchHandler::class); - } - - public function it_should_send_an_add_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $add = new LdapMessageRequest(1, new AddRequest(Entry::create('cn=foo,dc=bar'))); - - $handler->add(Argument::any(), $add->getRequest())->shouldBeCalled(); - $this->handleRequest($add, $token); - } - - public function it_should_send_a_delete_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $delete = new LdapMessageRequest(1, new DeleteRequest('cn=foo,dc=bar')); - - $handler->delete(Argument::any(), $delete->getRequest())->shouldBeCalled(); - $this->handleRequest($delete, $token); - } - - public function it_should_send_a_modify_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $modify = new LdapMessageRequest(1, new ModifyRequest('cn=foo,dc=bar', Change::add('foo', 'bar'))); - - $handler->modify(Argument::any(), $modify->getRequest())->shouldBeCalled(); - $this->handleRequest($modify, $token); - } - - public function it_should_send_a_modify_dn_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $modifyDn = new LdapMessageRequest(1, new ModifyDnRequest('cn=foo,dc=bar', 'cn=bar', true)); - - $handler->modifyDn(Argument::any(), $modifyDn->getRequest())->shouldBeCalled(); - $this->handleRequest($modifyDn, $token); - } - - public function it_should_send_an_extended_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $ext = new LdapMessageRequest(1, new ExtendedRequest('foo', 'bar')); - - $handler->extended(Argument::any(), $ext->getRequest())->shouldBeCalled(); - $this->handleRequest($ext, $token); - } - - public function it_should_send_a_compare_request_to_the_request_handler( - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $compare = new LdapMessageRequest(1, new CompareRequest('cn=foo,dc=bar', Filters::equal('foo', 'bar'))); - - $handler->compare(Argument::any(), $compare->getRequest())->shouldBeCalled()->willReturn(true); - $this->handleRequest($compare, $token); - } - - public function it_should_throw_an_operation_exception_if_the_request_is_unsupported(TokenInterface $token): void - { - $request = new LdapMessageRequest(2, new AbandonRequest(1)); - - $this->shouldThrow(OperationException::class)->during( - 'handleRequest', - [$request, $token, new ServerOptions()] - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerSpec.php deleted file mode 100644 index 652fa021..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerSpec.php +++ /dev/null @@ -1,156 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingUnsupportedHandler; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerPagingUnsupportedHandlerSpec extends ObjectBehavior -{ - public function let( - ServerQueue $queue, - RequestHandlerInterface $handler, - ): void { - $this->beConstructedWith( - $queue, - $handler, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerPagingUnsupportedHandler::class); - } - - public function it_should_send_a_search_request_to_the_request_handler_if_paging_is_not_critical( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $search = new LdapMessageRequest( - 2, - (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar'), - (new PagingControl(10, ''))->setCriticality(false) - ); - - $entries = new Entries(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']), Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])); - $resultEntry1 = new LdapMessageResponse( - 2, - new SearchResultEntry(Entry::create('dc=foo,dc=bar', ['cn' => 'foo'])) - ); - $resultEntry2 = new LdapMessageResponse( - 2, - new SearchResultEntry(Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])) - ); - - $handler->search(Argument::any(), $search->getRequest()) - ->shouldBeCalled() - ->willReturn($entries); - - $queue->sendMessage( - $resultEntry1, - $resultEntry2, - new LdapMessageResponse( - 2, - new SearchResultDone( - 0, - 'dc=foo,dc=bar' - ) - ) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } - - public function it_should_throw_an_unavailable_critical_extension_if_paging_is_marked_critical( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $search = new LdapMessageRequest( - 2, - (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar'), - (new PagingControl(10, ''))->setCriticality(true) - ); - - $handler->search(Argument::any(), $search->getRequest()) - ->shouldNotBeCalled(); - - $this->shouldThrow(new OperationException('The server does not support the paging control.', ResultCode::UNAVAILABLE_CRITICAL_EXTENSION))->during('handleRequest', [ - $search, - $token, - $handler, - $queue, - new ServerOptions() - ]); - } - - public function it_should_send_a_SearchResultDone_with_an_operation_exception_thrown_from_the_handler( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $search = new LdapMessageRequest( - 2, - (new SearchRequest(Filters::equal( - 'foo', - 'bar' - )))->base('dc=foo,dc=bar'), - new PagingControl( - 10, - '' - ) - ); - - $handler->search(Argument::any(), Argument::any()) - ->willThrow(new OperationException( - "Fail", - ResultCode::OPERATIONS_ERROR - )); - - $queue->sendMessage(new LdapMessageResponse( - 2, - new SearchResultDone( - ResultCode::OPERATIONS_ERROR, - 'dc=foo,dc=bar', - "Fail" - ) - ))->shouldBeCalled() - ->willReturn($queue); - - $this->handleRequest( - $search, - $token, - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerRootDseHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerRootDseHandlerSpec.php deleted file mode 100644 index 8c6a6b5d..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerRootDseHandlerSpec.php +++ /dev/null @@ -1,239 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerRootDseHandler; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerRootDseHandlerSpec extends ObjectBehavior -{ - public function let(ServerQueue $queue): void - { - $this->beConstructedWith( - new ServerOptions(), - $queue, - null - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerRootDseHandler::class); - } - - public function it_should_send_back_a_RootDSE( - ServerQueue $queue, - TokenInterface $token - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setDseVendorName('Foo') - ->setDseNamingContexts('dc=Foo,dc=Bar'), - $queue, - ); - - $search = new LdapMessageRequest( - 1, - (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope() - ); - - $queue->sendMessage( - new LdapMessageResponse(1, new SearchResultEntry(Entry::create('', [ - 'namingContexts' => 'dc=Foo,dc=Bar', - 'supportedExtension' => [ - ExtendedRequest::OID_WHOAMI, - ], - 'supportedLDAPVersion' => ['3'], - 'vendorName' => 'Foo', - ]))), - new LdapMessageResponse(1, new SearchResultDone(0)) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } - - public function it_should_send_back_a_RootDSE_with_paging_support_if_the_paging_handler_is_set( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token, - PagingHandlerInterface $pagingHandler, - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setDseVendorName('Foo') - ->setDseNamingContexts('dc=Foo,dc=Bar') - ->setPagingHandler($pagingHandler->getWrappedObject()), - $queue, - ); - - $search = new LdapMessageRequest( - 1, - (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope() - ); - - $queue->sendMessage( - Argument::that(function (LdapMessageResponse $response) { - /** @var SearchResultEntry $search */ - $search = $response->getResponse(); - $entry = $search->getEntry(); - - return $entry->get('supportedControl') - ->has(Control::OID_PAGING); - }), - new LdapMessageResponse(1, new SearchResultDone(0)) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } - - public function it_should_send_a_request_to_the_dispatcher_if_it_implements_a_rootdse_aware_interface( - ServerQueue $queue, - TokenInterface $token, - RootDseHandlerInterface $rootDseHandler - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setDseVendorName('Foo') - ->setDseNamingContexts('dc=Foo,dc=Bar'), - $queue, - $rootDseHandler, - ); - - $searchReqeust = (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope(); - $search = new LdapMessageRequest( - 1, - $searchReqeust - ); - $rootDse = Entry::create('', [ - 'namingContexts' => 'dc=Foo,dc=Bar', - 'supportedExtension' => [ - ExtendedRequest::OID_WHOAMI, - ], - 'supportedLDAPVersion' => ['3'], - 'vendorName' => 'Foo', - ]); - - $handlerRootDse = Entry::fromArray('', ['foo' => 'bar']); - $rootDseHandler->rootDse(Argument::type(RequestContext::class), $searchReqeust, $rootDse) - ->shouldBeCalled() - ->willReturn($handlerRootDse); - - $queue->sendMessage( - new LdapMessageResponse(1, new SearchResultEntry($handlerRootDse)), - new LdapMessageResponse(1, new SearchResultDone(0)) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } - - public function it_should_only_return_attribute_names_from_the_RootDSE_if_requested( - ServerQueue $queue, - TokenInterface $token - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setDseVendorName('Foo') - ->setDseNamingContexts('dc=Foo,dc=Bar'), - $queue, - ); - - $search = new LdapMessageRequest( - 1, - (new SearchRequest(Filters::present('objectClass'))) - ->base('') - ->useBaseScope() - ->setAttributesOnly(true) - ); - - $queue->sendMessage( - new LdapMessageResponse(1, new SearchResultEntry(Entry::create('', [ - 'namingContexts' => [], - 'supportedExtension' => [], - 'supportedLDAPVersion' => [], - 'vendorName' => [], - ]))), - new LdapMessageResponse(1, new SearchResultDone(0)) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } - - public function it_should_only_return_specific_attributes_from_the_RootDSE_if_requested( - ServerQueue $queue, - TokenInterface $token - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setDseVendorName('Foo') - ->setDseNamingContexts('dc=Foo,dc=Bar'), - $queue, - ); - - $search = new LdapMessageRequest( - 1, - (new SearchRequest(Filters::present('objectClass'))) - ->base('') - ->useBaseScope() - ->setAttributes('namingcontexts') - ); - - # The reset below is needed, unfortunately, to properly spec due to how the objects change... - $entry = Entry::create('', ['namingContexts' => 'dc=Foo,dc=Bar', ]); - $entry->changes()->reset(); - $entry->get('namingContexts')->equals(new Attribute('foo')); - - $queue->sendMessage( - new LdapMessageResponse( - 1, - new SearchResultEntry($entry) - ), - new LdapMessageResponse(1, new SearchResultDone(0)) - )->shouldBeCalled(); - - $this->handleRequest( - $search, - $token, - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerSearchHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerSearchHandlerSpec.php deleted file mode 100644 index e368c30f..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerSearchHandlerSpec.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerSearchHandler; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerSearchHandlerSpec extends ObjectBehavior -{ - public function let( - ServerQueue $queue, - RequestHandlerInterface $handler, - ): void { - $this->beConstructedWith( - $queue, - $handler, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerSearchHandler::class); - } - - public function it_should_send_a_search_request_to_the_request_handler( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $search = new LdapMessageRequest( - 2, - (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar') - ); - - $entries = new Entries(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']), Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])); - $resultEntry1 = new LdapMessageResponse(2, new SearchResultEntry(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']))); - $resultEntry2 = new LdapMessageResponse(2, new SearchResultEntry(Entry::create('dc=bar,dc=foo', ['cn' => 'bar']))); - - $handler->search(Argument::any(), $search->getRequest()) - ->shouldBeCalled() - ->willReturn($entries); - - $queue->sendMessage( - $resultEntry1, - $resultEntry2, - new LdapMessageResponse(2, new SearchResultDone(0, 'dc=foo,dc=bar')) - )->shouldBeCalled(); - - $this->handleRequest($search, $token); - } - - public function it_should_send_a_SearchResultDone_with_an_operation_exception_thrown_from_the_handler( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { - $search = new LdapMessageRequest( - 2, - (new SearchRequest(Filters::equal( - 'foo', - 'bar' - )))->base('dc=foo,dc=bar') - ); - - $handler->search(Argument::any(), Argument::any()) - ->willThrow(new OperationException( - "Fail", - ResultCode::OPERATIONS_ERROR - )); - - $queue->sendMessage(new LdapMessageResponse( - 2, - new SearchResultDone( - ResultCode::OPERATIONS_ERROR, - 'dc=foo,dc=bar', - "Fail" - ) - ))->shouldBeCalled() - ->willReturn($queue); - - $this->handleRequest( - $search, - $token, - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerStartTlsHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerStartTlsHandlerSpec.php deleted file mode 100644 index 79b32c28..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerStartTlsHandlerSpec.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerStartTlsHandler; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; - -class ServerStartTlsHandlerSpec extends ObjectBehavior -{ - public function let(ServerQueue $queue): void - { - $this->beConstructedWith( - new ServerOptions(), - $queue, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerStartTlsHandler::class); - } - - public function it_should_handle_a_start_tls_request( - ServerQueue $queue, - TokenInterface $token, - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setSslCert('foo'), - $queue, - ); - - $queue->isEncrypted()->willReturn(false); - - $queue->encrypt()->shouldBeCalled()->willReturn($queue); - $queue->sendMessage(new LdapMessageResponse( - 1, - new ExtendedResponse( - new LdapResult(0), - ExtendedRequest::OID_START_TLS - ) - ))->shouldBeCalled(); - - $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); - $this->handleRequest( - $startTls, - $token, - ); - } - - public function it_should_send_back_an_error_if_the_queue_is_already_encrypted( - ServerQueue $queue, - TokenInterface $token, - ): void { - $this->beConstructedWith( - (new ServerOptions()) - ->setSslCert('foo'), - $queue, - ); - - $queue->isEncrypted()->willReturn(true); - - $queue->encrypt()->shouldNotBeCalled(); - $queue->sendMessage(new LdapMessageResponse( - 1, - new ExtendedResponse( - new LdapResult(ResultCode::OPERATIONS_ERROR, '', 'The current LDAP session is already encrypted.'), - ExtendedRequest::OID_START_TLS - ) - ))->shouldBeCalled(); - - $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); - $this->handleRequest( - $startTls, - $token, - ); - } - - public function it_should_send_back_an_error_if_encryption_is_not_supported( - ServerQueue $queue, - TokenInterface $token - ): void { - $queue->isEncrypted()->willReturn(false); - - $queue->encrypt()->shouldNotBeCalled(); - $queue->sendMessage(new LdapMessageResponse( - 1, - new ExtendedResponse( - new LdapResult(ResultCode::PROTOCOL_ERROR), - ExtendedRequest::OID_START_TLS - ) - ))->shouldBeCalled(); - - $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); - $this->handleRequest( - $startTls, - $token, - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerUnbindHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerUnbindHandlerSpec.php deleted file mode 100644 index 0a359618..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerUnbindHandlerSpec.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Operation\Request\UnbindRequest; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerUnbindHandler; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ServerUnbindHandlerSpec extends ObjectBehavior -{ - public function let(ServerQueue $queue): void - { - $this->beConstructedWith($queue); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerUnbindHandler::class); - } - - public function it_should_handle_an_unbind_request( - ServerQueue $queue, - TokenInterface $token, - ): void { - $queue->close()->shouldBeCalled(); - $queue->sendMessage(Argument::any())->shouldNotBeCalled(); - - $unbind = new LdapMessageRequest(1, new UnbindRequest()); - $this->handleRequest($unbind, $token); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerSpec.php deleted file mode 100644 index 45c133de..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerSpec.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; - -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerWhoAmIHandler; -use FreeDSx\Ldap\Server\Token\AnonToken; -use FreeDSx\Ldap\Server\Token\BindToken; -use PhpSpec\ObjectBehavior; - -class ServerWhoAmIHandlerSpec extends ObjectBehavior -{ - public function let(ServerQueue $queue): void - { - $this->beConstructedWith($queue); - } - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerWhoAmIHandler::class); - } - - public function it_should_handle_a_who_am_i_when_there_is_a_token_with_a_DN_name(ServerQueue $queue): void - { - $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); - - $queue->sendMessage(new LdapMessageResponse( - 2, - new ExtendedResponse(new LdapResult(0), null, 'dn:cn=foo,dc=foo,dc=bar') - )); - - $this->handleRequest( - $request, - new BindToken('cn=foo,dc=foo,dc=bar', '12345'), - ); - } - - public function it_should_handle_a_who_am_i_when_there_is_a_token_with_a_non_DN_name(ServerQueue $queue): void - { - $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); - - $queue->sendMessage(new LdapMessageResponse( - 2, - new ExtendedResponse(new LdapResult(0), null, 'u:foo@bar.local') - ))->shouldBeCalled(); - - $this->handleRequest( - $request, - new BindToken('foo@bar.local', '12345'), - ); - } - - public function it_should_handle_a_who_am_i_when_there_is_no_token_yet(ServerQueue $queue): void - { - $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); - - $queue->getMessage()->willReturn(new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)), null); - $queue->sendMessage(new LdapMessageResponse( - 2, - new ExtendedResponse(new LdapResult(0), null, '') - ))->shouldBeCalled(); - - $this->handleRequest( - $request, - new AnonToken(), - ); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandlerSpec.php deleted file mode 100644 index b3686248..00000000 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandlerSpec.php +++ /dev/null @@ -1,261 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Protocol; - -use FreeDSx\Asn1\Exception\EncoderException; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Request\AnonBindRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\ExtendedResponse; -use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; -use FreeDSx\Ldap\Operation\Response\ModifyResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Protocol\Authenticator; -use FreeDSx\Ldap\Protocol\Factory\ServerProtocolHandlerFactory; -use FreeDSx\Ldap\Protocol\LdapMessageRequest; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Protocol\Queue\ServerQueue; -use FreeDSx\Ldap\Protocol\ServerAuthorization; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler; -use FreeDSx\Ldap\Server\Token\BindToken; -use FreeDSx\Ldap\ServerOptions; -use FreeDSx\Socket\Exception\ConnectionException; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use Psr\Log\LoggerInterface; - -class ServerProtocolHandlerSpec extends ObjectBehavior -{ - public function let( - ServerQueue $queue, - ServerProtocolHandlerFactory $protocolHandlerFactory, - LoggerInterface $logger, - Authenticator $authenticator, - ServerProtocolHandler\ServerProtocolHandlerInterface $protocolHandler - ): void { - $queue->close()->hasReturnVoid(); - $queue->isConnected()->willReturn(true); - $queue->isEncrypted()->willReturn(false); - $queue->sendMessage(Argument::any())->willReturn($queue); - - $protocolHandlerFactory - ->get(Argument::any(), Argument::any()) - ->willReturn($protocolHandler); - - $this->beConstructedWith( - $queue, - $protocolHandlerFactory, - new ServerAuthorization(new ServerOptions()), - $authenticator, - $logger, - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ServerProtocolHandler::class); - } - - public function it_should_enforce_anonymous_bind_requirements( - ServerQueue $queue, - Authenticator $authenticator, - ): void { - $queue->getMessage()->willReturn( - new LdapMessageRequest(1, new AnonBindRequest('foo')), - null - ); - - $queue->sendMessage(new LdapMessageResponse( - 1, - new BindResponse(new LdapResult( - ResultCode::AUTH_METHOD_UNSUPPORTED, - '', - 'The requested authentication type is not supported.' - )) - ))->shouldBeCalled(); - - $authenticator - ->bind(Argument::any()) - ->shouldNotBeCalled(); - - $this->handle(); - } - - public function it_should_not_allow_a_previous_message_ID_from_a_new_request( - ServerQueue $queue, - Authenticator $authenticator, - ServerProtocolHandler\ServerProtocolHandlerInterface $protocolHandler - ): void { - $queue->getMessage()->willReturn( - new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')), - new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)), - null - ); - - $authenticator->bind(Argument::any())->willReturn( - new BindToken('foo', 'bar') - ); - $protocolHandler->handleRequest(Argument::any(), Argument::any()) - ->shouldNotBeCalled(); - - $queue->sendMessage(new LdapMessageResponse( - 0, - new ExtendedResponse(new LdapResult( - ResultCode::PROTOCOL_ERROR, - '', - 'The message ID 1 is not valid.' - )) - ))->shouldBeCalled(); - - $this->handle(); - } - - public function it_should_enforce_authentication_requirements(ServerQueue $queue, ServerProtocolHandler\ServerProtocolHandlerInterface $protocolHandler): void - { - $queue->isConnected()->willReturn(true, false); - $queue->getMessage()->willReturn( - new LdapMessageRequest( - 1, - new ModifyDnRequest('cn=foo,dc=bar', 'cn=bar', true) - ), - null - ); - - $queue->sendMessage(new LdapMessageResponse( - 1, - new ModifyDnResponse( - ResultCode::INSUFFICIENT_ACCESS_RIGHTS, - 'cn=foo,dc=bar', - 'Authentication required.' - ) - ))->shouldBeCalled()->willReturn($queue); - - $protocolHandler->handleRequest(Argument::any(), Argument::any()) - ->shouldNotBeCalled(); - - $this->handle(); - } - - public function it_should_send_a_notice_of_disconnect_on_a_protocol_exception_from_the_message_queue(ServerQueue $queue): void - { - $queue->getMessage()->willThrow(new ProtocolException()); - - $queue->sendMessage(new LdapMessageResponse(0, new ExtendedResponse( - new LdapResult(ResultCode::PROTOCOL_ERROR, '', 'The message encoding is malformed.'), - ExtendedResponse::OID_NOTICE_OF_DISCONNECTION - )))->shouldBeCalled(); - - $this->handle(); - } - - public function it_should_handle_a_socket_exception_from_the_message_queue_and_end_normally(ServerQueue $queue): void - { - $queue->getMessage()->willThrow(new ConnectionException('Foo')); - - $this->handle(); - } - - public function it_should_send_a_notice_of_disconnect_on_an_encoder_exception_from_the_message_queue(ServerQueue $queue): void - { - $queue->getMessage()->willThrow(new EncoderException()); - - $queue->sendMessage(new LdapMessageResponse(0, new ExtendedResponse( - new LdapResult(ResultCode::PROTOCOL_ERROR, '', 'The message encoding is malformed.'), - ExtendedResponse::OID_NOTICE_OF_DISCONNECTION - )))->shouldBeCalled(); - - $this->handle(); - } - - public function it_should_not_allow_a_message_with_an_ID_of_zero(ServerQueue $queue): void - { - $queue->getMessage()->willReturn(new LdapMessageRequest(0, new ExtendedRequest(ExtendedRequest::OID_START_TLS)), null); - - $queue->sendMessage(new LdapMessageResponse(0, new ExtendedResponse(new LdapResult( - ResultCode::PROTOCOL_ERROR, - '', - 'The message ID 0 cannot be used in a client request.' - ))))->shouldBeCalled(); - - $this->handle(); - } - - public function it_should_send_a_bind_request_to_the_bind_request_handler( - ServerQueue $queue, - Authenticator $authenticator, - ServerProtocolHandler\ServerProtocolHandlerInterface $protocolHandler - ): void { - $queue->getMessage()->willReturn( - new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar')), - null - ); - - $authenticator->bind(Argument::any()) - ->shouldBeCalledOnce() - ->willReturn(new BindToken('foo@bar', 'bar')); - $protocolHandler->handleRequest(Argument::any(), Argument::any()) - ->shouldNotBeCalled(); - - $this->handle(); - } - - public function it_should_handle_operation_errors_thrown_from_the_request_handlers( - ServerQueue $queue, - Authenticator $authenticator, - ServerProtocolHandler\ServerProtocolHandlerInterface $protocolHandler, - ): void { - $queue->isConnected()->willReturn(true, false); - $queue->getMessage()->willReturn( - new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar')), - new LdapMessageRequest(2, new ModifyRequest('cn=foo,dc=bar')), - null - ); - - $authenticator->bind(Argument::any()) - ->willReturn(new BindToken('foo@bar', 'bar')); - - $protocolHandler->handleRequest(Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willThrow(new OperationException('Foo.', ResultCode::CONFIDENTIALITY_REQUIRED)); - - $queue->sendMessage(new LdapMessageResponse( - 2, - new ModifyResponse( - ResultCode::CONFIDENTIALITY_REQUIRED, - 'cn=foo,dc=bar', - 'Foo.' - ) - ))->shouldBeCalled(); - - $this->handle(); - } - - public function it_should_send_a_notice_of_disconnect_and_close_the_queue_on_shutdown(ServerQueue $queue): void - { - $queue->sendMessage(new LdapMessageResponse(0, new ExtendedResponse( - new LdapResult(ResultCode::UNAVAILABLE, '', 'The server is shutting down.'), - ExtendedResponse::OID_NOTICE_OF_DISCONNECTION - )))->shouldBeCalled(); - $queue->close() - ->shouldBeCalled(); - - $this->shutdown(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/DirSyncSpec.php b/tests/spec/FreeDSx/Ldap/Search/DirSyncSpec.php deleted file mode 100644 index 3acdce25..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/DirSyncSpec.php +++ /dev/null @@ -1,197 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; -use FreeDSx\Ldap\Control\Ad\DirSyncResponseControl; -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Search\DirSync; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class DirSyncSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - private LdapMessageResponse $initialResponse; - - private LdapMessageResponse $secondResponse; - - public function let(LdapClient $client): void - { - $this->initialResponse = $this::makeSearchResponseFromEntries( - entries: new Entries(), - messageId: 0, - controls: [new DirSyncResponseControl(1, 0, 'foo')], - ); - $this->secondResponse = $this::makeSearchResponseFromEntries( - entries: new Entries(), - messageId: 1, - controls: [new DirSyncResponseControl(0, 0, 'fbar')], - ); - - $client->send(Argument::that(function ($search) { - return $search->getFilter()->toString() == '(objectClass=*)'; - }), Argument::type(DirSyncRequestControl::class))->willReturn($this->initialResponse); - $client->readOrFail('', ['defaultNamingContext'])->willReturn(new Entry('', new Attribute('defaultNamingContext', 'dc=foo,dc=bar'))); - - $this->beConstructedWith($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(DirSync::class); - } - - public function it_should_set_the_naming_context($client): void - { - $client->send(Argument::that(function ($search) { - return $search->getBaseDn()->toString() == 'dc=foo'; - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useNamingContext('dc=foo')->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_set_the_filter($client): void - { - $client->send(Argument::that(function ($search) { - return $search->getFilter()->toString() == '(foo=bar)'; - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useFilter(Filters::equal('foo', 'bar'))->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_set_the_attributes_to_select($client): void - { - $client->send(Argument::that(function ($search) { - return $search->getAttributes()[0]->getName() === 'foo'; - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->selectAttributes('foo')->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_set_the_incremental_values_flag($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return $control->getFlags() !== DirSyncRequestControl::FLAG_INCREMENTAL_VALUES; - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useIncrementalValues(false)->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_object_security_flag($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getFlags() & DirSyncRequestControl::FLAG_OBJECT_SECURITY); - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useObjectSecurity()->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_set_ancestor_first_order($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getFlags() & DirSyncRequestControl::FLAG_ANCESTORS_FIRST_ORDER); - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useAncestorFirstOrder()->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_set_the_cookie($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getCookie() === 'foo'); - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->useCookie('foo')->shouldReturnAnInstanceOf(DirSync::class); - $this->getChanges(); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie()->shouldBeEqualTo(''); - $this->getChanges(); - $this->getCookie()->shouldBeEqualTo('foo'); - } - - public function it_should_set_the_cookie_from_the_response_after_the_initial_query($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getCookie() === ''); - }), Argument::any())->shouldBeCalled()->willReturn($this->initialResponse); - - $this->getChanges(); - - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getCookie() === 'foo'); - }), Argument::any())->shouldBeCalled()->willReturn($this->secondResponse); - - $this->getChanges(); - } - - public function it_should_check_the_root_dse_for_the_default_naming_context($client): void - { - $client->readOrFail(Argument::any(), Argument::any())->shouldBeCalledOnce(); - - $this->getChanges(); - $this->getChanges(); - } - - public function it_should_not_check_the_root_dse_for_the_default_naming_context_if_it_was_provided($client): void - { - $this->useNamingContext('dc=foo'); - $client->readOrFail(Argument::any(), Argument::any())->shouldNotBeCalled(); - - $this->getChanges(); - } - - public function it_should_return_false_for_changes_if_no_queries_have_been_made_yet(): void - { - $this->hasChanges()->shouldBeEqualTo(false); - } - - public function it_should_return_true_for_changes_if_the_dir_sync_control_indicates_there_are(): void - { - $this->getChanges(); - $this->hasChanges()->shouldBeEqualTo(true); - } - - public function it_should_return_false_for_changes_if_the_dir_sync_control_indicates_there_are_none_left($client): void - { - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getCookie() === ''); - }), Argument::any())->willReturn($this->initialResponse); - - $this->getChanges(); - - $client->send(Argument::any(), Argument::that(function ($control) { - return ($control->getCookie() === 'foo'); - }), Argument::any())->willReturn($this->secondResponse); - - $this->getChanges(); - $this->hasChanges()->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/AndFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/AndFilterSpec.php deleted file mode 100644 index b181a371..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/AndFilterSpec.php +++ /dev/null @@ -1,143 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Search\Filter\AndFilter; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filter\FilterContainerInterface; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\SubstringFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class AndFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Filters::equal('foo', 'bar'), Filters::gte('foo', '2')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AndFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_implement_filter_container_interface(): void - { - $this->shouldImplement(FilterContainerInterface::class); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_implement_iterator_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_get_the_filters_it_contains(): void - { - $this->get()->shouldBeLike([ - Filters::equal('foo', 'bar'), - Filters::gte('foo', '2') - ]); - } - - public function it_should_set_the_filters(): void - { - $this->set(Filters::equal('bar', 'foo')); - - $this->get()->shouldBeLike([Filters::equal('bar', 'foo')]); - } - - public function it_should_add_to_the_filters(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->add($filter); - $this->get()->shouldContain($filter); - } - - public function it_should_remove_from_the_filters(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->add($filter); - $this->get()->shouldContain($filter); - - $this->remove($filter); - $this->get()->shouldNotContain($filter); - } - - public function it_should_check_if_a_filter_exists(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->has($filter)->shouldBeEqualTo(false); - $this->add($filter); - $this->has($filter)->shouldBeEqualTo(true); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(0, Asn1::setOf( - Filters::equal('foo', 'bar')->toAsn1(), - Filters::gte('foo', '2')->toAsn1() - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $and = new AndFilter(new EqualityFilter('foo', 'bar'), new SubstringFilter('bar', 'foo')); - - $this::fromAsn1($and->toAsn1())->shouldBeLike($and); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(&(foo=bar)(foo>=2))'); - } - - public function it_should_get_the_string_filter_representation_with_nested_containers(): void - { - $this->add(Filters::or(Filters::equal('foo', 'bar'))); - - $this->toString()->shouldBeEqualTo('(&(foo=bar)(foo>=2)(|(foo=bar)))'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(&(foo=bar)(foo>=2))'); - } - - public function it_should_get_the_count(): void - { - $this->count()->shouldBeEqualTo(2); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/ApproximateFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/ApproximateFilterSpec.php deleted file mode 100644 index 1ee0a350..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/ApproximateFilterSpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\ApproximateFilter; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use PhpSpec\ObjectBehavior; - -class ApproximateFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ApproximateFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(8, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new ApproximateFilter('foo', 'bar'))->toAsn1())->shouldBeLike(new ApproximateFilter('foo', 'bar')); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo~=bar)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo~=bar)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', ')(bar=foo'); - $this->toString()->shouldBeEqualTo('(foo~=\29\28bar=foo)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/EqualityFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/EqualityFilterSpec.php deleted file mode 100644 index ec9897c3..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/EqualityFilterSpec.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use PhpSpec\ObjectBehavior; - -class EqualityFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(EqualityFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('foo'); - $this->setAttribute('foobar')->getAttribute()->shouldBeEqualTo('foobar'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(3, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new EqualityFilter('foo', 'bar'))->toAsn1())->shouldBeLike(new EqualityFilter('foo', 'bar')); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo=bar)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo=bar)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', ')(bar=foo'); - $this->toString()->shouldBeEqualTo('(foo=\29\28bar=foo)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/GreaterThanOrEqualFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/GreaterThanOrEqualFilterSpec.php deleted file mode 100644 index d4740c9e..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/GreaterThanOrEqualFilterSpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; -use PhpSpec\ObjectBehavior; - -class GreaterThanOrEqualFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(GreaterThanOrEqualFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(5, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new GreaterThanOrEqualFilter('foo', 'bar'))->toAsn1())->shouldBeLike(new GreaterThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo>=bar)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo>=bar)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', ')(bar=*5'); - $this->toString()->shouldBeEqualTo('(foo>=\29\28bar=\2a5)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/LessThanOrEqualFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/LessThanOrEqualFilterSpec.php deleted file mode 100644 index dfb01a31..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/LessThanOrEqualFilterSpec.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\LessThanOrEqualFilter; -use PhpSpec\ObjectBehavior; - -class LessThanOrEqualFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(LessThanOrEqualFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('bar'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(6, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::octetString('bar') - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new LessThanOrEqualFilter('foo', 'bar'))->toAsn1())->shouldBeLike(new LessThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo<=bar)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo<=bar)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', ')(bar=*5'); - $this->toString()->shouldBeEqualTo('(foo<=\29\28bar=\2a5)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/MatchingRuleFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/MatchingRuleFilterSpec.php deleted file mode 100644 index 40bfcacf..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/MatchingRuleFilterSpec.php +++ /dev/null @@ -1,137 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\MatchingRuleFilter; -use PhpSpec\ObjectBehavior; - -class MatchingRuleFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar', 'foobar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(MatchingRuleFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_value(): void - { - $this->getValue()->shouldBeEqualTo('foobar'); - } - - public function it_should_not_use_dn_attributes_by_default(): void - { - $this->getUseDnAttributes()->shouldBeEqualTo(false); - } - - public function it_should_set_whether_to_use_dn_attributes_by_default(): void - { - $this->setUseDnAttributes(true); - $this->getUseDnAttributes()->shouldBeEqualTo(true); - } - - public function it_should_set_the_matching_rule(): void - { - $this->setMatchingRule('bleep'); - $this->getMatchingRule()->shouldBeEqualTo('bleep'); - } - - public function it_should_be_able_to_set_the_attribute_to_null(): void - { - $this->setAttribute(null); - $this->getAttribute()->shouldBeNull(); - } - - public function it_should_be_able_to_set_the_matching_rule_to_null(): void - { - $this->setMatchingRule(null); - $this->getMatchingRule()->shouldBeNull(); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(9, Asn1::sequence( - Asn1::context(1, Asn1::octetString('foo')), - Asn1::context(2, Asn1::octetString('bar')), - Asn1::context(3, Asn1::octetString('foobar')), - Asn1::context(4, Asn1::boolean(false)) - ))); - - $this->setUseDnAttributes(true); - $this->toAsn1()->shouldBeLike(Asn1::context(9, Asn1::sequence( - Asn1::context(1, Asn1::octetString('foo')), - Asn1::context(2, Asn1::octetString('bar')), - Asn1::context(3, Asn1::octetString('foobar')), - Asn1::context(4, Asn1::boolean(true)) - ))); - - $this->setMatchingRule(null); - $this->toAsn1()->shouldBeLike(Asn1::context(9, Asn1::sequence( - Asn1::context(2, Asn1::octetString('bar')), - Asn1::context(3, Asn1::octetString('foobar')), - Asn1::context(4, Asn1::boolean(true)) - ))); - - $this->setAttribute(null); - $this->toAsn1()->shouldBeLike(Asn1::context(9, Asn1::sequence( - Asn1::context(3, Asn1::octetString('foobar')), - Asn1::context(4, Asn1::boolean(true)) - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $rule = new MatchingRuleFilter('foo', 'foo', 'bar', true); - - $this->fromAsn1($rule->toAsn1())->shouldBeLike($rule); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(bar:foo:=foobar)'); - } - - public function it_should_get_the_filter_representation_with_a_dn_match(): void - { - $this->setUseDnAttributes(true); - - $this->toString()->shouldBeEqualTo('(bar:foo:dn:=foobar)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(bar:foo:=foobar)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', 'bar', ')(bar=*5'); - $this->toString()->shouldBeEqualTo('(bar:foo:=\29\28bar=\2a5)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/NotFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/NotFilterSpec.php deleted file mode 100644 index ab23f4f1..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/NotFilterSpec.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filter\NotFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class NotFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Filters::equal('foo', 'bar')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(NotFilter::class); - } - - public function it_should_set_the_filter(): void - { - $this->set(Filters::gte('foobar', 'foo')); - $this->get()->shouldBeLike(Filters::gte('foobar', 'foo')); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(2, Asn1::sequence(Filters::equal('foo', 'bar')->toAsn1()))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new NotFilter(Filters::equal('foo', 'bar')))->toAsn1())->shouldBeLike( - new NotFilter(new EqualityFilter('foo', 'bar')) - ); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(!(foo=bar))'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(!(foo=bar))'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith(Filters::equal('foo', '*bar')); - $this->toString()->shouldBeEqualTo('(!(foo=\2abar))'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/OrFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/OrFilterSpec.php deleted file mode 100644 index d3e75eb9..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/OrFilterSpec.php +++ /dev/null @@ -1,143 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filter\FilterContainerInterface; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\OrFilter; -use FreeDSx\Ldap\Search\Filter\SubstringFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class OrFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(Filters::equal('foo', 'bar'), Filters::gte('foo', '2')); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(OrFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_implement_filter_container_interface(): void - { - $this->shouldImplement(FilterContainerInterface::class); - } - - public function it_should_implement_countable(): void - { - $this->shouldImplement('\Countable'); - } - - public function it_should_implement_iterator_aggregate(): void - { - $this->shouldImplement('\IteratorAggregate'); - } - - public function it_should_get_the_filters_it_contains(): void - { - $this->get()->shouldBeLike([ - Filters::equal('foo', 'bar'), - Filters::gte('foo', '2') - ]); - } - - public function it_should_set_the_filters(): void - { - $this->set(Filters::equal('bar', 'foo')); - - $this->get()->shouldBeLike([Filters::equal('bar', 'foo')]); - } - - public function it_should_add_to_the_filters(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->add($filter); - $this->get()->shouldContain($filter); - } - - public function it_should_remove_from_the_filters(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->add($filter); - $this->get()->shouldContain($filter); - - $this->remove($filter); - $this->get()->shouldNotContain($filter); - } - - public function it_should_check_if_a_filter_exists(): void - { - $filter = Filters::equal('foobar', 'foobar'); - - $this->has($filter)->shouldBeEqualTo(false); - $this->add($filter); - $this->has($filter)->shouldBeEqualTo(true); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(1, Asn1::setOf( - Filters::equal('foo', 'bar')->toAsn1(), - Filters::gte('foo', '2')->toAsn1() - ))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $or = new OrFilter(new EqualityFilter('foo', 'bar'), new SubstringFilter('bar', 'foo')); - - $this::fromAsn1($or->toAsn1())->shouldBeLike($or); - } - - public function it_should_not_be_constructed_from_invalid_asn1(): void - { - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::sequence()]); - $this->shouldThrow(ProtocolException::class)->during('fromAsn1', [Asn1::octetString('foo')]); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(|(foo=bar)(foo>=2))'); - } - - public function it_should_get_the_string_filter_representation_with_nested_containers(): void - { - $this->add(Filters::and(Filters::equal('foo', 'bar'))); - - $this->toString()->shouldBeEqualTo('(|(foo=bar)(foo>=2)(&(foo=bar)))'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(|(foo=bar)(foo>=2))'); - } - - public function it_should_get_the_count(): void - { - $this->count()->shouldBeEqualTo(2); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/PresentFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/PresentFilterSpec.php deleted file mode 100644 index 474300fc..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/PresentFilterSpec.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Search\Filter\FilterInterface; -use FreeDSx\Ldap\Search\Filter\PresentFilter; -use PhpSpec\ObjectBehavior; - -class PresentFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(PresentFilter::class); - } - - public function it_should_implement_fiter_interface(): void - { - $this->shouldImplement(FilterInterface::class); - } - - public function it_should_get_the_attribute_name(): void - { - $this->getAttribute()->shouldBeEqualTo('foo'); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(7, Asn1::octetString('foo'))); - } - - public function it_should_be_constructed_from_asn1(): void - { - $this::fromAsn1((new PresentFilter('foo'))->toAsn1())->shouldBeLike(new PresentFilter('foo')); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo=*)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo=*)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Filter/SubstringFilterSpec.php b/tests/spec/FreeDSx/Ldap/Search/Filter/SubstringFilterSpec.php deleted file mode 100644 index bb56b756..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Filter/SubstringFilterSpec.php +++ /dev/null @@ -1,172 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Filter; - -use FreeDSx\Asn1\Asn1; -use FreeDSx\Ldap\Exception\RuntimeException; -use FreeDSx\Ldap\Search\Filter\SubstringFilter; -use PhpSpec\ObjectBehavior; - -class SubstringFilterSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'f', 'o', 'o', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SubstringFilter::class); - } - - public function it_should_get_the_starts_with_value(): void - { - $this->getStartsWith()->shouldBeEqualTo('f'); - $this->setStartsWith(null)->getStartsWith()->shouldBeEqualTo(null); - } - - public function it_should_get_the_ends_with_value(): void - { - $this->getEndsWith()->shouldBeEqualTo('o'); - $this->setEndsWith(null)->getEndsWith()->shouldBeEqualTo(null); - } - - public function it_should_get_the_contains_value(): void - { - $this->getContains()->shouldBeEqualTo(['o', 'bar']); - $this->setContains(...[])->getContains()->shouldBeEqualTo([]); - } - - public function it_should_generate_correct_asn1(): void - { - $this->toAsn1()->shouldBeLike(Asn1::context(4, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequenceOf( - Asn1::context(0, Asn1::octetString('f')), - Asn1::context(1, Asn1::octetString('o')), - Asn1::context(1, Asn1::octetString('bar')), - Asn1::context(2, Asn1::octetString('o')) - ) - ))); - - $this->setStartsWith(null); - $this->toAsn1()->shouldBeLike(Asn1::context(4, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequenceOf( - Asn1::context(1, Asn1::octetString('o')), - Asn1::context(1, Asn1::octetString('bar')), - Asn1::context(2, Asn1::octetString('o')) - ) - ))); - - $this->setEndsWith(null); - $this->toAsn1()->shouldBeLike(Asn1::context(4, Asn1::sequence( - Asn1::octetString('foo'), - Asn1::sequenceOf( - Asn1::context(1, Asn1::octetString('o')), - Asn1::context(1, Asn1::octetString('bar')) - ) - ))); - } - - public function it_should_error_if_no_starts_with_ends_with_or_contains_was_supplied(): void - { - $this->setStartsWith(null); - $this->setEndsWith(null); - $this->setContains(...[]); - - $this->shouldThrow(RuntimeException::class)->duringToAsn1(); - } - - public function it_should_be_constructed_from_asn1(): void - { - $substring = new SubstringFilter('foo', 'foo', 'bar', 'foobar', 'wee'); - $this::fromAsn1($substring->toAsn1())->shouldBeLike($substring); - - $substring = new SubstringFilter('foo', 'foo', 'bar'); - $this::fromAsn1($substring->toAsn1())->shouldBeLike($substring); - - $substring = new SubstringFilter('foo', 'foo'); - $this::fromAsn1($substring->toAsn1())->shouldBeLike($substring); - - $substring = new SubstringFilter('foo', null, 'foo'); - $this::fromAsn1($substring->toAsn1())->shouldBeLike($substring); - $substring = new SubstringFilter('foo', null, null, 'foo', 'bar'); - $this::fromAsn1($substring->toAsn1())->shouldBeLike($substring); - } - - public function it_should_get_the_string_filter_representation(): void - { - $this->toString()->shouldBeEqualTo('(foo=f*o*bar*o)'); - } - - public function it_should_get_the_filter_representation_with_a_starts_with(): void - { - $this->setStartsWith('bar'); - $this->setEndsWith(null); - $this->setContains(...[]); - - $this->toString()->shouldBeEqualTo('(foo=bar*)'); - } - - public function it_should_get_the_filter_representation_with_an_ends_with(): void - { - $this->setStartsWith(null); - $this->setEndsWith('bar'); - $this->setContains(...[]); - - $this->toString()->shouldBeEqualTo('(foo=*bar)'); - } - - public function it_should_get_a_filter_representation_with_a_start_and_end(): void - { - $this->setStartsWith('foo'); - $this->setEndsWith('bar'); - $this->setContains(...[]); - - $this->toString()->shouldBeEqualTo('(foo=foo*bar)'); - } - - public function it_should_get_a_filter_representation_with_a_start_and_contains(): void - { - $this->setStartsWith('foo'); - $this->setEndsWith(null); - $this->setContains('b', 'a', 'r'); - - $this->toString()->shouldBeEqualTo('(foo=foo*b*a*r*)'); - } - - public function it_should_get_a_filter_representation_with_an_end_and_contains(): void - { - $this->setStartsWith(null); - $this->setEndsWith('foo'); - $this->setContains('b', 'a', 'r'); - - $this->toString()->shouldBeEqualTo('(foo=*b*a*r*foo)'); - } - - public function it_should_have_a_filter_as_a_toString_representation(): void - { - $this->__toString()->shouldBeEqualTo('(foo=f*o*bar*o)'); - } - - public function it_should_escape_values_on_the_string_representation(): void - { - $this->beConstructedWith('foo', ')(bar=*5'); - $this->setStartsWith('*'); - $this->setEndsWith(')(o=*'); - $this->setContains('fo*'); - $this->toString()->shouldBeEqualTo('(foo=\2a*fo\2a*\29\28o=\2a)'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/FilterParserSpec.php b/tests/spec/FreeDSx/Ldap/Search/FilterParserSpec.php deleted file mode 100644 index 945c4a84..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/FilterParserSpec.php +++ /dev/null @@ -1,202 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Exception\FilterParseException; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class FilterParserSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_should_parse_an_equals_filter(): void - { - $this::parse('(foo=bar)')->shouldBeLike(Filters::equal('foo', 'bar')); - } - - public function it_should_parse_an_approximate_filter(): void - { - $this::parse('(foo~=bar)')->shouldBeLike(Filters::approximate('foo', 'bar')); - } - - public function it_should_parse_a_greater_than_or_equals_filter(): void - { - $this::parse('(foo>=1)')->shouldBeLike(Filters::gte('foo', '1')); - } - - public function it_should_parse_a_less_than_or_equals_filter(): void - { - $this::parse('(foo<=1)')->shouldBeLike(Filters::lte('foo', '1')); - } - - public function it_should_parse_a_present_filter(): void - { - $this->parse('(foo=*)')->shouldBeLike(Filters::present('foo')); - } - - public function it_should_parse_a_substring_starts_with_filter(): void - { - $this::parse('(foo=bar*)')->shouldBeLike(Filters::startsWith('foo', 'bar')); - } - - public function it_should_parse_a_substring_ends_with_filter(): void - { - $this::parse('(foo=*bar)')->shouldBeLike(Filters::endsWith('foo', 'bar')); - } - - public function it_should_parse_a_substring_contains_filter(): void - { - $this::parse('(foo=*bar*)')->shouldBeLike(Filters::contains('foo', 'bar')); - } - - public function it_should_parse_a_mixed_substring_filter(): void - { - $this::parse('(foo=this*is*a*filter)')->shouldBeLike(Filters::substring('foo', 'this', 'filter', 'is', 'a')); - } - - public function it_should_parse_an_extensible_match_filter_with_an_attribute_only(): void - { - $this::parse('givenName:=Chad')->shouldBeLike(Filters::extensible('givenName', 'Chad', null)); - } - - public function it_should_parse_an_extensible_match_filter_with_a_dn_and_matching_rule(): void - { - $this::parse(':dn:1.2.3:=Chad')->shouldBeLike(Filters::extensible(null, 'Chad', null)->setUseDnAttributes(true)->setMatchingRule('1.2.3')); - } - - public function it_should_parse_an_extensible_match_filter_with_a_dn_and_attribute_type(): void - { - $this::parse('o:dn:=Chad')->shouldBeLike(Filters::extensible('o', 'Chad', null)->setUseDnAttributes(true)); - } - - public function it_should_error_on_invalid_matching_rule_syntax(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', [':dn::=Chad']); - $this->shouldThrow(FilterParseException::class)->during('parse', ['&^]:=Chad']); - $this->shouldThrow(FilterParseException::class)->during('parse', ['?:=Chad']); - } - - public function it_should_error_when_parsing_an_extensible_match_with_no_matching_rule_or_type(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', [':dn:=Chad']); - } - - public function it_should_parse_a_simple_comparison_filter_without_parenthesis(): void - { - $this::parse('foo=bar')->shouldBeLike(Filters::equal('foo', 'bar')); - } - - public function it_should_parse_an_and_filter(): void - { - $this::parse('(&(foo=bar)(bar~=foo))')->shouldBeLike(Filters::and( - Filters::equal('foo', 'bar'), - Filters::approximate('bar', 'foo') - )); - } - - public function it_should_parse_an_or_filter(): void - { - $this::parse('(|(foo=bar)(bar~=foo))')->shouldBeLike(Filters::or( - Filters::equal('foo', 'bar'), - Filters::approximate('bar', 'foo') - )); - } - - public function it_should_parse_filter_containers_with_nested_containers(): void - { - $this::parse('(&(|(foo=*bar)(bar<=5))(foo~=bar))')->shouldBeLike(Filters::and( - Filters::or(Filters::endsWith('foo', 'bar'), Filters::lte('bar', '5')), - Filters::approximate('foo', 'bar') - )); - $this::parse('(&(|(foo=*bar)(bar<=5))(foo~=bar)(&(foo=bar)(|(bar=*)(cn=Chad))))')->shouldBeLike(Filters::and( - Filters::or(Filters::endsWith('foo', 'bar'), Filters::lte('bar', '5')), - Filters::approximate('foo', 'bar'), - Filters::and( - Filters::equal('foo', 'bar'), - Filters::or( - Filters::present('bar'), - Filters::equal('cn', 'Chad') - ) - ) - )); - } - - public function it_should_parse_a_not_filter(): void - { - $this::parse('(!(foo=bar))')->shouldBeLike(Filters::not(Filters::equal('foo', 'bar'))); - } - - public function it_should_not_allow_a_not_filter_to_contain_more_than_one_filter(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(!(&(foo=bar)))']); - } - - public function it_should_decode_hex_encoded_values(): void - { - $this::parse('o=Parens R Us \28for all your parenthetical needs\29')->shouldBeLike(Filters::equal('o', 'Parens R Us (for all your parenthetical needs)')); - $this::parse('(cn=*\2A*)')->shouldBeLike(Filters::contains('cn', '*')); - $this::parse('(bin=\00\00\00\04)')->shouldBeLike(Filters::equal('bin', "\x00\x00\x00\x04")); - $this::parse('(sn=Lu\c4\8di\c4\87)')->shouldBeLike(Filters::equal('sn', 'Lučić')); - } - - public function it_should_error_on_nested_unmatched_parenthesis(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(&(foo=bar)(|(&(foo=bar)))']); - } - - public function it_should_error_on_an_unmatched_parenthesis(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['foo=bar)']); - } - - public function it_should_error_on_unrecognized_values_at_the_end_of_the_filter(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(foo=bar)(foo)']); - $this->shouldThrow(FilterParseException::class)->during('parse', ['(foo=bar)foo']); - } - - public function it_should_error_on_empty_values(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(foo=)']); - } - - public function it_should_error_on_unrecognized_operators(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(foo>4)']); - } - - public function it_should_error_on_an_empty_attribute_in_the_filter(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(=bar)']); - } - - public function it_should_error_on_empty_containers(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(&)']); - } - - public function it_should_error_on_an_empty_filter(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['']); - } - - public function it_should_error_on_a_malformed_container(): void - { - $this->shouldThrow(FilterParseException::class)->during('parse', ['(&']); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/FiltersSpec.php b/tests/spec/FreeDSx/Ldap/Search/FiltersSpec.php deleted file mode 100644 index e7d25e48..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/FiltersSpec.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Search\Filter\AndFilter; -use FreeDSx\Ldap\Search\Filter\ApproximateFilter; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; -use FreeDSx\Ldap\Search\Filter\LessThanOrEqualFilter; -use FreeDSx\Ldap\Search\Filter\MatchingRuleFilter; -use FreeDSx\Ldap\Search\Filter\NotFilter; -use FreeDSx\Ldap\Search\Filter\OrFilter; -use FreeDSx\Ldap\Search\Filter\PresentFilter; -use FreeDSx\Ldap\Search\Filter\SubstringFilter; -use FreeDSx\Ldap\Search\Filters; -use PhpSpec\ObjectBehavior; - -class FiltersSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(Filters::class); - } - - public function it_should_create_an_and_filter(): void - { - $this::and(new EqualityFilter('foo', 'bar'), new EqualityFilter('bar', 'foo'))->shouldBeLike(new AndFilter( - new EqualityFilter('foo', 'bar'), - new EqualityFilter('bar', 'foo') - )); - } - - public function it_should_create_an_or_filter(): void - { - $this::or(new EqualityFilter('foo', 'bar'), new EqualityFilter('bar', 'foo'))->shouldBeLike(new OrFilter( - new EqualityFilter('foo', 'bar'), - new EqualityFilter('bar', 'foo') - )); - } - - public function it_should_create_an_equality_filter(): void - { - $this::equal('foo', 'bar')->shouldBeLike(new EqualityFilter('foo', 'bar')); - } - - public function it_should_create_an_approximate_filter(): void - { - $this::approximate('foo', 'bar')->shouldBeLike(new ApproximateFilter('foo', 'bar')); - } - - public function it_should_create_a_greater_than_or_equal_filter(): void - { - $this::greaterThanOrEqual('foo', 'bar')->shouldBeLike(new GreaterThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_have_gte_as_an_alias(): void - { - $this::gte('foo', 'bar')->shouldBeLike(new GreaterThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_create_a_less_than_or_equal_filter(): void - { - $this::lessThanOrEqual('foo', 'bar')->shouldBeLike(new LessThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_have_lte_as_an_alias(): void - { - $this::lte('foo', 'bar')->shouldBeLike(new LessThanOrEqualFilter('foo', 'bar')); - } - - public function it_should_create_a_substring_filter(): void - { - $this::substring('foo', 'fo', 'ob', 'ar')->shouldBeLike(new SubstringFilter('foo', 'fo', 'ob', 'ar')); - } - - public function it_should_create_a_substring_starts_with_filter(): void - { - $this::startsWith('foo', 'bar')->shouldBeLike(new SubstringFilter('foo', 'bar')); - } - - public function it_should_create_a_substring_ends_with_filter(): void - { - $this::endsWith('foo', 'bar')->shouldBeLike(new SubstringFilter('foo', null, 'bar')); - } - - public function it_should_create_a_substring_contains_filter(): void - { - $this::contains('foo', 'bar', 'foo')->shouldBeLike(new SubstringFilter('foo', null, null, 'bar', 'foo')); - } - - public function it_should_create_an_extensible_filter(): void - { - $this::extensible('foo', 'bar', 'foobar')->shouldBeLike(new MatchingRuleFilter('foobar', 'foo', 'bar')); - } - - public function it_should_create_a_not_filter(): void - { - $this::not(new EqualityFilter('foo', 'bar'))->shouldBeLike(new NotFilter(new EqualityFilter('foo', 'bar'))); - } - - public function it_should_create_a_filter_from_a_raw_string_filter(): void - { - $this::raw('(foo=*)')->shouldBeLike(new PresentFilter('foo')); - $this::raw('foo=bar')->shouldBeLike(new EqualityFilter('foo', 'bar')); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/PagingSpec.php b/tests/spec/FreeDSx/Ldap/Search/PagingSpec.php deleted file mode 100644 index ecf9b12e..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/PagingSpec.php +++ /dev/null @@ -1,158 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Paging; -use PhpSpec\ObjectBehavior; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class PagingSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let(LdapClient $client, SearchRequest $search): void - { - $client->sendAndReceive( - $search, - new PagingControl( - 1000, - '' - ) - ) - ->willReturn( - $this::makeSearchResponseFromEntries( - entries: new Entries( - Entry::create('foo'), - Entry::create('bar'), - ), - controls: [new PagingControl(100, 'foo')] - ) - ); - - $this->beConstructedWith( - $client, - $search, - 1000 - ); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Paging::class); - } - - public function it_should_check_whether_paging_has_entries_left_and_return_true_on_start(): void - { - $this->hasEntries()->shouldBeEqualTo(true); - } - - public function it_should_return_true_for_entries_when_the_cookie_is_not_empty(): void - { - $this->getEntries(); - $this->hasEntries()->shouldBeEqualTo(true); - } - - public function it_should_return_false_for_entries_when_the_cookie_is_empty($client, $search): void - { - $client->sendAndReceive($search, new PagingControl(100, ''))->willReturn( - $this::makeSearchResponseFromEntries( - controls: [new PagingControl(0, '')] - ) - ); - $this->getEntries(100); - - $this->hasEntries()->shouldBeEqualTo(false); - } - - public function it_should_abort_a_paging_operation_if_end_is_called($client, $search): void - { - $client->sendAndReceive($search, new PagingControl(0, 'foo'))->shouldBeCalled()->willReturn( - $this::makeSearchResponseFromEntries( - controls: [new PagingControl(0, 'foo')] - ) - ); - - $this->getEntries(); - $this->end(); - } - - public function it_should_get_the_size_estimate_from_the_server_response(): void - { - $this->sizeEstimate()->shouldBe(null); - $this->getEntries(); - $this->sizeEstimate()->shouldBeEqualTo(100); - } - - public function it_should_get_the_entries_from_the_response(): void - { - $this->getEntries()->shouldBeLike(new Entries(Entry::create('foo'), Entry::create('bar'))); - } - - public function it_should_get_marked_as_ended_if_not_critical_and_no_control_is_returned( - LdapClient $newClient, - SearchRequest $searchRequest - ): void { - $newClient->sendAndReceive( - $searchRequest, - (new PagingControl(1000, ''))->setCriticality(false) - )->willReturn( - $this::makeSearchResponseFromEntries( - entries: new Entries( - Entry::create('foo'), - Entry::create('bar') - ), - ) - ); - $this->beConstructedWith( - $newClient, - $searchRequest, - 1000 - ); - - $this->getEntries()->shouldBeLike(new Entries(Entry::create('foo'), Entry::create('bar'))); - $this->hasEntries()->shouldBeEqualTo(false); - } - - - public function it_should_throw_an_exception_if_marked_as_critical_and_no_control_is_received( - LdapClient $newClient, - SearchRequest $searchRequest - ): void { - $newClient->sendAndReceive( - $searchRequest, - (new PagingControl(1000, ''))->setCriticality(true) - )->willReturn( - $this::makeSearchResponseFromEntries( - entries: new Entries( - Entry::create('foo'), - Entry::create('bar') - ) - ) - ); - $this->beConstructedWith( - $newClient, - $searchRequest, - 1000 - ); - - $this->isCritical(); - $this->shouldThrow(new ProtocolException('Expected a paging control, but received none.'))->during('getEntries'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/RangeRetrievalSpec.php b/tests/spec/FreeDSx/Ldap/Search/RangeRetrievalSpec.php deleted file mode 100644 index 32a96de0..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/RangeRetrievalSpec.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Entry\Attribute; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Search\RangeRetrieval; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class RangeRetrievalSpec extends ObjectBehavior -{ - public function let(LdapClient $client): void - { - $this->beConstructedWith($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(RangeRetrieval::class); - } - - public function it_should_get_a_specific_ranged_attribute_from_an_entry_if_it_exists(): void - { - $this->getRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => []]), 'member')->getName()->shouldBeEqualTo('member'); - } - - public function it_should_return_null_on_a_request_for_a_specific_ranged_attribute_that_does_not_exist(): void - { - $this->getRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => []]), 'bar')->shouldBeNull(); - } - - public function it_should_get_all_ranged_attributes_for_an_entry_as_an_array(): void - { - $this->getAllRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => [], 'bar;range=0-1000' => []]))->shouldHaveCount(2); - } - - public function it_should_return_whether_an_entry_has_an_ranged_attributes(): void - { - $this->hasRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => []]))->shouldBeEqualTo(true); - $this->hasRanged(Entry::create('dc=foo', ['member' => [], 'foo' => []]))->shouldBeEqualTo(false); - } - - public function it_should_return_whether_an_entry_has_a_specific_ranged_attribute(): void - { - $this->hasRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => []]), 'member')->shouldBeEqualTo(true); - $this->hasRanged(Entry::create('dc=foo', ['member;range=0-1500' => [], 'foo' => []]), 'foo')->shouldBeEqualTo(false); - } - - public function it_should_check_if_a_ranged_attribute_has_more_values_to_retrieve(): void - { - $this->hasMoreValues(new Attribute('member'))->shouldBeEqualTo(false); - $this->hasMoreValues(new Attribute('member;range=0-*'))->shouldBeEqualTo(false); - } - - public function it_should_get_more_values_for_a_ranged_attribute($client): void - { - $attrResult = new Attribute('member;range=1501-2000'); - $entry = new Entry('dc=foo', $attrResult); - - $client->readOrFail('dc=foo', Argument::that(function ($attr) { - return $attr[0]->getOptions()->first()->getLowRange() == '1501' && $attr[0]->getOptions()->first()->getHighRange() == '*'; - }))->shouldBeCalled()->willReturn($entry); - - $this->getMoreValues('dc=foo', new Attribute('member;range=0-1500'))->shouldBeEqualTo($attrResult); - } - - public function it_should_use_a_specific_ranged_amount_of_values_to_retrieve_if_specified($client): void - { - $attrResult = new Attribute('member;range=1501-1600'); - $entry = new Entry('dc=foo', $attrResult); - - $client->readOrFail('dc=foo', Argument::that(function ($attr) { - return $attr[0]->getOptions()->first()->getLowRange() == '1501' && $attr[0]->getOptions()->first()->getHighRange() == '1600'; - }))->shouldBeCalled()->willReturn($entry); - - $this->getMoreValues('dc=foo', new Attribute('member;range=0-1500'), 100)->shouldBeEqualTo($attrResult); - } - - public function it_should_retrieve_all_values_for_a_specific_attribute($client): void - { - $entry1 = new Entry('dc=foo', new Attribute('member;range=0-1500', 'foo')); - $entry2 = new Entry('dc=foo', new Attribute('member;range=1501-*', 'bar')); - - $client->readOrFail('dc=foo', Argument::that(function ($attr) { - return $attr[0]->getOptions()->first()->getLowRange() == '0' && $attr[0]->getOptions()->first()->getHighRange() == '*'; - }))->shouldBeCalled()->willReturn($entry1); - $client->readOrFail('dc=foo', Argument::that(function ($attr) { - return $attr[0]->getOptions()->first()->getLowRange() == '1501' && $attr[0]->getOptions()->first()->getHighRange() == '*'; - }))->shouldBeCalled()->willReturn($entry2); - - $this->getAllValues('dc=foo', 'member')->getValues()->shouldBeEqualTo(['foo', 'bar']); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Result/EntryResultSpec.php b/tests/spec/FreeDSx/Ldap/Search/Result/EntryResultSpec.php deleted file mode 100644 index aec06404..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Result/EntryResultSpec.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Result; - -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\UnexpectedValueException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use PhpSpec\ObjectBehavior; - -class EntryResultSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SearchResultEntry( - new Entry('cn=foo') - ), - ) - ); - } - - public function it_should_get_the_entry(): void - { - $this->getEntry() - ->shouldBeLike(new Entry('cn=foo')); - } - - public function it_should_get_the_raw_message(): void - { - $this->getMessage() - ->shouldBeLike( - new LdapMessageResponse( - 1, - new SearchResultEntry( - new Entry('cn=foo') - ), - ) - ); - } - - public function it_should_have_a_string_representation_if_the_dn_of_the_entry(): void - { - $this->__toString() - ->shouldBeEqualTo('cn=foo'); - } - - public function it_must_have_a_SearchEntryResponse(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SearchResultReference( - new LdapUrl('foo') - ), - ) - ); - - $this->shouldThrow(new UnexpectedValueException(sprintf( - 'Expected an instance of "%s", but got "%s".', - SearchResultEntry::class, - SearchResultReference::class, - )))->during('getEntry'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/Result/ReferralResultSpec.php b/tests/spec/FreeDSx/Ldap/Search/Result/ReferralResultSpec.php deleted file mode 100644 index 326296ef..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/Result/ReferralResultSpec.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search\Result; - -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\UnexpectedValueException; -use FreeDSx\Ldap\LdapUrl; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Operation\Response\SearchResultReference; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use PhpSpec\ObjectBehavior; - -class ReferralResultSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SearchResultReference( - new LdapUrl('foo'), - ), - ) - ); - } - - public function it_should_get_the_referrals(): void - { - $this->getReferrals() - ->shouldBeLike([ - new LdapUrl('foo'), - ]); - } - - public function it_should_get_the_number_of_referrals(): void - { - $this->count() - ->shouldBeEqualTo(1); - } - - public function it_should_iterate_the_referrals(): void - { - $this->getIterator() - ->shouldBeLike( - new \ArrayIterator([ - new LdapUrl('foo') - ]) - ); - } - - public function it_should_have_a_string_representation_of_the_string_referral(): void - { - $this->__toString() - ->shouldBeEqualTo('ldap://foo/'); - } - - public function it_must_have_a_SearchReferenceResponse(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SearchResultEntry( - new Entry('cn=foo') - ), - ) - ); - - $this->shouldThrow(new UnexpectedValueException(sprintf( - 'Expected an instance of "%s", but got "%s".', - SearchResultReference::class, - SearchResultEntry::class, - )))->during('getReferrals'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Search/VlvSpec.php b/tests/spec/FreeDSx/Ldap/Search/VlvSpec.php deleted file mode 100644 index 5ccd18d7..00000000 --- a/tests/spec/FreeDSx/Ldap/Search/VlvSpec.php +++ /dev/null @@ -1,285 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Search; - -use FreeDSx\Ldap\Control\Sorting\SortingControl; -use FreeDSx\Ldap\Control\Sorting\SortKey; -use FreeDSx\Ldap\Control\Vlv\VlvControl; -use FreeDSx\Ldap\Control\Vlv\VlvResponseControl; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Vlv; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; -use spec\FreeDSx\Ldap\TestFactoryTrait; - -class VlvSpec extends ObjectBehavior -{ - use TestFactoryTrait; - - public function let(LdapClient $client, SearchRequest $search): void - { - $this->beConstructedWith($client, $search, 'cn'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(Vlv::class); - } - - public function it_should_accept_a_sort_key_as_a_sort_argument($client, $search): void - { - $this->beConstructedWith($client, $search, new SortKey('foo')); - - $client->sendAndReceive(Argument::any(), Argument::any(), new SortingControl(new SortKey('foo'))) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(50, 150, 0, 'foo')] - )); - - $this->getEntries(); - } - - public function it_should_accept_a_sort_control_as_a_sort_argument($client, $search): void - { - $this->beConstructedWith($client, $search, new SortingControl(new SortKey('foo'), new SortKey('bar'))); - - $client->sendAndReceive( - Argument::any(), - Argument::any(), - new SortingControl(new SortKey('foo'), new SortKey('bar')) - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(50, 150, 0, 'foo')] - )); - - - $this->getEntries(); - } - - public function it_should_set_the_offset_using_startAt($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 1000, 0, null, null), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(50, 150, 0, 'foo')] - )); - - $this->startAt(1000); - $this->getEntries(); - } - - public function it_should_set_the_offset_using_moveTo($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 1000, 0, null, null), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(50, 150, 0, 'foo')], - )); - - $this->moveTo(1000); - $this->getEntries(); - } - - public function it_should_return_null_on_position_if_nothing_has_happened(): void - { - $this->position()->shouldBeNull(); - } - - public function it_should_return_the_offset_on_a_call_to_position($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(250, 150, 0, 'foo')], - )); - - $this->getEntries(); - $this->position()->shouldBeEqualTo(250); - } - - public function it_should_return_the_size_of_the_list_returned_from_the_server($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(0, 200, 0, 'foo')], - )); - - $this->getEntries(); - $this->listSize()->shouldBeEqualTo(200); - } - - public function it_should_get_the_offset_returned_by_the_server_when_calling_list_offset($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(10, 200, 0, 'foo')], - )); - - $this->getEntries(); - $this->listOffset()->shouldBeEqualTo(10); - } - - public function it_should_check_if_we_are_at_the_start_of_the_list($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(1, 200, 0, 'foo')], - )); - - $this->isAtStartOfList()->shouldBeEqualTo(false); - $this->getEntries(); - $this->isAtStartOfList()->shouldBeEqualTo(true); - } - - public function it_should_check_if_we_are_at_the_start_of_the_list_based_on_the_offset_and_before_value($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(101, 200, 0, 'foo')], - )); - - $this->beforePosition(100); - $this->isAtStartOfList()->shouldBeEqualTo(false); - $this->getEntries(); - $this->isAtStartOfList()->shouldBeEqualTo(true); - } - - public function it_should_check_if_we_are_at_the_end_of_the_list($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(200, 200, 0, 'foo')], - )); - - $this->isAtEndOfList()->shouldBeEqualTo(false); - $this->getEntries(); - $this->isAtEndOfList()->shouldBeEqualTo(true); - } - - public function it_should_check_if_we_are_at_the_end_of_the_list_based_on_the_offset_and_after_value($client): void - { - $client->sendAndReceive(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(101, 200, 0, 'foo')], - )); - - $this->isAtEndOfList()->shouldBeEqualTo(false); - $this->getEntries(); - $this->isAtEndOfList()->shouldBeEqualTo(true); - } - - public function it_should_set_the_before_and_after_positions($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(25, 75, 1, 0), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(1, 200, 0, 'foo')], - )); - - $this->beforePosition(25); - $this->afterPosition(75); - $this->getEntries(); - } - - public function it_should_indicate_the_position_as_a_percentage_if_specified($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 1, 100), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(150, 200, 0, 'foo')], - )); - - $this->asPercentage(true); - $this->getEntries(); - $this->position()->shouldBeEqualTo(75); - } - - public function it_should_move_forward_as_a_percentage_if_specified($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 1, 100), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(1, 200, 0, 'foo')], - )); - - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 20, 200, null, 'foo'), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(20, 200, 0, 'foo')], - )); - - $this->asPercentage(true); - $this->getEntries(); - $this->position()->shouldBeEqualTo(1); - $this->moveForward(9); - $this->getEntries(); - $this->position()->shouldBeEqualTo(10); - $this->listOffset()->shouldBeEqualTo(20); - } - - public function it_should_move_backward_as_a_percentage_if_specified($client): void - { - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 50, 100), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(100, 200, 0, 'foo')], - )); - - $client->sendAndReceive( - Argument::any(), - new VlvControl(0, 100, 80, 200, null, 'foo'), - Argument::any() - )->shouldBeCalled() - ->willReturn($this::makeSearchResponseFromEntries( - controls: [new VlvResponseControl(80, 200, 0, 'foo')], - )); - - $this->asPercentage(true); - $this->startAt(50); - $this->getEntries(); - $this->position()->shouldBeEqualTo(50); - $this->moveBackward(10); - $this->getEntries(); - $this->position()->shouldBeEqualTo(40); - $this->listOffset()->shouldBeEqualTo(80); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/ChildProcessSpec.php b/tests/spec/FreeDSx/Ldap/Server/ChildProcessSpec.php deleted file mode 100644 index ad34ab7b..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/ChildProcessSpec.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server; - -use FreeDSx\Ldap\Server\ChildProcess; -use FreeDSx\Socket\Socket; -use PhpSpec\ObjectBehavior; - -class ChildProcessSpec extends ObjectBehavior -{ - public function let(Socket $socket): void - { - $this->beConstructedWith(9001, $socket); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ChildProcess::class); - } - - public function it_should_get_the_pid(): void - { - $this->getPid()->shouldBeEqualTo(9001); - } - - public function it_should_get_the_socket(): void - { - $this->getSocket()->shouldBeAnInstanceOf(Socket::class); - } - - public function it_should_close_the_socket(Socket $socket): void - { - $socket->close()->shouldBeCalled(); - - $this->closeSocket(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestSpec.php b/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestSpec.php deleted file mode 100644 index fb91efff..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestSpec.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\Paging; - -use DateTime; -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use PhpSpec\ObjectBehavior; - -class PagingRequestSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - new PagingControl(100, ''), - new SearchRequest(new EqualityFilter('foo', 'bar')), - new ControlBag(), - 'bar', - new DateTime('01-01-2021') - ); - } - - public function it_should_be_true_for_is_paging_starting_before_it_has_been_processed(): void - { - $this->isPagingStart()->shouldBeEqualTo(true); - } - - public function it_should_be_false_for_is_paging_starting_after_it_has_been_processed(): void - { - $this->markProcessed(); - - $this->isPagingStart()->shouldBeEqualTo(false); - } - - public function it_should_have_a_created_at(): void - { - $this->createdAt()->shouldBeLike(new DateTime('01-01-2021')); - } - - public function it_should_increase_iteration_after_being_processed(): void - { - $this->markProcessed(); - - $this->getIteration()->shouldBeEqualTo(2); - } - - public function it_should_have_a_last_processed_time_when_it_is_processed(): void - { - $this->markProcessed(); - - $this->lastProcessedAt()->shouldNotBeEqualTo(null); - } - - public function it_should_have_an_initial_iteration_of_1(): void - { - $this->getIteration()->shouldBeEqualTo(1); - } - - public function it_should_get_the_size_of_the_paging_control(): void - { - $this->getSize()->shouldBeEqualTo(100); - } - - public function it_should_get_the_cookie_from_the_paging_control(): void - { - $this->getCookie()->shouldBeEqualTo(''); - } - - public function it_should_get_the_next_cookie(): void - { - $this->getNextCookie()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_controls(): void - { - $this->controls()->shouldBeLike(new ControlBag()); - } - - public function it_should_get_the_search_request(): void - { - $this->getSearchRequest()->shouldBeLike(new SearchRequest(new EqualityFilter('foo', 'bar'))); - } - - public function it_should_update_the_next_cookie(): void - { - $this->updateNextCookie('new'); - - $this->getNextCookie()->shouldBeEqualTo('new'); - } - - public function it_should_update_the_paging_control(): void - { - $this->updatePagingControl(new PagingControl(50, 'new')); - - $this->getCookie()->shouldBeEqualTo('new'); - $this->getSize()->shouldBeEqualTo(50); - } - - public function it_should_return_false_when_not_an_abandon_request(): void - { - $this->isAbandonRequest()->shouldBeEqualTo(false); - } - - public function it_should_return_true_when_it_is_an_abandon_request(): void - { - $this->beConstructedWith( - new PagingControl(0, 'foo'), - new SearchRequest(new EqualityFilter('foo', 'bar')), - new ControlBag(), - 'bar', - new DateTime('01-01-2021') - ); - - $this->isAbandonRequest()->shouldBeEqualTo(true); - } - - public function it_should_not_have_a_last_processed_time_when_it_is_not_yet_processed(): void - { - $this->lastProcessedAt()->shouldBeNull(); - } - - public function it_should_get_a_unique_id(): void - { - $this->getUniqueId()->shouldBeString(); - } - - public function it_should_get_the_criticality_of_the_control(): void - { - $this->isCritical()->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestsSpec.php b/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestsSpec.php deleted file mode 100644 index 5122a015..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestsSpec.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\Paging; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Filter\EqualityFilter; -use FreeDSx\Ldap\Server\Paging\PagingRequest; -use PhpSpec\ObjectBehavior; - -class PagingRequestsSpec extends ObjectBehavior -{ - private PagingRequest $pagingRequest; - - public function let(): void - { - $this->pagingRequest = new PagingRequest( - new PagingControl(1, 'foo'), - new SearchRequest(new EqualityFilter('cn', 'foo')), - new ControlBag(), - 'bar' - ); - - $this->beConstructedWith([ - $this->pagingRequest, - ]); - } - - public function it_should_return_true_when_it_has_the_paging_request(): void - { - $this->has('bar')->shouldBeEqualTo(true); - } - - public function it_should_return_false_when_it_does_not_have_the_paging_request(): void - { - $this->has('foo')->shouldBeEqualTo(false); - } - - public function it_should_return_the_paging_request_when_it_exists(): void - { - $this->findByNextCookie('bar')->shouldBeEqualTo($this->pagingRequest); - } - - public function it_should_remove_the_paging_request(): void - { - $this->remove($this->pagingRequest); - - $this->has('bar')->shouldBeEqualTo(false); - } - - public function it_should_add_the_paging_request(): void - { - $new = new PagingRequest( - new PagingControl(1, 'bar'), - new SearchRequest(new EqualityFilter('cn', 'foo')), - new ControlBag(), - 'foo' - ); - $this->add($new); - - $this->findByNextCookie('foo')->shouldBeEqualTo($new); - } - - public function it_should_throw_an_exception_when_the_request_does_not_exist(): void - { - $this->shouldThrow(ProtocolException::class) - ->during('findByNextCookie', ['ohno']); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingResponseSpec.php b/tests/spec/FreeDSx/Ldap/Server/Paging/PagingResponseSpec.php deleted file mode 100644 index d67aec22..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingResponseSpec.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\Paging; - -use FreeDSx\Ldap\Entry\Entries; -use PhpSpec\ObjectBehavior; - -class PagingResponseSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new Entries()); - } - - public function it_should_not_be_complete_by_default(): void - { - $this->isComplete()->shouldBeEqualTo(false); - } - - public function it_should_have_the_size_remaining(): void - { - $this->getRemaining()->shouldBeEqualTo(0); - } - - public function it_should_get_the_entries(): void - { - $this->getEntries()->shouldBeLike(new Entries()); - } - - public function it_should_make_a_complete_response(): void - { - $this->beConstructedThrough('makeFinal', [new Entries()]); - - $this->isComplete()->shouldBeEqualTo(true); - } - - public function it_should_make_a_regular_response(): void - { - $this->beConstructedThrough('make', [new Entries()]); - - $this->isComplete()->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestContextSpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestContextSpec.php deleted file mode 100644 index a4a6e0ef..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestContextSpec.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\Token\AnonToken; -use PhpSpec\ObjectBehavior; - -class RequestContextSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new ControlBag(), new AnonToken(null)); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(RequestContext::class); - } - - public function it_should_get_the_token(): void - { - $this->token()->shouldBeAnInstanceOf(new AnonToken(null)); - } - - public function it_should_get_the_controls(): void - { - $this->controls()->shouldBeAnInstanceOf(new ControlBag()); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/GenericRequestHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHandler/GenericRequestHandlerSpec.php deleted file mode 100644 index 4796909e..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/GenericRequestHandlerSpec.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\RequestHandler; - -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\Operation\Request\AddRequest; -use FreeDSx\Ldap\Operation\Request\CompareRequest; -use FreeDSx\Ldap\Operation\Request\DeleteRequest; -use FreeDSx\Ldap\Operation\Request\ExtendedRequest; -use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; -use FreeDSx\Ldap\Operation\Request\ModifyRequest; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use PhpSpec\ObjectBehavior; - -class GenericRequestHandlerSpec extends ObjectBehavior -{ - public function it_is_initializable(): void - { - $this->shouldHaveType(GenericRequestHandler::class); - } - - public function it_should_implement_request_handler_interface(): void - { - $this->shouldImplement(RequestHandlerInterface::class); - } - - public function it_should_throw_an_operations_exception_on_an_add_request(RequestContext $context, AddRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('add', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_a_delete_request(RequestContext $context, DeleteRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('delete', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_a_modify_request(RequestContext $context, ModifyRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('modify', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_a_modify_dn_request(RequestContext $context, ModifyDnRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('modifyDn', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_a_search_request(RequestContext $context, SearchRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('search', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_a_compare_request(RequestContext $context, CompareRequest $request): void - { - $this->shouldThrow(OperationException::class)->during('compare', [$context, $request]); - } - - public function it_should_throw_an_operations_exception_on_an_extended_request(RequestContext $context, ExtendedRequest $request): void - { - $request->getName()->willReturn('foo'); - - $this->shouldThrow(OperationException::class)->during('extended', [$context, $request]); - } - - public function it_should_return_false_on_a_bind_request(): void - { - $this->bind('foo', 'bar')->shouldBeEqualTo(false); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/HandlerFactorySpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHandler/HandlerFactorySpec.php deleted file mode 100644 index 02f9981a..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/HandlerFactorySpec.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\RequestHandler; - -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; -use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; -use FreeDSx\Ldap\Server\RequestHandler\ProxyPagingHandler; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; - -class HandlerFactorySpec extends ObjectBehavior -{ - public function it_should_allow_a_request_handler_as_an_object(): void - { - $handler = new GenericRequestHandler(); - $this->beConstructedWith( - (new ServerOptions())->setRequestHandler($handler) - ); - - $this->makeRequestHandler()->shouldBeEqualTo($handler); - } - - public function it_should_allow_a_rootdse_handler_as_an_object(): void - { - $rootDseHandler = new ProxyHandler(new LdapClient()); - $this->beConstructedWith( - (new ServerOptions())->setRootDseHandler($rootDseHandler) - ); - - $this->makeRootDseHandler()->shouldBeEqualTo($rootDseHandler); - } - - public function it_should_allow_a_null_rootdse_handler(): void - { - $this->beConstructedWith( - (new ServerOptions())->setRootDseHandler(null) - ); - - $this->makeRootDseHandler()->shouldBeNull(); - } - - public function it_should_allow_a_paging_handler_as_an_object(PagingHandlerInterface $pagingHandler): void - { - $pagingHandler = new ProxyPagingHandler(new LdapClient()); - $this->beConstructedWith( - (new ServerOptions())->setPagingHandler($pagingHandler) - ); - - $this->makePagingHandler()->shouldBeEqualTo($pagingHandler); - } - - public function it_should_allow_a_null_paging_handler(): void - { - $this->beConstructedWith( - (new ServerOptions())->setPagingHandler(null) - ); - - $this->makePagingHandler()->shouldBeNull(); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyHandlerSpec.php deleted file mode 100644 index bcd68f14..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyHandlerSpec.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\RequestHandler; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface; -use FreeDSx\Ldap\Server\Token\BindToken; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ProxyHandlerSpec extends ObjectBehavior -{ - public function let( - LdapClient $client, - RequestContext $context - ): void { - $context->controls()->willReturn(new ControlBag()); - $context->token()->willReturn(new BindToken('foo', 'bar')); - - $this->beConstructedWith($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ProxyHandler::class); - } - - public function it_should_implement_request_handler_interface(): void - { - $this->shouldImplement(RequestHandlerInterface::class); - } - - public function it_should_implement_root_dse_handler_interface(): void - { - $this->shouldImplement(RootDseHandlerInterface::class); - } - - public function it_should_handle_a_root_dse_request( - LdapClient $client, - RequestContext $context, - SearchRequest $request - ): void { - $rootDse = new Entry(''); - $client->search($request, Argument::any()) - ->shouldBeCalled() - ->willReturn(new Entries($rootDse)); - - $this->rootDse($context, $request, new Entry(''))->shouldBeEqualTo($rootDse); - } - - public function it_should_handle_a_root_dse_request_when_non_is_returned( - LdapClient $client, - RequestContext $context, - SearchRequest $request - ): void { - $client->search($request, Argument::any()) - ->shouldBeCalled() - ->willReturn(new Entries()); - - $this->shouldThrow(new OperationException('Entry not found.', ResultCode::NO_SUCH_OBJECT)) - ->during('rootDse', [$context, $request, new Entry('')]); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyPagingHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyPagingHandlerSpec.php deleted file mode 100644 index 7ca1ea5e..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyPagingHandlerSpec.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\RequestHandler; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Control\PagingControl; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Search\Paging; -use FreeDSx\Ldap\Server\Paging\PagingRequest; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\ProxyPagingHandler; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class ProxyPagingHandlerSpec extends ObjectBehavior -{ - public function let(LdapClient $client, RequestContext $context): void - { - $context->controls()->willReturn(new ControlBag()); - - $this->beConstructedWith($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ProxyPagingHandler::class); - } - - public function it_should_implement_paging_handler_interface(): void - { - $this->shouldImplement(PagingHandlerInterface::class); - } - - public function it_should_handle_a_paging_request_when_paging_is_stil_going( - LdapClient $client, - RequestContext $context, - Paging $paging - ): void { - $request = new SearchRequest(Filters::equal('cn', 'foo')); - $entries = new Entries(new Entry('cn=foo,dc=foo,dc=bar')); - $client->paging($request, Argument::any()) - ->shouldBeCalled() - ->willReturn($paging); - - $paging->isCritical(false) - ->shouldBeCalled() - ->willReturn($paging); - $paging->getEntries(25)->shouldBeCalled() - ->willReturn($entries); - $paging->hasEntries()->shouldBeCalled() - ->willReturn(true); - $paging->sizeEstimate()->shouldBeCalled() - ->willReturn(25); - - $pagingRequest = new PagingRequest( - new PagingControl(25, ''), - $request, - new ControlBag(), - 'foo' - ); - - $this->page($pagingRequest, $context)->isComplete()->shouldBeEqualTo(false); - $this->page($pagingRequest, $context)->getEntries()->shouldBeEqualTo($entries); - $this->page($pagingRequest, $context)->getRemaining()->shouldBeEqualTo(25); - } - - public function it_should_handle_a_paging_request_when_paging_is_complete( - LdapClient $client, - RequestContext $context, - Paging $paging - ): void { - $request = new SearchRequest(Filters::equal('cn', 'foo')); - $entries = new Entries(new Entry('cn=foo,dc=foo,dc=bar')); - $client->paging($request, Argument::any()) - ->shouldBeCalled() - ->willReturn($paging); - - $paging->isCritical(false) - ->shouldBeCalled() - ->willReturn($paging); - $paging->getEntries(25)->shouldBeCalled() - ->willReturn($entries); - $paging->hasEntries()->shouldBeCalled() - ->willReturn(false); - - $pagingRequest = new PagingRequest( - new PagingControl(25, ''), - $request, - new ControlBag(), - 'foo' - ); - - $this->page($pagingRequest, $context)->isComplete()->shouldBeEqualTo(true); - $this->page($pagingRequest, $context)->getEntries()->shouldBeEqualTo($entries); - $this->page($pagingRequest, $context)->getRemaining()->shouldBeEqualTo(0); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyRequestHandlerSpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyRequestHandlerSpec.php deleted file mode 100644 index e32b0943..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHandler/ProxyRequestHandlerSpec.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\RequestHandler; - -use FreeDSx\Ldap\Control\ControlBag; -use FreeDSx\Ldap\Entry\Change; -use FreeDSx\Ldap\Entry\Entries; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\BindException; -use FreeDSx\Ldap\Exception\OperationException; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\LdapResult; -use FreeDSx\Ldap\Operation\Response\BindResponse; -use FreeDSx\Ldap\Operation\Response\CompareResponse; -use FreeDSx\Ldap\Operation\ResultCode; -use FreeDSx\Ldap\Operations; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Server\RequestContext; -use FreeDSx\Ldap\Server\RequestHandler\ProxyRequestHandler; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; -use FreeDSx\Ldap\Server\Token\BindToken; -use PhpSpec\ObjectBehavior; - -class ProxyRequestHandlerSpec extends ObjectBehavior -{ - public function let(LdapClient $client, RequestContext $context): void - { - $context->controls()->willReturn(new ControlBag()); - $context->token()->willReturn(new BindToken('foo', 'bar')); - $this->setLdapClient($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(ProxyRequestHandler::class); - } - - public function it_should_implement_request_handler_interface(): void - { - $this->shouldImplement(RequestHandlerInterface::class); - } - - public function it_should_send_an_add_request($client, $context): void - { - $add = Operations::add(Entry::create('cn=foo,dc=freedsx,dc=local')); - - $client->sendAndReceive($add, ...[])->shouldBeCalled(); - $this->add($context, $add); - } - - public function it_should_send_a_delete_request($client, $context): void - { - $delete = Operations::delete('cn=foo,dc=freedsx,dc=local'); - - $client->sendAndReceive($delete, ...[])->shouldBeCalled(); - $this->delete($context, $delete); - } - - public function it_should_send_a_modify_request($client, $context): void - { - $modify = Operations::modify('cn=foo,dc=freedsx,dc=local', Change::add('foo', 'bar')); - - $client->sendAndReceive($modify, ...[])->shouldBeCalled(); - $this->modify($context, $modify); - } - - public function it_should_send_a_modify_dn_request($client, $context): void - { - $modifyDn = Operations::rename('cn=foo,dc=freedsx,dc=local', 'cn=bar'); - - $client->sendAndReceive($modifyDn, ...[])->shouldBeCalled(); - $this->modifyDn($context, $modifyDn); - } - - public function it_should_send_a_search_request($client, $context): void - { - $search = Operations::search(Filters::present('objectClass'), 'cn')->base('dc=foo'); - $entries = new Entries(Entry::create('dc=foo')); - - $client->search($search)->shouldBeCalled()->willReturn($entries); - $this->search($context, $search)->shouldBeEqualTo($entries); - } - - public function it_should_send_a_compare_request_and_return_false_on_no_match($client, $context): void - { - $compare = Operations::compare('foo', 'foo', 'bar'); - - $client->sendAndReceive($compare)->shouldBeCalled()->willReturn(new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_FALSE))); - - $this->compare($context, $compare)->shouldBeEqualTo(false); - } - - public function it_should_send_a_compare_request_and_return_true_on_match($client, $context): void - { - $compare = Operations::compare('foo', 'foo', 'bar'); - - $client->sendAndReceive($compare)->shouldBeCalled()->willReturn(new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_TRUE))); - - $this->compare($context, $compare)->shouldBeEqualTo(true); - } - - public function it_should_send_an_extended_request($client, $context): void - { - $extended = Operations::extended('foo', 'bar'); - - $client->send($extended)->shouldBeCalled(); - $this->extended($context, $extended); - } - - public function it_should_handle_a_bind_request($client): void - { - $client->bind('foo', 'bar')->shouldBeCalled()->willReturn(new LdapMessageResponse(1, new BindResponse(new LdapResult(0)))); - - $this->bind('foo', 'bar')->shouldBeEqualTo(true); - } - - public function it_should_handle_a_bind_request_failure($client): void - { - $client->bind('foo', 'bar')->shouldBeCalled()->willThrow(new BindException('Foo!', 49)); - - $this->shouldThrow(new OperationException('Foo!', 49))->during('bind', ['foo', 'bar']); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/RequestHistorySpec.php b/tests/spec/FreeDSx/Ldap/Server/RequestHistorySpec.php deleted file mode 100644 index 0778d477..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/RequestHistorySpec.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server; - -use FreeDSx\Ldap\Exception\ProtocolException; -use FreeDSx\Ldap\Server\Paging\PagingRequests; -use PhpSpec\ObjectBehavior; - -class RequestHistorySpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new PagingRequests()); - } - - public function it_should_add_a_valid_id(): void - { - $this->shouldNotThrow(ProtocolException::class)->during('addId', [1]); - } - - public function it_should_throw_when_adding_an_existing_id(): void - { - $this->addId(1); - - $this->shouldThrow(ProtocolException::class)->during('addId', [1]); - } - - public function it_should_throw_when_adding_an_invalid_id(): void - { - $this->shouldThrow(ProtocolException::class)->during('addId', [0]); - } - - public function it_should_get_the_paging_requests(): void - { - $this->pagingRequest()->shouldBeAnInstanceOf(PagingRequests::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/ServerProtocolFactorySpec.php b/tests/spec/FreeDSx/Ldap/Server/ServerProtocolFactorySpec.php deleted file mode 100644 index 0e7fff51..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/ServerProtocolFactorySpec.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server; - -use FreeDSx\Ldap\Protocol\ServerAuthorization; -use FreeDSx\Ldap\Protocol\ServerProtocolHandler; -use FreeDSx\Ldap\Server\HandlerFactoryInterface; -use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; -use FreeDSx\Ldap\ServerOptions; -use FreeDSx\Socket\Socket; -use PhpSpec\ObjectBehavior; - -class ServerProtocolFactorySpec extends ObjectBehavior -{ - public function let( - HandlerFactoryInterface $handlerFactory, - ServerAuthorization $serverAuthorization, - ): void { - $this->beConstructedWith( - $handlerFactory, - new ServerOptions(), - $serverAuthorization, - ); - } - - public function it_should_malke_a_ServerProtocolInstance( - Socket $socket, - HandlerFactoryInterface $handlerFactory, - ): void { - $handlerFactory - ->makeRequestHandler() - ->willReturn(new GenericRequestHandler()); - - $this->make($socket) - ->shouldBeAnInstanceOf(ServerProtocolHandler::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/SocketServerFactorySpec.php b/tests/spec/FreeDSx/Ldap/Server/SocketServerFactorySpec.php deleted file mode 100644 index 1188ea5d..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/SocketServerFactorySpec.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server; - -use FreeDSx\Ldap\ServerOptions; -use FreeDSx\Socket\SocketServer; -use PhpSpec\Exception\Example\SkippingException; -use PhpSpec\ObjectBehavior; -use Psr\Log\LoggerInterface; - -class SocketServerFactorySpec extends ObjectBehavior -{ - private $tmpUnixSocketResource; - - private string $tmpUnixSocketFilePath; - - public function let(LoggerInterface $logger): void - { - $this->tmpUnixSocketResource = tmpfile(); - $this->tmpUnixSocketFilePath = stream_get_meta_data($this->tmpUnixSocketResource)['uri']; - - $this->beConstructedWith( - (new ServerOptions()) - ->setPort(3390), - $logger, - ); - } - - public function letGo(): void - { - fclose($this->tmpUnixSocketResource); - } - - public function it_should_make_and_bind_the_socket_server(): void - { - $this->makeAndBind() - ->shouldBeAnInstanceOf(SocketServer::class); - } - - public function it_should_make_a_unix_based_socket_server(LoggerInterface $logger): void - { - if (str_starts_with(strtoupper(PHP_OS), 'WIN')) { - throw new SkippingException('Cannot construct unix based socket on Windows.'); - } - $this->beConstructedWith( - (new ServerOptions()) - ->setUnixSocket($this->tmpUnixSocketFilePath) - ->setTransport('unix'), - $logger, - ); - - $this->makeAndBind() - ->shouldBeAnInstanceOf(SocketServer::class); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/Token/AnonTokenSpec.php b/tests/spec/FreeDSx/Ldap/Server/Token/AnonTokenSpec.php deleted file mode 100644 index 342e1361..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/Token/AnonTokenSpec.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\Token; - -use FreeDSx\Ldap\Server\Token\AnonToken; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use PhpSpec\ObjectBehavior; - -class AnonTokenSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(AnonToken::class); - } - - public function it_should_implement_token_interface(): void - { - $this->shouldImplement(TokenInterface::class); - } - - public function it_should_get_the_username(): void - { - $this->getUsername()->shouldBeEqualTo('foo'); - } - - public function it_should_get_a_null_password(): void - { - $this->getPassword()->shouldBeNull(); - } - - public function it_should_get_the_version(): void - { - $this->getVersion()->shouldBeEqualTo(3); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Server/Token/BindTokenSpec.php b/tests/spec/FreeDSx/Ldap/Server/Token/BindTokenSpec.php deleted file mode 100644 index cfaf40aa..00000000 --- a/tests/spec/FreeDSx/Ldap/Server/Token/BindTokenSpec.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Server\Token; - -use FreeDSx\Ldap\Server\Token\BindToken; -use FreeDSx\Ldap\Server\Token\TokenInterface; -use PhpSpec\ObjectBehavior; - -class BindTokenSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith('foo', 'bar'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(BindToken::class); - } - - public function it_should_implement_token_interface(): void - { - $this->shouldImplement(TokenInterface::class); - } - - public function it_should_get_the_username(): void - { - $this->getUsername()->shouldBeEqualTo('foo'); - } - - public function it_should_get_the_password(): void - { - $this->getPassword()->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_version(): void - { - $this->getVersion()->shouldBeEqualTo(3); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/MockSyncEntryHandler.php b/tests/spec/FreeDSx/Ldap/Sync/MockSyncEntryHandler.php deleted file mode 100644 index d7a51986..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/MockSyncEntryHandler.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync; - -use FreeDSx\Ldap\Sync\Result\SyncEntryResult; -use FreeDSx\Ldap\Sync\Session; - -/** - * Used only for collaborator purposes to test sync entry handling invocation. - * - * @internal - */ -class MockSyncEntryHandler -{ - public function __invoke( - SyncEntryResult $syncEntryResult, - Session $session - ): void { - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/MockSyncIdSetHandler.php b/tests/spec/FreeDSx/Ldap/Sync/MockSyncIdSetHandler.php deleted file mode 100644 index c5f3aba2..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/MockSyncIdSetHandler.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync; - -use FreeDSx\Ldap\Sync\Result\SyncIdSetResult; -use FreeDSx\Ldap\Sync\Session; - -/** - * Used only for collaborator purposes to test sync entry handling invocation. - * - * @internal - */ -class MockSyncIdSetHandler -{ - public function __invoke( - SyncIdSetResult $result, - Session $session, - ): void { - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/MockSyncReferralHandler.php b/tests/spec/FreeDSx/Ldap/Sync/MockSyncReferralHandler.php deleted file mode 100644 index 42d1b088..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/MockSyncReferralHandler.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync; - -use FreeDSx\Ldap\Sync\Result\SyncReferralResult; -use FreeDSx\Ldap\Sync\Session; - -/** - * Used only for collaborator purposes to test sync entry handling invocation. - * - * @internal - */ -class MockSyncReferralHandler -{ - public function __invoke( - SyncReferralResult $result, - Session $session, - ): void { - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncEntryResultSpec.php b/tests/spec/FreeDSx/Ldap/Sync/Result/SyncEntryResultSpec.php deleted file mode 100644 index 34b2703d..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncEntryResultSpec.php +++ /dev/null @@ -1,176 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync\Result; - -use FreeDSx\Ldap\Control\Sync\SyncStateControl; -use FreeDSx\Ldap\Entry\Entry; -use FreeDSx\Ldap\Exception\RuntimeException; -use FreeDSx\Ldap\Operation\Response\SearchResultEntry; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Search\Result\EntryResult; -use PhpSpec\ObjectBehavior; - -class SyncEntryResultSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_ADD, - 'foo', - 'bar' - ) - ) - )); - } - - public function it_should_get_the_entry(): void - { - $this->getEntry() - ->shouldBeLike(new Entry('cn=foo')); - } - - public function it_should_get_the_sync_state(): void - { - $this->getState() - ->shouldBeEqualTo(SyncStateControl::STATE_ADD); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie() - ->shouldBeEqualTo('bar'); - } - - public function it_should_get_the_entry_uuid(): void - { - $this->getEntryUuid() - ->shouldBeEqualTo('foo'); - } - - public function it_should_be_able_to_check_what_the_state_is(): void - { - $this->isState(SyncStateControl::STATE_ADD) - ->shouldBeEqualTo(true); - } - - public function it_should_be_able_to_check_what_the_state_is_not(): void - { - $this->isState(SyncStateControl::STATE_MODIFY) - ->shouldBeEqualTo(false); - } - - - public function it_should_tell_if_it_is_for_a_present_state(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_PRESENT, - 'foo', - 'bar' - ) - ) - )); - - $this->isPresent() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_add_state(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_ADD, - 'foo', - 'bar' - ) - ) - )); - - $this->isAdd() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_modify_state(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_MODIFY, - 'foo', - 'bar' - ) - ) - )); - - $this->isModify() - ->shouldBeEqualTo(true); - } - - public function it_should_tell_if_it_is_for_a_delete_state(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_DELETE, - 'foo', - 'bar' - ) - ) - )); - - $this->isDelete() - ->shouldBeEqualTo(true); - } - - public function it_should_get_the_raw_message(): void - { - $this->getMessage() - ->shouldBeLike(new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - new SyncStateControl( - SyncStateControl::STATE_ADD, - 'foo', - 'bar' - ) - )); - } - - public function it_should_thrown_an_error_if_there_is_no_sync_state_control(): void - { - $this->beConstructedWith(new EntryResult( - new LdapMessageResponse( - 1, - new SearchResultEntry(new Entry('cn=foo')), - ) - )); - - $this->shouldThrow(new RuntimeException('Expected a SyncStateControl, but none was found.')) - ->during('getState'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncIdSetResultSpec.php b/tests/spec/FreeDSx/Ldap/Sync/Result/SyncIdSetResultSpec.php deleted file mode 100644 index 86330dd5..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncIdSetResultSpec.php +++ /dev/null @@ -1,130 +0,0 @@ -beConstructedWith( - new LdapMessageResponse( - 1, - new SyncIdSet( - ['foo', 'bar'], - true, - 'tasty' - ), - ) - ); - } - - public function it_should_get_the_entry_uuids(): void - { - $this->getEntryUuids() - ->shouldBeEqualTo([ - 'foo', - 'bar', - ]); - } - - public function it_should_get_the_count_of_the_set(): void - { - $this->count() - ->shouldBeEqualTo(2); - } - - public function it_should_get_the_iterable_set(): void - { - $this->getIterator() - ->shouldBeLike(new \ArrayIterator([ - 'foo', - 'bar', - ])); - } - - public function it_should_get_the_raw_message(): void - { - $this->getMessage() - ->shouldBeLike( - new LdapMessageResponse( - 1, - new SyncIdSet( - ['foo', 'bar'], - true, - 'tasty' - ), - ) - ); - } - - public function it_should_get_the_cookie(): void - { - $this->getCookie() - ->shouldBeEqualTo('tasty'); - } - - public function it_should_get_if_this_is_for_entry_deletes(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SyncIdSet( - ['foo', 'bar'], - true, - 'tasty' - ), - ) - ); - - $this->isDeleted() - ->shouldBeEqualTo(true); - $this->isPresent() - ->shouldBeEqualTo(false); - } - - public function it_should_get_if_this_is_for_entry_that_are_present(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SyncIdSet( - ['foo', 'bar'], - false, - 'tasty' - ), - ) - ); - - $this->isPresent() - ->shouldBeEqualTo(true); - $this->isDeleted() - ->shouldBeEqualTo(false); - } - - public function it_must_have_a_SearchEntryResponse(): void - { - $this->beConstructedWith( - new LdapMessageResponse( - 1, - new SearchResultReference( - new LdapUrl('foo') - ), - ) - ); - - $this->shouldThrow(new UnexpectedValueException(sprintf( - 'Expected an instance of "%s", but got "%s".', - SyncIdSet::class, - SearchResultReference::class, - )))->during('getEntryUuids'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/SessionSpec.php b/tests/spec/FreeDSx/Ldap/Sync/SessionSpec.php deleted file mode 100644 index 8f38f3ad..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/SessionSpec.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync; - -use FreeDSx\Ldap\Sync\Session; -use PhpSpec\ObjectBehavior; - -class SessionSpec extends ObjectBehavior -{ - public function let(): void - { - $this->beConstructedWith( - Session::MODE_POLL, - null - ); - } - - public function it_should_get_the_phase_when_it_is_not_set(): void - { - $this->getPhase() - ->shouldBeNull(); - } - - public function it_should_get_the_phase_when_it_is_set(): void - { - $this->updatePhase(Session::PHASE_DELETE); - - $this->getPhase() - ->shouldBeEqualTo(Session::PHASE_DELETE); - } - - public function it_should_get_the_cookie_when_it_is_not_set(): void - { - $this->getCookie() - ->shouldBeNull(); - } - - public function it_should_get_the_cookie_when_it_is_set(): void - { - $this->updateCookie('foo'); - - $this->getCookie() - ->shouldBeEqualTo('foo'); - } -} diff --git a/tests/spec/FreeDSx/Ldap/Sync/SyncReplSpec.php b/tests/spec/FreeDSx/Ldap/Sync/SyncReplSpec.php deleted file mode 100644 index 85e0bc53..00000000 --- a/tests/spec/FreeDSx/Ldap/Sync/SyncReplSpec.php +++ /dev/null @@ -1,213 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace spec\FreeDSx\Ldap\Sync; - -use FreeDSx\Ldap\Control\Control; -use FreeDSx\Ldap\Control\Sync\SyncDoneControl; -use FreeDSx\Ldap\Control\Sync\SyncRequestControl; -use FreeDSx\Ldap\LdapClient; -use FreeDSx\Ldap\Operation\Request\SearchRequest; -use FreeDSx\Ldap\Operation\Request\SyncRequest; -use FreeDSx\Ldap\Operation\Response\SearchResultDone; -use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use FreeDSx\Ldap\Search\Filters; -use FreeDSx\Ldap\Sync\Result\SyncEntryResult; -use FreeDSx\Ldap\Sync\Result\SyncIdSetResult; -use FreeDSx\Ldap\Sync\Result\SyncReferralResult; -use FreeDSx\Ldap\Sync\SyncRepl; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; - -class SyncReplSpec extends ObjectBehavior -{ - public function let(LdapClient $client): void - { - $this->beConstructedWith($client); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(SyncRepl::class); - } - - public function it_should_poll_for_changes(LdapClient $client): void - { - $client->sendAndReceive( - Argument::any(), - Argument::that(function (SyncRequestControl $control): bool { - return $control->getMode() === SyncRequestControl::MODE_REFRESH_ONLY; - }), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_listen_for_changes(LdapClient $client): void - { - $client->sendAndReceive( - Argument::any(), - Argument::that(function (SyncRequestControl $control): bool { - return $control->getMode() === SyncRequestControl::MODE_REFRESH_AND_PERSIST; - }), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->listen(); - } - - public function it_should_use_a_filter_if_specified(LdapClient $client): void - { - $this->useFilter(Filters::present('foo')); - - $client->sendAndReceive( - Argument::that(function (SyncRequest $request): bool { - return $request->getFilter()->toString() === Filters::present('foo')->toString(); - }), - Argument::any(), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_use_added_controls_if_specified(LdapClient $client): void - { - $control = new Control('foo'); - - $this->controls()->add($control); - - $client->sendAndReceive( - Argument::any(), - Argument::any(), - Argument::any(), - Argument::exact($control), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_use_the_cookie_if_specified(LdapClient $client): void - { - $this->useCookie('tasty'); - - $client->sendAndReceive( - Argument::any(), - Argument::that(function (SyncRequestControl $control): bool { - return $control->getCookie() === 'tasty'; - }), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_use_the_entry_handler_specified(LdapClient $client): void - { - $handler = fn (SyncEntryResult $result) => $result->getEntry(); - - $client->sendAndReceive( - Argument::that(function (SyncRequest $request) use ($handler): bool { - return $request->getEntryHandler() === $handler; - }), - Argument::any(), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll($handler); - } - - public function it_should_use_the_referral_handler_specified(LdapClient $client): void - { - $handler = fn (SyncReferralResult $result) => $result->getReferrals(); - - $this->useReferralHandler($handler); - - $client->sendAndReceive( - Argument::that(function (SyncRequest $request) use ($handler): bool { - return $request->getReferralHandler() === $handler; - }), - Argument::any(), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_use_the_idSet_handler_specified(LdapClient $client): void - { - $handler = fn (SyncIdSetResult $result) => $result->getEntryUuids(); - - $this->useIdSetHandler($handler); - - $client->sendAndReceive( - Argument::that(function (SyncRequest $request) use ($handler): bool { - return $request->getIdSetHandler() === $handler; - }), - Argument::any(), - Argument::any(), - )->shouldBeCalledOnce() - ->willReturn(new LdapMessageResponse( - 1, - new SearchResultDone(0), - new SyncDoneControl('foo') - )); - - $this->poll(); - } - - public function it_should_use_the_continue_strategy_if_specified(): void - { - $this->useContinueOnCancel(); - - $this->request() - ->getCancelStrategy() - ->shouldBe(SearchRequest::CANCEL_CONTINUE); - } -} diff --git a/tests/unit/ContainerTest.php b/tests/unit/ContainerTest.php new file mode 100644 index 00000000..2ae1df95 --- /dev/null +++ b/tests/unit/ContainerTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Container; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler; +use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; +use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; +use FreeDSx\Ldap\Protocol\RootDseLoader; +use FreeDSx\Ldap\Protocol\ServerAuthorization; +use FreeDSx\Ldap\Server\HandlerFactoryInterface; +use FreeDSx\Ldap\Server\ServerProtocolFactory; +use FreeDSx\Ldap\Server\ServerRunner\ServerRunnerInterface; +use FreeDSx\Ldap\Server\SocketServerFactory; +use FreeDSx\Ldap\ServerOptions; +use FreeDSx\Socket\SocketPool; +use PHPUnit\Framework\TestCase; + +class ContainerTest extends TestCase +{ + private Container $subject; + + protected function setUp(): void + { + parent::setUp(); + + $client = new LdapClient(); + $serverOptions = new ServerOptions(); + + $this->subject = new Container([ + LdapClient::class => $client, + ClientOptions::class => $client->getOptions(), + ServerOptions::class => $serverOptions, + ]); + } + + public static function buildableDependenciesDataProvider(): array + { + return [ + [LdapClient::class], + [ClientProtocolHandler::class], + [ClientQueueInstantiator::class], + [ClientProtocolHandlerFactory::class], + [SocketPool::class], + [RootDseLoader::class], + [ServerProtocolFactory::class], + [HandlerFactoryInterface::class], + [ServerAuthorization::class], + [SocketServerFactory::class], + ]; + } + + /** + * @param class-string $class + * + * @dataProvider buildableDependenciesDataProvider + */ + public function test_it_builds_the_dependencies( + string $class, + ): void { + self::assertInstanceOf( + $class, + $this->subject->get($class), + ); + } + + public function test_it_should_make_the_default_ServerRunner(): void + { + if (str_starts_with(strtoupper(PHP_OS), 'WIN')) { + self::markTestSkipped('Cannot construct the default PCNTL runner on Windows.'); + } + + self::assertInstanceOf( + ServerRunnerInterface::class, + $this->subject->get(ServerRunnerInterface::class), + ); + } +} diff --git a/tests/unit/Control/Ad/DirSyncRequestControlTest.php b/tests/unit/Control/Ad/DirSyncRequestControlTest.php new file mode 100644 index 00000000..d34a5299 --- /dev/null +++ b/tests/unit/Control/Ad/DirSyncRequestControlTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; + +class DirSyncRequestControlTest extends \PHPUnit\Framework\TestCase +{ + private DirSyncRequestControl $subject; + + protected function setUp(): void + { + $this->subject = new DirSyncRequestControl(); + } + + public function test_it_should_set_the_flags(): void + { + $this->subject->setFlags(DirSyncRequestControl::FLAG_PUBLIC_DATA_ONLY); + + self::assertSame( + DirSyncRequestControl::FLAG_PUBLIC_DATA_ONLY, + $this->subject->getFlags(), + ); + } + + public function test_it_should_have_incremental_values_as_the_default_flags(): void + { + self::assertSame( + DirSyncRequestControl::FLAG_INCREMENTAL_VALUES, + $this->subject->getFlags(), + ); + } + + public function test_it_should_set_the_cookie(): void + { + $this->subject->setCookie('foo'); + + self::assertSame( + 'foo', + $this->subject->getCookie(), + ); + } + + public function test_it_should_have_an_empty_cookie_by_default(): void + { + self::assertSame( + '', + $this->subject->getCookie(), + ); + } + + public function test_it_should_set_the_max_bytes(): void + { + $this->subject->setMaxBytes(2000); + + self::assertSame( + 2000, + $this->subject->getMaxBytes(), + ); + } + + public function test_it_should_have_the_max_value_for_max_bytes_by_default(): void + { + self::assertSame( + 2147483647, + $this->subject->getMaxBytes(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->toAsn1(), + Asn1::sequence( + Asn1::octetString(Control::OID_DIR_SYNC), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(-0x80000000), + Asn1::integer(2147483647), + Asn1::octetString('') + ))) + ) + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new DirSyncRequestControl(), + DirSyncRequestControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_DIR_SYNC), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(-0x80000000), + Asn1::integer(2147483647), + Asn1::octetString('') + ))) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/Control/Ad/DirSyncResponseControlTest.php b/tests/unit/Control/Ad/DirSyncResponseControlTest.php new file mode 100644 index 00000000..fe879141 --- /dev/null +++ b/tests/unit/Control/Ad/DirSyncResponseControlTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\DirSyncResponseControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class DirSyncResponseControlTest extends TestCase +{ + private DirSyncResponseControl $subject; + + protected function setUp(): void + { + $this->subject = new DirSyncResponseControl(0); + } + + public function test_it_should_get_the_more_results_value(): void + { + self::assertSame( + 0, + $this->subject->getMoreResults() + ); + } + + public function test_it_should_return_false_for_has_more_results_when_more_results_is_0(): void + { + self::assertFalse($this->subject->hasMoreResults()); + } + + public function test_it_should_return_false_for_has_more_results_when_more_results_is_not_0(): void + { + $this->subject = new DirSyncResponseControl(1); + + self::assertTrue($this->subject->hasMoreResults()); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + '', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_the_unused_value(): void + { + self::assertSame( + 0, + $this->subject->getUnused(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_DIR_SYNC), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(0), + Asn1::integer(0), + Asn1::octetString('') + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + (new DirSyncResponseControl(0)), + DirSyncResponseControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_DIR_SYNC), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(0), + Asn1::integer(0), + Asn1::octetString('') + ))), + ))->setValue(null), + ); + } +} diff --git a/tests/unit/Control/Ad/ExpectedEntryCountControlTest.php b/tests/unit/Control/Ad/ExpectedEntryCountControlTest.php new file mode 100644 index 00000000..c9330be0 --- /dev/null +++ b/tests/unit/Control/Ad/ExpectedEntryCountControlTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\ExpectedEntryCountControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class ExpectedEntryCountControlTest extends TestCase +{ + private ExpectedEntryCountControl $subject; + + protected function setUp(): void + { + $this->subject = new ExpectedEntryCountControl( + min: 1, + max: 50, + ); + } + + public function test_it_should_set_the_maximum(): void + { + $this->subject->setMaximum(100); + + self::assertSame( + 100, + $this->subject->getMaximum(), + ); + } + + public function test_it_should_get_the_maximum(): void + { + self::assertSame( + 50, + $this->subject->getMaximum(), + ); + } + + public function test_it_should_set_the_minimum(): void + { + $this->subject->setMinimum(100); + + self::assertSame( + 100, + $this->subject->getMinimum(), + ); + } + + public function test_it_should_get_the_minimum(): void + { + self::assertSame( + 1, + $this->subject->getMinimum(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->toAsn1(), + Asn1::sequence( + Asn1::octetString(Control::OID_EXPECTED_ENTRY_COUNT), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1), + Asn1::integer(50) + ))) + ) + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new ExpectedEntryCountControl(min: 1, max: 50), + ExpectedEntryCountControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_EXPECTED_ENTRY_COUNT), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1), + Asn1::integer(50) + ))) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/Control/Ad/ExtendedDnControlTest.php b/tests/unit/Control/Ad/ExtendedDnControlTest.php new file mode 100644 index 00000000..e43df92f --- /dev/null +++ b/tests/unit/Control/Ad/ExtendedDnControlTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\ExtendedDnControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class ExtendedDnControlTest extends TestCase +{ + private ExtendedDnControl $subject; + + protected function setUp(): void + { + $this->subject = new ExtendedDnControl(); + } + + public function test_it_should_set_whether_or_not_to_use_hex_format(): void + { + $this->subject->setUseHexFormat(true); + + self::assertTrue($this->subject->getUseHexFormat()); + } + + public function test_it_should_not_use_hex_format_by_default(): void + { + self::assertFalse($this->subject->getUseHexFormat()); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_EXTENDED_DN), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1) + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new ExtendedDnControl(), + ExtendedDnControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_EXTENDED_DN), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1) + ))) + ))->setValue(null) + ); + } + + public function test_it_should_be_constructed_from_asn1_when_the_empty_value_form_is_used(): void + { + self::assertEquals( + new ExtendedDnControl(), + ExtendedDnControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_EXTENDED_DN), + Asn1::boolean(false) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/Control/Ad/PolicyHintsControlTest.php b/tests/unit/Control/Ad/PolicyHintsControlTest.php new file mode 100644 index 00000000..07cc07b8 --- /dev/null +++ b/tests/unit/Control/Ad/PolicyHintsControlTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\PolicyHintsControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class PolicyHintsControlTest extends TestCase +{ + private PolicyHintsControl $subject; + + protected function setUp(): void + { + $this->subject = new PolicyHintsControl(); + } + + public function test_it_should_be_enabled_by_default(): void + { + self::assertTrue($this->subject->getIsEnabled()); + } + + public function test_it_should_set_whether_or_not_it_is_enabled(): void + { + $this->subject->setIsEnabled(false); + + self::assertFalse($this->subject->getIsEnabled()); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->toAsn1(), + Asn1::sequence( + Asn1::octetString(Control::OID_POLICY_HINTS), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1) + ))) + ) + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new PolicyHintsControl(), + PolicyHintsControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_POLICY_HINTS), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1) + ))) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/Control/Ad/SdFlagsControlTest.php b/tests/unit/Control/Ad/SdFlagsControlTest.php new file mode 100644 index 00000000..3ccb6674 --- /dev/null +++ b/tests/unit/Control/Ad/SdFlagsControlTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\SdFlagsControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; + +class SdFlagsControlTest extends \PHPUnit\Framework\TestCase +{ + protected SdFlagsControl $subject; + protected function setUp(): void + { + $this->subject = new SdFlagsControl(SdFlagsControl::DACL_SECURITY_INFORMATION); + } + + public function test_it_should_get_the_flags(): void + { + self::assertSame( + SdFlagsControl::DACL_SECURITY_INFORMATION, + $this->subject->getFlags(), + ); + } + + public function test_it_should_set_the_flags(): void + { + $this->subject->setFlags(SdFlagsControl::SACL_SECURITY_INFORMATION); + + self::assertSame( + SdFlagsControl::SACL_SECURITY_INFORMATION, + $this->subject->getFlags(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->toAsn1(), + Asn1::sequence( + Asn1::octetString(Control::OID_SD_FLAGS), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(4) + ))) + ) + ); + } +} diff --git a/tests/unit/Control/Ad/SetOwnerControlTest.php b/tests/unit/Control/Ad/SetOwnerControlTest.php new file mode 100644 index 00000000..9b359b83 --- /dev/null +++ b/tests/unit/Control/Ad/SetOwnerControlTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Ad; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Ad\SetOwnerControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SetOwnerControlTest extends TestCase +{ + private SetOwnerControl $subject; + + protected function setUp(): void + { + $this->subject = new SetOwnerControl('foo'); + } + + public function test_it_should_get_the_sid(): void + { + self::assertSame( + 'foo', + $this->subject->getSid(), + ); + } + + public function test_it_should_set_the_sid(): void + { + $this->subject->setSid('bar'); + + self::assertSame( + 'bar', + $this->subject->getSid(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->toAsn1(), + Asn1::sequence( + Asn1::octetString(Control::OID_SET_OWNER), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::octetString('foo'))) + ) + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SetOwnerControl('foo'), + SetOwnerControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SET_OWNER), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::octetString('foo'))) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/Control/ControlBagTest.php b/tests/unit/Control/ControlBagTest.php new file mode 100644 index 00000000..6ba3976e --- /dev/null +++ b/tests/unit/Control/ControlBagTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control; + +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Control\Sync\SyncRequestControl; +use PHPUnit\Framework\TestCase; + +class ControlBagTest extends TestCase +{ + private ControlBag $subject; + + protected function setUp(): void + { + $this->subject = new ControlBag( + new Control('foo'), + new Control('bar'), + ); + } + + public function test_it_should_get_the_control_count(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_get_a_control_by_string(): void + { + self::assertEquals( + new Control('foo'), + $this->subject->get('foo'), + ); + } + + public function test_it_should_return_null_on_a_control_that_doesnt_exist(): void + { + self::assertNull($this->subject->get('foobar')); + } + + public function test_it_should_add_a_control(): void + { + $this->subject->add(new Control('foobar')); + + self::assertTrue($this->subject->has('foobar'));; + } + + public function test_it_should_check_if_a_control_exists_with_has(): void + { + self::assertFalse($this->subject->has('foobar')); + self::assertTrue($this->subject->has('bar')); + } + + public function test_it_should_check_if_a_control_exists_by_an_object_check(): void + { + $foobar = new Control('foobar'); + + self::assertFalse($this->subject->has($foobar)); + + $this->subject->add($foobar); + + self::assertTrue($this->subject->has($foobar)); + } + + public function test_it_should_remove_a_control_by_string(): void + { + $this->subject->remove('foo'); + + self::assertFalse($this->subject->has('foo')); + } + + public function test_it_should_remove_a_control_by_object(): void + { + $foobar = new Control('foobar'); + $this->subject->add($foobar); + + self::assertTrue($this->subject->has($foobar)); + + $this->subject->remove($foobar); + + self::assertFalse($this->subject->has($foobar)); + } + + public function test_it_should_get_the_controls_as_an_array(): void + { + self::assertEquals( + [ + new Control('foo'), + new Control('bar'), + ], + $this->subject->toArray(), + ); + } + + public function test_it_should_reset_the_controls(): void + { + $this->subject->reset(); + + self::assertCount( + 0, + $this->subject, + ); + } + + public function test_it_should_get_a_control_by_its_class_name(): void + { + $this->subject->add(new SyncRequestControl()); + + self::assertInstanceOf( + SyncRequestControl::class, + $this->subject->getByClass(SyncRequestControl::class), + ); + } + + public function test_it_should_return_null_if_a_control_by_its_class_name_does_not_exist(): void + { + self::assertNull($this->subject->get(SyncRequestControl::class)); + } +} diff --git a/tests/unit/Control/ControlTest.php b/tests/unit/Control/ControlTest.php new file mode 100644 index 00000000..93950232 --- /dev/null +++ b/tests/unit/Control/ControlTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use PHPUnit\Framework\TestCase; + +class ControlTest extends TestCase +{ + private Control $subject; + + protected function setUp(): void + { + $this->subject = new Control('foo'); + } + + public function test_it_should_get_the_control_type(): void + { + $this->subject->setTypeOid('bar'); + + self::assertSame( + 'bar', + $this->subject->getTypeOid(), + ); + } + + public function test_it_should_get_the_control_value(): void + { + $this->subject->setValue('bar'); + + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_get_the_criticality(): void + { + $this->subject->setCriticality(true); + + self::assertTrue( + $this->subject->getCriticality(), + ); + } + + public function test_it_should_have_a_string_representation_of_the_oid_type(): void + { + self::assertSame( + 'foo', + (string) $this->subject, + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + self::assertEquals( + Asn1::sequence( + Asn1::octetString('foo'), + Asn1::boolean(false) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_ASN1(): void + { + $result = Control::fromAsn1(Asn1::sequence( + Asn1::octetString('foobar'), + Asn1::boolean(true) + )); + + self::assertSame( + 'foobar', + $result->getTypeOid(), + ); + self::assertTrue( + $result->getCriticality(), + ); + } +} diff --git a/tests/unit/Control/PagingControlTest.php b/tests/unit/Control/PagingControlTest.php new file mode 100644 index 00000000..55dcce61 --- /dev/null +++ b/tests/unit/Control/PagingControlTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class PagingControlTest extends TestCase +{ + private PagingControl $subject; + + protected function setUp(): void + { + $this->subject = new PagingControl( + size: 0, + cookie: 'foo', + ); + } + + public function test_it_should_default_to_an_empty_cookie_on_construction(): void + { + $this->subject = new PagingControl(0); + + self::assertSame( + '', + $this->subject->getCookie(), + ); + } + + public function test_it_should_set_the_size(): void + { + $this->subject->setSize(1); + + self::assertSame( + 1, + $this->subject->getSize(), + ); + } + + public function test_it_should_set_the_cookie(): void + { + $this->subject->setCookie('foo'); + + self::assertSame( + 'foo', + $this->subject->getCookie(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_PAGING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(0), + Asn1::octetString('foo') + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = PagingControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_PAGING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1), + Asn1::octetString('foobar') + ))) + )); + + self::assertSame( + 1, + $result->getSize(), + ); + self::assertSame( + 'foobar', + $result->getCookie(), + ); + } +} diff --git a/tests/unit/Control/PwdPolicyResponseControlTest.php b/tests/unit/Control/PwdPolicyResponseControlTest.php new file mode 100644 index 00000000..196f8406 --- /dev/null +++ b/tests/unit/Control/PwdPolicyResponseControlTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\PwdPolicyResponseControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class PwdPolicyResponseControlTest extends TestCase +{ + private PwdPolicyResponseControl $subject; + + protected function setUp(): void + { + $this->subject = new PwdPolicyResponseControl( + timeBeforeExpiration: 1, + graceAuthRemaining: 2, + error: 3, + ); + } + + public function test_it_should_get_the_error(): void + { + self::assertSame( + 3, + $this->subject->getError(), + ); + } + + public function test_it_should_get_the_time_before_expiration(): void + { + self::assertSame( + 1, + $this->subject->getTimeBeforeExpiration(), + ); + } + + public function test_it_should_get_the_grace_attempts_remaining(): void + { + self::assertSame( + 2, + $this->subject->getGraceAttemptsRemaining(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = PwdPolicyResponseControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_PWD_POLICY), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(0, Asn1::sequence(Asn1::context(0, Asn1::integer(100)))), + Asn1::context(1, Asn1::enumerated(2)) + ))) + )); + + self::assertSame( + 100, + $result->getTimeBeforeExpiration() + ); + self::assertSame( + 2, + $result->getError() + ); + self::assertSame( + Control::OID_PWD_POLICY, + $result->getTypeOid() + ); + self::assertFalse($result->getCriticality()); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + $this->subject = new PwdPolicyResponseControl( + timeBeforeExpiration: 100, + graceAuthRemaining: null, + error: 2, + ); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_PWD_POLICY), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(0, Asn1::sequence(Asn1::context(0, Asn1::integer(100)))), + Asn1::context(1, Asn1::enumerated(2)) + ))) + ), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Control/Sorting/SortKeyTest.php b/tests/unit/Control/Sorting/SortKeyTest.php new file mode 100644 index 00000000..0b111f85 --- /dev/null +++ b/tests/unit/Control/Sorting/SortKeyTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sorting; + +use FreeDSx\Ldap\Control\Sorting\SortKey; +use PHPUnit\Framework\TestCase; + +class SortKeyTest extends TestCase +{ + private SortKey $subject; + + protected function setUp(): void + { + $this->subject = new SortKey('cn'); + } + + public function test_it_should_be_constructed_via_reverse_order(): void + { + $this->subject = new SortKey(attribute: 'cn', useReverseOrder: true); + + self::assertTrue($this->subject->getUseReverseOrder()); + } + + public function test_it_should_be_constructed_ascending(): void + { + $this->subject = SortKey::ascending('foo'); + + self::assertFalse($this->subject->getUseReverseOrder()); + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_be_constructed_descending(): void + { + $this->subject = SortKey::descending('foo'); + + self::assertTrue($this->subject->getUseReverseOrder()); + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_not_use_reverse_order_by_default(): void + { + self::assertFalse($this->subject->getUseReverseOrder()); + } + + public function test_it_should_set_the_attribute_to_use(): void + { + $this->subject->setAttribute('foo'); + + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_set_the_ordering_rule(): void + { + $this->subject->setOrderingRule('foo'); + + self::assertSame( + 'foo', + $this->subject->getOrderingRule(), + ); + } + + public function test_it_should_set_whether_to_use_reverse_order(): void + { + $this->subject->setUseReverseOrder(true); + + self::assertTrue($this->subject->getUseReverseOrder()); + } +} diff --git a/tests/unit/Control/Sorting/SortingControlTest.php b/tests/unit/Control/Sorting/SortingControlTest.php new file mode 100644 index 00000000..d6294fa6 --- /dev/null +++ b/tests/unit/Control/Sorting/SortingControlTest.php @@ -0,0 +1,161 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sorting; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sorting\SortingControl; +use FreeDSx\Ldap\Control\Sorting\SortKey; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SortingControlTest extends TestCase +{ + private SortingControl $subject; + + protected function setUp(): void + { + $this->subject = new SortingControl( + new SortKey('foo'), + new SortKey('bar'), + ); + } + + public function test_it_should_have_the_sorting_oid(): void + { + self::assertSame( + Control::OID_SORTING, + $this->subject->getTypeOid(), + ); + } + + public function test_it_should_get_the_sort_keys(): void + { + self::assertEquals( + [ + new SortKey('foo'), + new SortKey('bar'), + ], + $this->subject->getSortKeys(), + ); + } + + public function test_it_should_set_sort_keys(): void + { + $this->subject->setSortKeys(new SortKey('foobar')); + + self::assertEquals( + [new SortKey('foobar')], + $this->subject->getSortKeys(), + ); + } + + public function test_it_should_add_sort_keys(): void + { + $key = new SortKey('foobar'); + $this->subject->addSortKeys($key); + + self::assertContains( + $key, + $this->subject->getSortKeys(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $this->subject->addSortKeys( + new SortKey( + 'foobar', + true, + 'bleh' + ) + ); + + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SORTING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequenceOf( + Asn1::sequence(Asn1::octetString('foo')), + Asn1::sequence(Asn1::octetString('bar')), + Asn1::sequence( + Asn1::octetString('foobar'), + Asn1::context(0, Asn1::octetString('bleh')), + Asn1::context(1, Asn1::boolean(true)) + ) + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SortingControl( + new SortKey('foo'), + new SortKey('bar'), + new SortKey('foobar', true, 'bleh') + ), + SortingControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SORTING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequenceOf( + Asn1::sequence(Asn1::octetString('foo')), + Asn1::sequence(Asn1::octetString('bar')), + Asn1::sequence( + Asn1::octetString('foobar'), + Asn1::context(0, Asn1::octetString('bleh')), + Asn1::context(1, Asn1::boolean(true)) + ) + ))) + ))->setValue(null) + ); + } + + public function test_it_should_throw_an_error_parsing_sorting_keys_with_no_attribute(): void + { + $encoder = new LdapEncoder(); + + self::expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SORTING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequenceOf( + Asn1::sequence(Asn1::octetString('')) + ))) + )); + } + + public function test_it_should_throw_an_error_parsing_sorting_keys_with_unexpected_values(): void + { + $encoder = new LdapEncoder(); + + self::expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SORTING), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequenceOf( + Asn1::sequence(Asn1::octetString('foo'), Asn1::enumerated(1)) + ))) + )); + } +} diff --git a/tests/unit/Control/Sorting/SortingResponseControlTest.php b/tests/unit/Control/Sorting/SortingResponseControlTest.php new file mode 100644 index 00000000..4870a22e --- /dev/null +++ b/tests/unit/Control/Sorting/SortingResponseControlTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sorting; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sorting\SortingResponseControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SortingResponseControlTest extends TestCase +{ + protected SortingResponseControl $subject; + + protected function setUp(): void + { + $this->subject = new SortingResponseControl( + result: 0, + attribute: 'cn' + ); + } + + public function test_it_should_get_the_result(): void + { + self::assertSame( + 0, + $this->subject->getResult(), + ); + } + + public function test_it_should_get_the_attribute(): void + { + self::assertSame( + 'cn', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SortingResponseControl(0, 'cn'), + SortingResponseControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SORTING_RESPONSE), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('cn') + ))) + ))->setValue(null) + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SORTING_RESPONSE), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(0), + Asn1::context(0, Asn1::octetString('cn')) + ))) + ), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Control/SubentriesControlTest.php b/tests/unit/Control/SubentriesControlTest.php new file mode 100644 index 00000000..4bad164c --- /dev/null +++ b/tests/unit/Control/SubentriesControlTest.php @@ -0,0 +1,72 @@ +subject = new SubentriesControl(); + } + + public function test_it_should_have_a_default_visibility_of_true(): void + { + self::assertTrue($this->subject->getIsVisible()); + } + + public function test_it_should_set_the_visibility(): void + { + $this->subject->setIsVisible(false); + + self::assertFalse($this->subject->getIsVisible()); + } + + public function test_it_should_have_the_subentries_oid(): void + { + self::assertSame( + Control::OID_SUBENTRIES, + $this->subject->getTypeOid(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SUBENTRIES), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::boolean(true))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = SubentriesControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SUBENTRIES), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::boolean(true))) + )); + + self::assertTrue($result->getIsVisible()); + self::assertSame( + Control::OID_SUBENTRIES, + $result->getTypeOid() + ); + } +} diff --git a/tests/unit/Control/Sync/SyncDoneControlTest.php b/tests/unit/Control/Sync/SyncDoneControlTest.php new file mode 100644 index 00000000..936d8eb8 --- /dev/null +++ b/tests/unit/Control/Sync/SyncDoneControlTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sync; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sync\SyncDoneControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SyncDoneControlTest extends TestCase +{ + private SyncDoneControl $subject; + + protected function setUp(): void + { + $this->subject = new SyncDoneControl( + cookie: 'omnomnom', + refreshDeletes: false, + ); + } + + public function test_it_should_get_refresh_deletes(): void + { + self::assertFalse($this->subject->getRefreshDeletes()); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_DONE), + Asn1::boolean(true), + Asn1::octetString( + $encoder->encode(Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + )) + ) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = SyncDoneControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_DONE), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))) + )); + + self::assertFalse($result->getRefreshDeletes()); + self::assertSame( + 'omnomnom', + $result->getCookie(), + ); + self::assertSame( + Control::OID_SYNC_DONE, + $result->getTypeOid(), + ); + } +} diff --git a/tests/unit/Control/Sync/SyncRequestControlTest.php b/tests/unit/Control/Sync/SyncRequestControlTest.php new file mode 100644 index 00000000..1b48adbf --- /dev/null +++ b/tests/unit/Control/Sync/SyncRequestControlTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sync; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sync\SyncRequestControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SyncRequestControlTest extends TestCase +{ + private SyncRequestControl $subject; + + protected function setUp(): void + { + $this->subject = new SyncRequestControl( + mode: 1, + cookie: 'omnomnom' + ); + } + + public function test_it_should_get_the_mode(): void + { + self::assertSame( + 1, + $this->subject->getMode(), + ); + } + + public function test_it_should_get_the_reload_hint(): void + { + self::assertFalse($this->subject->getReloadHint()); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_REQUEST), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(1), + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = SyncRequestControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_REQUEST), + Asn1::boolean(true), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(1), + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))) + )); + + + self::assertSame( + 1, + $result->getMode(), + ); + self::assertSame( + 'omnomnom', + $result->getCookie(), + ); + self::assertSame( + Control::OID_SYNC_REQUEST, + $result->getTypeOid(), + ); + self::assertFalse($result->getReloadHint()); + } +} diff --git a/tests/unit/Control/Sync/SyncStateControlTest.php b/tests/unit/Control/Sync/SyncStateControlTest.php new file mode 100644 index 00000000..61d87356 --- /dev/null +++ b/tests/unit/Control/Sync/SyncStateControlTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Sync; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sync\SyncStateControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class SyncStateControlTest extends TestCase +{ + private SyncStateControl $subject; + + protected function setUp(): void + { + $this->subject = new SyncStateControl( + state: 0, + entryUuid: 'foo', + cookie: 'omnomnom', + ); + } + + public function test_it_should_get_the_state(): void + { + self::assertSame( + 0, + $this->subject->getState(), + ); + } + + public function test_it_should_get_the_entry_uuid(): void + { + self::assertSame( + 'foo', + $this->subject->getEntryUuid(), + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_tell_if_it_is_for_a_present_state(): void + { + $this->subject = new SyncStateControl( + state: SyncStateControl::STATE_PRESENT, + entryUuid: 'foo', + ); + + self::assertTrue($this->subject->isPresent()); + } + + public function test_it_should_tell_if_it_is_for_an_add_state(): void + { + $this->subject = new SyncStateControl( + state: SyncStateControl::STATE_ADD, + entryUuid: 'foo', + ); + + self::assertTrue($this->subject->isAdd()); + } + + public function test_it_should_tell_if_it_is_for_a_modify_state(): void + { + $this->subject = new SyncStateControl( + state: SyncStateControl::STATE_MODIFY, + entryUuid: 'foo', + ); + + self::assertTrue($this->subject->isModify()); + } + + public function test_it_should_tell_if_it_is_for_a_delete_state(): void + { + $this->subject = new SyncStateControl( + state: SyncStateControl::STATE_DELETE, + entryUuid: 'foo', + ); + + self::assertTrue($this->subject->isDelete()); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_STATE), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('foo'), + Asn1::octetString('omnomnom') + ))) + ), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $result = SyncStateControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_SYNC_STATE), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('foo'), + Asn1::octetString('omnomnom') + ))) + )); + + self::assertSame( + 0, + $result->getState(), + ); + self::assertSame( + 'foo', + $result->getEntryUuid(), + ); + self::assertSame( + 'omnomnom', + $result->getCookie(), + ); + self::assertSame( + Control::OID_SYNC_STATE, + $result->getTypeOid(), + ); + } +} diff --git a/tests/unit/Control/Vlv/VlvControlTest.php b/tests/unit/Control/Vlv/VlvControlTest.php new file mode 100644 index 00000000..13d6864f --- /dev/null +++ b/tests/unit/Control/Vlv/VlvControlTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Vlv; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Vlv\VlvControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class VlvControlTest extends TestCase +{ + private VlvControl $subject; + + protected function setUp(): void + { + $this->subject = new VlvControl( + before: 10, + after: 9, + offset: 8, + count: 0, + ); + } + + public function test_it_should_have_a_count_of_zero_by_default(): void + { + self::assertSame( + 0, + $this->subject->getCount(), + ); + } + + public function test_it_should_get_and_set_the_value_for_after(): void + { + $this->subject->setAfter(9); + + self::assertSame( + 9, + $this->subject->getAfter(), + ); + } + + public function test_it_should_get_and_set_the_value_for_before(): void + { + $this->subject->setBefore(20); + + self::assertSame( + 20, + $this->subject->getBefore(), + ); + } + + public function test_it_should_get_and_set_the_value_for_the_offset(): void + { + $this->subject->setOffset(16); + + self::assertSame( + 16, + $this->subject->getOffset(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::sequence( + Asn1::octetString(Control::OID_VLV), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(10), + Asn1::integer(9), + Asn1::context(0, Asn1::sequence( + Asn1::integer(8), + Asn1::integer(0) + )) + ))) + ), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Control/Vlv/VlvResponseControlTest.php b/tests/unit/Control/Vlv/VlvResponseControlTest.php new file mode 100644 index 00000000..629445b7 --- /dev/null +++ b/tests/unit/Control/Vlv/VlvResponseControlTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Control\Vlv; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Vlv\VlvResponseControl; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +class VlvResponseControlTest extends TestCase +{ + private VlvResponseControl $subject; + + protected function setUp(): void + { + $this->subject = new VlvResponseControl( + offset: 10, + count: 9, + result: 0, + ); + } + + public function test_it_should_get_the_offset(): void + { + self::assertSame( + 10, + $this->subject->getOffset(), + ); + } + + public function test_it_should_get_the_count(): void + { + self::assertSame( + 9, + $this->subject->getCount(), + ); + } + + public function test_it_should_get_the_context_id(): void + { + self::assertNull($this->subject->getContextId()); + } + + public function test_it_should_get_the_result(): void + { + self::assertSame( + 0, + $this->subject->getResult(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new VlvResponseControl(1, 300, 0, 'foo'), + VlvResponseControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_VLV_RESPONSE), + Asn1::boolean(false), + Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1), + Asn1::integer(300), + Asn1::enumerated(0), + Asn1::octetString('foo') + ))) + ))->setValue(null) + ); + } +} diff --git a/tests/unit/ControlsTest.php b/tests/unit/ControlsTest.php new file mode 100644 index 00000000..5087e548 --- /dev/null +++ b/tests/unit/ControlsTest.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; +use FreeDSx\Ldap\Control\Ad\ExpectedEntryCountControl; +use FreeDSx\Ldap\Control\Ad\ExtendedDnControl; +use FreeDSx\Ldap\Control\Ad\PolicyHintsControl; +use FreeDSx\Ldap\Control\Ad\SdFlagsControl; +use FreeDSx\Ldap\Control\Ad\SetOwnerControl; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Control\Sorting\SortingControl; +use FreeDSx\Ldap\Control\Sorting\SortKey; +use FreeDSx\Ldap\Control\SubentriesControl; +use FreeDSx\Ldap\Control\Sync\SyncRequestControl; +use FreeDSx\Ldap\Control\Vlv\VlvControl; +use FreeDSx\Ldap\Controls; +use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +class ControlsTest extends TestCase +{ + public function testItShouldCreateAPagingControl(): void + { + $result = Controls::paging(100); + $expected = new PagingControl(100, ''); + + $this->assertEquals( + $expected, + $result + ); + } + + public function testItShouldCreateAVlvOffsetControl(): void + { + $result = Controls::vlv(10, 12); + $expected = new VlvControl(10, 12, 1, 0); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAVlvFilterControl(): void + { + $result = Controls::vlvFilter( + 10, + 12, + Filters::gte('foo', 'bar') + ); + $expected = new VlvControl( + 10, + 12, + null, + null, + new GreaterThanOrEqualFilter('foo', 'bar') + ); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAnSdFlagsControl(): void + { + $result = Controls::sdFlags(7); + $expected = new SdFlagsControl(7); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAPasswordPolicyControl(): void + { + $result = Controls::pwdPolicy(); + $expected = new Control(Control::OID_PWD_POLICY, true); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateASubtreeDeleteControl(): void + { + $result = Controls::subtreeDelete(); + $expected = new Control(Control::OID_SUBTREE_DELETE); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateASortingControlUsingAString(): void + { + $result = Controls::sort('cn'); + $expected = new SortingControl(new SortKey('cn')); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateASortingControlUsingASortKey(): void + { + $result = Controls::sort(new SortKey('foo')); + $expected = new SortingControl(new SortKey('foo')); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAnExtendedDnControl(): void + { + $result = Controls::extendedDn(); + $expected = new ExtendedDnControl(); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateADirSyncControl(): void + { + $result = Controls::dirSync(); + $expected = new DirSyncRequestControl(); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateADirSyncControlWithOptions(): void + { + $result = Controls::dirSync( + DirSyncRequestControl::FLAG_INCREMENTAL_VALUES + | DirSyncRequestControl::FLAG_OBJECT_SECURITY, + 'foo' + ); + $expected = new DirSyncRequestControl( + DirSyncRequestControl::FLAG_INCREMENTAL_VALUES + | DirSyncRequestControl::FLAG_OBJECT_SECURITY, + 'foo' + ); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAnExpectedEntryCountControl(): void + { + $result = Controls::expectedEntryCount(1, 100); + $expected = new ExpectedEntryCountControl(1, 100); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAPolicyHintsControl(): void + { + $result = Controls::policyHints(); + $expected = new PolicyHintsControl(); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateASetOwnersControl(): void + { + $result = Controls::setOwner('foo'); + $expected = new SetOwnerControl('foo'); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAShowDeletedControl(): void + { + $result = Controls::showDeleted(); + $expected = new Control(Control::OID_SHOW_DELETED, true); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAShowRecycledControl(): void + { + $result = Controls::showRecycled(); + $expected = new Control(Control::OID_SHOW_RECYCLED, true); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateASubentriesControl(): void + { + $result = Controls::subentries(); + $expected = new SubentriesControl(true); + + $this->assertEquals($expected, $result); + } + + public function testItShouldCreateAManageDsaItControl(): void + { + $result = Controls::manageDsaIt(); + $expected = new Control(Control::OID_MANAGE_DSA_IT, true); + + $this->assertEquals($expected, $result); + } + + public function testItShouldGetASyncRequestControl(): void + { + $result = Controls::syncRequest(); + $expected = new SyncRequestControl(); + + $this->assertEquals($expected, $result); + + $result = Controls::syncRequest( + 'foo', + SyncRequestControl::MODE_REFRESH_AND_PERSIST + ); + $expected = new SyncRequestControl( + SyncRequestControl::MODE_REFRESH_AND_PERSIST, + 'foo', + ); + + $this->assertEquals($expected, $result); + } +} diff --git a/tests/unit/Entry/AttributeTest.php b/tests/unit/Entry/AttributeTest.php new file mode 100644 index 00000000..b69b646d --- /dev/null +++ b/tests/unit/Entry/AttributeTest.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Option; +use FreeDSx\Ldap\Entry\Options; +use PHPUnit\Framework\TestCase; + +class AttributeTest extends TestCase +{ + private Attribute $subject; + + protected function setUp(): void + { + $this->subject = new Attribute( + 'cn', + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_name(): void + { + self::assertSame('cn', $this->subject->getName()); + + $this->subject->getOptions()->add('foo'); + + self::assertSame( + 'cn', + $this->subject->getName() + ); + } + + public function test_it_should_get_the_complete_attribute_description(): void + { + self::assertSame( + 'cn', + $this->subject->getDescription() + ); + + $this->subject->getOptions()->add('foo'); + + self::assertSame( + 'cn;foo', + $this->subject->getDescription() + ); + } + + public function test_it_should_return_false_for_hasOptions_when_there_are_none(): void + { + self::assertFalse($this->subject->hasOptions()); + } + + public function test_it_should_get_options(): void + { + self::assertEquals( + new Options(), + $this->subject->getOptions(), + ); + } + + public function test_it_should_get_the_values(): void + { + self::assertSame( + ['foo', 'bar'], + $this->subject->getValues(), + ); + } + + public function test_it_should_get_the_first_value_if_it_exists(): void + { + self::assertSame( + 'foo', + $this->subject->firstValue(), + ); + } + + public function test_it_should_get_the_last_value_if_it_exists(): void + { + self::assertSame( + 'bar', + $this->subject->lastValue(), + ); + } + + public function test_it_should_get_null_if_the_first_value_does_not_exist(): void + { + $this->subject = new Attribute('foo'); + + self::assertNull($this->subject->firstValue());; + } + + public function test_it_should_get_null_if_the_last_value_does_not_exist(): void + { + $this->subject = new Attribute('foo'); + + self::assertNull($this->subject->lastValue()); + } + + public function test_it_should_have_a_string_representation(): void + { + self::assertSame( + 'foo, bar', + (string) $this->subject, + ); + } + + public function test_it_should_get_a_count_of_values(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_add_values(): void + { + $this->subject->add('foobar', 'meh'); + + self::assertSame( + ['foo', 'bar', 'foobar', 'meh'], + $this->subject->getValues(), + ); + } + + public function test_it_should_remove_values(): void + { + $this->subject->remove('bar'); + + self::assertSame( + ['foo'], + $this->subject->getValues(), + ); + } + + public function test_it_should_set_values(): void + { + $this->subject->set('foo'); + + self::assertSame( + ['foo'], + $this->subject->getValues(), + ); + } + + public function test_it_should_reset_values(): void + { + $this->subject->reset(); + + self::assertEmpty($this->subject->getValues()); + } + + public function test_it_should_check_if_a_value_exists(): void + { + self::assertTrue($this->subject->has('foo')); + self::assertFalse($this->subject->has('bleh')); + } + + public function test_it_should_check_if_it_equals_another_attribute(): void + { + self::assertTrue($this->subject->equals(new Attribute('cn'))); + self::assertTrue($this->subject->equals(new Attribute('CN'))); + self::assertFalse($this->subject->equals(new Attribute('foo'))); + } + + public function test_it_should_check_if_it_equals_another_attribute_with_options(): void + { + self::assertFalse($this->subject->equals(new Attribute('cn;foo'))); + + $this->subject->getOptions()->add('foo'); + + self::assertTrue($this->subject->equals(new Attribute('cn;foo'))); + } + + public function test_it_should_be_check_equality_with_the_name_only_by_default(): void + { + $this->subject->getOptions()->add('foo'); + + self::assertTrue($this->subject->equals(new Attribute('cn'))); + } + + public function test_it_should_be_check_equality_with_name_and_options_when_strict_is_set(): void + { + $this->subject->getOptions()->add('foo'); + + self::assertFalse($this->subject->equals(new Attribute('cn'), true));; + } + + public function test_it_should_escape_a_value(): void + { + self::assertSame( + '\28foo=\2a\5cbar\29\00', + Attribute::escape("(foo=*\bar)\x00") + ); + } + + public function test_it_should_escape_a_value_to_complete_hex(): void + { + self::assertSame( + '\66\6f\6f\62\61\72', + Attribute::escapeAll("foobar"), + ); + } + + public function test_it_should_ignore_an_empty_value_when_escaping(): void + { + self::assertSame( + '', + Attribute::escape(''), + ); + } + + public function test_it_should_not_escape_a_string_that_is_already_hex_encoded(): void + { + self::assertSame( + '\66\6f\6f\62\61\72', + Attribute::escape('\66\6f\6f\62\61\72'), + ); + } + + public function test_it_should_parse_options_in_the_attribute(): void + { + $this->subject = new Attribute( + 'foo;lang-en-us', + 'bar', + ); + + self::assertSame( + 'foo', + $this->subject->getName(), + ); + self::assertEquals( + new Options(new Option('lang-en-us')), + $this->subject->getOptions(), + ); + } +} diff --git a/tests/unit/Entry/ChangeTest.php b/tests/unit/Entry/ChangeTest.php new file mode 100644 index 00000000..a86d02ec --- /dev/null +++ b/tests/unit/Entry/ChangeTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Change; +use PHPUnit\Framework\TestCase; + +class ChangeTest extends TestCase +{ + private Change $subject; + + protected function setUp(): void + { + $this->subject = new Change( + Change::TYPE_REPLACE, + new Attribute('foo', 'bar') + ); + } + + public function test_it_should_get_the_mod_type(): void + { + self::assertSame( + Change::TYPE_REPLACE, + $this->subject->getType(), + ); + } + + public function test_it_should_set_the_mod_type(): void + { + $this->subject->setType(Change::TYPE_ADD); + + self::assertSame( + Change::TYPE_ADD, + $this->subject->getType(), + ); + } + + public function test_it_should_get_the_attribute(): void + { + self::assertEquals( + new Attribute('foo', 'bar'), + $this->subject->getAttribute(), + ); + } + + public function test_it_should_set_the_attribute(): void + { + $this->subject->setAttribute(new Attribute( + 'foo', + 'bar' + )); + + self::assertEquals( + new Attribute('foo', 'bar'), + $this->subject->getAttribute(), + ); + } + + public function test_it_should_construct_a_reset_change(): void + { + $this->subject = Change::reset('foo'); + + self::assertEquals( + new Attribute('foo'), + $this->subject->getAttribute(), + ); + self::assertSame( + Change::TYPE_DELETE, + $this->subject->getType(), + ); + } + + public function test_it_should_construct_an_add_change(): void + { + $this->subject = Change::add('foo', 'bar'); + + self::assertEquals( + new Attribute('foo', 'bar'), + $this->subject->getAttribute(), + ); + self::assertSame( + Change::TYPE_ADD, + $this->subject->getType(), + ); + } + + public function test_it_should_construct_a_delete_change(): void + { + $this->subject = Change::delete('foo', 'bar', 'foobar'); + + self::assertEquals( + new Attribute('foo', 'bar', 'foobar'), + $this->subject->getAttribute(), + ); + self::assertSame( + Change::TYPE_DELETE, + $this->subject->getType(), + ); + } + + public function test_it_should_construct_a_replace_change(): void + { + $this->subject = Change::replace('foo', 'bar'); + + self::assertEquals( + new Attribute('foo', 'bar'), + $this->subject->getAttribute(), + ); + self::assertSame( + Change::TYPE_REPLACE, + $this->subject->getType(), + ); + } + + public function test_it_should_check_whether_it_is_an_add(): void + { + self::assertFalse($this->subject->isAdd()); + + $this->subject->setType(Change::TYPE_ADD); + + self::assertTrue($this->subject->isAdd()); + } + + public function test_it_should_check_whether_it_is_a_delete(): void + { + self::assertFalse($this->subject->isDelete()); + + $this->subject->setType(Change::TYPE_DELETE); + + self::assertTrue($this->subject->isDelete()); + } + + public function test_it_should_check_whether_it_is_a_replace(): void + { + $this->subject->setType(Change::TYPE_REPLACE); + + self::assertTrue($this->subject->isReplace()); + } + + public function test_it_should_check_whether_it_is_a_reset(): void + { + self::assertFalse($this->subject->isReset()); + + $this->subject->setType(Change::TYPE_DELETE); + + self::assertFalse($this->subject->isReset()); + + $this->subject->setAttribute(new Attribute('foo')); + + self::assertTrue($this->subject->isReset()); + } +} diff --git a/tests/unit/Entry/ChangesSpec.php b/tests/unit/Entry/ChangesSpec.php new file mode 100644 index 00000000..0a427203 --- /dev/null +++ b/tests/unit/Entry/ChangesSpec.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Changes; +use PHPUnit\Framework\TestCase; + +class ChangesSpec extends TestCase +{ + private Changes $subject; + + protected function setUp(): void + { + $this->subject = new Changes( + Change::replace( + 'foo', + 'bar', + ), + Change::delete('bar'), + ); + } + + public function test_it_should_add_changes(): void + { + $change = Change::add('sn', 'foo'); + + $this->subject->add($change); + + self::assertContains( + $change, + $this->subject->toArray(), + ); + } + + public function test_it_should_remove_changes(): void + { + $change = Change::add('sn', 'foo'); + + $this->subject->add($change); + $this->subject->remove($change); + + self::assertNotContains( + $change, + $this->subject->toArray(), + ); + } + + public function test_it_should_reset_changes(): void + { + $this->subject->reset(); + + self::assertCount( + 0, + $this->subject, + ); + } + + public function test_it_should_get_the_count_of_changes(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_get_the_changes_as_an_array(): void + { + self::assertEquals( + [ + Change::replace('foo', 'bar'), + Change::delete('bar') + ], + $this->subject->toArray(), + ); + } +} diff --git a/tests/unit/Entry/DnTest.php b/tests/unit/Entry/DnTest.php new file mode 100644 index 00000000..517dee62 --- /dev/null +++ b/tests/unit/Entry/DnTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Entry\Rdn; +use PHPUnit\Framework\TestCase; + +class DnTest extends TestCase +{ + private Dn $subject; + + protected function setUp(): void + { + $this->subject = new Dn('cn=fo\,o, dc=local,dc=example'); + } + + public function test_it_should_get_all_pieces_as_an_array_of_RDNs(): void + { + self::assertEquals( + [ + new Rdn("cn", "fo\,o"), + new Rdn("dc", "local"), + new Rdn("dc", "example"), + ], + $this->subject->toArray(), + ); + } + + public function test_it_should_get_the_parent_dn(): void + { + self::assertEquals( + new Dn('dc=local,dc=example'), + $this->subject->getParent(), + ); + } + + public function test_it_should_get_the_rdn(): void + { + self::assertEquals( + new Rdn('cn', 'fo\,o'), + $this->subject->getRdn(), + ); + } + + public function test_it_should_return_a_count(): void + { + self::assertEquals( + 3, + $this->subject->count() + ); + } + + public function test_it_should_get_the_string_representation(): void + { + self::assertSame( + 'cn=fo\,o, dc=local,dc=example', + $this->subject->toString(), + ); + } + + public function test_it_should_check_if_it_is_a_valid_dn(): void + { + self::assertTrue(Dn::isValid('cn=foo,dc=bar,dc=foo')); + self::assertFalse(Dn::isValid('foo')); + self::assertTrue(Dn::isValid('')); + } + + public function test_it_should_handle_a_rootdse_as_a_dn(): void + { + $this->subject = new Dn(''); + + self::assertSame( + '', + $this->subject->toString(), + ); + self::assertSame( + [], + $this->subject->toArray(), + ); + self::assertCount( + 0, + $this->subject, + ); + self::assertNull($this->subject->getParent()); + } +} diff --git a/tests/unit/Entry/EntriesTest.php b/tests/unit/Entry/EntriesTest.php new file mode 100644 index 00000000..18a06e16 --- /dev/null +++ b/tests/unit/Entry/EntriesTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use PHPUnit\Framework\TestCase; + +class EntriesTest extends TestCase +{ + private Entries $subject; + + protected function setUp(): void + { + $this->subject = new Entries( + new Entry('foo'), + new Entry('bar'), + ); + } + + public function test_it_should_get_the_count(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_get_the_first_entry(): void + { + self::assertEquals( + new Entry('foo'), + $this->subject->first(), + ); + } + + public function test_it_should_return_null_if_the_first_entry_does_not_exist(): void + { + $this->subject = new Entries(); + + self::assertNull($this->subject->first()); + } + + public function test_it_should_get_the_last_entry(): void + { + self::assertEquals( + new Entry('bar'), + $this->subject->last(), + ); + } + + public function test_it_should_return_null_if_the_last_entry_does_not_exist(): void + { + $this->subject = new Entries(); + + self::assertNull($this->subject->last()); + } + + public function test_it_should_add_entries(): void + { + $this->subject->add( + new Entry('cn=new'), + new Entry('cn=another'), + ); + + self::assertCount( + 4, + $this->subject, + ); + } + + public function test_it_should_remove_entries(): void + { + $entry1 = new Entry('cn=new'); + $entry2 = new Entry('cn=another'); + + $this->subject->add($entry1, $entry2); + $this->subject->remove($entry1, $entry2); + + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_not_remove_entries_if_they_dont_exist(): void + { + $this->subject->remove(new Entry('cn=meh')); + + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_check_if_an_entry_object_is_in_the_collection(): void + { + $entry = new Entry('cn=meh'); + + self::assertFalse($this->subject->has($entry)); + + $this->subject->add($entry); + + self::assertTrue($this->subject->has($entry)); + } + + public function test_it_should_check_if_an_entry_is_in_the_collection_by_dn(): void + { + self::assertTrue($this->subject->has('foo')); + self::assertFalse($this->subject->has('cn=meh')); + } + + public function test_it_should_get_an_entry_by_dn(): void + { + self::assertEquals( + new Entry('foo'), + $this->subject->get('foo'), + ); + } + + public function test_it_should_return_null_when_trying_to_get_an_entry_that_doesnt_exist(): void + { + self::assertNull($this->subject->get('meh')); + } + + public function test_it_should_get_the_array_of_entries(): void + { + self::assertEquals( + [ + new Entry('foo'), + new Entry('bar') + ], + $this->subject->toArray(), + ); + } +} diff --git a/tests/unit/Entry/EntryTest.php b/tests/unit/Entry/EntryTest.php new file mode 100644 index 00000000..03c4185b --- /dev/null +++ b/tests/unit/Entry/EntryTest.php @@ -0,0 +1,386 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Entry\Entry; +use PHPUnit\Framework\TestCase; + +class EntryTest extends TestCase +{ + private Entry $subject; + + protected function setUp(): void + { + $this->subject = new Entry( + new Dn('cn=foo,dc=example,dc=local'), + new Attribute('cn', 'foo'), + new Attribute('telephoneNumber', '123', '456'), + new Attribute('cn;lang-en-us', 'bar'), + new Attribute('member;range=0-*', 'dc=foo') + ); + } + + public function test_it_should_get_the_dn(): void + { + self::assertEquals( + new Dn('cn=foo,dc=example,dc=local'), + $this->subject->getDn(), + ); + } + + public function test_it_should_allow_being_constructed_with_a_string_dn(): void + { + $this->subject = new Entry('cn=foo,dc=example,dc=local'); + + self::assertEquals( + new Dn('cn=foo,dc=example,dc=local'), + $this->subject->getDn(), + ); + } + + public function test_it_should_be_constructed_from_an_array_using_create(): void + { + $this->subject = Entry::create( + 'cn=foobar,dc=example,dc=local', + [ + 'cn' => 'foobar', + 'telephoneNumber' => ['123', '456'], + ] + ); + + self::assertEquals( + new Dn('cn=foobar,dc=example,dc=local'), + $this->subject->getDn(), + ); + self::assertEquals( + [ + new Attribute('cn', 'foobar'), + new Attribute('telephoneNumber', '123', '456') + ], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_be_constructed_from_an_array_using_fromArray(): void + { + $this->subject = Entry::fromArray( + 'cn=foobar,dc=example,dc=local', + [ + 'cn' => 'foobar', + 'telephoneNumber' => ['123', '456'], + ] + ); + + self::assertEquals( + new Dn('cn=foobar,dc=example,dc=local'), + $this->subject->getDn(), + ); + self::assertEquals( + [ + new Attribute('cn', 'foobar'), + new Attribute('telephoneNumber', '123', '456') + ], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_be_constructed_from_an_array_using_fromArray_when_not_all_data_are_strings(): void + { + $this->subject = Entry::fromArray( + 'cn=foobar,dc=example,dc=local', + [ + 'cn' => 'foobar', + 'telephoneNumber' => [123, 456], + 'is_bad_idea' => true, + ] + ); + + self::assertEquals( + new Dn('cn=foobar,dc=example,dc=local'), + $this->subject->getDn(), + ); + self::assertEquals( + [ + new Attribute('cn', 'foobar'), + new Attribute('telephoneNumber', '123', '456'), + new Attribute('is_bad_idea', '1') + ], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_get_the_entry_as_an_associative_array(): void + { + self::assertSame( + [ + 'cn' => ['foo'], + 'telephoneNumber' => [ '123', '456'], + 'cn;lang-en-us' => ['bar'], + 'member;range=0-*' => ['dc=foo'] + ], + $this->subject->toArray(), + ); + } + + public function test_it_should_get_the_count_as_the_amount_of_attributes_in_the_entry(): void + { + self::assertCount( + 4, + $this->subject + ); + } + + public function test_it_should_have_a_string_representation_of_the_dn(): void + { + self::assertSame( + 'cn=foo,dc=example,dc=local', + (string) $this->subject->getDn(), + ); + } + + public function test_it_should_return_null_for_an_attribute_that_doesnt_exist(): void + { + self::assertNull($this->subject->get('foobar')); + } + + public function test_it_should_get_an_attribute_using_a_string(): void + { + self::assertSame( + 'cn', + $this->subject->get('cN')?->getName(), + ); + } + + public function test_it_should_get_an_attribute_using_an_attribute(): void + { + self::assertSame( + 'cn', + $this->subject->get(new Attribute('cn'))?->getName(), + ); + } + + public function test_it_should_get_an_attribute_with_options_using_only_the_name(): void + { + self::assertSame( + ['foo'], + $this->subject->get('cn')?->getValues(), + ); + } + + public function test_it_should_get_an_attribute_with_options_using_the_options(): void + { + self::assertSame( + ['bar'], + $this->subject->get('cn;lang-en-us')?->getValues() + ); + } + + public function test_it_should_not_get_an_attribute_with_the_same_name_if_the_requested_options_are_not_the_same(): void + { + self::assertNull($this->subject->get('member;foo')); + } + + public function test_it_should_respect_the_strict_option_for_getting_an_attribute(): void + { + self::assertNull($this->subject->get('member', true)); + self::assertInstanceOf( + Attribute::class, + $this->subject->get('member') + ); + } + + public function test_it_should_reset_an_attribute_using_a_string(): void + { + $this->subject->reset('cn'); + + self::assertNull($this->subject->get('cn', true)); + } + + public function test_it_should_remove_an_attribute_using_an_attribute(): void + { + $this->subject->reset(new Attribute('cn')); + + self::assertNull($this->subject->get('cn', true)); + } + + public function if_should_check_if_it_has_an_attribute_using_a_string() + { + self::assertTrue($this->subject->has('Cn')); + self::assertFalse($this->subject->has('bleh')); + } + + public function test_it_should_check_if_it_has_an_attribute_using_an_attribute(): void + { + self::assertTrue($this->subject->has(new Attribute('cn'))); + self::assertFalse($this->subject->has(new Attribute('bleh'))); + } + + public function test_it_should_get_an_attribute_through_the_magic_get(): void + { + self::assertSame( + 'cn', + $this->subject->__get('CN')?->getName(), + ); + } + + public function test_it_should_get_null_on_an_attribute_that_doesnt_exist_through_the_magic_get(): void + { + self::assertNull($this->subject->__get('foo')); + } + + public function test_it_should_set_an_attribute_through_the_magic_set(): void + { + $this->subject->__set('foo', 'bar'); + $this->subject->__set('bar', ['foo', 'bar']); + + self::assertSame( + ['bar'], + $this->subject->get('foo')?->getValues(), + ); + self::assertSame( + ['foo', 'bar'], + $this->subject->get('bar')?->getValues(), + ); + } + + public function test_it_should_check_if_an_attribute_exists_through_the_magic_isset(): void + { + self::assertTrue($this->subject->__isset('CN')); + self::assertFalse($this->subject->__isset('foo')); + } + + public function test_it_should_remove_a_variable_through_the_magic_unset(): void + { + $this->subject->__unset('cn'); + + self::assertNull($this->subject->get('cn', true)); + } + + public function test_it_should_add_to_an_attributes_values_if_it_already_exists_while_adding(): void + { + $this->subject->add('cn', 'bar'); + + self::assertSame( + ['foo', 'bar'], + $this->subject->get('cn')?->getValues(), + ); + + $this->subject->add(new Attribute('telephonenumber', '789')); + + self::assertSame( + ['123', '456', '789'], + $this->subject->get('telephoneNumber')?->getValues(), + ); + + $this->subject->add('sn', 'smith'); + + self::assertSame( + ['smith'], + $this->subject->get('sn')?->getValues(), + ); + } + + public function test_it_should_remove_an_attributes_values_if_it_already_exists_when_deleting(): void + { + $this->subject->remove('telephonenumber', '123'); + + self::assertNotContains( + '123', + (array) $this->subject->get('telephoneNumber')?->getValues() + ); + + $this->subject->remove(new Attribute('telephonenumber', '456')); + + self::assertSame( + [], + $this->subject->get('telephoneNumber')?->getValues(), + ); + } + + public function test_it_should_not_generate_a_delete_change_when_no_values_are_provided(): void + { + $this->subject->remove('telephonenumber'); + + self::assertSame( + [], + $this->subject->changes()->toArray(), + ); + } + + public function test_it_should_generate_a_delete_change_when_unsetting_an_attribute(): void + { + $this->subject->__unset('telephonenumber'); + + $change = $this->subject->changes()->toArray()[0]; + + self::assertNotNull($change); + self::assertSame( + 'telephonenumber', + $change->getAttribute()->getName(), + ); + self::assertSame( + [], + $change->getAttribute()->getValues(), + ); + self::assertSame( + Change::TYPE_DELETE, + $change->getType(), + ); + } + + public function test_it_should_generate_a_replace_change_when_setting_an_attribute(): void + { + $this->subject->set('cn', 'foo'); + + $change = $this->subject->changes()->toArray()[0]; + + self::assertNotNull($change); + self::assertSame( + 'cn', + $change->getAttribute()->getName(), + ); + self::assertSame( + ['foo'], + $change->getAttribute()->getValues(), + ); + self::assertSame( + Change::TYPE_REPLACE, + $change->getType(), + ); + } + + public function test_it_should_generate_an_add_change_when_adding_an_attribute(): void + { + $this->subject->add('sn', 'Smith'); + + $change = $this->subject->changes()->toArray()[0]; + + self::assertNotNull($change); + self::assertSame( + 'sn', + $change->getAttribute()->getName(), + ); + self::assertSame( + ['Smith'], + $change->getAttribute()->getValues(), + ); + self::assertSame( + Change::TYPE_ADD, + $change->getType(), + ); + } +} diff --git a/tests/unit/Entry/OptionTest.php b/tests/unit/Entry/OptionTest.php new file mode 100644 index 00000000..4403d055 --- /dev/null +++ b/tests/unit/Entry/OptionTest.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Option; +use PHPUnit\Framework\TestCase; + +class OptionTest extends TestCase +{ + private Option $subject; + + protected function setUp(): void + { + $this->subject = new Option('foo'); + } + + public function test_it_should_detect_if_it_is_not_a_language_option(): void + { + self::assertFalse($this->subject->isLanguageTag()); + } + + public function test_it_should_detect_if_it_is_a_language_option(): void + { + $this->subject = new Option('lang-en'); + + self::assertTrue($this->subject->isLanguageTag()); + } + + public function test_it_should_detect_if_it_is_not_a_range_option(): void + { + self::assertFalse($this->subject->isRange()); + } + + public function test_it_should_detect_if_it_is_a_range_option(): void + { + $this->subject = new Option('range=0-1500'); + + self::assertTrue($this->subject->isRange()); + } + + public function test_it_should_get_the_high_range_value_of_an_option(): void + { + $this->subject = new Option('range=0-1500'); + + self::assertSame( + '1500', + $this->subject->getHighRange(), + ); + } + + public function test_it_should_return_an_empty_string_if_the_high_range_cannot_be_parsed(): void + { + self::assertSame( + '', + $this->subject->getHighRange(), + ); + } + + public function test_it_should_get_the_low_range_value_of_an_option(): void + { + $this->subject = new Option('range=0-1500'); + + self::assertSame( + '0', + $this->subject->getLowRange(), + ); + } + + public function test_it_should_return_null_if_the_low_range_cannot_be_parsed(): void + { + self::assertNull($this->subject->getLowRange()); + } + + public function test_it_should_have_a_factory_method_for_a_range(): void + { + $this->subject = Option::fromRange('0'); + + self::assertTrue($this->subject->isRange()); + self::assertSame( + '0', + $this->subject->getLowRange(), + ); + self::assertSame( + '*', + $this->subject->getHighRange(), + ); + } + + public function test_it_should_return_whether_the_option_starts_with_a_string(): void + { + self::assertTrue($this->subject->startsWith('fo')); + self::assertFalse($this->subject->startsWith('bar')); + } + + public function test_it_should_get_the_string_option_with_toString(): void + { + self::assertSame( + 'foo', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_string_representation(): void + { + self::assertSame( + 'foo', + (string) $this->subject, + ); + } + + public function test_it_should_check_for_equality_with_another_option(): void + { + self::assertTrue($this->subject->equals(new Option('FOO'))); + self::assertTrue($this->subject->equals(new Option('foo'))); + self::assertFalse($this->subject->equals(new Option('bar'))); + } +} diff --git a/tests/unit/Entry/OptionsSpec.php b/tests/unit/Entry/OptionsSpec.php new file mode 100644 index 00000000..de458bc2 --- /dev/null +++ b/tests/unit/Entry/OptionsSpec.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Option; +use FreeDSx\Ldap\Entry\Options; +use PHPUnit\Framework\TestCase; + +class OptionsSpec extends TestCase +{ + private Options $subject; + + protected function setUp(): void + { + $this->subject = new Options( + 'foo', + 'Bar', + 'lang-en', + 'range=1500-*', + ); + } + + public function test_it_should_get_the_first_option(): void + { + self::assertEquals( + new Option('foo'), + $this->subject->first(), + ); + } + + public function test_it_should_get_the_last_option(): void + { + self::assertEquals( + new Option('range=1500-*'), + $this->subject->last(), + ); + } + + public function test_it_should_return_null_for_the_first_option_when_there_are_none(): void + { + $this->subject = new Options(); + + self::assertNull($this->subject->first()); + } + + public function test_it_should_return_null_for_the_last_option_when_there_are_none(): void + { + $this->subject = new Options(); + + self::assertNull($this->subject->last()); + } + + public function test_it_should_get_a_semi_colon_separated_string_representation_calling_toString(): void + { + self::assertSame( + 'foo;Bar;lang-en;range=1500-*', + $this->subject->toString(), + ); + } + + public function test_it_should_sort_and_lowercase_the_string_representation_if_requested(): void + { + self::assertSame( + 'bar;foo;lang-en;range=1500-*', + $this->subject->toString(true), + ); + } + + public function test_it_should_have_a_string_representation(): void + { + self::assertSame( + 'foo;Bar;lang-en;range=1500-*', + (string) $this->subject, + ); + } + + public function test_it_should_get_the_count(): void + { + self::assertCount( + 4, + $this->subject, + ); + } + + public function test_it_should_get_an_array_of_options_if_requested(): void + { + self::assertEquals( + [ + new Option('foo'), + new Option('Bar'), + new Option('lang-en'), + new Option('range=1500-*') + ], + $this->subject->toArray(), + ); + } + + public function test_it_should_add_an_option(): void + { + $this->subject->add('x-bar'); + + self::assertTrue($this->subject->has('x-bar'));; + } + + public function test_it_should_remove_an_option(): void + { + $this->subject->remove('foo'); + + self::assertFalse($this->subject->has('foo')); + } + + public function test_it_should_set_the_options(): void + { + $this->subject->set('foo'); + + self::assertCount( + 1, + $this->subject, + ); + } + + public function test_it_should_check_for_an_option(): void + { + self::assertTrue($this->subject->has('foo')); + self::assertFalse($this->subject->has('x-foo')); + } +} diff --git a/tests/unit/Entry/RdnTest.php b/tests/unit/Entry/RdnTest.php new file mode 100644 index 00000000..fe1c1bd4 --- /dev/null +++ b/tests/unit/Entry/RdnTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Entry; + +use FreeDSx\Ldap\Entry\Rdn; +use FreeDSx\Ldap\Exception\InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +class RdnTest extends TestCase +{ + private Rdn $subject; + + protected function setUp(): void + { + $this->subject = new Rdn( + 'cn', + 'foo', + ); + } + + public function test_it_should_get_the_name(): void + { + self::assertSame( + 'cn', + $this->subject->getName(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'foo', + $this->subject->getValue(), + ); + } + + public function test_it_should_get_the_string_representation(): void + { + self::assertSame( + 'cn=foo', + $this->subject->toString(), + ); + } + + public function test_it_should_get_whether_it_is_multivalued(): void + { + self::assertFalse($this->subject->isMultivalued()); + } + + public function test_it_should_be_created_from_a_string_rdn(): void + { + $this->subject = Rdn::create('cn=foobar'); + + self::assertSame( + 'cn', + $this->subject->getName(), + ); + self::assertSame( + 'foobar', + $this->subject->getValue(), + ); + } + + public function test_it_should_error_when_constructing_an_rdn_that_is_invalid(): void + { + $this->expectException(InvalidArgumentException::class); + + Rdn::create('foobar'); + } + + public function test_it_should_escape_an_rdn_value_with_leading_and_trailing_spaces(): void + { + self::assertSame( + '\20foo\2c= bar\20', + Rdn::escape(' foo,= bar '), + ); + } + + public function test_it_should_escape_an_rdn_value_with_a_leading_pound_sign(): void + { + self::assertSame( + '\23 foo\20', + Rdn::escape('# foo '), + ); + } + + public function test_it_should_escape_required_values(): void + { + self::assertSame( + '\5cfoo \2b \22bar\22\2c \3e bar \3c foo\3b', + Rdn::escape('\foo + "bar", > bar < foo;'), + ); + } + + public function test_it_should_escape_all_characters(): void + { + self::assertSame( + '\23\20\66\6f\6f\20', + Rdn::escapeAll('# foo '), + ); + } +} diff --git a/tests/unit/Exception/OperationExceptionTest.php b/tests/unit/Exception/OperationExceptionTest.php new file mode 100644 index 00000000..3bffe8b7 --- /dev/null +++ b/tests/unit/Exception/OperationExceptionTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Exception; + +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\ResultCode; +use PHPUnit\Framework\TestCase; + +final class OperationExceptionTest extends TestCase +{ + private OperationException $subject; + + protected function setUp(): void + { + $this->subject = new OperationException(); + } + + public function test_it_should_have_a_default_code_of_operations_error(): void + { + self::assertSame( + ResultCode::OPERATIONS_ERROR, + $this->subject->getCode(), + ); + } + + public function test_it_should_get_the_code_short_string(): void + { + self::assertSame( + 'operationsError', + $this->subject->getCodeShort(), + ); + } + + public function test_it_should_get_the_code_description(): void + { + self::assertSame( + ResultCode::MEANING_DESCRIPTION[ResultCode::OPERATIONS_ERROR], + $this->subject->getCodeDescription(), + ); + } + + public function test_it_should_generate_a_message_if_none_was_provided(): void + { + self::assertSame( + 'The result code 1 was thrown (operationsError). Indicates that the operation is not properly sequenced with relation to other operations (of same or different type).', + $this->subject->getMessage(), + ); + } +} diff --git a/tests/unit/Exception/ReferralExceptionTest.php b/tests/unit/Exception/ReferralExceptionTest.php new file mode 100644 index 00000000..2b7f85e8 --- /dev/null +++ b/tests/unit/Exception/ReferralExceptionTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Exception; + +use FreeDSx\Ldap\Exception\ReferralException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\ResultCode; +use PHPUnit\Framework\TestCase; + +final class ReferralExceptionTest extends TestCase +{ + private ReferralException $subject; + + protected function setUp(): void + { + $this->subject = new ReferralException( + 'foo', + new LdapUrl('foo'), + new LdapUrl('bar'), + ); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEquals( + [ + new LdapUrl('foo'), + new LdapUrl('bar'), + ], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_set_the_message(): void + { + self::assertSame( + 'foo', + $this->subject->getMessage(), + ); + } + + public function test_it_should_have_a_code_of_the_referral_result_code(): void + { + self::assertSame( + ResultCode::REFERRAL, + $this->subject->getCode(), + ); + } +} diff --git a/tests/unit/Exception/UnsolicitedNotificationExceptionTest.php b/tests/unit/Exception/UnsolicitedNotificationExceptionTest.php new file mode 100644 index 00000000..813a780b --- /dev/null +++ b/tests/unit/Exception/UnsolicitedNotificationExceptionTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Exception; + +use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; +use PHPUnit\Framework\TestCase; + +final class UnsolicitedNotificationExceptionTest extends TestCase +{ + private UnsolicitedNotificationException $subject; + + protected function setUp(): void + { + $this->subject = new UnsolicitedNotificationException( + 'foo', + 0, + null, + 'bar', + ); + } + + public function test_it_should_get_the_name_oid(): void + { + self::assertSame( + 'bar', + $this->subject->getOid(), + ); + } +} diff --git a/tests/unit/LdapClientTest.php b/tests/unit/LdapClientTest.php new file mode 100644 index 00000000..c251fff9 --- /dev/null +++ b/tests/unit/LdapClientTest.php @@ -0,0 +1,539 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Container; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use FreeDSx\Ldap\Operation\Response\AddResponse; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\CompareResponse; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; +use FreeDSx\Ldap\Operation\Response\ModifyResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; +use FreeDSx\Ldap\Search\DirSync; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Search\Paging; +use FreeDSx\Ldap\Search\RangeRetrieval; +use FreeDSx\Ldap\Search\Vlv; +use FreeDSx\Ldap\Sync\SyncRepl; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class LdapClientTest extends TestCase +{ + use TestFactoryTrait; + + private ClientProtocolHandler&MockObject $mockHandler; + + private Container $container; + + private ClientQueueInstantiator&MockObject $mockQueueInstantiator; + + private LdapClient $subject; + + protected function setUp(): void { + $this->mockHandler = $this->createMock(ClientProtocolHandler::class); + $this->mockQueueInstantiator = $this->createMock(ClientQueueInstantiator::class); + + $this->mockQueueInstantiator + ->method('isInstantiatedAndConnected') + ->willReturn(false); + + $this->container = new Container( + [ + ClientProtocolHandler::class => $this->mockHandler, + ClientQueueInstantiator::class => $this->mockQueueInstantiator, + ] + ); + + $this->subject = new LdapClient( + new ClientOptions(), + $this->container, + ); + } + + public function test_it_should_send_a_message_and_throw_an_exception_if_no_response_is_received_on_sendAndReceive(): void + { + $this->expectException(OperationException::class); + + $this->mockHandler + ->method('send') + ->willReturn(null); + + $this->subject->sendAndReceive(Operations::read('')); + } + + public function test_it_should_send_a_message_and_return_the_response_on_sendAndReceive(): void { + $mockResponse = $this->createMock(LdapMessageResponse::class); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->willReturn($mockResponse); + + self::assertSame( + $mockResponse, + $this->subject->sendAndReceive(Operations::read('')), + ); + } + + public function test_it_should_send_a_search_and_get_entries_back(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->willReturn( + $this::makeSearchResponseFromEntries(new Entries( + Entry::create('dc=foo,dc=bar') + )) + ); + + $search = Operations::search(Filters::equal( + 'foo', + 'bar' + )); + + self::assertEquals( + new Entries(Entry::create('dc=foo,dc=bar')), + $this->subject->search($search), + ); + } + + public function test_it_should_bind(): void + { + $response = new LdapMessageResponse( + 1, + new BindResponse(new LdapResult( + 0, + '' + )) + ); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(new SimpleBindRequest( + 'foo', + 'bar', + 3 + )) + ->willReturn($response); + + self::assertEquals( + $response, + $this->subject->bind( + 'foo', + 'bar', + ) + ); + } + + public function test_it_should_construct_a_pager_helper(): void + { + self::assertInstanceOf( + Paging::class, + $this->subject->paging(Operations::search( + Filters::equal( + 'foo', + 'bar' + )) + ), + ); + } + + public function test_it_should_construct_a_vlv_helper(): void + { + self::assertInstanceOf( + Vlv::class, + $this->subject->vlv( + Operations::search(Filters::equal( + 'foo', + 'bar' + )), + 'cn', + 100 + ), + ); + } + + public function test_it_should_construct_a_dirsync_helper(): void + { + self::assertInstanceOf( + DirSync::class, + $this->subject->dirSync() + ); + } + + public function test_it_should_construct_a_range_retrieval_helper(): void + { + self::assertInstanceOf( + RangeRetrieval::class, + $this->subject->range(), + ); + } + + public function test_it_should_start_tls(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::extended(ExtendedRequest::OID_START_TLS)) + ->willReturn(null); + + $this->subject->startTls();; + } + + public function test_it_should_unbind_if_requested(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(new UnbindRequest()) + ->willReturn(null); + + $this->subject->unbind(); + } + + public function test_it_should_return_a_whoami(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::extended(ExtendedRequest::OID_WHOAMI)) + ->willReturn(new LdapMessageResponse( + 1, + new ExtendedResponse( + new LdapResult(0, ''), + null, + 'foo' + ) + )); + + self::assertSame( + 'foo', + $this->subject->whoami(), + ); + } + + public function test_it_should_return_a_correct_compare_response_on_a_match(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::compare( + 'cn=foo', + 'foo', + 'bar' + )) + ->willReturn(new LdapMessageResponse( + 1, + new CompareResponse(ResultCode::COMPARE_TRUE) + )); + + self::assertTrue( + $this->subject->compare( + 'cn=foo', + 'foo', + 'bar', + ) + ); + } + + public function test_it_should_return_a_correct_compare_response_on_a_non_match(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::compare( + 'cn=foo', + 'foo', + 'bar', + )) + ->willReturn(new LdapMessageResponse( + 1, + new CompareResponse(ResultCode::COMPARE_FALSE) + )); + + self::assertFalse( + $this->subject->compare( + 'cn=foo', + 'foo', + 'bar' + ) + ); + } + + public function test_it_should_send_a_modify_operation_on_update(): void + { + $entry = Entry::create('cn=foo,dc=local', ['cn' => 'foo']); + $entry->set('sn', 'bar'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::modify( + $entry->getDn(), + ...$entry->changes() + )) + ->willReturn(new LdapMessageResponse( + 1, + new ModifyResponse(ResultCode::SUCCESS) + )); + + $this->subject->update($entry); + } + + public function test_it_should_send_an_add_operation_on_create(): void + { + $entry = Entry::create('cn=foo,dc=local', ['cn' => 'foo']); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::add($entry)) + ->willReturn(new LdapMessageResponse( + 1, + new AddResponse(ResultCode::SUCCESS) + )); + + $this->subject->create($entry); + } + + public function test_it_should_send_a_delete_operation_on_delete(): void + { + $entry = new Entry('cn=foo,dc=local'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::delete('cn=foo,dc=local')) + ->willReturn(new LdapMessageResponse( + 1, + new DeleteResponse(ResultCode::SUCCESS) + )); + + $this->subject->delete($entry->getDn()->toString()); + } + + public function test_it_should_send_a_modify_dn_operation_on_move(): void + { + $entry = new Entry('cn=foo,dc=local'); + $parent = new Entry('cn=bar,dc=local'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::move('cn=foo,dc=local', 'cn=bar,dc=local')) + ->willReturn(new LdapMessageResponse( + 1, + new ModifyDnResponse(ResultCode::SUCCESS) + )); + + $this->subject->move( + $entry, + $parent, + ); + } + + public function test_it_should_send_a_modify_dn_operation_on_rename(): void + { + $entry = new Entry('cn=foo,dc=local'); + $newRdn = 'cn=bar'; + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::rename('cn=foo,dc=local', 'cn=bar')) + ->willReturn(new LdapMessageResponse( + 1, + new ModifyDnResponse(ResultCode::SUCCESS) + )); + + $this->subject->rename( + $entry, + $newRdn, + ); + } + + public function test_it_should_send_a_base_search_on_a_read_and_return_an_entry(): void + { + $entry = new Entry('cn=foo,dc=local'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('cn=foo,dc=local')) + ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); + + self::assertEquals( + $entry, + $this->subject->read($entry->getDn()->toString()) + ); + } + + public function test_it_should_send_a_read_to_the_RootDSE_if_it_is_called_with_no_arguments(): void + { + $entry = new Entry(''); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('')) + ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); + + self::assertSame( + $entry, + $this->subject->read() + ); + } + + public function test_it_should_send_a_base_search_on_a_read_and_return_null_if_it_does_not_exist(): void + { + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('cn=foo,dc=local')) + ->willThrowException(new OperationException( + '', + ResultCode::NO_SUCH_OBJECT + )); + + $entry = new Entry('cn=foo,dc=local'); + + self::assertNull( + $this->subject->read($entry->getDn()->toString()) + ); + } + + public function test_it_should_throw_an_exception_on_read_or_fail_if_the_entry_does_not_exist(): void + { + $exception = new OperationException('', ResultCode::NO_SUCH_OBJECT); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('cn=foo,dc=local')) + ->willThrowException($exception); + + $this->expectExceptionObject($exception); + + $this->subject->readOrFail('cn=foo,dc=local');; + } + + public function test_it_should_return_an_entry_on_a_readOrFail_if_it_exists(): void + { + $entry = new Entry('cn=foo,dc=local'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('cn=foo,dc=local')) + ->willReturn($this::makeSearchResponseFromEntries(new Entries($entry))); + + self::assertSame( + $entry, + $this->subject->readOrFail($entry->getDn()->toString()), + ); + } + + public function test_it_should_send_a_base_search_on_a_read_and_throw_an_unrelated_operation_exception(): void + { + $entry = new Entry('cn=foo,dc=local'); + + $this->mockHandler + ->expects($this->once()) + ->method('send') + ->with(Operations::read('cn=foo,dc=local')) + ->willThrowException(new OperationException( + '', + ResultCode::INSUFFICIENT_ACCESS_RIGHTS + )); + + $this->expectException(OperationException::class); + + $this->subject->read($entry->getDn()->toString());; + } + + public function test_it_should_get_the_default_options(): void + { + self::assertSame( + [ + 'version' => 3, + 'servers' => [], + 'port' => 389, + 'transport' => 'tcp', + 'base_dn' => null, + 'page_size' => 1000, + 'use_ssl' => false, + 'ssl_validate_cert' => true, + 'ssl_allow_self_signed' => false, + 'ssl_ca_cert' => null, + 'ssl_peer_name' => null, + 'timeout_connect' => 3, + 'timeout_read' => 10, + 'referral' => 'throw', + 'referral_chaser' => null, + 'referral_limit' => 10, + ], + $this->subject->getOptions()->toArray(), + ); + } + + public function test_it_should_set_the_options(): void + { + $options = (new ClientOptions()) + ->setServers([ + 'foo', + 'bar', + ]); + + $this->subject->setOptions($options); + + self::assertSame( + $options, + $this->subject->getOptions(), + ); + } + + public function test_it_should_construct_a_syncrepl_helper(): void + { + $syncRequest = Filters::present('foo'); + + self::assertEquals( + new SyncRepl( + $this->subject, + $syncRequest, + ), + $this->subject->syncRepl($syncRequest), + ); + self::assertEquals( + new SyncRepl($this->subject), + $this->subject->syncRepl(), + ); + } +} diff --git a/tests/unit/LdapServerTest.php b/tests/unit/LdapServerTest.php new file mode 100644 index 00000000..60bf24e8 --- /dev/null +++ b/tests/unit/LdapServerTest.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\LdapServer; +use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; +use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; +use FreeDSx\Ldap\Server\RequestHandler\ProxyPagingHandler; +use FreeDSx\Ldap\Server\RequestHandler\ProxyRequestHandler; +use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface; +use FreeDSx\Ldap\Server\ServerRunner\ServerRunnerInterface; +use FreeDSx\Ldap\ServerOptions; +use FreeDSx\Socket\SocketServer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class LdapServerTest extends TestCase +{ + private LdapServer $subject; + + private ServerOptions $options; + + private ServerRunnerInterface&MockObject $mockServerRunner; + + protected function setUp(): void + { + $this->mockServerRunner = $this->createMock(ServerRunnerInterface::class); + + $this->options = (new ServerOptions()) + ->setPort(33389) + ->setServerRunner($this->mockServerRunner); + + $this->subject = new LdapServer($this->options); + } + + public function test_it_should_run_the_server(): void + { + $this->mockServerRunner + ->expects(self::once()) + ->method('run') + ->with(self::isInstanceOf(SocketServer::class)); + + $this->subject->run(); + } + + public function test_it_should_use_the_request_handler_specified(): void + { + $requestHandler = $this->createMock(RequestHandlerInterface::class); + + $this->subject->useRequestHandler($requestHandler); + + self::assertSame( + $requestHandler, + $this->subject->getOptions() + ->getRequestHandler() + ); + } + + public function test_it_should_use_the_rootdse_handler_specified(): void + { + $rootDseHandler = $this->createMock(RootDseHandlerInterface::class); + + $this->subject->useRootDseHandler($rootDseHandler); + + self::assertSame( + $rootDseHandler, + $this->subject->getOptions() + ->getRootDseHandler() + ); + } + + public function test_it_should_use_the_paging_handler_specified(): void + { + $pagingHandler = $this->createMock(PagingHandlerInterface::class); + + $this->subject->usePagingHandler($pagingHandler); + + self::assertSame( + $pagingHandler, + $this->subject->getOptions() + ->getPagingHandler() + ); + } + + public function test_it_should_use_the_logger_specified(): void + { + $logger = $this->createMock(LoggerInterface::class); + + $this->subject->useLogger($logger); + + self::assertSame( + $logger, + $this->subject->getOptions() + ->getLogger() + ); + } + + public function test_it_should_get_the_default_options(): void + { + self::assertEquals( + [ + 'ip' => "0.0.0.0", + 'port' => 33389, + 'unix_socket' => "/var/run/ldap.socket", + 'transport' => "tcp", + 'idle_timeout' => 600, + 'require_authentication' => true, + 'allow_anonymous' => false, + 'request_handler' => null, + 'rootdse_handler' => null, + 'paging_handler' => null, + 'logger' => null, + 'use_ssl' => false, + 'ssl_cert' => null, + 'ssl_cert_key' => null, + 'ssl_cert_passphrase' => null, + 'dse_alt_server' => null, + 'dse_naming_contexts' => [ + "dc=FreeDSx,dc=local" + ], + 'dse_vendor_name' => "FreeDSx", + 'dse_vendor_version' => null, + ], + $this->subject->getOptions()->toArray(), + ); + } + + public function test_it_should_make_a_proxy_server(): void + { + $client = new LdapClient( + (new ClientOptions()) + ->setServers(['localhost']) + ); + $serverOptions = new ServerOptions(); + $proxyRequestHandler = new ProxyHandler($client); + $server = new LdapServer($serverOptions); + $server->useRequestHandler($proxyRequestHandler); + $server->useRootDseHandler($proxyRequestHandler); + $server->usePagingHandler(new ProxyPagingHandler($client)); + + $proxyOptions = LdapServer::makeProxy('localhost') + ->getOptions(); + + self::assertInstanceOf( + ProxyPagingHandler::class, + $proxyOptions->getPagingHandler() + ); + self::assertInstanceOf( + ProxyRequestHandler::class, + $proxyOptions->getRequestHandler() + ); + self::assertInstanceOf( + ProxyRequestHandler::class, + $proxyOptions->getRootDseHandler(), + ); + } +} diff --git a/tests/unit/LdapUrlExtensionTest.php b/tests/unit/LdapUrlExtensionTest.php new file mode 100644 index 00000000..e150a8c0 --- /dev/null +++ b/tests/unit/LdapUrlExtensionTest.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\LdapUrlExtension; +use PHPUnit\Framework\TestCase; + +class LdapUrlExtensionTest extends TestCase +{ + private LdapUrlExtension $subject; + + protected function setUp(): void + { + $this->subject = new LdapUrlExtension('foo'); + } + + public function test_it_should_get_the_extension_name(): void + { + self::assertSame( + 'foo', + $this->subject->getName(), + ); + + $this->subject->setName('bar'); + + self::assertSame( + 'bar', + $this->subject->getName(), + ); + } + + public function test_it_should_get_the_extension_value(): void + { + self::assertNull($this->subject->getValue()); + + $this->subject->setValue('bar'); + + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_get_the_criticality(): void + { + self::assertFalse($this->subject->getIsCritical()); + + $this->subject->setIsCritical(true); + + self::assertTrue($this->subject->getIsCritical()); + } + + public function test_it_should_parse_an_extension_with_only_a_name(): void + { + self::assertEquals( + new LdapUrlExtension('foo'), + LdapUrlExtension::parse('foo'), + ); + } + + public function test_it_should_generate_a_string_extension_with_only_a_name(): void + { + self::assertSame( + 'foo', + $this->subject->toString(), + ); + } + + public function test_it_should_parse_an_extension_with_a_criticality(): void + { + self::assertEquals( + new LdapUrlExtension( + 'foo', + null, + true + ), + LdapUrlExtension::parse('!foo'), + ); + } + + public function test_it_should_generate_a_string_extension_with_a_criticality(): void + { + $this->subject->setIsCritical(true); + + self::assertSame( + '!foo', + $this->subject->toString(), + ); + } + + public function test_it_should_parse_an_extension_with_a_value(): void + { + self::assertEquals( + new LdapUrlExtension( + 'foo', + 'bar' + ), + LdapUrlExtension::parse('foo=bar'), + ); + } + + public function test_it_should_generate_a_string_extension_with_a_value(): void + { + $this->subject->setValue('bar'); + + self::assertSame( + 'foo=bar', + $this->subject->toString(), + ); + } + + public function test_it_should_parse_an_extension_and_decode_it_if_needed(): void + { + self::assertEquals( + new LdapUrlExtension( + 'e-bindname', + 'cn=Manager,dc=example,dc=com' + ), + LdapUrlExtension::parse('e-bindname=cn=Manager%2cdc=example%2cdc=com') + ); + } + + public function test_it_should_generate_a_string_extension_and_encode_it_if_needed(): void + { + $this->subject = new LdapUrlExtension( + 'e-bindname', + 'cn=Manager,dc=example,dc=com', + ); + + self::assertSame( + 'e-bindname=cn=Manager%2cdc=example%2cdc=com', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/LdapUrlTest.php b/tests/unit/LdapUrlTest.php new file mode 100644 index 00000000..7941bfba --- /dev/null +++ b/tests/unit/LdapUrlTest.php @@ -0,0 +1,356 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\InvalidArgumentException; +use FreeDSx\Ldap\Exception\UrlParseException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\LdapUrlExtension; +use PHPUnit\Framework\TestCase; + +class LdapUrlTest extends TestCase +{ + private LdapUrl $subject; + + protected function setUp(): void + { + $this->subject = new LdapUrl('foo'); + } + + public function test_it_should_have_a_string_representation(): void + { + self::assertSame( + 'ldap://foo/', + (string) $this->subject + ); + } + + public function test_it_should_parse_a_url_with_no_host_but_a_path(): void + { + self::assertEquals( + LdapUrl::parse('ldap:///o=University%20of%20Michigan,c=US'), + (new LdapUrl())->setDn('o=University of Michigan,c=US') + ); + } + + public function test_it_should_generate_a_url_with_no_host_but_a_path(): void + { + $this->subject = new LdapUrl(null); + $this->subject->setDn('o=University of Michigan,c=US'); + + self::assertSame( + 'ldap:///o=University%20of%20Michigan,c=US', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_a_host_and_path_but_no_query_elements(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US'), + (new LdapUrl('ldap1.example.net'))->setDn('o=University of Michigan,c=US') + ); + } + + public function test_it_should_generate_a_url_with_a_host_and_path_but_no_query_elements(): void + { + $this->subject = new LdapUrl('ldap1.example.net'); + $this->subject->setDn('o=University of Michigan,c=US'); + + self::assertSame( + 'ldap://ldap1.example.net/o=University%20of%20Michigan,c=US', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_a_host_path_and_attribute(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap1.example.net/o=University%20of%20Michigan,c=US?postalAddress'), + (new LdapUrl('ldap1.example.net')) + ->setDn('o=University of Michigan,c=US') + ->setAttributes('postalAddress') + ); + } + + public function test_it_should_generate_a_url_with_a_host_path_and_attribute(): void + { + $this->subject = new LdapUrl('ldap1.example.net'); + $this->subject->setDn('o=University of Michigan,c=US'); + $this->subject->setAttributes('postalAddress'); + + self::assertSame( + 'ldap://ldap1.example.net/o=University%20of%20Michigan,c=US?postalAddress', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_a_host_port_path_scope_and_filter(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap1.example.net:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)'), + (new LdapUrl('ldap1.example.net')) + ->setPort(6666) + ->setDn('o=University of Michigan,c=US') + ->setFilter('(cn=Babs Jensen)') + ->setScope('sub') + ); + } + + public function test_it_should_generate_a_url_with_a_host_port_path_scope_and_filter(): void + { + $this->subject = new LdapUrl('ldap1.example.net'); + $this->subject->setPort(6666); + $this->subject->setDn('o=University of Michigan,c=US'); + $this->subject->setFilter('(cn=Babs Jensen)'); + $this->subject->setScope('sub'); + + self::assertSame( + 'ldap://ldap1.example.net:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_a_host_path_single_scope_and_attribute(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap1.example.com/c=GB?objectClass?ONE'), + (new LdapUrl('ldap1.example.com')) + ->setDn('c=GB') + ->setAttributes('objectClass') + ->setScope('one') + ); + } + + public function test_it_should_generate_a_url_with_a_host_path_single_scope_and_attribute(): void + { + $this->subject = new LdapUrl('ldap1.example.com'); + $this->subject->setDn('c=GB'); + $this->subject->setAttributes('objectClass'); + $this->subject->setScope('ONE'); + + self::assertSame( + 'ldap://ldap1.example.com/c=GB?objectClass?one', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_a_percent_encoded_question_mark_in_the_path(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap2.example.com/o=Question%3f,c=US?mail'), + (new LdapUrl('ldap2.example.com')) + ->setDn('o=Question?,c=US') + ->setAttributes('mail') + ); + } + + public function test_it_should_generate_a_url_with_a_percent_encoded_question_mark_in_the_path(): void + { + $this->subject = new LdapUrl('ldap2.example.com'); + $this->subject->setDn('o=Question?,c=US'); + $this->subject->setAttributes('mail'); + + self::assertSame( + 'ldap://ldap2.example.com/o=Question%3f,c=US?mail', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_percent_encoded_filter_that_was_hex_escaped(): void + { + self::assertEquals( + LdapUrl::parse('ldap://ldap3.example.com/o=Babsco,c=US???(four-octet=%5c00%5c00%5c00%5c04)'), + (new LdapUrl('ldap3.example.com')) + ->setDn('o=Babsco,c=US') + ->setFilter('(four-octet=\00\00\00\04)') + ); + } + + public function test_it_should_generate_a_url_with_percent_encoded_filter_that_was_hex_escaped(): void + { + $this->subject = new LdapUrl('ldap3.example.com'); + $this->subject->setDn('o=Babsco,c=US'); + $this->subject->setFilter('(four-octet=\00\00\00\04)'); + + self::assertSame( + 'ldap://ldap3.example.com/o=Babsco,c=US???(four-octet=%5c00%5c00%5c00%5c04)', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_extensions(): void + { + self::assertEquals( + LdapUrl::parse('ldap:///??sub??e-bindname=cn=Manager%2cdc=example%2cdc=com'), + (new LdapUrl(null)) + ->setScope('sub') + ->setExtensions(new LdapUrlExtension('e-bindname', 'cn=Manager,dc=example,dc=com')) + ); + } + + public function test_it_should_generate_a_url_with_extensions(): void + { + $this->subject = new LdapUrl(null); + $this->subject->setScope('sub'); + $this->subject->setExtensions(new LdapUrlExtension('e-bindname', 'cn=Manager,dc=example,dc=com')); + + self::assertSame( + 'ldap:///??sub??e-bindname=cn=Manager%2cdc=example%2cdc=com', + (string) $this->subject, + ); + } + + public function test_it_should_parse_a_url_with_all_default_query_fields(): void + { + self::assertEquals( + LdapUrl::parse('ldap://foo/????'), + (new LdapUrl('foo')) + ); + self::assertEquals( + LdapUrl::parse('ldap:///????'), + (new LdapUrl()) + ); + } + + public function test_it_should_set_the_port(): void + { + self::assertNull($this->subject->getPort()); + + $this->subject->setPort(9001); + + self::assertSame( + 9001, + $this->subject->getPort(), + ); + } + + public function test_it_should_set_the_valid_scopes(): void + { + self::assertNull($this->subject->getScope()); + + $this->subject->setScope('base'); + + self::assertSame( + 'base', + $this->subject->getScope(), + ); + + $this->subject->setScope('one'); + + self::assertSame( + 'one', + $this->subject->getScope(), + ); + + $this->subject->setScope('sub'); + + self::assertSame( + 'sub', + $this->subject->getScope(), + ); + } + + public function test_it_should_reject_an_invalid_scope(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->subject->setScope('foo'); + } + + public function test_it_should_set_the_filter(): void + { + self::assertNull($this->subject->getFilter()); + + $this->subject->setFilter('foo=bar'); + + self::assertSame( + 'foo=bar', + $this->subject->getFilter(), + ); + } + + public function test_it_should_set_the_dn(): void + { + self::assertNull($this->subject->getDn()); + + $this->subject->setDn('dc=foo'); + + self::assertEquals( + new Dn('dc=foo'), + $this->subject->getDn(), + ); + } + + public function test_it_should_set_the_attributes(): void + { + self::assertEmpty($this->subject->getAttributes()); + + $this->subject->setAttributes('foo', 'bar'); + self::assertEquals( + [ + new Attribute('foo'), + new Attribute('bar') + ], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_set_the_host(): void + { + self::assertSame( + 'foo', + $this->subject->getHost(), + ); + + $this->subject->setHost('bar'); + + self::assertSame( + 'bar', + $this->subject->getHost(), + ); + } + + public function test_it_should_set_whether_or_not_ssl_is_used(): void + { + self::assertFalse($this->subject->getUseSsl()); + + $this->subject->setUseSsl(true); + + self::assertTrue($this->subject->getUseSsl()); + } + + public function test_it_should_throw_an_error_if_the_scheme_is_not_ldap(): void + { + $this->expectException(UrlParseException::class); + + LdapUrl::parse('https://foo/?'); + } + + public function test_it_should_throw_an_error_if_the_scheme_is_not_ldap_with_no_host(): void + { + $this->expectException(UrlParseException::class); + + LdapUrl::parse('https:///?'); + } + + public function test_it_should_throw_an_error_on_a_malformed_url(): void + { + $this->expectException(UrlParseException::class); + + LdapUrl::parse('ldap'); + } +} diff --git a/tests/unit/Operation/LdapResultTest.php b/tests/unit/Operation/LdapResultTest.php new file mode 100644 index 00000000..0f9a6e5f --- /dev/null +++ b/tests/unit/Operation/LdapResultTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class LdapResultTest extends TestCase +{ + private LdapResult $subject; + + protected function setUp(): void + { + $this->subject = new LdapResult( + 0, + 'foo', + 'bar' + ); + } + + public function test_it_should_get_the_diagnostic_message(): void + { + self::assertSame( + 'bar', + $this->subject->getDiagnosticMessage(), + ); + } + + public function test_it_should_get_the_result_code(): void + { + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + } + + public function test_it_should_get_the_dn(): void + { + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEmpty($this->subject->getReferrals()); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $this->subject = LdapResult::fromAsn1(Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(3, (new IncompleteType( + $encoder->encode(Asn1::octetString('ldap://foo')) + . $encoder->encode(Asn1::octetString('ldap://bar')) + ))->setIsConstructed(true)) + )); + + self::assertEquals( + new Dn('dc=foo,dc=bar'), + $this->subject->getDn(), + ); + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + self::assertSame( + 'foo', + $this->subject->getDiagnosticMessage(), + ); + self::assertEquals( + [ + new LdapUrl('foo'), + new LdapUrl('bar'), + ], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_throw_a_protocol_exception_if_the_referral_cannot_be_parsed(): void + { + $encoder = new LdapEncoder(); + + self::expectException(ProtocolException::class); + + LdapResult::fromAsn1(Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(3, (new IncompleteType( + $encoder->encode(Asn1::octetString('ldap://foo')) + . $encoder->encode(Asn1::octetString('bar')) + ))->setIsConstructed(true)) + )); + } +} diff --git a/tests/unit/Operation/Request/AbandonRequestTest.php b/tests/unit/Operation/Request/AbandonRequestTest.php new file mode 100644 index 00000000..fc1c08e9 --- /dev/null +++ b/tests/unit/Operation/Request/AbandonRequestTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\AbandonRequest; +use PHPUnit\Framework\TestCase; + +final class AbandonRequestTest extends TestCase +{ + private AbandonRequest $subject; + + protected function setUp(): void + { + parent::setUp(); + $this->subject = new AbandonRequest(1); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(AbandonRequest::class, $this->subject); + } + + public function testItShouldGetTheMessageId(): void + { + $this->assertEquals(1, $this->subject->getMessageId()); + $this->assertEquals(2, $this->subject->setMessageId(2)->getMessageId()); + } + + public function testItShouldGenerateCorrectAsn1(): void + { + $expected = Asn1::application(16, Asn1::integer(1)); + $this->assertEquals($expected, $this->subject->toAsn1()); + } + + public function testItShouldBeConstructedFromAsn1(): void + { + $result = AbandonRequest::fromAsn1(Asn1::application(16, Asn1::integer(1))); + $expected = new AbandonRequest(1); + $this->assertEquals($expected, $result); + } + + public function testItShouldNotAllowNonIntegersFromAsn1(): void + { + $this->expectException(ProtocolException::class); + + AbandonRequest::fromAsn1( + Asn1::application( + 16, + Asn1::octetString('foo') + ) + ); + } +} diff --git a/tests/unit/Operation/Request/AddRequestTest.php b/tests/unit/Operation/Request/AddRequestTest.php new file mode 100644 index 00000000..2bf53149 --- /dev/null +++ b/tests/unit/Operation/Request/AddRequestTest.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\AddRequest; +use PHPUnit\Framework\TestCase; + +final class AddRequestTest extends TestCase +{ + private AddRequest $subject; + + protected function setUp(): void + { + $this->subject = new AddRequest(Entry::create( + 'cn=foo,dc=foo,dc=bar', + ['cn' => 'foo'] + )); + } + + public function test_it_should_set_entry(): void + { + $entry = Entry::create('cn=foobar,dc=foo,dc=bar', ['cn' => 'foobar']); + + self::assertEquals( + Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo']), + $this->subject->getEntry(), + ); + + $this->subject->setEntry($entry); + + self::assertEquals( + $entry, + $this->subject->getEntry(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $this->subject->setEntry(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo', 'sn' => ['foo', 'bar']])); + + self::assertEquals( + Asn1::application(8, Asn1::sequence( + Asn1::octetString('cn=foo,dc=foo,dc=bar'), + Asn1::sequenceOf( + Asn1::sequence( + Asn1::octetString('cn'), + Asn1::setOf( + Asn1::octetString('foo') + ) + ), + Asn1::sequence( + Asn1::octetString('sn'), + Asn1::setOf( + Asn1::octetString('foo'), + Asn1::octetString('bar') + ) + ) + ) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $add = new AddRequest(Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo', 'sn' => ['foo', 'bar']])); + + self::assertEquals( + new AddRequest(Entry::create( + 'cn=foo,dc=foo,dc=bar', + ['cn' => 'foo', 'sn' => ['foo', 'bar']] + )), + AddRequest::fromAsn1($add->toAsn1()) + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_a_malformed_asn1_request(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider() + { + return [ + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence(), + Asn1::octetString('bar') + )], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence(), + Asn1::octetString('bar') + )], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::integer(2) + )], + [Asn1::octetString('foo')], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence() + ) + ) + )], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::sequence( + Asn1::octetString('foo') + ) + ) + )] + ]; + } +} diff --git a/tests/unit/Operation/Request/AnonBindRequestTest.php b/tests/unit/Operation/Request/AnonBindRequestTest.php new file mode 100644 index 00000000..16fd42b8 --- /dev/null +++ b/tests/unit/Operation/Request/AnonBindRequestTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\AnonBindRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use PHPUnit\Framework\TestCase; + +final class AnonBindRequestTest extends TestCase +{ + private AnonBindRequest $subject; + + protected function setUp(): void + { + $this->subject = new AnonBindRequest(); + } + + public function test_it_should_default_to_ldap_v3(): void + { + self::assertSame( + 3, + $this->subject->getVersion(), + ); + } + + public function test_it_should_have_an_empty_username_by_default(): void + { + self::assertSame( + '', + $this->subject->getUsername(), + ); + } + + public function test_it_should_set_the_username(): void + { + $this->subject->setUsername('foo'); + + self::assertSame( + 'foo', + $this->subject->getUsername(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(0, Asn1::sequence( + Asn1::integer(3), + Asn1::octetString(''), + Asn1::context(0, Asn1::octetString('')) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $anon = new AnonBindRequest('foo', 2); + + self::assertEquals( + new AnonBindRequest('foo', 2), + AnonBindRequest::fromAsn1($anon->toAsn1()), + ); + } + + public function test_it_should_check_that_a_password_is_empty_properly(): void + { + self::assertNotInstanceOf( + AnonBindRequest::class, + AnonBindRequest::fromAsn1( + (new SimpleBindRequest('foo', '0'))->toAsn1() + ) + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::sequence( + Asn1::integer(3), + Asn1::octetString('foo'), + Asn1::context(3, Asn1::octetString('foo')) + )], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::integer(2) + )], + [Asn1::sequence()], + [Asn1::integer(2)], + ]; + } +} diff --git a/tests/unit/Operation/Request/CancelRequestTest.php b/tests/unit/Operation/Request/CancelRequestTest.php new file mode 100644 index 00000000..2e6807f6 --- /dev/null +++ b/tests/unit/Operation/Request/CancelRequestTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\CancelRequest; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class CancelRequestTest extends TestCase +{ + private CancelRequest $subject; + + protected function setUp(): void + { + $this->subject = new CancelRequest(1); + } + + public function test_it_should_set_the_message_id(): void + { + self::assertSame( + 1, + $this->subject->getMessageId(), + ); + + $this->subject->setMessageId(2); + + self::assertSame( + 2, + $this->subject->getMessageId(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_CANCEL)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::integer(1) + )))) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new CancelRequest(2), + CancelRequest::fromAsn1((new CancelRequest(2))->toAsn1()), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_invalid_asn1_from_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + CancelRequest::fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + $req = new ExtendedRequest('foo', Asn1::octetString('foo')); + + return [ + [$req->toAsn1()], + [$req->setValue(Asn1::sequence())->toAsn1()], + [$req->setValue(Asn1::sequence(Asn1::octetString('bar')))->toAsn1()] + ]; + } +} diff --git a/tests/unit/Operation/Request/CompareRequestTest.php b/tests/unit/Operation/Request/CompareRequestTest.php new file mode 100644 index 00000000..ac7d9fa7 --- /dev/null +++ b/tests/unit/Operation/Request/CompareRequestTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\CompareRequest; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use PHPUnit\Framework\TestCase; + +final class CompareRequestTest extends TestCase +{ + private CompareRequest $subject; + + protected function setUp(): void + { + $this->subject = new CompareRequest( + 'dc=foo,dc=bar', + new EqualityFilter('foo', 'bar') + ); + } + + public function test_it_should_set_the_dn(): void + { + self::assertEquals( + 'dc=foo,dc=bar', + $this->subject->getDn(), + ); + + $this->subject->setDn('dc=foobar'); + + self::assertEquals( + new Dn('dc=foobar'), + $this->subject->getDn(), + ); + } + + public function test_it_should_set_the_filter(): void + { + self::assertEquals( + new EqualityFilter('foo', 'bar'), + $this->subject->getFilter(), + ); + + $this->subject->setFilter(new EqualityFilter('cn', 'foo')); + + self::assertEquals( + new EqualityFilter('cn', 'foo'), + $this->subject->getFilter(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(14, Asn1::sequence( + Asn1::octetString('dc=foo,dc=bar'), + Asn1::universal(AbstractType::TAG_TYPE_SEQUENCE, (new EqualityFilter('foo', 'bar'))->toAsn1()) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $req = new CompareRequest('foo', new EqualityFilter('foo', 'bar')); + + self::assertEquals( + $req, + CompareRequest::fromAsn1($req->toAsn1()), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_invalid_asn1_from_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + CompareRequest::fromAsn1(Asn1::octetString('foo')); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::octetString('foo')], + [Asn1::sequence()], + [Asn1::sequence(Asn1::octetString('foo'))], + [Asn1::sequence(Asn1::octetString('foo'), Asn1::sequence())], + ]; + } +} diff --git a/tests/unit/Operation/Request/DeleteRequestTest.php b/tests/unit/Operation/Request/DeleteRequestTest.php new file mode 100644 index 00000000..33976607 --- /dev/null +++ b/tests/unit/Operation/Request/DeleteRequestTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use PHPUnit\Framework\TestCase; + +final class DeleteRequestTest extends TestCase +{ + private DeleteRequest $subject; + + protected function setUp(): void + { + $this->subject = new DeleteRequest('cn=foo,dc=foo,dc=bar'); + } + + public function test_it_should_set_the_dn(): void + { + self::assertEquals( + new Dn('cn=foo,dc=foo,dc=bar'), + $this->subject->getDn(), + ); + + $this->subject->setDn(new Dn('foo')); + + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(10, Asn1::octetString('cn=foo,dc=foo,dc=bar')), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $this->subject = DeleteRequest::fromAsn1(Asn1::application(10, Asn1::octetString( + 'dc=foo,dc=bar' + ))); + + self::assertEquals( + new Dn('dc=foo,dc=bar'), + $this->subject->getDn(), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_not_be_constructed_from_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + DeleteRequest::fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::application(11, Asn1::octetString( + 'dc=foo,dc=bar' + ))], + [Asn1::application(11, Asn1::integer( + 2 + ))], + ]; + } +} diff --git a/tests/unit/Operation/Request/ExtendedRequestTest.php b/tests/unit/Operation/Request/ExtendedRequestTest.php new file mode 100644 index 00000000..2552929c --- /dev/null +++ b/tests/unit/Operation/Request/ExtendedRequestTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use PHPUnit\Framework\TestCase; + +final class ExtendedRequestTest extends TestCase +{ + private ExtendedRequest $subject; + + protected function setUp(): void + { + $this->subject = new ExtendedRequest(ExtendedRequest::OID_START_TLS);; + } + + public function test_it_should_get_the_extended_request_name(): void + { + self::assertSame( + ExtendedRequest::OID_START_TLS, + $this->subject->getName(), + ); + + $this->subject->setName('foo'); + + self::assertSame( + 'foo', + $this->subject->getName(), + ); + } + + public function test_it_should_get_the_extended_request_value(): void + { + $this->subject->setValue('foo'); + + self::assertSame( + 'foo', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_START_TLS)) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setValue('foo'); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_START_TLS)), + Asn1::context(1, Asn1::octetString('foo')) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1_with_no_value(): void + { + $request = new ExtendedRequest('foo'); + + self::assertEquals( + $request, + ExtendedRequest::fromAsn1($request->toAsn1()), + ); + } + + public function test_it_should_be_constructed_from_asn1_with_a_value(): void + { + $request = new ExtendedRequest('foo', 'bar'); + + self::assertEquals( + $request, + ExtendedRequest::fromAsn1($request->toAsn1()), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + ExtendedRequest::fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::octetString('foo')], + [Asn1::sequence(Asn1::octetString('foo'))], + [Asn1::sequence()], + ]; + } +} diff --git a/tests/unit/Operation/Request/ModifyDnRequestTest.php b/tests/unit/Operation/Request/ModifyDnRequestTest.php new file mode 100644 index 00000000..05c239e3 --- /dev/null +++ b/tests/unit/Operation/Request/ModifyDnRequestTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Entry\Rdn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; +use PHPUnit\Framework\TestCase; + +final class ModifyDnRequestTest extends TestCase +{ + private ModifyDnRequest $subject; + + protected function setUp(): void + { + $this->subject = new ModifyDnRequest( + dn: 'cn=foo,dc=foo,dc=bar', + newRdn: 'cn=bar', + deleteOldRdn: true, + ); + } + + public function test_it_should_set_the_dn(): void + { + self::assertEquals( + new Dn('cn=foo,dc=foo,dc=bar'), + $this->subject->getDn(), + ); + + $this->subject->setDn(new Dn('foo')); + + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); + } + + public function test_it_should_set_the_new_rdn(): void + { + self::assertEquals( + Rdn::create('cn=bar'), + $this->subject->getNewRdn(), + ); + + $this->subject->setNewRdn(Rdn::create('cn=foo')); + + self::assertEquals( + Rdn::create('cn=foo'), + $this->subject->getNewRdn(), + ); + } + + public function test_it_should_set_whether_to_delete_the_old_rdn(): void + { + self::assertTrue($this->subject->getDeleteOldRdn());; + + $this->subject->setDeleteOldRdn(false); + + self::assertFalse($this->subject->getDeleteOldRdn()); + } + + public function test_it_should_set_the_new_parent_dn(): void + { + self::assertNull($this->subject->getNewParentDn()); + + $this->subject->setNewParentDn(new Dn('foo')); + + self::assertEquals( + new Dn('foo'), + $this->subject->getNewParentDn(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(12, Asn1::sequence( + Asn1::octetString('cn=foo,dc=foo,dc=bar'), + Asn1::octetString('cn=bar'), + Asn1::boolean(true) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setNewParentDn('dc=foobar'); + + self::assertEquals( + Asn1::application(12, Asn1::sequence( + Asn1::octetString('cn=foo,dc=foo,dc=bar'), + Asn1::octetString('cn=bar'), + Asn1::boolean(true), + Asn1::context(0, Asn1::octetString('dc=foobar')) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $req = new ModifyDnRequest( + 'foo', + 'cn=bar', + false, + 'foobar' + ); + + self::assertEquals( + $req, + $req->fromAsn1($req->toAsn1()) + ); + + $req = new ModifyDnRequest( + 'foo', + 'cn=bar', + false + ); + + self::assertEquals( + $req, + $req->fromAsn1($req->toAsn1()) + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_not_be_constructed_from_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::octetString('foo')], + [Asn1::sequence(Asn1::integer(1))], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::octetString('cn=foo'), + Asn1::boolean(true), + Asn1::octetString('foobar') + )] + ]; + } +} diff --git a/tests/unit/Operation/Request/ModifyRequestTest.php b/tests/unit/Operation/Request/ModifyRequestTest.php new file mode 100644 index 00000000..10a18555 --- /dev/null +++ b/tests/unit/Operation/Request/ModifyRequestTest.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\ModifyRequest; +use PHPUnit\Framework\TestCase; + +final class ModifyRequestTest extends TestCase +{ + private ModifyRequest $subject; + + protected function setUp(): void + { + $this->subject = new ModifyRequest( + 'cn=foo,dc=foo,dc=bar', + Change::replace('foo', 'bar'), + Change::add('sn', 'bleep', 'blorp') + ); + } + + public function test_it_should_set_the_dn(): void + { + self::assertEquals( + new Dn('cn=foo,dc=foo,dc=bar'), + $this->subject->getDn(), + ); + + $this->subject->setDn(new Dn('foo')); + + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); + } + + public function test_it_should_set_the_changes(): void + { + self::assertEquals( + [ + Change::replace('foo', 'bar'), + Change::add('sn', 'bleep', 'blorp') + ], + $this->subject->getChanges(), + ); + + $this->subject->setChanges(Change::delete('foo', 'bar')); + + self::assertEquals( + [Change::delete('foo', 'bar')], + $this->subject->getChanges(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(6, Asn1::sequence( + Asn1::octetString('cn=foo,dc=foo,dc=bar'), + Asn1::sequenceOf( + Asn1::sequence( + Asn1::enumerated(2), + Asn1::sequence( + Asn1::octetString('foo'), + Asn1::setOf( + Asn1::octetString('bar') + ) + ) + ), + Asn1::sequence( + Asn1::enumerated(0), + Asn1::sequence( + Asn1::octetString('sn'), + Asn1::setOf( + Asn1::octetString('bleep'), + Asn1::octetString('blorp') + ) + ) + ) + ) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $req = new ModifyRequest( + 'foo', + Change::add('foo', 'bar'), + Change::delete('bar', 'foo'), + Change::replace('foobar', 'foo') + ); + + self::assertEquals( + new ModifyRequest( + 'foo', + Change::add('foo', 'bar'), + Change::delete('bar', 'foo'), + Change::replace('foobar', 'foo') + ), + ModifyRequest::fromAsn1($req->toAsn1()) + ); + } + + public function test_it_should_not_be_constructed_from_asn1_with_an_invalid_dn_type(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::integer(1), + Asn1::sequence() + )); + } + + public function test_it_should_not_be_constructed_from_asn1_with_an_invalid_changelist(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::set( + Asn1::octetString('dc=foo'), + Asn1::sequence() + )); + } + + public function test_it_should_not_be_constructed_from_asn1_with_an_invalid_changelist_type(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::integer(1), + Asn1::sequence() + )); + } + + public function test_it_should_not_be_constructed_from_asn1_with_invalid_attribute_values(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::sequence( + Asn1::enumerated(2), + Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::octetString('bar') + ) + ) + ) + ) + )); + } + + public function test_it_should_not_be_constructed_from_asn1_without_a_partial_attribute_description(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::sequence( + Asn1::enumerated(2), + Asn1::sequence( + Asn1::setOf( + Asn1::octetString('bar') + ) + ) + ) + ) + )); + } + + public function test_it_should_not_be_constructed_from_asn1_without_a_change_type(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1(Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequence( + Asn1::sequence( + Asn1::integer(999), + Asn1::sequence( + Asn1::setOf( + Asn1::octetString('bar') + ) + ) + ) + ) + )); + } +} diff --git a/tests/unit/Operation/Request/PasswordModifyRequestTest.php b/tests/unit/Operation/Request/PasswordModifyRequestTest.php new file mode 100644 index 00000000..e2b12c1e --- /dev/null +++ b/tests/unit/Operation/Request/PasswordModifyRequestTest.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\PasswordModifyRequest; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class PasswordModifyRequestTest extends TestCase +{ + private PasswordModifyRequest $subject; + + protected function setUp(): void + { + $this->subject = new PasswordModifyRequest( + 'foo', + 'bar', + '12345' + ); + } + + public function test_it_should_get_the_new_password(): void + { + self::assertSame( + '12345', + $this->subject->getNewPassword(), + ); + + $this->subject->setNewPassword('foo'); + + self::assertSame( + 'foo', + $this->subject->getNewPassword(), + ); + } + + public function test_it_should_get_the_old_password(): void + { + self::assertEquals( + 'bar', + $this->subject->getOldPassword() + ); + + $this->subject->setOldPassword('foo'); + + self::assertEquals( + 'foo', + $this->subject->getOldPassword() + ); + } + + public function test_it_should_get_the_username(): void + { + self::assertEquals( + 'foo', + $this->subject->getUsername() + ); + + $this->subject->setUsername('bar'); + + self::assertEquals( + 'bar', + $this->subject->getUsername(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(0, Asn1::octetString('foo')), + Asn1::context(1, Asn1::octetString('bar')), + Asn1::context(2, Asn1::octetString('12345')) + )))) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setUsername(null); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(1, Asn1::octetString('bar')), + Asn1::context(2, Asn1::octetString('12345')) + )))) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setOldPassword(null); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(2, Asn1::octetString('12345')) + )))) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setNewPassword(null); + + self::assertEquals( + Asn1::application(23, Asn1::sequence( + Asn1::context(0, Asn1::octetString(ExtendedRequest::OID_PWD_MODIFY)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::sequence()))) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $passwdMod = new PasswordModifyRequest('foo', 'bar', '12345'); + $result = PasswordModifyRequest::fromAsn1($passwdMod->toAsn1()); + + self::assertEquals( + $passwdMod->setValue(null), + $result->setValue(null), + ); + + $passwdMod = new PasswordModifyRequest(null, 'bar', '12345'); + $expected = PasswordModifyRequest::fromAsn1($passwdMod->toAsn1()); + self::assertEquals( + $passwdMod->setValue(null), + $expected->setValue(null), + ); + + $passwdMod = new PasswordModifyRequest('foo', null, '12345'); + $expected = PasswordModifyRequest::fromAsn1($passwdMod->toAsn1()); + self::assertEquals( + $passwdMod->setValue(null), + $expected->setValue(null), + ); + } + + public function test_it_should_not_be_constructed_from_invalid_asn1(): void + { + $this->expectException(ProtocolException::class); + + PasswordModifyRequest::fromAsn1( + (new ExtendedRequest('foo')) + ->setValue(Asn1::set()) + ->toAsn1() + ); + } +} diff --git a/tests/unit/Operation/Request/SearchRequestTest.php b/tests/unit/Operation/Request/SearchRequestTest.php new file mode 100644 index 00000000..0204aebc --- /dev/null +++ b/tests/unit/Operation/Request/SearchRequestTest.php @@ -0,0 +1,298 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Exception\UnexpectedValueException; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Result\EntryResult; +use FreeDSx\Ldap\Search\Result\ReferralResult; +use PHPUnit\Framework\TestCase; + +final class SearchRequestTest extends TestCase +{ + private SearchRequest $subject; + + protected function setUp(): void + { + $this->subject = new SearchRequest(new EqualityFilter('cn', 'foo')); + } + + public function test_it_should_set_the_attributes(): void + { + $this->subject->setAttributes(new Attribute('foo')); + + self::assertEquals( + [new Attribute('foo')], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_set_the_attributes_using_simple_string_values(): void + { + $this->subject->setAttributes('foo', 'bar'); + + self::assertEquals( + [new Attribute('foo'), new Attribute('bar')], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_set_the_base_dn(): void + { + self::assertNull($this->subject->getBaseDn()); + + $this->subject->setBaseDn('dc=foo'); + + self::assertEquals( + new Dn('dc=foo'), + $this->subject->getBaseDn(), + ); + } + + public function test_it_should_set_the_scope(): void + { + self::assertEquals( + SearchRequest::SCOPE_WHOLE_SUBTREE, + $this->subject->getScope() + ); + + $this->subject->setScope(SearchRequest::SCOPE_BASE_OBJECT); + + self::assertEquals( + SearchRequest::SCOPE_BASE_OBJECT, + $this->subject->getScope() + ); + } + + public function test_it_should_set_whether_or_not_to_dereference_aliases(): void + { + self::assertSame( + SearchRequest::DEREF_NEVER, + $this->subject->getDereferenceAliases(), + ); + + $this->subject->setDereferenceAliases(SearchRequest::DEREF_ALWAYS); + + self::assertSame( + SearchRequest::DEREF_ALWAYS, + $this->subject->getDereferenceAliases(), + ); + } + + public function test_it_should_set_a_size_limit(): void + { + self::assertSame( + 0, + $this->subject->getSizeLimit() + ); + + $this->subject->setSizeLimit(100); + + self::assertSame( + 100, + $this->subject->getSizeLimit(), + ); + } + + public function test_it_should_set_a_time_limit(): void + { + self::assertSame( + 0, + $this->subject->getTimeLimit(), + ); + + $this->subject->setTimeLimit(100); + + self::assertSame( + 100, + $this->subject->getTimeLimit(), + ); + } + + public function test_it_should_set_whether_or_not_to_get_attributes_only(): void + { + self::assertFalse($this->subject->getAttributesOnly()); + + $this->subject->setAttributesOnly(true); + + self::assertTrue($this->subject->getAttributesOnly()); + } + + public function test_it_should_have_an_alias_for_set_attributes_called_select(): void + { + $this->subject->select('foo', 'bar'); + + self::assertEquals( + [new Attribute('foo'), new Attribute('bar')], + $this->subject->getAttributes(), + ); + } + + public function test_it_should_have_an_alias_for_setBaseDn_called_base(): void + { + $this->subject->base('dc=foo'); + + self::assertEquals( + new Dn('dc=foo'), + $this->subject->getBaseDn(), + ); + } + + public function test_it_should_have_a_method_to_set_the_scopes(): void + { + $this->subject->useBaseScope(); + self::assertSame( + SearchRequest::SCOPE_BASE_OBJECT, + $this->subject->getScope(), + ); + + $this->subject->useSubtreeScope(); + self::assertSame( + SearchRequest::SCOPE_WHOLE_SUBTREE, + $this->subject->getScope(), + ); + + $this->subject->useSingleLevelScope(); + self::assertSame( + SearchRequest::SCOPE_SINGLE_LEVEL, + $this->subject->getScope(), + ); + } + + public function test_it_should_set_and_get_an_entry_handler(): void + { + $handler = fn (EntryResult $result) => $result->getEntry(); + + $this->subject->useEntryHandler($handler); + + self::assertSame( + $handler, + $this->subject->getEntryHandler(), + ); + } + + public function test_it_should_set_and_get_a_referral_handler(): void + { + $handler = fn (ReferralResult $result) => $result->getReferrals(); + + $this->subject->useReferralHandler($handler); + + self::assertSame( + $handler, + $this->subject->getReferralHandler(), + ); + } + + public function test_it_should_set_and_get_the_cancel_strategy(): void + { + $this->subject->useCancelStrategy(SearchRequest::CANCEL_CONTINUE); + + self::assertSame( + SearchRequest::CANCEL_CONTINUE, + $this->subject->getCancelStrategy(), + ); + } + + public function test_it_should_not_allow_invalid_cancel_stragegies(): void + { + $this->expectException(UnexpectedValueException::class); + + $this->subject->useCancelStrategy('foo'); + } + + public function test_it_should_generate_correct_asn1(): void + { + $this->subject->setBaseDn('dc=foo,dc=bar'); + + self::assertEquals( + Asn1::application(3, Asn1::sequence( + Asn1::octetString('dc=foo,dc=bar'), + Asn1::enumerated(2), + Asn1::enumerated(0), + Asn1::integer(0), + Asn1::integer(0), + Asn1::boolean(false), + (new EqualityFilter('cn', 'foo'))->toAsn1(), + Asn1::sequenceOf() + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $search = (new SearchRequest(new EqualityFilter('foo', 'bar'), 'cn')) + ->base('dc,=foo,dc=bar') + ->timeLimit(10) + ->sizeLimit(5) + ->useBaseScope() + ->setAttributesOnly(true) + ->setDereferenceAliases(2); + + $this->subject = SearchRequest::fromAsn1($search->toAsn1()); + + self::assertEquals( + new Dn('dc,=foo,dc=bar'), + $this->subject->getBaseDn(), + ); + self::assertSame( + 5, + $this->subject->getSizeLimit(), + ); + self::assertSame( + 10, + $this->subject->getTimeLimit(), + ); + self::assertSame( + SearchRequest::SCOPE_BASE_OBJECT, + $this->subject->getScope(), + ); + self::assertTrue($this->subject->getAttributesOnly()); + self::assertSame( + 2, + $this->subject->getDereferenceAliases(), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_not_be_constructed_from_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + SearchRequest::fromAsn1($type); + } + + /** + * @return array> + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::set()], + [Asn1::sequence()], + [Asn1::sequence( + Asn1::integer(5), + Asn1::octetString('foo') + )], + ]; + } +} diff --git a/tests/unit/Operation/Request/SimpleBindRequestTest.php b/tests/unit/Operation/Request/SimpleBindRequestTest.php new file mode 100644 index 00000000..12ed8681 --- /dev/null +++ b/tests/unit/Operation/Request/SimpleBindRequestTest.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\BindException; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use PHPUnit\Framework\TestCase; + +final class SimpleBindRequestTest extends TestCase +{ + private SimpleBindRequest $subject; + + protected function setUp(): void + { + $this->subject = new SimpleBindRequest( + '', + '' + ); + } + + public function test_it_should_default_to_ldap_v3(): void + { + self::assertSame( + 3, + $this->subject->getVersion(), + ); + } + + public function test_it_should_set_the_username(): void + { + $this->subject->setUsername('bar'); + + self::assertSame( + 'bar', + $this->subject->getUsername(), + ); + } + + public function test_it_should_set_the_password(): void + { + $this->subject->setPassword('bar'); + + self::assertSame( + 'bar', + $this->subject->getPassword(), + ); + } + + public function test_it_should_allow_non_empty_username_and_password(): void + { + $this->subject + ->setUsername('foo') + ->setPassword('bar'); + + self::assertInstanceOf( + AbstractType::class, + $this->subject->toAsn1() + ); + } + + /** + * @dataProvider unallowedUsernamePasswordProvider + */ + public function test_it_should_not_allow_empty_username_and_password_combos( + string $username, + string $password, + ): void { + $this->expectException(BindException::class); + + $this->subject + ->setUsername($username) + ->setPassword($password); + + $this->subject->toAsn1(); + } + + public function test_it_should_correctly_detect_a_zero_string_as_non_empty(): void + { + $this->subject->setUsername('foo'); + $this->subject->setPassword('0'); + + self::assertInstanceOf( + AbstractType::class, + $this->subject->toAsn1() + ); + + $this->subject->setUsername('0'); + + self::assertInstanceOf( + AbstractType::class, + $this->subject->toAsn1() + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + $this->subject + ->setUsername('foo') + ->setPassword('bar'); + + self::assertEquals( + Asn1::application(0, Asn1::sequence( + Asn1::integer(3), + Asn1::octetString('foo'), + Asn1::context(0, Asn1::octetString('bar')) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $bind = new SimpleBindRequest('foo', 'bar'); + + self::assertEquals( + $bind, + SimpleBindRequest::fromAsn1($bind->toAsn1()), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_detect_invalid_asn1_from_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + SimpleBindRequest::fromAsn1($type); + } + + /** + * @return array + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::octetString('foo')], + [Asn1::sequence( + Asn1::octetString('foo'), + Asn1::integer(3) + )], + ]; + } + + /** + * @return array + */ + public static function unallowedUsernamePasswordProvider(): array + { + return [ + ['', ''], + ['foo', ''], + ['', 'foo'], + ]; + } +} diff --git a/tests/unit/Operation/Request/SyncRequestTest.php b/tests/unit/Operation/Request/SyncRequestTest.php new file mode 100644 index 00000000..1a7685d3 --- /dev/null +++ b/tests/unit/Operation/Request/SyncRequestTest.php @@ -0,0 +1,40 @@ +subject = new SyncRequest(); + } + + public function test_it_should_get_constructed_with_a_present_filter_by_default(): void + { + self::assertEquals( + Filters::present('objectClass'), + $this->subject->getFilter() + ); + } + + public function test_it_should_set_and_get_the_setId_handler(): void + { + $handler = fn (SyncIdSetResult $result) => $result->getEntryUuids(); + + $this->subject->useIdSetHandler($handler); + + self::assertSame( + $handler, + $this->subject->getIdSetHandler() + ); + } +} diff --git a/tests/unit/Operation/Request/UnbindRequestTest.php b/tests/unit/Operation/Request/UnbindRequestTest.php new file mode 100644 index 00000000..0e3db99a --- /dev/null +++ b/tests/unit/Operation/Request/UnbindRequestTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Request; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\NullType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use PHPUnit\Framework\TestCase; + +final class UnbindRequestTest extends TestCase +{ + public function test_it_should_form_correct_asn1(): void + { + $subject = new UnbindRequest(); + + self::assertEquals( + (new NullType()) + ->setTagClass(NullType::TAG_CLASS_APPLICATION) + ->setTagNumber(2), + $subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new UnbindRequest(), + UnbindRequest::fromAsn1(Asn1::null()), + ); + } + + public function test_it_should_not_be_constructed_from_invalid_asn1(): void + { + $this->expectException(ProtocolException::class); + + UnbindRequest::fromAsn1(Asn1::octetString('foo')); + } +} diff --git a/tests/unit/Operation/Response/AddResponseTest.php b/tests/unit/Operation/Response/AddResponseTest.php new file mode 100644 index 00000000..78d0c724 --- /dev/null +++ b/tests/unit/Operation/Response/AddResponseTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\Response\AddResponse; +use PHPUnit\Framework\TestCase; + +final class AddResponseTest extends TestCase +{ + private AddResponse $subject; + + protected function setUp(): void + { + $this->subject = new AddResponse( + 0, + 'foo', + 'bar' + ); + } + + public function test_it_has_the_expected_tag_number() + { + self::assertSame( + 9, + $this->subject->toAsn1()->getTagNumber(), + ); + } +} diff --git a/tests/unit/Operation/Response/BindResponseTest.php b/tests/unit/Operation/Response/BindResponseTest.php new file mode 100644 index 00000000..9cea7a83 --- /dev/null +++ b/tests/unit/Operation/Response/BindResponseTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class BindResponseTest extends TestCase +{ + private BindResponse $subject; + + protected function setUp(): void + { + $this->subject = new BindResponse( + new LdapResult( + 0, + 'foo', + 'bar', + new LdapUrl('foo') + ), + 'foo', + ); + } + + public function test_it_should_get_the_sasl_creds(): void + { + self::assertSame( + 'foo', + $this->subject->getSaslCredentials(), + ); + } + + public function test_it_should_get_the_ldap_result_data(): void + { + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); + self::assertSame( + 'bar', + $this->subject->getDiagnosticMessage(), + ); + self::assertEquals( + [new LdapUrl('foo')], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $this->subject = BindResponse::fromAsn1(Asn1::application(1, Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(3, (new IncompleteType($encoder->encode( + Asn1::octetString('ldap://foo') + )) + )->setIsConstructed(true)), + Asn1::context(7, Asn1::octetString('foo')) + ))); + + self::assertSame( + 'foo', + $this->subject->getSaslCredentials(), + ); + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + self::assertEquals( + new Dn('dc=foo,dc=bar'), + $this->subject->getDn(), + ); + self::assertSame( + 'foo', + $this->subject->getDiagnosticMessage(), + ); + self::assertEquals( + [new LdapUrl('foo')], + $this->subject->getReferrals(), + ); + } +} diff --git a/tests/unit/Operation/Response/CompareResponseTest.php b/tests/unit/Operation/Response/CompareResponseTest.php new file mode 100644 index 00000000..8f8e029b --- /dev/null +++ b/tests/unit/Operation/Response/CompareResponseTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\Response\CompareResponse; +use PHPUnit\Framework\TestCase; + +final class CompareResponseTest extends TestCase +{ + private CompareResponse $subject; + + protected function setUp(): void + { + $this->subject = new CompareResponse(0); + } + + public function test_it_has_the_correct_tag_number(): void + { + self::assertSame( + 15, + $this->subject->toAsn1()->getTagNumber() + ); + } +} diff --git a/tests/unit/Operation/Response/DeleteResponseTest.php b/tests/unit/Operation/Response/DeleteResponseTest.php new file mode 100644 index 00000000..c5474fe3 --- /dev/null +++ b/tests/unit/Operation/Response/DeleteResponseTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use PHPUnit\Framework\TestCase; + +final class DeleteResponseTest extends TestCase +{ + private DeleteResponse $subject; + + protected function setUp(): void + { + $this->subject = new DeleteResponse( + 0, + 'foo', + 'bar' + ); + } + + public function test_it_has_the_expected_tag_number(): void + { + self::assertSame( + 11, + $this->subject->toAsn1()->getTagNumber(), + ); + } +} diff --git a/tests/unit/Operation/Response/ExtendedResponseTest.php b/tests/unit/Operation/Response/ExtendedResponseTest.php new file mode 100644 index 00000000..ace3c243 --- /dev/null +++ b/tests/unit/Operation/Response/ExtendedResponseTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class ExtendedResponseTest extends TestCase +{ + private ExtendedResponse $subject; + + protected function setUp(): void + { + $this->subject = new ExtendedResponse( + new LdapResult( + 0, + 'dc=foo,dc=bar', + 'foo' + ), + 'foo', + 'bar' + ); + } + + public function test_it_should_get_the_name(): void + { + self::assertSame( + 'foo', + $this->subject->getName(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $encoder = new LdapEncoder(); + + $this->subject = ExtendedResponse::fromAsn1(Asn1::application(24, Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(3, (new IncompleteType( + $encoder->encode(Asn1::octetString('ldap://foo')) + . $encoder->encode(Asn1::octetString('ldap://bar')) + )))->setIsConstructed(true), + Asn1::context(10, Asn1::octetString('foo')), + Asn1::context(11, Asn1::octetString('bar')) + ))); + + self::assertSame( + 'foo', + $this->subject->getName(), + ); + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } +} diff --git a/tests/unit/Operation/Response/IntermediateResponseTest.php b/tests/unit/Operation/Response/IntermediateResponseTest.php new file mode 100644 index 00000000..55a93279 --- /dev/null +++ b/tests/unit/Operation/Response/IntermediateResponseTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Operation\Response\IntermediateResponse; +use PHPUnit\Framework\TestCase; + +final class IntermediateResponseTest extends TestCase +{ + private IntermediateResponse $subject; + + protected function setUp(): void + { + $this->subject = new IntermediateResponse( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $this->subject = IntermediateResponse::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString('foo')), + Asn1::context(1, Asn1::octetString('bar')), + ))); + + self::assertSame( + 'foo', + $this->subject->getName(), + ); + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString('foo')), + Asn1::context(1, Asn1::octetString('bar')), + )), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Operation/Response/ModifyDnResponseTest.php b/tests/unit/Operation/Response/ModifyDnResponseTest.php new file mode 100644 index 00000000..baa4e210 --- /dev/null +++ b/tests/unit/Operation/Response/ModifyDnResponseTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; +use PHPUnit\Framework\TestCase; + +final class ModifyDnResponseTest extends TestCase +{ + private ModifyDnResponse $subject; + + protected function setUp(): void + { + $this->subject = new ModifyDnResponse(0); + } + + public function test_it_should_get_the_tag_number(): void + { + self::assertSame( + 13, + $this->subject->toAsn1()->getTagNumber() + ); + } +} diff --git a/tests/unit/Operation/Response/ModifyResponseTest.php b/tests/unit/Operation/Response/ModifyResponseTest.php new file mode 100644 index 00000000..a9c17b92 --- /dev/null +++ b/tests/unit/Operation/Response/ModifyResponseTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\Response\ModifyResponse; +use PHPUnit\Framework\TestCase; + +final class ModifyResponseTest extends TestCase +{ + private ModifyResponse $subject; + + protected function setUp(): void + { + $this->subject = new ModifyResponse(0); + } + + public function test_it_should_get_the_tag_number(): void + { + self::assertSame( + 7, + $this->subject->toAsn1()->getTagNumber() + ); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/PasswordModifyResponseSpec.php b/tests/unit/Operation/Response/PasswordModifyResponseTest.php similarity index 50% rename from tests/spec/FreeDSx/Ldap/Operation/Response/PasswordModifyResponseSpec.php rename to tests/unit/Operation/Response/PasswordModifyResponseTest.php index bdcf0515..dc98e076 100644 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/PasswordModifyResponseSpec.php +++ b/tests/unit/Operation/Response/PasswordModifyResponseTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Operation\Response; +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; use FreeDSx\Asn1\Asn1; use FreeDSx\Asn1\Type\IncompleteType; @@ -19,40 +19,41 @@ use FreeDSx\Ldap\Operation\LdapResult; use FreeDSx\Ldap\Operation\Response\PasswordModifyResponse; use FreeDSx\Ldap\Protocol\LdapEncoder; -use PhpSpec\ObjectBehavior; +use PHPUnit\Framework\TestCase; -class PasswordModifyResponseSpec extends ObjectBehavior +final class PasswordModifyResponseTest extends TestCase { - public function let(): void - { - $this->beConstructedWith(new LdapResult(0, 'foo'), '12345'); - } - - public function it_is_initializable(): void - { - $this->shouldHaveType(PasswordModifyResponse::class); - } + private PasswordModifyResponse $subject; - public function it_should_get_the_result_code(): void + protected function setUp(): void { - $this->getResultCode()->shouldBeEqualTo(0); + $this->subject = new PasswordModifyResponse( + new LdapResult(0, 'foo'), + '12345', + ); } - public function it_should_get_the_dn(): void + public function test_it_should_get_the_dn(): void { - $this->getDn()->shouldBeLike(new Dn('foo')); + self::assertEquals( + new Dn('foo'), + $this->subject->getDn(), + ); } - public function it_should_get_the_generated_password(): void + public function test_it_should_get_the_generated_password(): void { - $this->getGeneratedPassword()->shouldBeEqualTo('12345'); + self::assertSame( + '12345', + $this->subject->getGeneratedPassword(), + ); } - public function it_should_be_constructed_from_asn1_with_a_generated_password(): void + public function test_it_should_be_constructed_from_asn1_with_a_generated_password(): void { $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::application(24, Asn1::sequence( + $this->subject = PasswordModifyResponse::fromAsn1(Asn1::application(24, Asn1::sequence( Asn1::enumerated(0), Asn1::octetString('dc=foo,dc=bar'), Asn1::octetString('foo'), @@ -62,19 +63,28 @@ public function it_should_be_constructed_from_asn1_with_a_generated_password(): ))->setIsConstructed(true)), Asn1::context(11, Asn1::octetString($encoder->encode(Asn1::sequence( Asn1::context(0, Asn1::octetString('bleep-blorp')) - )))) - ))]); + )))), + ))); - $this->getGeneratedPassword()->shouldBeEqualTo('bleep-blorp'); - $this->getResultCode()->shouldBeEqualTo(0); - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); + self::assertSame( + 'bleep-blorp', + $this->subject->getGeneratedPassword(), + ); + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + self::assertEquals( + new Dn('dc=foo,dc=bar'), + $this->subject->getDn(), + ); } - public function it_should_be_constructed_from_asn1_without_a_generated_password(): void + public function test_it_should_be_constructed_from_asn1_without_a_generated_password(): void { $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::application(24, Asn1::sequence( + $this->subject = PasswordModifyResponse::fromAsn1(Asn1::application(24, Asn1::sequence( Asn1::enumerated(0), Asn1::octetString('dc=foo,dc=bar'), Asn1::octetString('foo'), @@ -82,9 +92,9 @@ public function it_should_be_constructed_from_asn1_without_a_generated_password( $encoder->encode(Asn1::octetString('ldap://foo')) . $encoder->encode(Asn1::octetString('ldap://bar')) ))->setIsConstructed(true)), - Asn1::context(11, Asn1::octetString($encoder->encode(Asn1::sequence()))) - ))]); + Asn1::context(11, Asn1::octetString($encoder->encode(Asn1::sequence()))), + ))); - $this->getGeneratedPassword()->shouldBeNull(); + self::assertNull($this->subject->getGeneratedPassword());; } } diff --git a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResponseSpec.php b/tests/unit/Operation/Response/SearchResponseTest.php similarity index 59% rename from tests/spec/FreeDSx/Ldap/Operation/Response/SearchResponseSpec.php rename to tests/unit/Operation/Response/SearchResponseTest.php index 16aacd03..a400bd7f 100644 --- a/tests/spec/FreeDSx/Ldap/Operation/Response/SearchResponseSpec.php +++ b/tests/unit/Operation/Response/SearchResponseTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Operation\Response; +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Entry\Entries; @@ -24,13 +24,15 @@ use FreeDSx\Ldap\Protocol\LdapMessageResponse; use FreeDSx\Ldap\Search\Result\EntryResult; use FreeDSx\Ldap\Search\Result\ReferralResult; -use PhpSpec\ObjectBehavior; +use PHPUnit\Framework\TestCase; -class SearchResponseSpec extends ObjectBehavior +final class SearchResponseTest extends TestCase { - public function let(): void + private SearchResponse $subject; + + protected function setUp(): void { - $this->beConstructedWith( + $this->subject = new SearchResponse( new LdapResult( 0, 'dc=foo,dc=bar', @@ -56,42 +58,54 @@ public function let(): void ); } - public function it_is_initializable(): void - { - $this->shouldHaveType(SearchResponse::class); - } - - public function it_should_get_the_ldap_result_values(): void + public function test_it_should_get_the_ldap_result_values(): void { - $this->getResultCode()->shouldBeEqualTo(0); - $this->getDn()->shouldBeLike(new Dn('dc=foo,dc=bar')); - $this->getDiagnosticMessage()->shouldBeEqualTo('foo'); - $this->getReferrals()->shouldBeLike([new LdapUrl('foo')]); + self::assertSame( + 0, + $this->subject->getResultCode(), + ); + self::assertEquals( + new Dn('dc=foo,dc=bar'), + $this->subject->getDn(), + ); + self::assertSame( + 'foo', + $this->subject->getDiagnosticMessage(), + ); + self::assertEquals( + [new LdapUrl('foo')], + $this->subject->getReferrals(), + ); } - public function it_should_get_the_entries(): void + public function test_it_should_get_the_entries(): void { - $this->getEntries()->shouldBeLike(new Entries(...[ - Entry::create('foo'), - Entry::create('bar') - ])); + self::assertEquals( + new Entries( + Entry::create('foo'), + Entry::create('bar') + ), + $this->subject->getEntries(), + ); } - public function it_should_get_the_referral_results(): void + public function test_it_should_get_the_referral_results(): void { - $this->getReferralResults() - ->shouldBeLike([ + self::assertEquals( + [ new ReferralResult(new LdapMessageResponse( 1, new SearchResultReference(new LdapUrl('ldap://foo')) )), - ]); + ], + $this->subject->getReferralResults(), + ); } - public function it_should_get_the_entry_results(): void + public function test_it_should_get_the_entry_results(): void { - $this->getEntryResults() - ->shouldBeLike([ + self::assertEquals( + [ new EntryResult(new LdapMessageResponse( 1, new SearchResultEntry(Entry::create('foo')) @@ -100,6 +114,8 @@ public function it_should_get_the_entry_results(): void 1, new SearchResultEntry(Entry::create('bar')) )), - ]); + ], + $this->subject->getEntryResults(), + ); } } diff --git a/tests/unit/Operation/Response/SearchResultDoneTest.php b/tests/unit/Operation/Response/SearchResultDoneTest.php new file mode 100644 index 00000000..7b55c219 --- /dev/null +++ b/tests/unit/Operation/Response/SearchResultDoneTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use PHPUnit\Framework\TestCase; + +final class SearchResultDoneTest extends TestCase +{ + private SearchResultDone $subject; + + protected function setUp(): void + { + $this->subject = new SearchResultDone(0); + } + + public function test_it_should_get_the_tag_number(): void + { + self::assertSame( + 5, + $this->subject->toAsn1()->getTagNumber(), + ); + } +} diff --git a/tests/unit/Operation/Response/SearchResultEntryTest.php b/tests/unit/Operation/Response/SearchResultEntryTest.php new file mode 100644 index 00000000..54395aec --- /dev/null +++ b/tests/unit/Operation/Response/SearchResultEntryTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use PHPUnit\Framework\TestCase; + +final class SearchResultEntryTest extends TestCase +{ + private SearchResultEntry $subject; + + protected function setUp(): void + { + $this->subject = new SearchResultEntry(Entry::create( + 'cn=foo,dc=foo,dc=bar', + ['cn' => 'foo'] + )); + } + + public function test_it_should_get_the_entry(): void + { + self::assertEquals( + Entry::create('cn=foo,dc=foo,dc=bar', ['cn' => 'foo']), + $this->subject->getEntry(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $this->subject = SearchResultEntry::fromAsn1(Asn1::application(4, Asn1::sequence( + Asn1::octetString('dc=foo,dc=bar'), + Asn1::sequenceOf( + Asn1::sequence( + Asn1::octetString('cn'), + Asn1::sequenceOf( + Asn1::octetString('foo') + ) + ), + Asn1::sequence( + Asn1::octetString('sn'), + Asn1::sequenceOf( + Asn1::octetString('foo'), + Asn1::octetString('bar') + ) + ), + ), + ))); + + self::assertEquals( + Entry::create('dc=foo,dc=bar', ['cn' => ['foo'], 'sn' => ['foo', 'bar']]), + $this->subject->getEntry(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(4, Asn1::sequence( + Asn1::octetString('cn=foo,dc=foo,dc=bar'), + Asn1::sequenceOf( + Asn1::sequence( + Asn1::octetString('cn'), + Asn1::setOf( + Asn1::octetString('foo') + ), + ), + ), + )), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Operation/Response/SearchResultReferenceTest.php b/tests/unit/Operation/Response/SearchResultReferenceTest.php new file mode 100644 index 00000000..54714d3d --- /dev/null +++ b/tests/unit/Operation/Response/SearchResultReferenceTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use PHPUnit\Framework\TestCase; + +final class SearchResultReferenceTest extends TestCase +{ + private SearchResultReference $subject; + + protected function setUp(): void + { + $this->subject = new SearchResultReference( + new LdapUrl('foo'), + new LdapUrl('bar'), + ); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEquals( + [ + new LdapUrl('foo'), + new LdapUrl('bar'), + ], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $this->subject = SearchResultReference::fromAsn1(Asn1::application(19, Asn1::sequence( + Asn1::octetString('ldap://foo'), + Asn1::octetString('ldap://bar') + ))); + + self::assertEquals( + [ + new LdapUrl('foo'), + new LdapUrl('bar'), + ], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::application(19, Asn1::sequence( + Asn1::octetString('ldap://foo/'), + Asn1::octetString('ldap://bar/'), + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_throw_a_protocol_exception_if_the_referral_cannot_be_parsed(): void + { + self::expectException(ProtocolException::class); + + SearchResultReference::fromAsn1(Asn1::application(19, Asn1::sequence( + Asn1::octetString('ldap://foo/'), + Asn1::octetString('?bar'), + ))); + } +} diff --git a/tests/unit/Operation/Response/SyncInfo/SyncIdSetTest.php b/tests/unit/Operation/Response/SyncInfo/SyncIdSetTest.php new file mode 100644 index 00000000..a908433b --- /dev/null +++ b/tests/unit/Operation/Response/SyncInfo/SyncIdSetTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response\SyncInfo; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Operation\Response\IntermediateResponse; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncIdSet; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class SyncIdSetTest extends TestCase +{ + private SyncIdSet $subject; + + protected function setUp(): void + { + $this->subject = new SyncIdSet( + ['foo', 'bar'], + false, + 'omnomnom', + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_whether_to_refresh_deletes(): void + { + self::assertFalse($this->subject->getRefreshDeletes()); + } + + public function test_it_should_get_the_entry_uuids(): void + { + self::assertSame( + ['foo', 'bar'], + $this->subject->getEntryUuids(), + ); + } + + public function test_it_should_have_the_correct_response_name(): void + { + self::assertSame( + IntermediateResponse::OID_SYNC_INFO, + $this->subject->getName(), + ); + } + + public function test_it_should_be_constructed_from_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SyncIdSet( + ['foo', 'bar'], + false, + 'omnomnom' + ), + SyncIdSet::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false), + Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) + ))))), + ))) + ); + } + + public function test_it_should_be_constructed_through_the_intermediate_response_factory_methd(): void + { + $encoder = new LdapEncoder(); + + $result = IntermediateResponse::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false), + Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) + ))))) + ))); + + self::assertInstanceOf( + SyncIdSet::class, + $result, + ); + self::assertSame( + 'omnomnom', + $result->getCookie(), + ); + self::assertFalse($result->getRefreshDeletes()); + self::assertSame( + ['foo', 'bar'], + $result->getEntryUuids(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(3, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false), + Asn1::setOf(Asn1::octetString('foo'), Asn1::octetString('bar')) + ))))) + )), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Operation/Response/SyncInfo/SyncNewCookieTest.php b/tests/unit/Operation/Response/SyncInfo/SyncNewCookieTest.php new file mode 100644 index 00000000..0332cb20 --- /dev/null +++ b/tests/unit/Operation/Response/SyncInfo/SyncNewCookieTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response\SyncInfo; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Operation\Response\IntermediateResponse; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncNewCookie; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class SyncNewCookieTest extends TestCase +{ + private SyncNewCookie $subject; + + protected function setUp(): void + { + $this->subject = new SyncNewCookie('omnomnom'); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_have_the_correct_response_name(): void + { + self::assertSame( + IntermediateResponse::OID_SYNC_INFO, + $this->subject->getName(), + ); + } + + public function test_it_should_be_constructed_from_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SyncNewCookie('omnomnom'), + SyncNewCookie::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(0, Asn1::octetString('omnomnom'))))) + ))) + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(0, Asn1::octetString('omnomnom'))))) + )), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Operation/Response/SyncInfo/SyncRefreshDeleteTest.php b/tests/unit/Operation/Response/SyncInfo/SyncRefreshDeleteTest.php new file mode 100644 index 00000000..c2494fc0 --- /dev/null +++ b/tests/unit/Operation/Response/SyncInfo/SyncRefreshDeleteTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response\SyncInfo; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Operation\Response\IntermediateResponse; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshDelete; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class SyncRefreshDeleteTest extends TestCase +{ + private SyncRefreshDelete $subject; + + protected function setUp(): void + { + $this->subject = new SyncRefreshDelete( + false, + 'omnomnom', + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_whether_the_refresh_is_done(): void + { + self::assertFalse($this->subject->getRefreshDone()); + } + + public function test_it_should_have_the_correct_response_name(): void + { + self::assertSame( + IntermediateResponse::OID_SYNC_INFO, + $this->subject->getName(), + ); + } + + public function test_it_should_be_constructed_from_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SyncRefreshDelete(false, 'omnomnom'), + SyncRefreshDelete::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(1, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))))) + ))) + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(1, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + )))) + ))), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/Operation/Response/SyncInfo/SyncRefreshPresentTest.php b/tests/unit/Operation/Response/SyncInfo/SyncRefreshPresentTest.php new file mode 100644 index 00000000..b1f561cc --- /dev/null +++ b/tests/unit/Operation/Response/SyncInfo/SyncRefreshPresentTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Operation\Response\SyncInfo; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Operation\Response\IntermediateResponse; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshPresent; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class SyncRefreshPresentTest extends TestCase +{ + private SyncRefreshPresent $subject; + + protected function setUp(): void + { + $this->subject = new SyncRefreshPresent( + false, + 'omnomnom', + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'omnomnom', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_whether_the_refresh_is_done(): void + { + self::assertFalse($this->subject->getRefreshDone()); + } + + public function test_it_should_have_the_correct_response_name(): void + { + self::assertSame( + IntermediateResponse::OID_SYNC_INFO, + $this->subject->getName(), + ); + } + + public function test_it_should_be_constructed_from_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + new SyncRefreshPresent(false, 'omnomnom'), + SyncRefreshPresent::fromAsn1(Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(2, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))))) + ))) + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + Asn1::application(25, Asn1::sequence( + Asn1::context(0, Asn1::octetString(IntermediateResponse::OID_SYNC_INFO)), + Asn1::context(1, Asn1::octetString($encoder->encode(Asn1::context(2, Asn1::sequence( + Asn1::octetString('omnomnom'), + Asn1::boolean(false) + ))))), + )), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/unit/OperationsTest.php b/tests/unit/OperationsTest.php new file mode 100644 index 00000000..5499f6d4 --- /dev/null +++ b/tests/unit/OperationsTest.php @@ -0,0 +1,289 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\Request\AbandonRequest; +use FreeDSx\Ldap\Operation\Request\AddRequest; +use FreeDSx\Ldap\Operation\Request\AnonBindRequest; +use FreeDSx\Ldap\Operation\Request\CancelRequest; +use FreeDSx\Ldap\Operation\Request\CompareRequest; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; +use FreeDSx\Ldap\Operation\Request\ModifyRequest; +use FreeDSx\Ldap\Operation\Request\PasswordModifyRequest; +use FreeDSx\Ldap\Operation\Request\SaslBindRequest; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +class OperationsTest extends TestCase +{ + public function test_it_should_create_a_sasl_bind(): void + { + self::assertEquals( + new SaslBindRequest( + '', + null, + [ + 'username' => 'foo', + 'password' => 'bar' + ] + ), + Operations::bindSasl([ + 'username' => 'foo', + 'password' => 'bar', + ]) + ); + + self::assertEquals( + new SaslBindRequest( + 'DIGEST-MD5', + null, + [ + 'username' => 'foo', + 'password' => 'bar', + ] + ), + Operations::bindSasl( + [ + 'username' => 'foo', + 'password' => 'bar', + ], + 'DIGEST-MD5' + ) + ); + } + + public function test_it_should_create_an_add_operation(): void + { + self::assertEquals( + new AddRequest(new Entry('foo')), + Operations::add(new Entry('foo')), + ); + } + + public function test_it_should_create_a_modify_operation(): void + { + self::assertEquals( + new ModifyRequest( + 'foo', + Change::replace( + 'foo', + 'bar', + ), + ), + Operations::modify( + 'foo', + Change::replace( + 'foo', + 'bar' + ) + ) + ); + } + + public function test_it_should_create_a_rename_operation(): void + { + self::assertEquals( + new ModifyDnRequest( + 'cn=foo,dc=bar,dc=foo', + 'foo=bar', + true + ), + Operations::rename( + 'cn=foo,dc=bar,dc=foo', + 'foo=bar', + ), + ); + } + + public function test_it_should_create_a_move_operation(): void + { + // Calling getRdn triggers a cache of RDN pieces on the object. Need this for the check.. + $dn = new Dn('cn=foo,dc=example,dc=local'); + $dn->getRdn(); + + self::assertEquals( + new ModifyDnRequest( + 'cn=foo,dc=example,dc=local', + 'cn=foo', + true, + 'ou=foo,dc=example,dc=local' + ), + Operations::move( + new Dn('cn=foo,dc=example,dc=local'), + new Dn('ou=foo,dc=example,dc=local'), + ), + ); + } + + public function test_it_should_create_a_delete_operation(): void + { + self::assertEquals( + new DeleteRequest('cn=foo,dc=example,dc=local'), + Operations::delete('cn=foo,dc=example,dc=local'), + ); + } + + public function test_it_should_create_a_search_operation(): void + { + self::assertEquals( + new SearchRequest( + Filters::equal( + 'foo', + 'bar' + ), + 'cn', + ), + Operations::search( + new EqualityFilter( + 'foo', + 'bar' + ), + 'cn', + ) + ); + } + + public function test_it_should_create_a_compare_operation(): void + { + self::assertEquals( + new CompareRequest( + 'cn=foo,dc=example,dc=local', + new EqualityFilter( + 'foo', + 'bar', + ) + ), + Operations::compare( + 'cn=foo,dc=example,dc=local', + 'foo', + 'bar', + ), + ); + } + + public function test_it_should_create_an_unbind_operation(): void + { + self::assertEquals( + new UnbindRequest(), + Operations::unbind(), + ); + } + + public function test_it_should_create_an_anonymous_bind_operation(): void + { + self::assertEquals( + new AnonBindRequest(), + Operations::bindAnonymously(), + ); + } + + public function test_it_should_create_a_username_password_bind_operation(): void + { + self::assertEquals( + new SimpleBindRequest( + 'foo', + 'bar', + ), + Operations::bind( + 'foo', + 'bar', + ), + ); + } + + public function test_it_should_create_an_abandon_request(): void + { + self::assertEquals( + new AbandonRequest(9), + Operations::abandon(9), + ); + } + + public function test_it_should_create_a_whoami_request(): void + { + self::assertEquals( + new ExtendedRequest(ExtendedRequest::OID_WHOAMI), + Operations::whoami(), + ); + } + + public function test_it_should_create_a_cancel_request(): void + { + self::assertEquals( + new CancelRequest(1), + Operations::cancel(1), + ); + } + + public function test_it_should_create_a_base_object_search(): void + { + self::assertEquals( + (new SearchRequest(Filters::present('objectClass'))) + ->useBaseScope() + ->base('dc=foo,dc=bar'), + Operations::read('dc=foo,dc=bar'), + ); + + self::assertEquals( + (new SearchRequest(Filters::present('objectClass'))) + ->useBaseScope() + ->base('dc=foo,dc=bar') + ->select('foo', 'bar'), + Operations::read('dc=foo,dc=bar', 'foo', 'bar'), + ); + } + + public function test_it_should_create_a_single_level_search(): void + { + self::assertEquals( + (new SearchRequest(Filters::equal('foo', 'bar'), 'cn')) + ->base('dc=foo,dc=bar') + ->useSingleLevelScope(), + Operations::list( + Filters::equal( + 'foo', + 'bar' + ), + 'dc=foo,dc=bar', + 'cn' + ), + ); + } + + public function test_it_should_create_a_password_modify_request(): void + { + self::assertEquals( + new PasswordModifyRequest( + 'foo', + '12345', + '6789', + ), + Operations::passwordModify( + 'foo', + '12345', + '6789', + ) + ); + } +} diff --git a/tests/unit/Protocol/AuthenticatorTest.php b/tests/unit/Protocol/AuthenticatorTest.php new file mode 100644 index 00000000..c0ba6551 --- /dev/null +++ b/tests/unit/Protocol/AuthenticatorTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Protocol\Authenticator; +use FreeDSx\Ldap\Protocol\Bind\BindInterface; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class AuthenticatorTest extends TestCase +{ + private Authenticator $subject; + + private BindInterface&MockObject $authOne; + + private BindInterface&MockObject $authTwo; + + protected function setUp(): void + { + $this->authOne = $this->createMock(BindInterface::class); + $this->authTwo = $this->createMock(BindInterface::class); + + $this->subject = new Authenticator([ + $this->authOne, + $this->authTwo, + ]); + } + + public function test_it_should_throw_an_exception_on_an_unknown_bind_type(): void + { + self::expectException(OperationException::class); + + $this->authOne + ->method('supports') + ->willReturn(false); + $this->authTwo + ->method('supports') + ->willReturn(false); + + $this->subject->bind(new LdapMessageRequest( + 1, + new DeleteRequest('foo') + )); + } + + public function test_it_should_return_the_token(): void + { + $bindReq = new SimpleBindRequest( + 'foo', + 'bar', + ); + $message = new LdapMessageRequest( + 1, + $bindReq, + ); + + $token = new BindToken( + 'foo', + 'bar', + ); + + $this->authOne + ->method('supports') + ->willReturn(true); + $this->authOne + ->method('bind') + ->willReturn($token); + + self::assertSame( + $token, + $this->subject->bind($message) + ); + } +} diff --git a/tests/unit/Protocol/Bind/AnonymousBindTest.php b/tests/unit/Protocol/Bind/AnonymousBindTest.php new file mode 100644 index 00000000..a07d8742 --- /dev/null +++ b/tests/unit/Protocol/Bind/AnonymousBindTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Bind; + +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\AnonBindRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\Bind\AnonymousBind; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Server\Token\AnonToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class AnonymousBindTest extends TestCase +{ + private AnonymousBind $subject; + + private ServerQueue&MockObject $mockQueue; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + + $this->subject = new AnonymousBind($this->mockQueue); + } + + public function test_it_should_validate_the_version(): void + { + self::expectException(OperationException::class); + + $this->mockQueue + ->expects(self::never()) + ->method('sendMessage'); + + $this->subject->bind( + new LdapMessageRequest( + 1, + Operations::bindAnonymously()->setVersion(4) + ), + ); + } + + public function test_it_should_return_an_anon_token_with_the_supplied_username(): void + { + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with(self::equalTo( + new LdapMessageResponse( + 1, + new BindResponse(new LdapResult(0)) + ) + )) + ->willReturnSelf(); + + self::assertEquals( + new AnonToken('foo'), + $this->subject->bind(new LdapMessageRequest( + 1, + Operations::bindAnonymously('foo') + )), + ); + } + + public function test_it_should_only_support_anonymous_binds(): void + { + self::assertTrue( + $this->subject->supports(new LdapMessageRequest( + 1, + new AnonBindRequest() + )) + ); + self::assertFalse( + $this->subject->supports(new LdapMessageRequest( + 1, + new SimpleBindRequest( + 'foo', + 'bar', + ) + )) + ); + } +} diff --git a/tests/unit/Protocol/Bind/SimpleBindTest.php b/tests/unit/Protocol/Bind/SimpleBindTest.php new file mode 100644 index 00000000..e31bbd79 --- /dev/null +++ b/tests/unit/Protocol/Bind/SimpleBindTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Bind; + +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\AnonBindRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Bind\SimpleBind; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class SimpleBindTest extends TestCase +{ + private SimpleBind $subject; + + private RequestHandlerInterface&MockObject $mockDispatcher; + + private ServerQueue&MockObject $mockQueue; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockDispatcher = $this->createMock(RequestHandlerInterface::class); + + $this->subject = new SimpleBind( + $this->mockQueue, + $this->mockDispatcher, + ); + } + + public function test_it_should_return_a_token_on_success(): void + { + $this->mockDispatcher + ->expects(self::once()) + ->method('bind') + ->with('foo@bar', 'bar') + ->willReturn(true); + + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with(self::equalTo(new LdapMessageResponse( + 1, + new BindResponse(new LdapResult(0))) + )); + + $bind = new LdapMessageRequest( + 1, + new SimpleBindRequest( + 'foo@bar', + 'bar' + ) + ); + + self::assertEquals( + new BindToken( + 'foo@bar', + 'bar', + ), + $this->subject->bind($bind), + ); + } + + public function test_it_should_throw_an_operations_exception_with_invalid_credentials_if_they_are_wrong(): void + { + $this->mockDispatcher + ->expects(self::once()) + ->method('bind') + ->with('foo@bar', 'bar') + ->willReturn(false); + + $this->mockQueue + ->expects(self::never()) + ->method('sendMessage'); + + self::expectException(OperationException::class); + self::expectExceptionCode(ResultCode::INVALID_CREDENTIALS); + + $this->subject->bind(new LdapMessageRequest( + 1, + new SimpleBindRequest('foo@bar', 'bar') + )); + } + + public function test_it_should_validate_the_version(): void + { + $this->mockQueue + ->expects(self::never()) + ->method('sendMessage'); + + self::expectException(OperationException::class); + self::expectExceptionCode(ResultCode::PROTOCOL_ERROR); + + $this->subject->bind(new LdapMessageRequest( + 1, + new SimpleBindRequest( + username: 'foo@bar', + password: 'bar', + version: 5, + )) + ); + } + + public function test_it_should_only_support_simple_binds(): void + { + self::assertFalse($this->subject->supports(new LdapMessageRequest( + 1, + new AnonBindRequest() + ))); + self::assertTrue($this->subject->supports(new LdapMessageRequest( + 1, + new SimpleBindRequest( + 'foo', + 'bar', + ) + ))); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientBasicHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientBasicHandlerTest.php new file mode 100644 index 00000000..54c4d929 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientBasicHandlerTest.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\Exception\BindException; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\CompareRequest; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\CompareResponse; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientBasicHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientBasicHandlerTest extends TestCase +{ + private ClientBasicHandler $subject; + + private ClientQueue&MockObject $mockQueue; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + + $this->subject = new ClientBasicHandler($this->mockQueue); + } + + public function test_it_should_handle_a_request_and_return_a_response(): void + { + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage'); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn(new LdapMessageResponse( + 1, + new DeleteResponse(0) + )); + + self::assertNotNull($this->subject->handleRequest(new LdapMessageRequest( + 1, + new DeleteRequest('cn=foo') + ))); + } + + public function test_it_should_handle_a_response(): void + { + $messageRequest = new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')); + $messageFrom = new LdapMessageResponse(1, new BindResponse(new LdapResult(0))); + + self::assertSame( + $messageFrom, + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ) + ); + } + + public function test_it_should_handle_a_response_with_non_error_codes(): void + { + $messageRequest = new LdapMessageRequest(1, new CompareRequest('foo', new EqualityFilter('foo', 'bar'))); + $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_FALSE)); + + self::assertSame( + $messageFrom, + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ) + ); + + $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::COMPARE_TRUE)); + + self::assertSame( + $messageFrom, + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ) + ); + + $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::REFERRAL)); + + self::assertSame( + $messageFrom, + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ) + ); + } + + public function test_it_should_throw_an_operation_exception_on_errors(): void + { + $messageRequest = new LdapMessageRequest(1, new CompareRequest('foo', new EqualityFilter('foo', 'bar'))); + $messageFrom = new LdapMessageResponse(1, new CompareResponse(ResultCode::BUSY)); + + $this->expectException(OperationException::class); + + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ); + } + + public function test_it_should_throw_a_specific_bind_exception_for_a_bind_response(): void + { + $messageRequest = new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')); + $messageFrom = new LdapMessageResponse(1, new BindResponse(new LdapResult(ResultCode::INVALID_CREDENTIALS, 'foo', 'message'))); + + self::expectException(BindException::class); + self::expectExceptionCode(ResultCode::INVALID_CREDENTIALS); + + $this->subject->handleResponse( + $messageRequest, + $messageFrom, + ); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerTest.php new file mode 100644 index 00000000..b94787a5 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientExtendedOperationHandlerTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\PasswordModifyResponse; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientExtendedOperationHandler; +use FreeDSx\Ldap\Protocol\Factory\ExtendedResponseFactory; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientExtendedOperationHandlerTest extends TestCase +{ + private ClientExtendedOperationHandler $subject; + + private ClientQueue&MockObject $mockQueue; + + private ExtendedResponseFactory&MockObject $mockResponseFactory; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->mockResponseFactory = $this->createMock(ExtendedResponseFactory::class); + + $this->subject = new ClientExtendedOperationHandler( + $this->mockQueue, + $this->mockResponseFactory, + ); + } + + public function test_it_should_handle_a_response(): void + { + $this->mockResponseFactory + ->method('has') + ->willReturn(false); + + $this->mockResponseFactory + ->expects($this->never()) + ->method('get'); + + $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0), 'bar', 'foo')); + + self::assertSame( + $response, + $this->subject->handleResponse( + new LdapMessageRequest(1, new ExtendedRequest('foo', 'bar')), + $response, + ) + ); + } + + public function test_it_should_handle_an_extended_response_that_has_a_mapped_class(): void + { + $extendedResponse = new PasswordModifyResponse(new LdapResult(0)); + + $this->mockResponseFactory + ->method('has') + ->willReturn(true); + $this->mockResponseFactory + ->expects($this->once()) + ->method('get') + ->willReturn($extendedResponse); + + $request = new ExtendedRequest('foo', 'bar'); + $extendedRequest = new LdapMessageRequest(1, $request); + $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0), 'bar')); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo($extendedRequest)); + $this->mockQueue + ->method('getMessage') + ->willReturn($response); + + self::assertInstanceOf( + PasswordModifyResponse::class, + $this->subject->handleRequest($extendedRequest)?->getResponse() + ); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientReferralHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientReferralHandlerTest.php new file mode 100644 index 00000000..58d97663 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientReferralHandlerTest.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Exception\ConnectionException; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Exception\ReferralException; +use FreeDSx\Ldap\Exception\SkipReferralException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientReferralHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\ReferralChaserInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ClientReferralHandlerTest extends TestCase +{ + private ClientReferralHandler $subject; + + private ReferralChaserInterface&MockObject $mockChaser; + + private LdapClient&MockObject $mockLdapClient; + + private ClientOptions $options; + + protected function setUp(): void + { + $this->mockChaser = $this->createMock(ReferralChaserInterface::class); + $this->mockLdapClient = $this->createMock(LdapClient::class); + + $this->mockChaser + ->method('client') + ->willReturn($this->mockLdapClient); + + $this->options = new ClientOptions(); + $this->subject = new ClientReferralHandler($this->options); + } + + public function test_it_should_throw_an_exception_on_referrals(): void + { + $this->subject = new ClientReferralHandler( + $this->options->setReferral('throw') + ); + + $response = new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', 'foo', new LdapUrl('foo'))); + $request = new LdapMessageRequest(1, new DeleteRequest('cn=foo')); + + self::expectException(ReferralException::class); + + $this->subject->handleResponse( + $request, + $response, + ); + } + + public function test_it_should_follow_referrals_with_a_referral_chaser_if_specified(): void + { + $this->options + ->setReferral('follow') + ->setReferralLimit(10) + ->setReferralChaser($this->mockChaser); + + $bind = new SimpleBindRequest('foo', 'bar'); + $this->mockChaser + ->expects(self::once()) + ->method('chase') + ->willReturn($bind); + + $message = new LdapMessageResponse(2, new DeleteResponse(0)); + + $this->mockLdapClient + ->method('send') + ->will(self::onConsecutiveCalls( + null, + $message + )); + + self::assertEquals( + $message, + $this->subject->handleResponse( + new LdapMessageRequest(2, new DeleteRequest('foo')), + new LdapMessageResponse(1, new DeleteResponse(ResultCode::REFERRAL, '', '', new LdapUrl('foo'))) + ) + ); + } + + public function test_it_should_throw_an_exception_if_the_referral_limit_is_reached(): void + { + $this->options + ->setReferral('follow') + ->setReferralLimit(-1) + ->setReferralChaser($this->mockChaser); + + self::expectExceptionObject(new OperationException( + 'The referral limit of -1 has been reached.' + )); + + $this->subject->handleResponse( + new LdapMessageRequest( + 2, + new DeleteRequest('foo') + ), + new LdapMessageResponse( + 1, + new DeleteResponse( + ResultCode::REFERRAL, + '', + '', + new LdapUrl('foo') + ) + ), + ); + } + + public function test_it_should_throw_an_exception_if_all_referrals_have_been_tried_and_follow_is_specified(): void + { + $this->mockChaser + ->method('chase') + ->willThrowException(new SkipReferralException()); + + $this->options + ->setReferral('follow') + ->setReferralLimit(10) + ->setReferralChaser($this->mockChaser); + + self::expectExceptionObject(new OperationException( + 'All referral attempts have been exhausted. ', + ResultCode::REFERRAL + )); + + $this->subject->handleResponse( + new LdapMessageRequest( + 2, + new DeleteRequest('foo') + ), + new LdapMessageResponse( + 1, + new DeleteResponse( + ResultCode::REFERRAL, + '', + '', + new LdapUrl('foo') + ) + ), + ); + } + + public function test_it_should_continue_to_the_next_referral_if_a_connection_exception_is_thrown(): void + { + $this->options + ->setReferral('follow') + ->setReferralLimit(10) + ->setReferralChaser($this->mockChaser); + + $bind = new SimpleBindRequest('foo', 'bar'); + + $this->mockChaser + ->method('chase') + ->willReturn($bind); + + $message = new LdapMessageResponse(2, new DeleteResponse(0)); + + $this->mockLdapClient + ->method('send') + ->will(self::onConsecutiveCalls( + self::throwException(new ConnectionException()), + $message, + $message, + )); + + self::assertEquals( + $message, + $this->subject->handleResponse( + new LdapMessageRequest( + 1, + new DeleteRequest('foo') + ), + new LdapMessageResponse( + 1, + new DeleteResponse( + ResultCode::REFERRAL, + '', + '', + new LdapUrl('foo'), + new LdapUrl('bar'), + ) + ), + ), + ); + } + + public function test_it_should_continue_to_the_next_referral_if_an_operation_exception_with_a_referral_result_code_is_thrown(): void + { + $this->options + ->setReferral('follow') + ->setReferralLimit(10) + ->setReferralChaser($this->mockChaser); + + $bind = new SimpleBindRequest('foo', 'bar'); + + $this->mockChaser + ->method('chase') + ->willReturn($bind); + + $message = new LdapMessageResponse(2, new DeleteResponse(0)); + + $this->mockLdapClient + ->method('send') + ->will(self::onConsecutiveCalls( + self::throwException(new OperationException('fail', ResultCode::REFERRAL)), + $message, + $message, + )); + + self::assertEquals( + $message, + $this->subject->handleResponse( + new LdapMessageRequest( + 1, + new DeleteRequest('foo') + ), + new LdapMessageResponse( + 1, + new DeleteResponse( + ResultCode::REFERRAL, + '', + '', + new LdapUrl('foo'), + new LdapUrl('bar'), + ) + ), + ), + ); + } + + public function test_it_should_not_bind_on_the_referral_client_initially_if_the_referral_is_for_a_bind_request(): void + { + $this->options + ->setReferral('follow') + ->setReferralLimit(10) + ->setReferralChaser($this->mockChaser); + + $this->mockChaser + ->method('chase') + ->willReturn(new SimpleBindRequest('foo', 'bar')); + + $message = new LdapMessageResponse(1, new BindResponse(new LdapResult(0))); + + $this->mockLdapClient + ->expects(self::once()) + ->method('send') + ->with(new SimpleBindRequest('foo', 'bar')) + ->willReturn($message); + + self::assertEquals( + $message, + $this->subject->handleResponse( + new LdapMessageRequest( + 1, + new SimpleBindRequest( + 'foo', + 'bar' + ) + ), + new LdapMessageResponse( + 1, + new BindResponse( + new LdapResult( + ResultCode::REFERRAL, + '', + '', + new LdapUrl('foo') + ) + ) + ) + ) + ); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientSaslBindHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientSaslBindHandlerTest.php new file mode 100644 index 00000000..f68a6018 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientSaslBindHandlerTest.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\BindException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSaslBindHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Protocol\RootDseLoader; +use FreeDSx\Sasl\Challenge\ChallengeInterface; +use FreeDSx\Sasl\Mechanism\MechanismInterface; +use FreeDSx\Sasl\Sasl; +use FreeDSx\Sasl\SaslContext; +use FreeDSx\Sasl\Security\SecurityLayerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientSaslBindHandlerTest extends TestCase +{ + private LdapMessageResponse $saslChallenge; + + private LdapMessageResponse $saslComplete; + + private Sasl&MockObject $mockSasl; + + private ClientQueue&MockObject $mockQueue; + + private RootDseLoader&MockObject $mockRootDseLoader; + + private MechanismInterface&MockObject $mockMech; + + private ChallengeInterface&MockObject $mockChallenge; + + private ClientSaslBindHandler $subject; + + protected function setUp(): void + { + $this->mockSasl = $this->createMock(Sasl::class); + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->mockRootDseLoader = $this->createMock(RootDseLoader::class); + $this->mockMech = $this->createMock(MechanismInterface::class); + $this->mockChallenge = $this->createMock(ChallengeInterface::class); + + $this->mockQueue + ->method('sendMessage') + ->willReturnSelf(); + $this->mockQueue + ->method('generateId') + ->will($this->onConsecutiveCalls(2, 3, 4, 5, 6)); + + $this->saslChallenge = new LdapMessageResponse( + 1, + new BindResponse(new LdapResult(ResultCode::SASL_BIND_IN_PROGRESS)) + ); + $this->saslComplete = new LdapMessageResponse( + 2, + new BindResponse(new LdapResult(ResultCode::SUCCESS), 'foo') + ); + + $this->subject = new ClientSaslBindHandler( + $this->mockQueue, + $this->mockRootDseLoader, + $this->mockSasl, + ); + } + + public function test_it_should_handle_a_sasl_bind_request(): void + { + $this->withStandardRootDseResponse(); + $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar']); + $messageRequest = new LdapMessageRequest(1, $saslBind); + + $this->mockQueue + ->method('getMessage') + ->will(self::onConsecutiveCalls( + $this->saslChallenge, + $this->saslComplete, + )); + + $this->mockSasl + ->expects($this->once()) + ->method('select') + ->with(['DIGEST-MD5', 'CRAM-MD5'], ['username' => 'foo', 'password' => 'bar']) + ->willReturn($this->mockMech); + + $this->mockMech + ->method('getName') + ->willReturn('DIGEST-MD5'); + $this->mockMech + ->method('challenge') + ->willReturn($this->mockChallenge); + + $this->mockChallenge + ->method('challenge') + ->with(self::anything(), ['username' => 'foo', 'password' => 'bar']) + ->will($this->onConsecutiveCalls( + (new SaslContext())->setResponse('foo'), + (new SaslContext())->setResponse('foo')->setIsComplete(true) + )); + + $this->mockRootDseLoader + ->method('load') + ->willReturn(Entry::fromArray( + '', + ['supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5']] + )); + + self::assertSame( + $this->saslComplete, + $this->subject->handleRequest($messageRequest), + ); + } + + public function test_it_should_detect_a_downgrade_attack(): void { + $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar']); + $messageRequest = new LdapMessageRequest(1, $saslBind); + + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + $this->saslChallenge, + $this->saslComplete, + )); + + $this->mockSasl + ->method('select') + ->willReturn($this->mockMech); + + $this->mockMech + ->method('getName') + ->willReturn('PLAIN'); + $this->mockMech + ->method('challenge') + ->willReturn($this->mockChallenge); + + $this->mockChallenge + ->method('challenge') + ->with(self::anything(), ['username' => 'foo', 'password' => 'bar']) + ->will($this->onConsecutiveCalls( + (new SaslContext())->setResponse('foo'), + (new SaslContext())->setResponse('foo')->setIsComplete(true) + )); + + $this->mockRootDseLoader + ->method('load') + ->with($this->anything()) + ->will(self::onConsecutiveCalls( + Entry::fromArray('', [ + 'supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5'], + ]), + Entry::fromArray('', [ + 'supportedSaslMechanisms' => ['PLAIN'], + ]), + Entry::fromArray('', [ + 'supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5'], + ]), + )); + + self::expectException(BindException::class); + self::expectExceptionMessageMatches( + '/Possible SASL downgrade attack detected/i' + ); + + $this->subject->handleRequest($messageRequest); + } + + public function test_it_should_not_query_the_rootdse_if_the_mechanism_was_explicitly_specified(): void + { + $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar'], 'DIGEST-MD5'); + $messageRequest = new LdapMessageRequest(1, $saslBind); + + $this->withStandardRootDseResponse(); + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + $this->saslChallenge, + $this->saslComplete, + )); + + $this->mockSasl + ->method('get') + ->with('DIGEST-MD5') + ->willReturn($this->mockMech); + + $this->mockMech + ->method('getName') + ->willReturn('DIGEST-MD5'); + $this->mockMech + ->method('challenge') + ->willReturn($this->mockChallenge); + + $this->mockChallenge + ->method('challenge') + ->will(self::onConsecutiveCalls( + (new SaslContext())->setResponse('foo'), + (new SaslContext())->setResponse('foo')->setIsComplete(true) + )); + + $this->mockRootDseLoader + ->expects(self::never()) + ->method('load'); + + self::assertSame( + $this->saslComplete, + $this->subject->handleRequest($messageRequest), + ); + } + + public function test_it_should_set_the_set_the_security_layer_on_the_queue_if_one_was_negotiated(): void + { + $saslBind = Operations::bindSasl(['username' => 'foo', 'password' => 'bar'], 'DIGEST-MD5'); + $messageRequest = new LdapMessageRequest(1, $saslBind); + + $this->mockQueue + ->method('getMessage') + ->will(self::onConsecutiveCalls( + $this->saslChallenge, + $this->saslComplete, + )); + + $this->mockSasl + ->method('get') + ->willReturn($this->mockMech); + $this->mockMech + ->method('getName') + ->willReturn('DIGEST-MD5'); + $this->mockMech + ->method('challenge') + ->willReturn($this->mockChallenge); + + $this->mockChallenge + ->method('challenge') + ->will(self::onConsecutiveCalls( + (new SaslContext())->setResponse('foo'), + (new SaslContext())->setResponse('foo') + ->setHasSecurityLayer(true) + ->setIsAuthenticated(true) + ->setIsComplete(true) + )); + + $mockSecurityLayer = $this->createMock(SecurityLayerInterface::class); + $this->mockMech + ->method('securityLayer') + ->willReturn($mockSecurityLayer); + + $this->mockQueue + ->expects(self::once()) + ->method('setMessageWrapper') + ->willReturnSelf(); + + self::assertSame( + $this->saslComplete, + $this->subject->handleRequest($messageRequest), + ); + } + + private function withStandardRootDseResponse(): void + { + $this->mockRootDseLoader + ->method('load') + ->willReturn(Entry::fromArray( + '', + ['supportedSaslMechanisms' => ['DIGEST-MD5', 'CRAM-MD5']] + )); + + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientSearchHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientSearchHandlerTest.php new file mode 100644 index 00000000..7feebc53 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientSearchHandlerTest.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Response\SearchResponse; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSearchHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class ClientSearchHandlerTest extends TestCase +{ + use TestFactoryTrait; + + private ClientSearchHandler $subject; + + private ClientQueue&MockObject $mockQueue; + + private ClientOptions $options; + + private LdapMessageResponse&MockObject $mockResponse; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->mockResponse = $this->createMock(LdapMessageResponse::class); + $this->options = new ClientOptions(); + + $this->subject = new ClientSearchHandler( + $this->mockQueue, + $this->options, + ); + } + + public function test_it_should_send_a_request_and_get_a_response(): void + { + $request = Operations::search(new EqualityFilter('foo', 'bar')); + $message = new LdapMessageRequest(1, $request); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($message); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn($this->mockResponse);; + + self::assertSame( + $this->mockResponse, + $this->subject->handleRequest($message), + ); + } + + public function test_it_should_set_a_default_DN_for_a_request_that_has_none(): void + { + $this->options->setBaseDn('cn=foo'); + $mockMessage = $this->createMock(LdapMessageRequest::class); + $mockRequest = $this->createMock(SearchRequest::class); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn($this->mockResponse); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($mockMessage); + + $mockMessage + ->method('getMessageId') + ->willReturn(1); + $mockMessage + ->method('getRequest') + ->willReturn($mockRequest); + $mockRequest + ->method('getBaseDn') + ->willReturn(null); + + $mockRequest + ->expects($this->once()) + ->method('setBaseDn') + ->with('cn=foo'); + + $this->subject->handleRequest($mockMessage); + } + + public function test_it_should_not_keep_getting_messages_when_the_first_result_is_search_done(): void + { + $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); + $response = new LdapMessageResponse(1, new SearchResultDone(0)); + + $this->mockQueue + ->expects($this->never()) + ->method('getMessage'); + + self::assertInstanceOf( + SearchResponse::class, + $this->subject->handleResponse( + $messageTo, + $response, + )?->getResponse() + ); + } + + public function test_it_should_retrieve_results_until_it_receives_a_search_done_and_return_all_results(): void + { + $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); + $response = new LdapMessageResponse(1, new SearchResultEntry(new Entry('bar'))); + + $entries = [ + new SearchResultEntry(new Entry('foo')), + new SearchResultEntry(new Entry('foo')), + new SearchResultEntry(new Entry('foo')), + ]; + $referrals = [ + new SearchResultReference(new LdapUrl('ldap://foo')), + ]; + + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageResponse(1, $entries[0]), + new LdapMessageResponse(1, $entries[1]), + new LdapMessageResponse(1, $referrals[0]), + new LdapMessageResponse(1, $entries[2]), + new LdapMessageResponse(1, new SearchResultDone( + 0, + 'cn=foo', + 'bar' + )) + )); + + $this->mockQueue + ->expects($this->exactly(5)) + ->method('getMessage') + ->with(1); + + + self::assertEquals( + self::makeSearchResponseFromEntries( + dn: 'cn=foo', + diagnostic: 'bar', + searchEntryResults: [ + new SearchResultEntry(new Entry('bar')), + ...$entries, + ], + searchReferralResults: $referrals, + ), + $this->subject->handleResponse( + $messageTo, + $response, + ), + ); + } + + public function test_it_should_throw_an_exception_if_the_result_code_is_not_success(): void + { + $messageTo = new LdapMessageRequest(1, new SearchRequest(new EqualityFilter('foo', 'bar'))); + $response = new LdapMessageResponse(1, new SearchResultDone(ResultCode::SIZE_LIMIT_EXCEEDED)); + + self::expectException(OperationException::class); + self::expectExceptionCode(ResultCode::SIZE_LIMIT_EXCEEDED); + + $this->subject->handleResponse( + $messageTo, + $response, + ); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientStartTlsHandlerSpec.php b/tests/unit/Protocol/ClientProtocolHandler/ClientStartTlsHandlerTest.php similarity index 50% rename from tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientStartTlsHandlerSpec.php rename to tests/unit/Protocol/ClientProtocolHandler/ClientStartTlsHandlerTest.php index 902df7a3..45db3b77 100644 --- a/tests/spec/FreeDSx/Ldap/Protocol/ClientProtocolHandler/ClientStartTlsHandlerSpec.php +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientStartTlsHandlerTest.php @@ -11,53 +11,64 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Protocol\ClientProtocolHandler; +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; -use FreeDSx\Ldap\ClientOptions; use FreeDSx\Ldap\Exception\ConnectionException; use FreeDSx\Ldap\Operation\LdapResult; use FreeDSx\Ldap\Operation\Request\ExtendedRequest; use FreeDSx\Ldap\Operation\Response\ExtendedResponse; use FreeDSx\Ldap\Operation\ResultCode; use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientStartTlsHandler; -use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; use FreeDSx\Ldap\Protocol\LdapMessageRequest; use FreeDSx\Ldap\Protocol\LdapMessageResponse; use FreeDSx\Ldap\Protocol\Queue\ClientQueue; -use PhpSpec\ObjectBehavior; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ClientStartTlsHandlerSpec extends ObjectBehavior +final class ClientStartTlsHandlerTest extends TestCase { - public function let(ClientQueue $queue): void - { - $this->beConstructedWith($queue); - } + private ClientStartTlsHandler $subject; - public function it_is_initializable(): void - { - $this->shouldHaveType(ClientStartTlsHandler::class); - } + private ClientQueue&MockObject $mockQueue; - public function it_should_implement_ResponseHandlerInterface(): void + protected function setUp(): void { - $this->shouldBeAnInstanceOf(ResponseHandlerInterface::class); + $this->mockQueue = $this->createMock(ClientQueue::class); + + $this->subject = new ClientStartTlsHandler($this->mockQueue); } - public function it_should_encrypt_the_queue_if_the_message_response_is_successful(ClientQueue $queue): void + public function test_it_should_encrypt_the_queue_if_the_message_response_is_successful(): void { $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0), ExtendedRequest::OID_START_TLS)); - $queue->encrypt()->shouldBeCalledOnce()->willReturn($queue); - $this->handleResponse($startTls, $response)->shouldBeAnInstanceOf(LdapMessageResponse::class); + $this->mockQueue + ->expects($this->once()) + ->method('encrypt') + ->willReturnSelf(); + + self::assertNotNull($this->subject->handleResponse( + $startTls, + $response, + )); } - public function it_should_throw_an_exception_if_the_message_response_is_unsuccessful(ClientQueue $queue): void + public function test_it_should_throw_an_exception_if_the_message_response_is_unsuccessful(): void { $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); $response = new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(ResultCode::UNAVAILABLE_CRITICAL_EXTENSION), ExtendedRequest::OID_START_TLS)); - $queue->encrypt(true)->shouldNotBeCalled(); - $this->shouldThrow(ConnectionException::class)->during('handleResponse', [$startTls, $response, $queue, new ClientOptions()]); + $this->mockQueue + ->expects($this->never()) + ->method('encrypt') + ->with(true); + + self::expectException(ConnectionException::class); + + $this->subject->handleResponse( + $startTls, + $response, + ); } } diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientSyncHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientSyncHandlerTest.php new file mode 100644 index 00000000..2b0206e2 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientSyncHandlerTest.php @@ -0,0 +1,341 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Control\Sync\SyncDoneControl; +use FreeDSx\Ldap\Control\Sync\SyncRequestControl; +use FreeDSx\Ldap\Control\Sync\SyncStateControl; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\RuntimeException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\Request\SyncRequest; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncIdSet; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSyncHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Sync\Result\SyncEntryResult; +use FreeDSx\Ldap\Sync\Result\SyncIdSetResult; +use FreeDSx\Ldap\Sync\Result\SyncReferralResult; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class ClientSyncHandlerTest extends TestCase +{ + use TestFactoryTrait; + + private ClientSyncHandler $subject; + + private ClientQueue&MockObject $mockQueue; + + private ClientOptions $options; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->options = new ClientOptions(); + + $this->subject = new ClientSyncHandler( + $this->mockQueue, + $this->options, + ); + } + + public function test_it_should_set_a_default_DN_for_a_request_that_has_none(): void + { + $this->options->setBaseDn('cn=foo'); + + $nockResponse = $this->createMock(LdapMessageResponse::class); + $mockMessage = $this->createMock(LdapMessageRequest::class); + $mockSyncRequest = $this->createMock(SyncRequest::class); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->willReturn($nockResponse); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage'); + + $mockMessage + ->method('getRequest') + ->willReturn($mockSyncRequest); + $mockSyncRequest + ->method('getBaseDn') + ->willReturn(null); + + $mockSyncRequest + ->expects($this->once()) + ->method('setBaseDn') + ->with('cn=foo'); + + $this->subject->handleRequest($mockMessage); + } + + + public function test_it_should_retrieve_results_until_it_receives_a_search_done_with_a_sync_done_control(): void + { + $messageTo = new LdapMessageRequest( + 1, + new SyncRequest(), + new SyncRequestControl(), + ); + $response = new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('bar')) + ); + + $entries = [ + new SearchResultEntry(new Entry('foo')), + new SearchResultEntry(new Entry('foo')), + new SearchResultEntry(new Entry('foo')), + ]; + $referrals = [ + new SearchResultReference(new LdapUrl('ldap://foo')), + ]; + + $this->mockQueue + ->expects($this->exactly(5)) + ->method('getMessage') + ->with(1) + ->will(self::onConsecutiveCalls( + new LdapMessageResponse(1, $entries[0]), + new LdapMessageResponse(1, $entries[1]), + new LdapMessageResponse(1, $referrals[0]), + new LdapMessageResponse(1, $entries[2]), + new LdapMessageResponse( + 1, + new SearchResultDone( + 0, + 'cn=foo', + 'bar' + ), + new SyncDoneControl('foo') + ) + )); + + self::assertEquals( + self::makeSearchResponseFromEntries( + dn: 'cn=foo', + diagnostic: 'bar', + controls: [ + new SyncDoneControl('foo'), + ] + ), + $this->subject->handleResponse( + $messageTo, + $response, + ), + ); + } + + public function test_it_should_throw_an_exception_if_a_sync_request_control_was_not_provided(): void + { + self::expectExceptionObject(new RuntimeException(sprintf( + 'Expected a "%s", but there is none.', + SyncRequestControl::class, + ))); + + $this->subject->handleResponse( + new LdapMessageRequest( + 1, + new SyncRequest(), + ), + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('bar')) + ), + ); + } + + public function test_it_should_process_a_sync_entry(): void + { + $entry = new Entry('bar'); + $syncState = new SyncStateControl( + SyncStateControl::STATE_ADD, + 'foo', + 'bar' + ); + + $entyProcessed = null; + $handler = function (SyncEntryResult $result) use (&$entyProcessed) { + $entyProcessed = $result->getEntry(); + }; + + $messageTo = new LdapMessageRequest( + 1, + (new SyncRequest()) + ->useEntryHandler($handler(...)), + new SyncRequestControl(), + ); + $response = new LdapMessageResponse( + 1, + new SearchResultEntry($entry), + $syncState, + ); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn( + new LdapMessageResponse( + 1, + new SearchResultDone( + 0, + 'cn=foo', + 'bar' + ), + new SyncDoneControl('foo') + ) + ); + + self::assertEquals( + $this::makeSearchResponseFromEntries( + dn: 'cn=foo', + diagnostic: 'bar', + controls: [ + new SyncDoneControl('foo'), + ] + ), + $this->subject->handleResponse( + $messageTo, + $response, + ) + ); + self::assertSame( + $entry, + $entyProcessed, + ); + } + + public function test_it_should_process_a_sync_id_set(): void + { + $setProcessed = null; + $handler = function (SyncIdSetResult $result) use (&$setProcessed) { + $setProcessed = $result->getEntryUuids(); + }; + $messageTo = new LdapMessageRequest( + 1, + (new SyncRequest()) + ->useIdSetHandler($handler(...)), + new SyncRequestControl(), + ); + $response = new LdapMessageResponse( + 1, + new SyncIdSet(['bar']), + ); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn( + new LdapMessageResponse( + 1, + new SearchResultDone( + 0, + 'cn=foo', + 'bar' + ), + new SyncDoneControl('foo') + ) + ); + + self::assertEquals( + self::makeSearchResponseFromEntries( + dn: 'cn=foo', + diagnostic: 'bar', + controls: [ + new SyncDoneControl('foo'), + ] + ), + $this->subject->handleResponse( + $messageTo, + $response, + ) + ); + self::assertSame( + ['bar'], + $setProcessed, + ); + } + + public function test_it_should_process_a_sync_referral(): void + { + $referral = new LdapUrl('bar'); + $syncState = new SyncStateControl( + SyncStateControl::STATE_ADD, + 'foo', + 'bar' + ); + + $referralsProcessed = null; + $handler = function (SyncReferralResult $result) use (&$referralsProcessed) { + $referralsProcessed = $result->getReferrals(); + }; + + $messageTo = new LdapMessageRequest( + 1, + (new SyncRequest()) + ->useReferralHandler($handler(...)), + new SyncRequestControl(), + ); + $response = new LdapMessageResponse( + 1, + new SearchResultReference($referral), + $syncState, + ); + + $this->mockQueue + ->expects($this->once()) + ->method('getMessage') + ->with(1) + ->willReturn( + new LdapMessageResponse( + 1, + new SearchResultDone( + 0, + 'cn=foo', + 'bar' + ), + new SyncDoneControl('foo') + ) + ); + + self::assertEquals( + self::makeSearchResponseFromEntries( + dn: 'cn=foo', + diagnostic: 'bar', + controls: [ + new SyncDoneControl('foo'), + ] + ), + $this->subject->handleResponse( + $messageTo, + $response, + ) + ); + self::assertSame( + [$referral], + $referralsProcessed, + ); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/ClientUnbindHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandler/ClientUnbindHandlerTest.php new file mode 100644 index 00000000..c87015ff --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/ClientUnbindHandlerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientUnbindHandler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientUnbindHandlerTest extends TestCase +{ + private ClientUnbindHandler $subject; + + private ClientQueue&MockObject $mockQueue; + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + + $this->subject = new ClientUnbindHandler($this->mockQueue); + } + + public function test_it_should_send_the_message_and_close_the_queue(): void + { + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage'); + + $this->mockQueue + ->expects(self::once()) + ->method('close'); + + self::assertNull($this->subject->handleRequest( + new LdapMessageRequest(1, new UnbindRequest()) + )); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandler/RequestCancelerTest.php b/tests/unit/Protocol/ClientProtocolHandler/RequestCancelerTest.php new file mode 100644 index 00000000..96d9091c --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandler/RequestCancelerTest.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ClientProtocolHandler; + +use Closure; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\CancelRequest; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestCanceler; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class RequestCancelerTest extends TestCase +{ + private RequestCanceler $subject; + + private ClientQueue&MockObject $mockQueue; + + private Closure $processorForTesting; + + private int $closureCalls = 0; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->processorForTesting = (function (LdapMessageResponse $response) { + $this->closureCalls++; + })(...); + + $this->subject = new RequestCanceler($this->mockQueue); + } + + public function test_it_should_return_the_cancel_response(): void + { + $cancelResponse = new ExtendedResponse(new LdapResult(ResultCode::CANCELED)); + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageResponse(1, new SearchResultEntry(Entry::create(''))), + new LdapMessageResponse(1, new SearchResultReference()), + new LdapMessageResponse(2, $cancelResponse), + )); + + $this->mockQueue + ->method('generateId') + ->willReturn(2); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with(self::callback( + fn (LdapMessageRequest $message) => $message->getRequest() instanceof CancelRequest + )); + + self::assertSame( + $cancelResponse, + $this->subject->cancel(1) + ); + } + + public function test_it_should_keep_processing_on_the_continue_strategy(): void + { + $this->subject = new RequestCanceler( + $this->mockQueue, + SearchRequest::CANCEL_CONTINUE, + $this->processorForTesting, + ); + + $this->mockQueue + ->method('sendMessage') + ->willReturnSelf(); + + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageResponse(1, new SearchResultEntry(Entry::create(''))), + new LdapMessageResponse(1, new SearchResultReference()), + new LdapMessageResponse(2, new ExtendedResponse(new LdapResult(ResultCode::CANCELED))), + )); + + $this->mockQueue + ->method('generateId') + ->willReturn(2); + + $this->subject->cancel(1); + + self::assertSame( + 2, + $this->closureCalls, + ); + } + + public function test_it_should_throw_an_operation_error_if_the_cancel_result_code_was_not_success(): void + { + $cancelResponse = new ExtendedResponse(new LdapResult( + ResultCode::TOO_LATE, + '', + 'Fail' + )); + + $this->mockQueue + ->method('getMessage') + ->willReturn(new LdapMessageResponse(2, $cancelResponse)); + + $this->mockQueue + ->method('generateId') + ->willReturn(2); + + $this->mockQueue + ->method('sendMessage') + ->willReturnSelf(); + + self::expectException(OperationException::class); + self::expectExceptionCode(ResultCode::TOO_LATE); + + $this->subject->cancel(1); + } +} diff --git a/tests/unit/Protocol/ClientProtocolHandlerTest.php b/tests/unit/Protocol/ClientProtocolHandlerTest.php new file mode 100644 index 00000000..864a7f17 --- /dev/null +++ b/tests/unit/Protocol/ClientProtocolHandlerTest.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Exception\ConnectionException; +use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ResponseHandlerInterface; +use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class ClientProtocolHandlerTest extends TestCase +{ + use TestFactoryTrait; + + private ClientProtocolHandler $subject; + + private ClientQueue&MockObject $mockQueue; + + private ClientQueueInstantiator&MockObject $mockQueueInstantiator; + + private ClientProtocolHandlerFactory&MockObject $mockProtocolHandlerFactory; + + private ResponseHandlerInterface&MockObject $mockResponseHandler; + + private RequestHandlerInterface&MockObject $mockRequestHandler; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->mockQueueInstantiator = $this->createMock(ClientQueueInstantiator::class); + $this->mockProtocolHandlerFactory = $this->createMock(ClientProtocolHandlerFactory::class); + $this->mockResponseHandler = $this->createMock(ResponseHandlerInterface::class); + $this->mockRequestHandler = $this->createMock(RequestHandlerInterface::class); + + $this->mockProtocolHandlerFactory + ->expects($this->any()) + ->method('forResponse') + ->willReturn($this->mockResponseHandler); + + $this->mockProtocolHandlerFactory + ->expects($this->any()) + ->method('forRequest') + ->willReturn($this->mockRequestHandler); + + $this->mockQueueInstantiator + ->expects($this->any()) + ->method('make') + ->willReturn($this->mockQueue); + + $this->mockQueue + ->expects($this->any()) + ->method('generateId') + ->willReturn(1); + + $this->subject = new ClientProtocolHandler( + new ClientOptions(), + $this->mockQueueInstantiator, + $this->mockProtocolHandlerFactory, + ); + } + + public function test_it_should_close_the_queue_on_a_disconnect_notice_and_throw_a_connection_exception(): void + { + $this->expectException(ConnectionException::class); + + $this->mockRequestHandler + ->expects($this->any()) + ->method('handleRequest') + ->willThrowException( + new UnsolicitedNotificationException( + 'foo', + 0, + null, + ExtendedResponse::OID_NOTICE_OF_DISCONNECTION + ) + ); + + $this->mockQueue + ->expects($this->once()) + ->method('close'); + + $this->subject->send(new DeleteRequest('foo')); + } + + public function test_it_should_throw_a_ldap_specific_connection_exception_on_socket_issues(): void + { + $this->expectException(ConnectionException::class); + + $this->mockRequestHandler + ->expects($this->any()) + ->method('handleRequest') + ->willThrowException(new \FreeDSx\Socket\Exception\ConnectionException( + 'foo' + )); + + $this->subject->send(new DeleteRequest('foo')); + } + + public function test_it_should_send_a_request_and_handle_a_response(): void + { + $request = new DeleteRequest('cn=foo'); + $messageResponse = new LdapMessageResponse(1, new DeleteResponse(0)); + $messageRequest = new LdapMessageRequest(1, $request); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('handleRequest') + ->with($this->callback( + fn (LdapMessageRequest $messageRequest) => $messageRequest->getRequest() === $request) + )->willReturn($messageResponse); + + $this->mockResponseHandler + ->expects($this->once()) + ->method('handleResponse') + ->with($messageRequest, $messageResponse) + ->willReturn($messageResponse); + + self::assertSame( + $messageResponse, + $this->subject->send($request) + ); + } + + public function test_it_should_return_null_if_no_response_was_returned(): void + { + $request = new UnbindRequest(); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('handleRequest') + ->with($this->callback( + fn (LdapMessageRequest $messageRequest) => $messageRequest->getRequest() === $request + ))->willReturn(null); + + $this->mockResponseHandler + ->expects($this->never()) + ->method('handleResponse'); + + self::assertNull($this->subject->send($request)); + } + + public function test_it_should_throw_a_LDAP_specific_connection_exception_if_the_response_handler_throws_a_socket_exception(): void + { + $request = new DeleteRequest('cn=foo'); + $messageResponse = new LdapMessageResponse(1, new DeleteResponse(0)); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('handleRequest') + ->with($this->callback( + fn (LdapMessageRequest $messageRequest) => $messageRequest->getRequest() === $request + ))->willReturn($messageResponse); + + $this->mockResponseHandler + ->expects($this->once()) + ->method('handleResponse') + ->willThrowException(new \FreeDSx\Socket\Exception\ConnectionException('foo')); + + $this->expectException(ConnectionException::class); + + $this->subject->send($request); + } +} diff --git a/tests/unit/Protocol/Factory/ClientProtocolHandlerFactoryTest.php b/tests/unit/Protocol/Factory/ClientProtocolHandlerFactoryTest.php new file mode 100644 index 00000000..0f69d4a3 --- /dev/null +++ b/tests/unit/Protocol/Factory/ClientProtocolHandlerFactoryTest.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Factory; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\RequestInterface; +use FreeDSx\Ldap\Operation\Request\SaslBindRequest; +use FreeDSx\Ldap\Operation\Request\SyncRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SyncInfo\SyncRefreshDelete; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientBasicHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientExtendedOperationHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientReferralHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSaslBindHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSearchHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientStartTlsHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientSyncHandler; +use FreeDSx\Ldap\Protocol\ClientProtocolHandler\ClientUnbindHandler; +use FreeDSx\Ldap\Protocol\Factory\ClientProtocolHandlerFactory; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; +use FreeDSx\Ldap\Protocol\RootDseLoader; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientProtocolHandlerFactoryTest extends TestCase +{ + private ClientProtocolHandlerFactory $subject; + + private ClientQueue&MockObject $mockQueue; + + private RootDseLoader&MockObject $mockRootDseLoader; + + private ClientQueueInstantiator&MockObject $mockQueueInstantiator; + + private RequestInterface&MockObject $mockRequest; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ClientQueue::class); + $this->mockRootDseLoader = $this->createMock(RootDseLoader::class); + $this->mockQueueInstantiator = $this->createMock(ClientQueueInstantiator::class); + $this->mockRequest = $this->createMock(RequestInterface::class); + + $this->mockQueueInstantiator + ->method('make') + ->willReturn($this->mockQueue); + + $this->subject = new ClientProtocolHandlerFactory( + new ClientOptions(), + $this->mockQueueInstantiator, + $this->mockRootDseLoader + ); + } + + public function test_it_should_get_a_search_response_handler(): void + { + self::assertInstanceOf( + ClientSearchHandler::class, + $this->subject->forResponse($this->mockRequest, new SearchResultEntry(new Entry(''))) + ); + self::assertInstanceOf( + ClientSearchHandler::class, + $this->subject->forResponse($this->mockRequest, new SearchResultDone(0)) + ); + } + + public function test_it_should_get_an_unbind_request_handler(): void + { + self::assertInstanceOf( + ClientUnbindHandler::class, + $this->subject->forRequest(Operations::unbind()) + ); + } + + public function test_it_should_get_a_basic_request_handler(): void + { + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::delete('cn=foo')) + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::bind('foo', 'bar')), + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::add(new Entry(''))), + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::modify(new Entry(''))), + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::move('cn=foo', 'cn=bar')), + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::cancel(1)), + ); + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forRequest(Operations::whoami()), + ); + } + + public function test_it_should_get_a_referral_handler(): void + { + self::assertInstanceOf( + ClientReferralHandler::class, + $this->subject->forResponse( + $this->mockRequest, + new DeleteResponse(ResultCode::REFERRAL) + ) + ); + } + + public function test_it_should_get_an_extended_response_handler(): void + { + self::assertInstanceOf( + ClientExtendedOperationHandler::class, + $this->subject->forResponse( + $this->mockRequest, + new ExtendedResponse(new LdapResult(0), 'foo')) + ); + } + + public function test_it_should_get_a_start_tls_handler(): void + { + self::assertInstanceOf( + ClientStartTlsHandler::class, + $this->subject->forResponse( + new ExtendedRequest(ExtendedRequest::OID_START_TLS), + new ExtendedResponse( + new LdapResult(0), + ExtendedRequest::OID_START_TLS + ) + ) + ); + } + + public function test_it_should_get_a_basic_response_handler(): void + { + self::assertInstanceOf( + ClientBasicHandler::class, + $this->subject->forResponse( + $this->mockRequest, + new BindResponse(new LdapResult(0)) + ) + ); + } + + public function test_it_should_get_a_sasl_bind_handler(): void + { + self::assertInstanceOf( + ClientSaslBindHandler::class, + $this->subject->forRequest(new SaslBindRequest('DIGEST-MD5')) + ); + } + + public function test_it_should_get_a_sync_handler_for_a_request(): void + { + self::assertInstanceOf( + ClientSyncHandler::class, + $this->subject->forRequest(new SyncRequest()) + ); + } + + public function test_it_should_get_a_sync_handler_for_a_response(): void + { + self::assertInstanceOf( + ClientSyncHandler::class, + $this->subject->forResponse( + new SyncRequest(), + new SyncRefreshDelete() + ), + ); + } + + public function test_it_should_get_a_sync_handler_for_an_sync_info_response(): void + { + self::assertInstanceOf( + ClientSyncHandler::class, + $this->subject->forResponse( + new SyncRequest(), + new SyncRefreshDelete(), + ), + ); + } +} diff --git a/tests/unit/Protocol/Factory/ExtendedResponseFactoryTest.php b/tests/unit/Protocol/Factory/ExtendedResponseFactoryTest.php new file mode 100644 index 00000000..15bcd9b8 --- /dev/null +++ b/tests/unit/Protocol/Factory/ExtendedResponseFactoryTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Factory; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Response\PasswordModifyResponse; +use FreeDSx\Ldap\Protocol\Factory\ExtendedResponseFactory; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use PHPUnit\Framework\TestCase; + +final class ExtendedResponseFactoryTest extends TestCase +{ + private ExtendedResponseFactory $subject; + + protected function setUp(): void + { + $this->subject = new ExtendedResponseFactory(); + } + + public function test_it_should_check_if_a_mapping_exists_for_a_specific_request_oid(): void + { + self::assertTrue($this->subject->has(ExtendedRequest::OID_PWD_MODIFY)); + self::assertFalse($this->subject->has('foo')); + } + + public function test_it_should_add_a_mapping_for_a_specific_oid(): void + { + $this->subject->set('foo', PasswordModifyResponse::class); + + self::assertTrue($this->subject->has('foo')); + } + + public function test_it_should_get_a_mapping_based_on_an_oid_and_asn1(): void + { + $encoder = new LdapEncoder(); + + self::assertEquals( + $this->subject->get( + Asn1::application(24, Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(3, (new IncompleteType( + $encoder->encode(Asn1::octetString('ldap://foo')) + . $encoder->encode(Asn1::octetString('ldap://bar')) + ))->setIsConstructed(true)), + Asn1::context(11, Asn1::octetString($encoder->encode(Asn1::sequence( + Asn1::context(0, Asn1::octetString('bleep-blorp')) + )))) + )), ExtendedRequest::OID_PWD_MODIFY + ), + new PasswordModifyResponse( + new LdapResult(0, 'dc=foo,dc=bar', 'foo', new LdapUrl('foo'), new LdapUrl('bar')), + 'bleep-blorp' + ) + ); + } +} diff --git a/tests/unit/Protocol/Factory/FilterFactoryTest.php b/tests/unit/Protocol/Factory/FilterFactoryTest.php new file mode 100644 index 00000000..423847c8 --- /dev/null +++ b/tests/unit/Protocol/Factory/FilterFactoryTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Factory; + +use FreeDSx\Ldap\Protocol\Factory\FilterFactory; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use PHPUnit\Framework\TestCase; + +final class FilterFactoryTest extends TestCase +{ + public function test_it_should_check_if_a_mapping_exists(): void + { + self::assertTrue(FilterFactory::has(0)); + self::assertFalse(FilterFactory::has(99)); + } + + public function test_it_should_set_a_mapping(): void + { + FilterFactory::set(99, EqualityFilter::class); + + self::assertTrue(FilterFactory::has(99)); + } + + public function test_it_should_get_a_mapping(): void + { + self::assertEquals( + new EqualityFilter('foo', 'bar'), + FilterFactory::get((new EqualityFilter('foo', 'bar'))->toAsn1()) + ); + } +} diff --git a/tests/unit/Protocol/Factory/ResponseFactoryTest.php b/tests/unit/Protocol/Factory/ResponseFactoryTest.php new file mode 100644 index 00000000..3b0bc0dd --- /dev/null +++ b/tests/unit/Protocol/Factory/ResponseFactoryTest.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Factory; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\AddRequest; +use FreeDSx\Ldap\Operation\Request\CompareRequest; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; +use FreeDSx\Ldap\Operation\Request\ModifyRequest; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\AddResponse; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\CompareResponse; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; +use FreeDSx\Ldap\Operation\Response\ModifyResponse; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Factory\ResponseFactory; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class ResponseFactoryTest extends TestCase +{ + private ResponseFactory $subject; + + protected function setUp(): void + { + $this->subject = new ResponseFactory(); + } + + public function test_it_should_get_a_bind_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new BindResponse(new LdapResult(0, '', 'foo')) + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new SimpleBindRequest('foo', 'bar') + ), + 0, + 'foo' + ) + ); + } + + public function test_it_should_get_an_add_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new AddResponse(0, 'foo') + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new AddRequest(Entry::create('foo')) + ), + ) + ); + } + + public function test_it_should_get_a_compare_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new CompareResponse( + ResultCode::COMPARE_TRUE, + 'foo', + 'foo' + ) + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new CompareRequest( + 'foo', + Filters::equal('foo', 'bar') + ) + ), + ResultCode::COMPARE_TRUE, + 'foo' + ), + ); + } + + public function test_it_should_get_a_modify_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new ModifyResponse(0, 'foo', 'foo') + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new ModifyRequest('foo', Change::replace('foo', 'bar')) + ), + 0, + 'foo' + ), + ); + } + + public function test_it_should_get_a_modify_dn_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new ModifyDnResponse(0, 'foo', 'foo') + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new ModifyDnRequest('foo', 'cn=bar', true) + ), + 0, + 'foo' + ), + ); + } + + public function test_it_should_get_an_extended_response(): void + { + self::assertEquals( + new LdapMessageResponse(1, new ExtendedResponse(new LdapResult(0, '', 'foo'))), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new ExtendedRequest('foo', 'bar') + ), + 0, + 'foo' + ), + ); + } + + public function test_it_should_get_a_delete_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new DeleteResponse(0, 'foo', 'foo') + ), + $this->subject->getStandardResponse( + new LdapMessageRequest( + 1, + new DeleteRequest('foo') + ), + 0, + 'foo' + ), + ); + } + + public function test_it_should_get_a_search_response(): void + { + self::assertEquals( + new LdapMessageResponse(1, new SearchResultDone(0, '', 'foo')), + $this->subject->getStandardResponse( + new LdapMessageRequest(1, new SearchRequest(Filters::present('objectClass'))), + 0, + 'foo', + ) + ); + } + + public function test_it_should_get_an_extended_error_response(): void + { + self::assertEquals( + new LdapMessageResponse( + 0, + new ExtendedResponse(new LdapResult( + ResultCode::PROTOCOL_ERROR, + '', + 'foo' + )) + ), + $this->subject->getExtendedError( + 'foo', + ResultCode::PROTOCOL_ERROR + ), + ); + } +} diff --git a/tests/unit/Protocol/Factory/ServerProtocolHandlerFactoryTest.php b/tests/unit/Protocol/Factory/ServerProtocolHandlerFactoryTest.php new file mode 100644 index 00000000..a9661356 --- /dev/null +++ b/tests/unit/Protocol/Factory/ServerProtocolHandlerFactoryTest.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Factory; + +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\Factory\ServerProtocolHandlerFactory; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerDispatchHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingUnsupportedHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerRootDseHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerSearchHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerStartTlsHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerUnbindHandler; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerWhoAmIHandler; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Server\HandlerFactoryInterface; +use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; +use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; +use FreeDSx\Ldap\Server\RequestHistory; +use FreeDSx\Ldap\ServerOptions; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerProtocolHandlerFactoryTest extends TestCase +{ + private ServerProtocolHandlerFactory $subject; + + private ServerQueue&MockObject $mockQueue; + + private HandlerFactoryInterface&MockObject $mockHandlerFactory; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockHandlerFactory = $this->createMock(HandlerFactoryInterface::class); + + $this->subject = new ServerProtocolHandlerFactory( + $this->mockHandlerFactory, + new ServerOptions(), + new RequestHistory(), + $this->mockQueue + ); + } + + public function test_it_should_get_a_start_tls_hanlder(): void + { + self::assertInstanceof( + ServerStartTlsHandler::class, + $this->subject->get( + Operations::extended(ExtendedRequest::OID_START_TLS), + new ControlBag(), + ), + ); + } + + public function test_it_should_get_a_whoami_handler(): void + { + self::assertInstanceof( + ServerWhoAmIHandler::class, + $this->subject->get( + Operations::whoami(), + new ControlBag(), + ) + ); + } + + public function test_it_should_get_a_search_handler(): void + { + $this->mockHandlerFactory + ->expects(self::once())->method('makeRequestHandler') + ->willReturn(new GenericRequestHandler()); + + self::assertInstanceof( + ServerSearchHandler::class, + $this->subject->get( + Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), + new ControlBag(), + ) + ); + } + + public function test_it_should_get_a_paging_handler_when_supported(): void + { + $controls = new ControlBag(new PagingControl(10)); + + $mockPagingHandler = $this->createMock(PagingHandlerInterface::class); + + $this->mockHandlerFactory + ->expects(self::once()) + ->method('makePagingHandler') + ->willReturn($mockPagingHandler); + + self::assertInstanceOf( + ServerPagingHandler::class, + $this->subject->get( + Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), + $controls, + ) + ); + } + + public function test_it_should_get_a_paging_unsupported_handler_when_no_paging_handler_exists(): void + { + $controls = new ControlBag(new PagingControl(10)); + + $this->mockHandlerFactory + ->expects(self::once()) + ->method('makePagingHandler') + ->willReturn(null); + + $this->mockHandlerFactory + ->expects(self::once()) + ->method('makeRequestHandler') + ->willReturn(new GenericRequestHandler()); + + self::assertInstanceOf( + ServerPagingUnsupportedHandler::class, + $this->subject->get( + Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo'), + $controls + ) + ); + } + + public function test_it_should_get_a_root_dse_handler(): void + { + self::assertInstanceOf( + ServerRootDseHandler::class, + $this->subject->get( + Operations::read(''), + new ControlBag() + ), + ); + } + + public function test_it_should_get_an_unbind_handler(): void + { + self::assertInstanceOf( + ServerUnbindHandler::class, + $this->subject->get( + Operations::unbind(), + new ControlBag() + ) + ); + } + + public function test_it_should_get_the_dispatch_handler_for_common_requests(): void + { + $this->mockHandlerFactory + ->method('makeRequestHandler') + ->willReturn(new GenericRequestHandler()); + + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::delete('cn=foo'), new ControlBag()) + ); + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::add(Entry::fromArray('cn=foo')), new ControlBag()) + ); + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::compare('cn=foo', 'foo', 'bar'), new ControlBag()) + ); + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::modify('cn=foo'), new ControlBag()), + ); + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::move('cn=foo', 'foo=bar'), new ControlBag()), + ); + self::assertInstanceOf( + ServerDispatchHandler::class, + $this->subject->get(Operations::rename('cn=foo', 'cn=foo'), new ControlBag()), + ); + } +} diff --git a/tests/unit/Protocol/LdapMessageRequestTest.php b/tests/unit/Protocol/LdapMessageRequestTest.php new file mode 100644 index 00000000..f998ee8a --- /dev/null +++ b/tests/unit/Protocol/LdapMessageRequestTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use PHPUnit\Framework\TestCase; + +final class LdapMessageRequestTest extends TestCase +{ + private LdapMessageRequest $subject; + + protected function setUp(): void + { + $this->subject = new LdapMessageRequest( + 1, + new DeleteRequest('dc=foo,dc=bar'), + new Control('foo') + ); + } + + public function test_it_should_get_the_response(): void + { + self::assertInstanceOf( + DeleteRequest::class, + $this->subject->getRequest(), + ); + } + + public function test_it_should_get_the_controls(): void + { + self::assertTrue($this->subject->controls()->has('foo')); + } + + public function test_it_should_get_the_message_id(): void + { + self::assertSame( + 1, + $this->subject->getMessageId(), + ); + } + + public function test_it_should_generate_correct_ASN1(): void + { + self::assertEquals( + Asn1::sequence( + Asn1::integer(1), + Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), + Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())) + ), + $this->subject->toAsn1(), + ); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/LdapMessageResponseSpec.php b/tests/unit/Protocol/LdapMessageResponseTest.php similarity index 50% rename from tests/spec/FreeDSx/Ldap/Protocol/LdapMessageResponseSpec.php rename to tests/unit/Protocol/LdapMessageResponseTest.php index ab3aa53a..079fb58d 100644 --- a/tests/spec/FreeDSx/Ldap/Protocol/LdapMessageResponseSpec.php +++ b/tests/unit/Protocol/LdapMessageResponseTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Protocol; +namespace Tests\Unit\FreeDSx\Ldap\Protocol; use FreeDSx\Asn1\Asn1; use FreeDSx\Asn1\Type\IncompleteType; @@ -21,13 +21,15 @@ use FreeDSx\Ldap\Operation\Response\SearchResultDone; use FreeDSx\Ldap\Protocol\LdapEncoder; use FreeDSx\Ldap\Protocol\LdapMessageResponse; -use PhpSpec\ObjectBehavior; +use PHPUnit\Framework\TestCase; -class LdapMessageResponseSpec extends ObjectBehavior +class LdapMessageResponseTest extends TestCase { - public function let(): void + private LdapMessageResponse $subject; + + protected function setUp(): void { - $this->beConstructedWith( + $this->subject = new LdapMessageResponse( 1, new SearchResponse( new SearchResultDone( @@ -36,35 +38,36 @@ public function let(): void '' ), ), - new Control('foo') + new Control('foo'), ); } - public function it_is_initializable(): void + public function test_it_should_get_the_response(): void { - $this->shouldHaveType(LdapMessageResponse::class); - } - - public function it_should_get_the_response(): void - { - $this->getResponse()->shouldBeAnInstanceOf(SearchResponse::class); + self::assertInstanceof( + SearchResponse::class, + $this->subject->getResponse(), + ); } - public function it_should_get_the_controls(): void + public function test_it_should_get_the_controls(): void { - $this->controls()->has('foo')->shouldBeEqualTo(true); + self::assertTrue($this->subject->controls()->has('foo')); } - public function it_should_get_the_message_id(): void + public function test_it_should_get_the_message_id(): void { - $this->getMessageId()->shouldBeEqualTo(1); + self::assertSame( + 1, + $this->subject->getMessageId(), + ); } - public function it_should_be_constructed_from_ASN1(): void + public function test_it_should_be_constructed_from_ASN1(): void { $encoder = new LdapEncoder(); - $this->beConstructedThrough('fromAsn1', [Asn1::sequence( + $this->subject = LdapMessageResponse::fromAsn1(Asn1::sequence( Asn1::integer(3), Asn1::application(11, Asn1::sequence( Asn1::integer(0), @@ -72,10 +75,20 @@ public function it_should_be_constructed_from_ASN1(): void Asn1::octetString('') )), Asn1::context(0, (new IncompleteType($encoder->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) - )]); + )); - $this->getMessageId()->shouldBeEqualTo(3); - $this->getResponse()->shouldBeLike(new DeleteResponse(0, 'dc=foo,dc=bar', '')); - $this->controls()->has('foo')->shouldBeEqualTo(true); + self::assertSame( + 3, + $this->subject->getMessageId(), + ); + self::assertEquals( + new DeleteResponse( + 0, + 'dc=foo,dc=bar', + '' + ), + $this->subject->getResponse(), + ); + self::assertTrue($this->subject->controls()->has('foo')); } } diff --git a/tests/unit/Protocol/LdapQueueTest.php b/tests/unit/Protocol/LdapQueueTest.php new file mode 100644 index 00000000..758228c8 --- /dev/null +++ b/tests/unit/Protocol/LdapQueueTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Asn1\Encoder\EncoderInterface; +use FreeDSx\Ldap\Protocol\LdapQueue; +use FreeDSx\Socket\Socket; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class LdapQueueTest extends TestCase +{ + private LdapQueue $subject; + + private Socket&MockObject $mockSocket; + + private EncoderInterface&MockObject $mockEncoder; + + protected function setUp(): void + { + $this->mockSocket = $this->createMock(Socket::class); + $this->mockEncoder = $this->createMock(EncoderInterface::class); + + $this->mockEncoder + ->expects($this->any()) + ->method('getLastPosition') + ->willReturn(3); + + $this->mockSocket + ->expects($this->any()) + ->method('read') + ->will($this->onConsecutiveCalls( + 'foo', + false + )); + + $this->subject = new LdapQueue( + $this->mockSocket, + $this->mockEncoder, + ); + } + + public function test_it_should_get_the_current_id(): void + { + self::assertSame( + 0, + $this->subject->currentId(), + ); + } + + public function test_it_should_generate_an_id(): void + { + self::assertSame( + 1, + $this->subject->generateId(), + ); + self::assertSame( + 2, + $this->subject->generateId(), + ); + } +} diff --git a/tests/unit/Protocol/Queue/ClientQueueInstantiatorTest.php b/tests/unit/Protocol/Queue/ClientQueueInstantiatorTest.php new file mode 100644 index 00000000..13f7e033 --- /dev/null +++ b/tests/unit/Protocol/Queue/ClientQueueInstantiatorTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Queue; + +use FreeDSx\Ldap\Protocol\Queue\ClientQueueInstantiator; +use FreeDSx\Socket\Socket; +use FreeDSx\Socket\SocketPool; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientQueueInstantiatorTest extends TestCase +{ + private ClientQueueInstantiator $subject; + + private SocketPool&MockObject $mockPool; + + private Socket&MockObject $mockSocket; + + protected function setUp(): void + { + $this->mockPool = $this->createMock(SocketPool::class); + $this->mockSocket = $this->createMock(Socket::class); + + $this->subject = new ClientQueueInstantiator($this->mockPool); + } + + public function test_it_should_return_false_if_not_instantiated_for_isConnectedAndInstantiated(): void + { + self::assertFalse($this->subject->isInstantiatedAndConnected()); + } + + public function test_it_should_return_false_if_instantiated_but_not_connected_for_isConnectedAndInstantiated(): void + { + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + + $this->mockSocket + ->method('isConnected') + ->willReturn(false); + + $this->subject->make(); + + self::assertFalse($this->subject->isInstantiatedAndConnected()); + } + + public function test_it_should_return_true_if_instantiated_and_connected_for_isConnectedAndInstantiated(): void + { + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + + $this->mockSocket + ->method('isConnected') + ->willReturn(true); + + $this->subject->make(); + + self::assertTrue($this->subject->isInstantiatedAndConnected()); + } + + public function test_it_should_return_an_instantiated_socket_on_make(): void { + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + $this->mockSocket + ->method('isConnected') + ->willReturn(true); + + $result = $this->subject->make(); + + self::assertTrue($result->isConnected()); + } + + public function test_it_should_return_the_same_queue_when_it_was_already_instantiated(): void + { + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + + self::assertSame( + $this->subject->make(), + $this->subject->make(), + ); + } +} diff --git a/tests/unit/Protocol/Queue/ClientQueueTest.php b/tests/unit/Protocol/Queue/ClientQueueTest.php new file mode 100644 index 00000000..406c6963 --- /dev/null +++ b/tests/unit/Protocol/Queue/ClientQueueTest.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Queue; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Encoder\EncoderInterface; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Exception\UnsolicitedNotificationException; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ClientQueue; +use FreeDSx\Ldap\Protocol\Queue\MessageWrapperInterface; +use FreeDSx\Socket\Queue\Buffer; +use FreeDSx\Socket\Socket; +use FreeDSx\Socket\SocketPool; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ClientQueueTest extends TestCase +{ + private ClientQueue $subject; + + private SocketPool&MockObject $mockPool; + + private Socket&MockObject $mockSocket; + + private EncoderInterface&MockObject $mockEncoder; + + protected function setUp(): void + { + $this->mockPool = $this->createMock(SocketPool::class); + $this->mockSocket = $this->createMock(Socket::class); + $this->mockEncoder = $this->createMock(EncoderInterface::class); + + $this->mockEncoder + ->method('getLastPosition') + ->willReturn(3); + + $this->mockPool + ->method('connect') + ->willReturn($this->mockSocket); + + $this->mockSocket + ->method('read') + ->will(self::onConsecutiveCalls( + 'foo', + false + )); + + $this->subject = new ClientQueue( + $this->mockPool, + $this->mockEncoder, + ); + } + + public function test_it_should_send_a_message(): void + { + $this->mockEncoder + ->expects($this->once()) + ->method('encode') + ->willReturn('foo'); + + $this->mockSocket + ->expects($this->once()) + ->method('write') + ->with('foo'); + + $this->subject->sendMessage(new LdapMessageRequest( + 1, + Operations::whoami() + )); + } + + public function test_it_should_get_a_response_message(): void + { + $this->mockEncoder + ->expects($this->once()) + ->method('decode') + ->willReturn( + Asn1::sequence( + Asn1::integer(3), + Asn1::application(11, Asn1::sequence( + Asn1::integer(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('') + )), + Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) + ) + ); + + self::assertInstanceOf( + LdapMessageResponse::class, + $this->subject->getMessage(), + ); + } + + public function test_it_should_throw_an_unsolicited_notification_exception_when_one_is_received(): void + { + $this->mockEncoder + ->method('decode') + ->willReturn( + Asn1::sequence( + Asn1::integer(0), + Asn1::application(24, Asn1::sequence( + Asn1::enumerated(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('foo'), + Asn1::context(10, Asn1::octetString(ExtendedResponse::OID_NOTICE_OF_DISCONNECTION)) + )) + ) + ); + + self::expectException(UnsolicitedNotificationException::class); + + $this->subject->getMessage(); + } + + public function test_it_should_throw_a_protocol_exception_if_the_message_id_was_unexpected(): void + { + $this->mockEncoder + ->method('decode') + ->willReturn( + Asn1::sequence( + Asn1::integer(3), + Asn1::application(11, Asn1::sequence( + Asn1::integer(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('') + )), + Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) + ) + ); + + self::expectException(ProtocolException::class); + + $this->subject->getMessage(2); + } + + public function test_it_should_set_a_message_wrapper_and_use_it_when_sending_messages(): void + { + $mockWrapper = $this->createMock(MessageWrapperInterface::class); + + $this->mockEncoder + ->method('encode') + ->willReturn('foo'); + $this->mockSocket + ->expects($this->once()) + ->method('write'); + + $mockWrapper + ->expects($this->atLeastOnce()) + ->method('wrap') + ->with('foo') + ->willReturn('bar'); + + $this->subject->setMessageWrapper($mockWrapper); + $this->subject->sendMessage(new LdapMessageRequest( + 1, + Operations::whoami(), + )); + } + + public function test_it_should_set_a_message_wrapper_and_use_it_when_receiving_messages(): void + { + $asn1 = Asn1::sequence( + Asn1::integer(3), + Asn1::application(11, Asn1::sequence( + Asn1::integer(0), + Asn1::octetString('dc=foo,dc=bar'), + Asn1::octetString('') + )), + Asn1::context(0, (new IncompleteType((new LdapEncoder())->encode((new Control('foo'))->toAsn1())))->setIsConstructed(true)) + ); + + $this->mockEncoder + ->method('decode') + ->with('bar') + ->willReturn($asn1); + + $mockWrapper = $this->createMock(MessageWrapperInterface::class); + $mockWrapper + ->method('unwrap') + ->with('foo') + ->willReturn(new Buffer('bar', 3)); + + $this->subject->setMessageWrapper($mockWrapper); + + self::assertInstanceOf( + LdapMessageResponse::class, + $this->subject->getMessage(), + ); + } +} diff --git a/tests/unit/Protocol/Queue/MessageWrapper/SaslMessageWrapperTest.php b/tests/unit/Protocol/Queue/MessageWrapper/SaslMessageWrapperTest.php new file mode 100644 index 00000000..7d84eac8 --- /dev/null +++ b/tests/unit/Protocol/Queue/MessageWrapper/SaslMessageWrapperTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Queue\MessageWrapper; + +use FreeDSx\Ldap\Protocol\Queue\MessageWrapper\SaslMessageWrapper; +use FreeDSx\Sasl\SaslContext; +use FreeDSx\Sasl\Security\SecurityLayerInterface; +use FreeDSx\Socket\Exception\PartialMessageException; +use FreeDSx\Socket\Queue\Buffer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class SaslMessageWrapperTest extends TestCase +{ + private SaslMessageWrapper $subject; + + private SecurityLayerInterface&MockObject $mockSecurityLayer; + + protected function setUp(): void + { + $this->mockSecurityLayer = $this->createMock(SecurityLayerInterface::class); + + $context = new SaslContext(); + $context->setResponse('foo'); + + $this->subject = new SaslMessageWrapper( + $this->mockSecurityLayer, + $context, + ); + } + + public function test_it_should_wrap_the_message(): void + { + $this->mockSecurityLayer + ->method('wrap') + ->with( + 'bar', + self::isInstanceOf(SaslContext::class), + ) + ->willReturn('foobar'); + + self::assertSame( + "\x00\x00\x00\x06foobar", + $this->subject->wrap('bar'), + ); + } + + public function test_it_should_unwrap_the_message(): void + { + $this->mockSecurityLayer + ->method('unwrap') + ->with( + 'foobar', + self::isInstanceOf(SaslContext::class), + )->willReturn('foobar'); + + self::assertEquals( + new Buffer("foobar", 10), + $this->subject->unwrap("\x00\x00\x00\x06foobar"), + ); + } + + public function test_it_should_throw_a_partial_message_exception_when_there_is_not_enough_data_to_unwrap(): void + { + $this->mockSecurityLayer + ->expects(self::never()) + ->method('unwrap'); + + self::expectException(PartialMessageException::class); + + $this->subject->unwrap("\x00\x00\x00\x06foo"); + } +} diff --git a/tests/unit/Protocol/Queue/ServerQueueTest.php b/tests/unit/Protocol/Queue/ServerQueueTest.php new file mode 100644 index 00000000..6d15a223 --- /dev/null +++ b/tests/unit/Protocol/Queue/ServerQueueTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\Queue; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Encoder\EncoderInterface; +use FreeDSx\Asn1\Type\IncompleteType; +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Operation\Response\DeleteResponse; +use FreeDSx\Ldap\Protocol\LdapEncoder; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\MessageWrapperInterface; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Socket\Queue\Buffer; +use FreeDSx\Socket\Socket; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerQueueTest extends TestCase +{ + private ServerQueue $subject; + + private Socket&MockObject $mockSocket; + + private EncoderInterface&MockObject $mockEncoder; + + protected function setUp(): void + { + $this->mockSocket = $this->createMock(Socket::class); + $this->mockEncoder = $this->createMock(EncoderInterface::class); + + $this->mockSocket + ->method('read') + ->will(self::onConsecutiveCalls( + 'foo', + false, + )); + + $this->mockEncoder + ->method('getLastPosition') + ->willReturn(3); + + $this->subject = new ServerQueue( + $this->mockSocket, + $this->mockEncoder, + ); + } + + public function test_it_should_send_a_message(): void + { + $this->mockEncoder + ->method('encode') + ->willReturn('foo'); + $this->mockSocket + ->expects($this->once()) + ->method('write'); + + $this->subject->sendMessage(new LdapMessageResponse( + 1, + new DeleteResponse(0) + ),); + } + + public function test_it_should_get_a_request_message(): void + { + $this->mockEncoder + ->method('decode') + ->willReturn( + Asn1::sequence( + Asn1::integer(1), + Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), + new IncompleteType((new LdapEncoder())->encode(Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())))) + ), + ); + + self::assertInstanceOf( + LdapMessageRequest::class, + $this->subject->getMessage(), + ); + } + + public function test_it_should_send_multiple_messages_with_write_and_respect_the_buffer_size(): void + { + $this->mockEncoder + ->expects(self::atLeast(2)) + ->method('encode') + ->willReturn(str_repeat('f', 8000)); + + $this->mockSocket + ->expects(self::atLeast(2)) + ->method('write'); + + $this->subject->sendMessage( + new LdapMessageResponse(1, new DeleteResponse(0)), + new LdapMessageResponse(2, new DeleteResponse(0)) + ); + } + + + public function test_it_should_set_a_message_wrapper_and_use_it_when_sending_messages(): void + { + $this->mockEncoder + ->method('encode') + ->willReturn('foo'); + $this->mockSocket + ->expects($this->once()) + ->method('write'); + + $mockWrapper = $this->createMock(MessageWrapperInterface::class); + $mockWrapper->method('wrap') + ->with('foo') + ->willReturn('bar'); + + $this->subject->setMessageWrapper($mockWrapper); + $this->subject->sendMessage(new LdapMessageResponse( + 1, + new DeleteResponse(0) + )); + } + + public function test_it_should_set_a_message_wrapper_and_use_it_when_receiving_messages(): void + { + $asn1 = Asn1::sequence( + Asn1::integer(1), + Asn1::application(10, Asn1::octetString('dc=foo,dc=bar')), + new IncompleteType((new LdapEncoder())->encode(Asn1::context(0, Asn1::sequenceOf((new Control('foo'))->toAsn1())))) + ); + + $this->mockEncoder + ->method('decode') + ->willReturn($asn1); + + $mockWrapper = $this->createMock(MessageWrapperInterface::class); + $mockWrapper + ->method('unwrap') + ->willReturn(new Buffer('bar', 3)); + + $this->subject->setMessageWrapper($mockWrapper); + + self::assertInstanceOf( + LdapMessageRequest::class, + $this->subject->getMessage(), + ); + } +} diff --git a/tests/unit/Protocol/ReferralContextTest.php b/tests/unit/Protocol/ReferralContextTest.php new file mode 100644 index 00000000..82174346 --- /dev/null +++ b/tests/unit/Protocol/ReferralContextTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Protocol\ReferralContext; +use PHPUnit\Framework\TestCase; + +final class ReferralContextTest extends TestCase +{ + private ReferralContext $subject; + + protected function setUp(): void + { + $this->subject = new ReferralContext(new LdapUrl('foo')); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEquals( + [new LdapUrl('foo')], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_check_if_it_has_a_specific_referral(): void + { + self::assertTrue($this->subject->hasReferral(new LdapUrl('Foo'))); + self::assertFalse($this->subject->hasReferral(new LdapUrl('bar'))); + } + + public function test_it_should_add_a_referral(): void + { + $this->subject->addReferral(new LdapUrl('bar')); + + self::assertEquals( + [ + new LdapUrl('foo'), + new LdapUrl('bar'), + ], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_get_the_referral_count(): void + { + self::assertCount( + 1, + $this->subject + ); + } +} diff --git a/tests/unit/Protocol/RootDseLoaderTest.php b/tests/unit/Protocol/RootDseLoaderTest.php new file mode 100644 index 00000000..2472e7b3 --- /dev/null +++ b/tests/unit/Protocol/RootDseLoaderTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Protocol\RootDseLoader; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class RootDseLoaderTest extends TestCase +{ + private RootDseLoader $subject; + + private LdapClient&MockObject $mockLdapClient; + + protected function setUp(): void + { + $this->mockLdapClient = $this->createMock(LdapClient::class); + + $this->subject = new RootDseLoader($this->mockLdapClient);; + } + + public function test_it_should_load_the_root_dse(): void + { + $entry = Entry::fromArray('', []); + + $this->mockLdapClient + ->expects(self::once()) + ->method('read') + ->willReturn($entry); + + self::assertSame( + $entry, + $this->subject->load(), + ); + } + + public function test_it_should_use_the_cached_root_dse_on_a_second_load_call(): void + { + $entry = Entry::fromArray('', []); + + $this->mockLdapClient + ->expects(self::once()) + ->method('read') + ->with('', $this->anything()) + ->willReturn($entry); + + self::assertSame( + $entry, + $this->subject->load(), + ); + } + + public function test_it_should_not_use_the_cached_root_if_the_reload_param_is_used(): void + { + $entry = Entry::fromArray('', []); + + $this->mockLdapClient + ->expects(self::atMost(2)) + ->method('read') + ->with('', $this->anything()) + ->willReturn($entry); + + $this->subject->load(); + + self::assertSame( + $entry, + $this->subject->load(reload: true), + ); + } + + public function test_it_should_throw_an_exception_if_no_root_dse_is_returned(): void + { + self::expectException(OperationException::class); + + $this->subject->load(); + } +} diff --git a/tests/unit/Protocol/ServerAuthorizationTest.php b/tests/unit/Protocol/ServerAuthorizationTest.php new file mode 100644 index 00000000..ba75a91f --- /dev/null +++ b/tests/unit/Protocol/ServerAuthorizationTest.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\RequestInterface; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\ServerAuthorization; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Server\Token\AnonToken; +use FreeDSx\Ldap\ServerOptions; +use PHPUnit\Framework\TestCase; + +final class ServerAuthorizationTest extends TestCase +{ + private ServerAuthorization $subject; + + protected function setUp(): void + { + $this->subject = new ServerAuthorization(new ServerOptions()); + } + + public function test_it_should_have_an_anonymous_token_by_default(): void + { + self::assertInstanceOf( + AnonToken::class, + $this->subject->getToken(), + ); + } + + public function test_it_should_set_the_token(): void + { + $token = new AnonToken('foo'); + + $this->subject->setToken($token); + + self::assertEquals( + $token, + $this->subject->getToken(), + ); + } + + public function test_it_should_not_require_authentication_for_a_start_tls_request(): void + { + self::assertFalse($this->subject->isAuthenticationRequired( + Operations::extended(ExtendedRequest::OID_START_TLS) + )); + } + + public function test_it_should_not_require_authentication_for_a_whoami_request(): void + { + self::assertFalse($this->subject->isAuthenticationRequired( + Operations::extended(ExtendedRequest::OID_WHOAMI) + )); + } + + public function test_it_should_not_require_authentication_for_a_bind_request(): void + { + self::assertFalse($this->subject->isAuthenticationRequired( + Operations::bind('foo', 'bar') + )); + } + + public function test_it_should_not_require_authentication_for_an_unbind_request(): void + { + self::assertFalse($this->subject->isAuthenticationRequired( + Operations::unbind() + )); + } + + public function test_it_should_not_require_authentication_for_a_rootdse_request(): void + { + self::assertFalse($this->subject->isAuthenticationRequired( + Operations::read('') + )); + } + + /** + * @dataProvider authExpectedRequestsDataProvider + */ + public function test_it_should_require_authentication_for_all_other_operations(RequestInterface $request): void + { + self::assertTrue($this->subject->isAuthenticationRequired($request)); + } + + /** + * @dataProvider authExpectedRequestsDataProvider + */ + public function test_it_should_not_require_authentication_if_it_has_been_explicitly_disabled(RequestInterface $request): void + { + $this->subject = new ServerAuthorization( + (new ServerOptions()) + ->setAllowAnonymous(false) + ->setRequireAuthentication(false), + new AnonToken() + ); + + self::assertFalse($this->subject->isAuthenticationRequired($request)); + } + + public function test_it_should_not_allow_anonymous_authentication_by_default(): void + { + self::assertFalse($this->subject->isAuthenticationTypeSupported(Operations::bindAnonymously())); + } + + public function test_it_should_respect_the_option_for_whether_anon_binds_are_allowed(): void + { + $this->subject = new ServerAuthorization( + (new ServerOptions()) + ->setAllowAnonymous(true), + new AnonToken() + ); + + self::assertTrue($this->subject->isAuthenticationTypeSupported(Operations::bindAnonymously())); + } + + public function test_it_should_allow_simple_bind_types(): void + { + self::assertTrue($this->subject->isAuthenticationTypeSupported( + Operations::bind('foo', 'bar') + )); + } + + public function test_it_should_tell_if_a_request_is_an_authentication_type(): void + { + self::assertTrue($this->subject->isAuthenticationRequest(Operations::bindAnonymously())); + self::assertTrue($this->subject->isAuthenticationRequest(Operations::bind('foo', 'bar'))); + } + + /** + * @dataProvider authExpectedRequestsDataProvider + */ + public function test_it_should_tell_if_a_request_is_not_an_authentication_type(RequestInterface $request): void + { + self::assertFalse($this->subject->isAuthenticationRequest($request)); + } + + public static function authExpectedRequestsDataProvider(): array + { + return [ + [Operations::read('cn=bar')], + [Operations::list(new EqualityFilter('foo', 'bar'), 'cn=foo')], + [Operations::search(new EqualityFilter('foo', 'bar'), 'cn=foo')], + [Operations::add(Entry::fromArray(''))], + [Operations::delete('cn=foo')], + [Operations::rename('cn=foo', 'cn=foo')], + [Operations::abandon(1)], + [Operations::passwordModify('', '', '')], + [Operations::modify('cn=foo', Change::reset('foo'))], + [Operations::compare('cn=foo', 'foo', 'bar')], + ]; + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerDispatchHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerDispatchHandlerTest.php new file mode 100644 index 00000000..bde858a6 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerDispatchHandlerTest.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\Request\AbandonRequest; +use FreeDSx\Ldap\Operation\Request\AddRequest; +use FreeDSx\Ldap\Operation\Request\CompareRequest; +use FreeDSx\Ldap\Operation\Request\DeleteRequest; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; +use FreeDSx\Ldap\Operation\Request\ModifyRequest; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerDispatchHandler; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerDispatchHandlerTest extends TestCase +{ + private ServerDispatchHandler $subject; + + private RequestHandlerInterface&MockObject $mockRequestHandler; + + private ServerQueue&MockObject $mockQueue; + + private TokenInterface&MockObject $mockToken; + + protected function setUp(): void + { + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockRequestHandler = $this->createMock(RequestHandlerInterface::class); + + $this->mockQueue + ->method('sendMessage') + ->willReturnSelf(); + + $this->subject = new ServerDispatchHandler( + $this->mockQueue, + $this->mockRequestHandler, + ); + } + + public function test_it_should_send_an_add_request_to_the_request_handler(): void + { + $add = new LdapMessageRequest(1, new AddRequest(Entry::create('cn=foo,dc=bar'))); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('add') + ->with(self::anything(), $add->getRequest()); + + $this->subject->handleRequest( + $add, + $this->mockToken, + ); + } + + public function test_it_should_send_a_delete_request_to_the_request_handler(): void + { + $delete = new LdapMessageRequest(1, new DeleteRequest('cn=foo,dc=bar')); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('delete') + ->with(self::anything(), $delete->getRequest()); + + $this->subject->handleRequest( + $delete, + $this->mockToken, + ); + } + + public function test_it_should_send_a_modify_request_to_the_request_handler(): void + { + $modify = new LdapMessageRequest(1, new ModifyRequest('cn=foo,dc=bar', Change::add('foo', 'bar'))); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('modify') + ->with(self::anything(), $modify->getRequest()); + + $this->subject->handleRequest( + $modify, + $this->mockToken, + ); + } + + public function test_it_should_send_a_modify_dn_request_to_the_request_handler(): void + { + $modifyDn = new LdapMessageRequest(1, new ModifyDnRequest('cn=foo,dc=bar', 'cn=bar', true)); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('modifyDn') + ->with(self::anything(), $modifyDn->getRequest()); + + $this->subject->handleRequest( + $modifyDn, + $this->mockToken, + ); + } + + public function test_it_should_send_an_extended_request_to_the_request_handler(): void + { + $ext = new LdapMessageRequest(1, new ExtendedRequest('foo', 'bar')); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('extended') + ->with(self::anything(), $ext->getRequest()); + + $this->subject->handleRequest( + $ext, + $this->mockToken, + ); + } + + public function test_it_should_send_a_compare_request_to_the_request_handler(): void + { + $compare = new LdapMessageRequest(1, new CompareRequest('cn=foo,dc=bar', Filters::equal('foo', 'bar'))); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('compare') + ->with(self::anything(), $compare->getRequest()) + ->willReturn(true); + + $this->subject->handleRequest( + $compare, + $this->mockToken, + ); + } + + public function test_it_should_throw_an_operation_exception_if_the_request_is_unsupported(): void + { + $request = new LdapMessageRequest(2, new AbandonRequest(1)); + + self::expectException(OperationException::class); + + $this->subject->handleRequest( + $request, + $this->mockToken + ); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingHandlerSpec.php b/tests/unit/Protocol/ServerProtocolHandler/ServerPagingHandlerTest.php similarity index 50% rename from tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingHandlerSpec.php rename to tests/unit/Protocol/ServerProtocolHandler/ServerPagingHandlerTest.php index f86cb9b8..94e81225 100644 --- a/tests/spec/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerPagingHandlerSpec.php +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerPagingHandlerTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Protocol\ServerProtocolHandler; +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; use FreeDSx\Ldap\Control\Control; use FreeDSx\Ldap\Control\ControlBag; @@ -31,41 +31,39 @@ use FreeDSx\Ldap\Server\Paging\PagingRequest; use FreeDSx\Ldap\Server\Paging\PagingResponse; use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; -use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; use FreeDSx\Ldap\Server\RequestHistory; use FreeDSx\Ldap\Server\Token\TokenInterface; -use FreeDSx\Ldap\ServerOptions; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ServerPagingHandlerSpec extends ObjectBehavior +class ServerPagingHandlerTest extends TestCase { private RequestHistory $requestHistory; - public function let( - ServerQueue $queue, - PagingHandlerInterface $pagingHandler - ): void { + private ServerQueue&MockObject $mockQueue; + + private PagingHandlerInterface&MockObject $mockPagingHandler; + + private ServerPagingHandler $subject; + + private TokenInterface&MockObject $mockToken; + + protected function setUp(): void + { + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockPagingHandler = $this->createMock(PagingHandlerInterface::class); $this->requestHistory = new RequestHistory(); - $this->beConstructedWith( - $queue, - $pagingHandler, - $this->requestHistory + $this->subject = new ServerPagingHandler( + $this->mockQueue, + $this->mockPagingHandler, + $this->requestHistory, ); } - public function it_is_initializable(): void + public function test_it_should_send_a_request_to_the_paging_handler_on_paging_start(): void { - $this->shouldHaveType(ServerPagingHandler::class); - } - - public function it_should_send_a_request_to_the_paging_handler_on_paging_start( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token, - PagingHandlerInterface $pagingHandler - ): void { $message = $this->makeSearchMessage(); $entries = new Entries( @@ -84,33 +82,33 @@ public function it_should_send_a_request_to_the_paging_handler_on_paging_start( $response = PagingResponse::make($entries); - $pagingHandler->page(Argument::any(), Argument::any()) - ->shouldBeCalled() + $this->mockPagingHandler + ->expects($this->once()) + ->method('page') ->willReturn($response); - $queue->sendMessage( - $resultEntry1, - $resultEntry2, - Argument::that(function (LdapMessageResponse $response) { - /** @var PagingControl $paging */ - $paging = $response->controls()->get(Control::OID_PAGING); - - return $paging && $paging->getCookie() !== ''; - }) - )->shouldBeCalled(); - - $this->handleRequest( + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + $resultEntry1, + $resultEntry2, + self::callback(function (LdapMessageResponse $response) { + /** @var PagingControl $paging */ + $paging = $response->controls()->get(Control::OID_PAGING); + + return $paging && $paging->getCookie() !== ''; + }) + ); + + $this->subject->handleRequest( $message, - $token, + $this->mockToken, ); } - public function it_should_send_the_correct_response_if_paging_is_complete( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token, - PagingHandlerInterface $pagingHandler - ): void { + public function test_it_should_send_the_correct_response_if_paging_is_complete(): void + { $message = $this->makeSearchMessage(); $entries = new Entries( @@ -129,36 +127,37 @@ public function it_should_send_the_correct_response_if_paging_is_complete( $response = PagingResponse::makeFinal($entries); - $pagingHandler->page(Argument::any(), Argument::any()) - ->shouldBeCalled() + $this->mockPagingHandler + ->expects($this->once()) + ->method('page') ->willReturn($response); - $pagingHandler->remove(Argument::any(), Argument::any()) - ->shouldBeCalled(); - - $queue->sendMessage( - $resultEntry1, - $resultEntry2, - Argument::that(function (LdapMessageResponse $response) { - /** @var PagingControl $paging */ - $paging = $response->controls()->get(Control::OID_PAGING); - - return $paging && $paging->getCookie() === ''; - }) - )->shouldBeCalled(); - - $this->handleRequest( + $this->mockPagingHandler + ->expects($this->once()) + ->method('remove'); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + $resultEntry1, + $resultEntry2, + self::callback(function (LdapMessageResponse $response) { + /** @var PagingControl $paging */ + $paging = $response->controls()->get(Control::OID_PAGING); + + return $paging && $paging->getCookie() === ''; + }) + ); + + $this->subject->handleRequest( $message, - $token, + $this->mockToken, ); } - public function it_should_send_the_correct_response_if_paging_is_abandoned( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token, - PagingHandlerInterface $pagingHandler - ): void { + public function test_it_should_send_the_correct_response_if_paging_is_abandoned(): void + { $pagingReq = $this->makeExistingPagingRequest(); $message = $this->makeSearchMessage( 0, @@ -166,33 +165,32 @@ public function it_should_send_the_correct_response_if_paging_is_abandoned( $pagingReq->getSearchRequest() ); - $pagingHandler->page(Argument::any(), Argument::any()) - ->shouldNotBeCalled(); + $this->mockPagingHandler + ->expects($this->never()) + ->method('page'); - $pagingHandler->remove(Argument::any(), Argument::any()) - ->shouldBeCalled(); + $this->mockPagingHandler + ->expects($this->once()) + ->method('remove'); - $queue->sendMessage( - Argument::that(function (LdapMessageResponse $response) { + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with(self::callback(function (LdapMessageResponse $response) { /** @var PagingControl $paging */ $paging = $response->controls()->get(Control::OID_PAGING); return $paging && $paging->getCookie() === ''; - }) - )->shouldBeCalled(); + })); - $this->handleRequest( + $this->subject->handleRequest( $message, - $token, + $this->mockToken, ); } - public function it_sends_a_result_code_error_in_SearchResultDone_if_the_old_and_new_paging_requests_are_different( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token, - PagingHandlerInterface $pagingHandler - ): void { + public function test_it_sends_a_result_code_error_in_SearchResultDone_if_the_old_and_new_paging_requests_are_different(): void + { $this->makeExistingPagingRequest(); $message = $this->makeSearchMessage( 0, @@ -200,56 +198,58 @@ public function it_sends_a_result_code_error_in_SearchResultDone_if_the_old_and_ $this->makeSearchRequest('(oh=no)') ); - $pagingHandler->page(Argument::any(), Argument::any()) - ->shouldNotBeCalled(); - $pagingHandler->remove(Argument::any(), Argument::any()) - ->shouldBeCalled(); - - $queue->sendMessage(new LdapMessageResponse( - $message->getMessageId(), - new SearchResultDone( - ResultCode::OPERATIONS_ERROR, - 'dc=foo,dc=bar', - "The search request and controls must be identical between paging requests." - ), - ...[new PagingControl( - 0, - '' - )] - ))->shouldBeCalled() - ->willReturn($queue); - - $this->handleRequest( + $this->mockPagingHandler + ->expects($this->never()) + ->method('page'); + $this->mockPagingHandler + ->expects($this->once()) + ->method('remove'); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with(self::equalTo( + new LdapMessageResponse( + $message->getMessageId(), + new SearchResultDone( + ResultCode::OPERATIONS_ERROR, + 'dc=foo,dc=bar', + "The search request and controls must be identical between paging requests." + ), + ...[new PagingControl( + 0, + '' + )] + ) + )); + + $this->subject->handleRequest( $message, - $token, + $this->mockToken, ); } - public function it_throws_an_exception_if_the_paging_cookie_does_not_exist( - ServerQueue $queue, - RequestHandlerInterface $handler, - TokenInterface $token - ): void { + public function test_it_throws_an_exception_if_the_paging_cookie_does_not_exist(): void + { $message = $this->makeSearchMessage( 0, 'foo', $this->makeSearchRequest('(oh=no)') ); - $this->shouldThrow(new OperationException("The supplied cookie is invalid."))->during('handleRequest', [ + self::expectExceptionObject(new OperationException("The supplied cookie is invalid.")); + + $this->subject->handleRequest( $message, - $token, - $handler, - $queue, - new ServerOptions() - ]); + $this->mockToken, + ); } private function makeExistingPagingRequest( int $size = 10, string $cookie = 'bar', string $nextCookie = 'foo', - SearchRequest $searchRequest = null + ?SearchRequest $searchRequest = null ): PagingRequest { $searchReq = $searchRequest ?? $this->makeSearchRequest(); @@ -269,7 +269,7 @@ private function makeExistingPagingRequest( private function makeSearchMessage( int $size = 10, string $cookie = '', - SearchRequest $searchRequest = null + ?SearchRequest $searchRequest = null ): LdapMessageRequest { return new LdapMessageRequest( 2, diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerTest.php new file mode 100644 index 00000000..b0468c45 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerPagingUnsupportedHandlerTest.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerPagingUnsupportedHandler; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerPagingUnsupportedHandlerTest extends TestCase +{ + private ServerPagingUnsupportedHandler $subject; + + private ServerQueue&MockObject $mockQueue; + + private RequestHandlerInterface&MockObject $mockRequestHandler; + + private TokenInterface&MockObject $mockToken; + + protected function setUp(): void + { + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockRequestHandler = $this->createMock(RequestHandlerInterface::class); + + $this->subject = new ServerPagingUnsupportedHandler( + $this->mockQueue, + $this->mockRequestHandler, + ); + } + + public function test_it_should_send_a_search_request_to_the_request_handler_if_paging_is_not_critical(): void + { + $search = new LdapMessageRequest( + 2, + (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar'), + (new PagingControl(10, ''))->setCriticality(false) + ); + + $entries = new Entries(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']), Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])); + $resultEntry1 = new LdapMessageResponse( + 2, + new SearchResultEntry(Entry::create('dc=foo,dc=bar', ['cn' => 'foo'])) + ); + $resultEntry2 = new LdapMessageResponse( + 2, + new SearchResultEntry(Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])) + ); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('search') + ->with(self::anything(), $search->getRequest()) + ->willReturn($entries); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + $resultEntry1, + $resultEntry2, + new LdapMessageResponse( + 2, + new SearchResultDone( + 0, + 'dc=foo,dc=bar' + ) + ), + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_throw_an_unavailable_critical_extension_if_paging_is_marked_critical(): void + { + $search = new LdapMessageRequest( + 2, + (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar'), + (new PagingControl(10, ''))->setCriticality(true) + ); + + $this->mockRequestHandler + ->expects($this->never()) + ->method('search') + ->with(self::anything(), $search->getRequest()); + + self::expectExceptionObject( + new OperationException( + 'The server does not support the paging control.', + ResultCode::UNAVAILABLE_CRITICAL_EXTENSION, + ), + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_send_a_SearchResultDone_with_an_operation_exception_thrown_from_the_handler(): void + { + $search = new LdapMessageRequest( + 2, + (new SearchRequest(Filters::equal( + 'foo', + 'bar' + )))->base('dc=foo,dc=bar'), + new PagingControl( + 10, + '' + ) + ); + + $this->mockRequestHandler + ->method('search') + ->willThrowException( + new OperationException( + 'Fail', + ResultCode::OPERATIONS_ERROR + ) + ); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with(self::equalTo(new LdapMessageResponse( + 2, + new SearchResultDone( + ResultCode::OPERATIONS_ERROR, + 'dc=foo,dc=bar', + "Fail" + ) + ))); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerRootDseHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerRootDseHandlerTest.php new file mode 100644 index 00000000..3a1f21c8 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerRootDseHandlerTest.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerRootDseHandler; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; +use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use FreeDSx\Ldap\ServerOptions; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerRootDseHandlerTest extends TestCase +{ + private ServerQueue&MockObject $mockQueue; + + private ServerRootDseHandler $subject; + + private TokenInterface&MockObject $mockToken; + + private ServerOptions $options; + + private PagingHandlerInterface&MockObject $mockPagingHandler; + + private RootDseHandlerInterface&MockObject $mockDseHandler; + + protected function setUp(): void + { + $this->options = new ServerOptions(); + $this->mockPagingHandler = $this->createMock(PagingHandlerInterface::class); + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockDseHandler = $this->createMock(RootDseHandlerInterface::class); + + $this->subject = new ServerRootDseHandler( + $this->options, + $this->mockQueue, + null, + ); + } + + public function test_it_should_send_back_a_RootDSE(): void + { + $this->options + ->setDseVendorName('Foo') + ->setDseNamingContexts('dc=Foo,dc=Bar'); + + $search = new LdapMessageRequest( + 1, + (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope() + ); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + self::equalTo(new LdapMessageResponse(1, new SearchResultEntry(Entry::create('', [ + 'namingContexts' => 'dc=Foo,dc=Bar', + 'supportedExtension' => [ + ExtendedRequest::OID_WHOAMI, + ], + 'supportedLDAPVersion' => ['3'], + 'vendorName' => 'Foo', + ])))), + self::equalTo(new LdapMessageResponse(1, new SearchResultDone(0))) + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_send_back_a_RootDSE_with_paging_support_if_the_paging_handler_is_set(): void + { + $this->options + ->setDseVendorName('Foo') + ->setDseNamingContexts('dc=Foo,dc=Bar') + ->setPagingHandler($this->mockPagingHandler); + + $search = new LdapMessageRequest( + 1, + (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope() + ); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + self::callback(function (LdapMessageResponse $response) { + /** @var SearchResultEntry $search */ + $search = $response->getResponse(); + $entry = $search->getEntry(); + + return $entry->get('supportedControl') + ->has(Control::OID_PAGING); + }), + new LdapMessageResponse(1, new SearchResultDone(0)), + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_send_a_request_to_the_dispatcher_if_it_implements_a_rootdse_aware_interface(): void + { + $this->options + ->setDseVendorName('Foo') + ->setDseNamingContexts('dc=Foo,dc=Bar'); + + $this->subject = new ServerRootDseHandler( + $this->options, + $this->mockQueue, + $this->mockDseHandler, + ); + + $searchReqeust = (new SearchRequest(Filters::present('objectClass')))->base('')->useBaseScope(); + $search = new LdapMessageRequest( + 1, + $searchReqeust + ); + $rootDse = Entry::create('', [ + 'namingContexts' => 'dc=Foo,dc=Bar', + 'supportedExtension' => [ + ExtendedRequest::OID_WHOAMI, + ], + 'supportedLDAPVersion' => ['3'], + 'vendorName' => 'Foo', + ]); + + $handlerRootDse = Entry::fromArray('', ['foo' => 'bar']); + + $this->mockDseHandler + ->expects($this->once()) + ->method('rootDse') + ->with( + self::isInstanceOf(RequestContext::class), + $searchReqeust, + $rootDse, + ) + ->willReturn($handlerRootDse); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + new LdapMessageResponse(1, new SearchResultEntry($handlerRootDse)), + new LdapMessageResponse(1, new SearchResultDone(0)) + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_only_return_attribute_names_from_the_RootDSE_if_requested(): void + { + $this->options + ->setDseVendorName('Foo') + ->setDseNamingContexts('dc=Foo,dc=Bar'); + + $search = new LdapMessageRequest( + 1, + (new SearchRequest(Filters::present('objectClass'))) + ->base('') + ->useBaseScope() + ->setAttributesOnly(true) + ); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + new LdapMessageResponse(1, new SearchResultEntry(Entry::create('', [ + 'namingContexts' => [], + 'supportedExtension' => [], + 'supportedLDAPVersion' => [], + 'vendorName' => [], + ]))), + new LdapMessageResponse(1, new SearchResultDone(0)) + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_only_return_specific_attributes_from_the_RootDSE_if_requested(): void + { + $this->options + ->setDseVendorName('Foo') + ->setDseNamingContexts('dc=Foo,dc=Bar'); + + $search = new LdapMessageRequest( + 1, + (new SearchRequest(Filters::present('objectClass'))) + ->base('') + ->useBaseScope() + ->setAttributes('namingcontexts') + ); + + # The reset below is needed, unfortunately, to properly spec due to how the objects change... + $entry = Entry::create('', ['namingContexts' => 'dc=Foo,dc=Bar', ]); + $entry->changes()->reset(); + $entry->get('namingContexts')->equals(new Attribute('foo')); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + new LdapMessageResponse( + 1, + new SearchResultEntry($entry) + ), + new LdapMessageResponse(1, new SearchResultDone(0)) + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerSearchHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerSearchHandlerTest.php new file mode 100644 index 00000000..c7cfce73 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerSearchHandlerTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerSearchHandler; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerSearchHandlerTest extends TestCase +{ + private ServerSearchHandler $subject; + + private ServerQueue&MockObject $mockQueue; + + private RequestHandlerInterface&MockObject $mockRequestHandler; + + private TokenInterface&MockObject $mockToken; + + protected function setUp(): void + { + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockRequestHandler = $this->createMock(RequestHandlerInterface::class); + + $this->subject = new ServerSearchHandler( + $this->mockQueue, + $this->mockRequestHandler, + ); + } + + public function test_it_should_send_a_search_request_to_the_request_handler(): void + { + $search = new LdapMessageRequest( + 2, + (new SearchRequest(Filters::equal('foo', 'bar')))->base('dc=foo,dc=bar') + ); + + $entries = new Entries(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']), Entry::create('dc=bar,dc=foo', ['cn' => 'bar'])); + $resultEntry1 = new LdapMessageResponse(2, new SearchResultEntry(Entry::create('dc=foo,dc=bar', ['cn' => 'foo']))); + $resultEntry2 = new LdapMessageResponse(2, new SearchResultEntry(Entry::create('dc=bar,dc=foo', ['cn' => 'bar']))); + + $this->mockRequestHandler + ->expects($this->once()) + ->method('search') + ->with(self::anything(), $search->getRequest()) + ->willReturn($entries); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with( + $resultEntry1, + $resultEntry2, + new LdapMessageResponse( + 2, + new SearchResultDone(0, 'dc=foo,dc=bar') + ), + ); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } + + public function test_it_should_send_a_SearchResultDone_with_an_operation_exception_thrown_from_the_handler(): void + { + $search = new LdapMessageRequest( + 2, + (new SearchRequest(Filters::equal( + 'foo', + 'bar' + )))->base('dc=foo,dc=bar') + ); + + $this->mockRequestHandler + ->method('search') + ->willThrowException( + new OperationException( + "Fail", + ResultCode::OPERATIONS_ERROR + ), + ); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with(self::equalTo(new LdapMessageResponse( + 2, + new SearchResultDone( + ResultCode::OPERATIONS_ERROR, + 'dc=foo,dc=bar', + "Fail" + ) + ))); + + $this->subject->handleRequest( + $search, + $this->mockToken, + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerStartTlsHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerStartTlsHandlerTest.php new file mode 100644 index 00000000..73575da4 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerStartTlsHandlerTest.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerStartTlsHandler; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use FreeDSx\Ldap\ServerOptions; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerStartTlsHandlerTest extends TestCase +{ + private ServerStartTlsHandler $subject; + + private ServerQueue&MockObject $mockQueue; + + private TokenInterface&MockObject $mockToken; + + private ServerOptions $options; + + protected function setUp(): void + { + $this->mockToken = $this->createMock(TokenInterface::class); + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->options = new ServerOptions(); + + $this->subject = new ServerStartTlsHandler( + $this->options, + $this->mockQueue, + ); + } + + public function test_it_should_handle_a_start_tls_request(): void + { + $this->options->setSslCert('foo'); + + $this->mockQueue + ->method('isEncrypted') + ->willReturn(false); + + $this->mockQueue + ->method('encrypt') + ->willReturnSelf(); + + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with(new LdapMessageResponse( + 1, + new ExtendedResponse( + new LdapResult(0), + ExtendedRequest::OID_START_TLS + ) + )); + + $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); + + $this->subject->handleRequest( + $startTls, + $this->mockToken, + ); + } + + public function test_it_should_send_back_an_error_if_the_queue_is_already_encrypted(): void + { + $this->options->setSslCert('foo'); + + $this->mockQueue + ->method('isEncrypted') + ->willReturn(true); + + $this->mockQueue + ->expects(self::never()) + ->method('encrypt'); + + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with(self::equalTo(new LdapMessageResponse( + 1, + new ExtendedResponse( + new LdapResult(ResultCode::OPERATIONS_ERROR, '', 'The current LDAP session is already encrypted.'), + ExtendedRequest::OID_START_TLS + ) + ))); + + $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); + + $this->subject->handleRequest( + $startTls, + $this->mockToken, + ); + } + + public function test_it_should_send_back_an_error_if_encryption_is_not_supported(): void + { + $this->mockQueue + ->method('isEncrypted') + ->willReturn(false); + + $this->mockQueue + ->expects(self::never()) + ->method('encrypt'); + + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with(self::equalTo(new LdapMessageResponse( + 1, + new ExtendedResponse( + new LdapResult(ResultCode::PROTOCOL_ERROR), + ExtendedRequest::OID_START_TLS + ) + ))); + + $startTls = new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_START_TLS)); + + $this->subject->handleRequest( + $startTls, + $this->mockToken, + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerUnbindHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerUnbindHandlerTest.php new file mode 100644 index 00000000..86158e11 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerUnbindHandlerTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Operation\Request\UnbindRequest; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerUnbindHandler; +use FreeDSx\Ldap\Server\Token\TokenInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerUnbindHandlerTest extends TestCase +{ + private ServerQueue&MockObject $mockQueue; + + private ServerUnbindHandler $subject; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + + $this->subject = new ServerUnbindHandler($this->mockQueue); + } + + public function test_it_should_handle_an_unbind_request(): void + { + $this->mockQueue + ->expects($this->once()) + ->method('close'); + $this->mockQueue + ->expects($this->never()) + ->method('sendMessage'); + + $this->subject->handleRequest( + new LdapMessageRequest(1, new UnbindRequest()), + $this->createMock(TokenInterface::class), + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php new file mode 100644 index 00000000..ce13b520 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol\ServerProtocolHandler; + +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler\ServerWhoAmIHandler; +use FreeDSx\Ldap\Server\Token\AnonToken; +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerWhoAmIHandlerTest extends TestCase +{ + private ServerQueue&MockObject $mockQueue; + + private ServerWhoAmIHandler $subject; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + + $this->subject = new ServerWhoAmIHandler($this->mockQueue); + } + + public function test_it_should_handle_a_who_am_i_when_there_is_a_token_with_a_DN_name(): void + { + $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo(new LdapMessageResponse( + 2, + new ExtendedResponse(new LdapResult(0), null, 'dn:cn=foo,dc=foo,dc=bar') + ))); + + $this->subject->handleRequest( + $request, + new BindToken('cn=foo,dc=foo,dc=bar', '12345'), + ); + } + + public function test_it_should_handle_a_who_am_i_when_there_is_a_token_with_a_non_DN_name(): void + { + $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo(new LdapMessageResponse( + 2, + new ExtendedResponse(new LdapResult(0), null, 'u:foo@bar.local') + ))); + + $this->subject->handleRequest( + $request, + new BindToken('foo@bar.local', '12345'), + ); + } + + public function test_it_should_handle_a_who_am_i_when_there_is_no_token_yet(): void + { + $request = new LdapMessageRequest(2, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo(new LdapMessageResponse( + 2, + new ExtendedResponse(new LdapResult(0), null, '') + ))); + + $this->mockQueue + ->method('getMessage') + ->willReturn(new LdapMessageRequest( + 2, + new ExtendedRequest(ExtendedRequest::OID_WHOAMI) + )); + + $this->subject->handleRequest( + $request, + new AnonToken(), + ); + } +} diff --git a/tests/unit/Protocol/ServerProtocolHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandlerTest.php new file mode 100644 index 00000000..093daab5 --- /dev/null +++ b/tests/unit/Protocol/ServerProtocolHandlerTest.php @@ -0,0 +1,359 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Protocol; + +use FreeDSx\Asn1\Exception\EncoderException; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Request\AnonBindRequest; +use FreeDSx\Ldap\Operation\Request\ExtendedRequest; +use FreeDSx\Ldap\Operation\Request\ModifyDnRequest; +use FreeDSx\Ldap\Operation\Request\ModifyRequest; +use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\ExtendedResponse; +use FreeDSx\Ldap\Operation\Response\ModifyDnResponse; +use FreeDSx\Ldap\Operation\Response\ModifyResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Authenticator; +use FreeDSx\Ldap\Protocol\Factory\ServerProtocolHandlerFactory; +use FreeDSx\Ldap\Protocol\LdapMessageRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Protocol\Queue\ServerQueue; +use FreeDSx\Ldap\Protocol\ServerAuthorization; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler; +use FreeDSx\Ldap\Server\Token\BindToken; +use FreeDSx\Ldap\ServerOptions; +use FreeDSx\Socket\Exception\ConnectionException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +final class ServerProtocolHandlerTest extends TestCase +{ + private ServerProtocolHandler $subject; + + private ServerQueue&MockObject $mockQueue; + + private ServerProtocolHandlerFactory&MockObject $mockProtocolHandlerFactory; + + private LoggerInterface&MockObject $mockLogger; + + private Authenticator&MockObject $mockAuthenticator; + + private ServerProtocolHandler\ServerProtocolHandlerInterface&MockObject $mockProtocolHandler; + + protected function setUp(): void + { + $this->mockQueue = $this->createMock(ServerQueue::class); + $this->mockProtocolHandlerFactory = $this->createMock(ServerProtocolHandlerFactory::class); + $this->mockLogger = $this->createMock(LoggerInterface::class); + $this->mockAuthenticator = $this->createMock(Authenticator::class); + $this->mockProtocolHandler = $this->createMock(ServerProtocolHandler\ServerProtocolHandlerInterface::class); + + $this->mockQueue + ->method('isConnected') + ->willReturn(true); + $this->mockQueue + ->method('isEncrypted') + ->willReturn(false); + + $this->mockQueue + ->method('sendMessage') + ->willReturnSelf(); + + $this->mockProtocolHandlerFactory + ->method('get') + ->willReturn($this->mockProtocolHandler); + + $this->subject = new ServerProtocolHandler( + $this->mockQueue, + $this->mockProtocolHandlerFactory, + new ServerAuthorization(new ServerOptions()), + $this->mockAuthenticator, + $this->mockLogger, + ); + } + + public function test_it_should_enforce_anonymous_bind_requirements(): void + { + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageRequest(1, new AnonBindRequest('foo')), + $this->throwException(new ConnectionException()), + )); + + $this->mockQueue + ->expects(self::once()) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse( + 1, + new BindResponse(new LdapResult( + ResultCode::AUTH_METHOD_UNSUPPORTED, + '', + 'The requested authentication type is not supported.' + )) + ) + )); + + $this->mockProtocolHandlerFactory + ->expects(self::never()) + ->method('get'); + + $this->subject->handle(); + } + + public function test_it_should_not_allow_a_previous_message_ID_from_a_new_request(): void + { + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageRequest(1, new SimpleBindRequest('foo', 'bar')), + new LdapMessageRequest(1, new ExtendedRequest(ExtendedRequest::OID_WHOAMI)), + $this->throwException(new ConnectionException()) + )); + + $this->mockAuthenticator + ->method('bind') + ->willReturn(new BindToken('foo', 'bar')); + + $this->mockProtocolHandler + ->expects($this->never()) + ->method('handleRequest'); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo(new LdapMessageResponse( + 0, + new ExtendedResponse(new LdapResult( + ResultCode::PROTOCOL_ERROR, + '', + 'The message ID 1 is not valid.' + )) + ))); + + $this->subject->handle(); + } + + public function test_it_should_enforce_authentication_requirements(): void + { + $this->mockQueue + ->method('isConnected') + ->willReturn(true); + $this->mockQueue + ->method('getMessage') + ->will( + $this->onConsecutiveCalls( + new LdapMessageRequest( + 1, + new ModifyDnRequest('cn=foo,dc=bar', 'cn=bar', true) + ), + $this->throwException(new ConnectionException()) + ) + ); + + $this->mockQueue + ->expects($this->atLeast(1)) + ->method('sendMessage') + ->with($this->equalTo(new LdapMessageResponse( + 1, + new ModifyDnResponse( + ResultCode::INSUFFICIENT_ACCESS_RIGHTS, + 'cn=foo,dc=bar', + 'Authentication required.' + ) + ))) + ->willReturnSelf(); + + $this->mockProtocolHandler + ->expects($this->never()) + ->method('handleRequest'); + + $this->subject->handle(); + } + + public function test_it_should_send_a_notice_of_disconnect_on_a_protocol_exception_from_the_message_queue(): void + { + $this->mockQueue + ->method('getMessage') + ->willThrowException(new ProtocolException()); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse(0, new ExtendedResponse( + new LdapResult(ResultCode::PROTOCOL_ERROR, '', 'The message encoding is malformed.'), + ExtendedResponse::OID_NOTICE_OF_DISCONNECTION + )) + )); + + $this->subject->handle(); + } + + public function test_it_should_handle_a_socket_exception_from_the_message_queue_and_end_normally(): void + { + $this->mockQueue + ->method('getMessage') + ->willThrowException(new ConnectionException("Foo")); + + $this->mockLogger + ->expects($this->once()) + ->method('log') + ->with( + LogLevel::INFO, + 'Ending LDAP client due to client connection issues.', + $this->anything() + ); + + $this->subject->handle(); + } + + public function test_it_should_send_a_notice_of_disconnect_on_an_encoder_exception_from_the_message_queue(): void + { + $this->mockQueue + ->method('getMessage') + ->willThrowException(new EncoderException()); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse(0, new ExtendedResponse( + new LdapResult(ResultCode::PROTOCOL_ERROR, '', 'The message encoding is malformed.'), + ExtendedResponse::OID_NOTICE_OF_DISCONNECTION + )) + )); + + $this->subject->handle(); + } + + public function test_it_should_not_allow_a_message_with_an_ID_of_zero(): void + { + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageRequest( + 0, + new ExtendedRequest(ExtendedRequest::OID_START_TLS) + ), + $this->throwException(new ConnectionException()) + )); + + $this->mockQueue + ->expects($this->atLeast(1)) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse(0, new ExtendedResponse(new LdapResult( + ResultCode::PROTOCOL_ERROR, + '', + 'The message ID 0 cannot be used in a client request.' + ))) + )); + + $this->subject->handle(); + } + + public function test_it_should_send_a_bind_request_to_the_bind_request_handler(): void + { + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageRequest( + 1, + new SimpleBindRequest('foo@bar', 'bar') + ), + $this->throwException(new ConnectionException()) + )); + + $this->mockAuthenticator + ->expects($this->once()) + ->method('bind') + ->willReturn(new BindToken('foo@bar', 'bar')); + + $this->mockProtocolHandler + ->expects($this->never()) + ->method('handleRequest'); + + $this->subject->handle(); + } + + public function test_it_should_handle_operation_errors_thrown_from_the_request_handlers(): void + { + $this->mockQueue + ->method('isConnected') + ->will($this->onConsecutiveCalls(true, false)); + + $this->mockQueue + ->method('getMessage') + ->will($this->onConsecutiveCalls( + new LdapMessageRequest(1, new SimpleBindRequest('foo@bar', 'bar')), + new LdapMessageRequest(2, new ModifyRequest('cn=foo,dc=bar')), + $this->throwException(new ConnectionException()), + )); + + $this->mockAuthenticator + ->expects($this->once()) + ->method('bind') + ->willReturn(new BindToken('foo@bar', 'bar')); + + $this->mockProtocolHandler + ->expects($this->once()) + ->method('handleRequest') + ->willThrowException(new OperationException( + 'Foo.', + ResultCode::CONFIDENTIALITY_REQUIRED + )); + + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse( + 2, + new ModifyResponse( + ResultCode::CONFIDENTIALITY_REQUIRED, + 'cn=foo,dc=bar', + 'Foo.' + ) + ) + )); + + $this->subject->handle(); + } + + public function test_it_should_send_a_notice_of_disconnect_and_close_the_queue_on_shutdown(): void + { + $this->mockQueue + ->expects($this->once()) + ->method('sendMessage') + ->with($this->equalTo( + new LdapMessageResponse(0, new ExtendedResponse( + new LdapResult(ResultCode::UNAVAILABLE, '', 'The server is shutting down.'), + ExtendedResponse::OID_NOTICE_OF_DISCONNECTION + )) + )); + + $this->mockQueue + ->expects($this->once()) + ->method('close'); + + $this->subject->shutdown(); + } +} diff --git a/tests/unit/Search/DirSyncTest.php b/tests/unit/Search/DirSyncTest.php new file mode 100644 index 00000000..743d0982 --- /dev/null +++ b/tests/unit/Search/DirSyncTest.php @@ -0,0 +1,291 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Control\Ad\DirSyncRequestControl; +use FreeDSx\Ldap\Control\Ad\DirSyncResponseControl; +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\DirSync; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class DirSyncTest extends TestCase +{ + use TestFactoryTrait; + + private LdapMessageResponse $initialResponse; + + private LdapMessageResponse $secondResponse; + + private DirSync $subject; + + private LdapClient&MockObject $client; + + protected function setUp(): void + { + $this->client = $this->createMock(LdapClient::class); + + $this->initialResponse = $this::makeSearchResponseFromEntries( + entries: new Entries(), + messageId: 0, + controls: [new DirSyncResponseControl(1, 0, 'foo')], + ); + $this->secondResponse = $this::makeSearchResponseFromEntries( + entries: new Entries(), + controls: [new DirSyncResponseControl(0, 0, 'fbar')], + ); + + $this->client + ->expects($this->any()) + ->method('readOrFail') + ->willReturn( + new Entry( + '', + new Attribute( + 'defaultNamingContext', + 'dc=foo,dc=bar') + ) + ); + + $this->subject = new DirSync($this->client); + } + + public function test_it_should_set_the_naming_context(): void + { + $this->addSendExpectation($this->callback( + fn (SearchRequest $search) => $search->getBaseDn()?->toString() == 'dc=foo') + ); + + $this->subject->useNamingContext('dc=foo'); + + $this->subject->getChanges(); + } + + public function test_it_should_set_the_filter(): void + { + $this->addSendExpectation($this->callback( + fn (SearchRequest $search) => $search->getFilter()->toString() == '(foo=bar)' + )); + + $this->subject->useFilter(Filters::equal( + 'foo', + 'bar' + )); + + $this->subject->getChanges(); + } + + public function test_it_should_set_the_attributes_to_select(): void + { + $this->addSendExpectation($this->callback( + fn (SearchRequest $search) => $search->getAttributes()[0]->getName() == 'foo' + )); + + $this->subject->selectAttributes('foo'); + + $this->subject->getChanges(); + } + + public function test_it_should_set_the_incremental_values_flag(): void + { + $this->addSendExpectation( + $this->anything(), + $this->callback( + fn ($control) => $control instanceof DirSyncRequestControl + && $control->getFlags() !== DirSyncRequestControl::FLAG_INCREMENTAL_VALUES, + ), + ); + + $this->subject->useIncrementalValues(false); + + $this->subject->getChanges(); + } + + public function test_it_should_object_security_flag(): void + { + $this->addSendExpectation( + $this->anything(), + $this->callback( + fn ($control) => $control instanceof DirSyncRequestControl + && $control->getFlags() !== DirSyncRequestControl::FLAG_OBJECT_SECURITY, + ) + ); + + $this->subject->useObjectSecurity(); + + $this->subject->getChanges(); + } + + public function test_it_should_set_ancestor_first_order(): void + { + $this->addSendExpectation( + $this->anything(), + $this->callback( + fn ($control) => $control instanceof DirSyncRequestControl + && $control->getFlags() & DirSyncRequestControl::FLAG_ANCESTORS_FIRST_ORDER, + ) + ); + + $this->subject->useAncestorFirstOrder(); + + $this->subject->getChanges(); + } + + public function test_it_should_set_the_cookie(): void + { + $this->addSendExpectation( + $this->anything(), + $this->callback( + fn ($control) => $control instanceof DirSyncRequestControl + && $control->getCookie() === 'foo', + ), + ); + + $this->subject->useCookie('foo'); + + $this->subject->getChanges(); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + '', + $this->subject->getCookie(), + ); + + $this->addSendExpectation(); + + $this->subject->getChanges(); + + self::assertSame( + 'foo', + $this->subject->getCookie(), + ); + } + + public function test_it_should_set_the_cookie_from_the_response_after_the_initial_query(): void + { + $this->client + ->expects($this->any()) + ->method('send') + ->will($this->onConsecutiveCalls( + $this->initialResponse, + $this->secondResponse, + )); + + $this->subject->getChanges(); + self::assertSame( + 'foo', + $this->subject->getCookie(), + ); + + $this->subject->getChanges(); + self::assertSame( + 'fbar', + $this->subject->getCookie(), + ); + } + + public function test_it_should_check_the_root_dse_for_the_default_naming_context(): void + { + $this->addSendAndOrderExpectation($this->atMost(2)); + $this->client + ->expects($this->atMost(1)) + ->method('readOrFail') + ->willReturn(new Entry( + '', + new Attribute( + 'defaultNamingContext', + 'dc=foo,dc=bar') + )); + + $this->subject->getChanges(); + $this->subject->getChanges(); + } + + public function test_it_should_not_check_the_root_dse_for_the_default_naming_context_if_it_was_provided(): void + { + $this->addSendExpectation(); + $this->client + ->expects($this->never()) + ->method('readOrFail'); + + $this->subject->useNamingContext('dc=foo'); + + $this->subject->getChanges(); + } + + public function test_it_should_return_false_for_changes_if_no_queries_have_been_made_yet(): void + { + self::assertFalse($this->subject->hasChanges()); + } + + public function test_it_should_return_true_for_changes_if_the_dir_sync_control_indicates_there_are(): void + { + $this->addSendExpectation(); + + $this->subject->getChanges(); + + self::assertTrue($this->subject->hasChanges()); + } + + public function test_it_should_return_false_for_changes_if_the_dir_sync_control_indicates_there_are_none_left(): void + { + $this->client + ->expects($this->any()) + ->method('send') + ->will($this->onConsecutiveCalls( + $this->initialResponse, + $this->secondResponse, + )); + + $this->subject->getChanges(); + $this->subject->getChanges(); + + self::assertFalse($this->subject->hasChanges()); + } + + /** + * @param mixed ...$arguments + */ + private function addSendExpectation(...$arguments): void { + $this->client + ->expects($this->once()) + ->method('send') + ->with(...$arguments) + ->willReturn($this->initialResponse); + } + + /** + * @param mixed ...$arguments + */ + private function addSendAndOrderExpectation( + InvocationOrder $order, + ...$arguments + ): void { + $this->client + ->expects($order) + ->method('send') + ->with(...$arguments) + ->willReturn($this->initialResponse); + } +} diff --git a/tests/unit/Search/Filter/AndFilterTest.php b/tests/unit/Search/Filter/AndFilterTest.php new file mode 100644 index 00000000..8d1677bb --- /dev/null +++ b/tests/unit/Search/Filter/AndFilterTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Search\Filter\AndFilter; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filter\SubstringFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class AndFilterTest extends TestCase +{ + private AndFilter $subject; + + protected function setUp(): void + { + $this->subject = new AndFilter( + Filters::equal('foo', 'bar'), + Filters::gte('foo', '2'), + ); + } + + public function test_it_should_get_the_filters_it_contains(): void + { + self::assertEquals( + [ + Filters::equal('foo', 'bar'), + Filters::gte('foo', '2'), + ], + $this->subject->get(), + ); + } + + public function test_it_should_set_the_filters(): void + { + $this->subject->set(Filters::equal('bar', 'foo')); + + self::assertEquals( + [Filters::equal('bar', 'foo')], + $this->subject->get(), + ); + } + + public function test_it_should_add_to_the_filters(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + $this->subject->add($filter); + + self::assertContains( + $filter, + $this->subject->get(), + ); + } + + public function test_it_should_remove_from_the_filters(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + $this->subject->add($filter); + + self::assertContains( + $filter, + $this->subject->get(), + ); + + $this->subject->remove($filter); + + self::assertNotContains( + $filter, + $this->subject->get(), + ); + } + + public function test_it_should_check_if_a_filter_exists(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + self::assertFalse($this->subject->has($filter)); + + $this->subject->add($filter); + + self::assertTrue($this->subject->has($filter)); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(0, Asn1::setOf( + Filters::equal('foo', 'bar')->toAsn1(), + Filters::gte('foo', '2')->toAsn1() + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $and = new AndFilter(new EqualityFilter('foo', 'bar'), new SubstringFilter('bar', 'foo')); + + self::assertEquals( + $and, + AndFilter::fromAsn1($and->toAsn1()) + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_not_be_constructed_from_invalid_asn1(AbstractType $type): void + { + self::expectException(ProtocolException::class); + + AndFilter::fromAsn1($type); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(&(foo=bar)(foo>=2))', + $this->subject->toString(), + ); + } + + public function test_it_should_get_the_string_filter_representation_with_nested_containers(): void + { + $this->subject->add(Filters::or(Filters::equal('foo', 'bar'))); + + self::assertSame( + '(&(foo=bar)(foo>=2)(|(foo=bar)))', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(&(foo=bar)(foo>=2))', + (string) $this->subject, + ); + } + + public function test_it_should_get_the_count(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + /** + * @return array + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::octetString('foo')], + [Asn1::sequence()], + ]; + } +} diff --git a/tests/unit/Search/Filter/ApproximateFilterTest.php b/tests/unit/Search/Filter/ApproximateFilterTest.php new file mode 100644 index 00000000..90b9f41f --- /dev/null +++ b/tests/unit/Search/Filter/ApproximateFilterTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\ApproximateFilter; +use PHPUnit\Framework\TestCase; + +final class ApproximateFilterTest extends TestCase +{ + private ApproximateFilter $subject; + + protected function setUp(): void + { + $this->subject = new ApproximateFilter( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(8, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::octetString('bar') + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new ApproximateFilter('foo', 'bar'), + ApproximateFilter::fromAsn1((new ApproximateFilter('foo', 'bar'))->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo~=bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo~=bar)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new ApproximateFilter('foo', ')(bar=foo'); + + self::assertSame( + '(foo~=\29\28bar=foo)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/EqualityFilterTest.php b/tests/unit/Search/Filter/EqualityFilterTest.php new file mode 100644 index 00000000..fc827e54 --- /dev/null +++ b/tests/unit/Search/Filter/EqualityFilterTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filter\FilterInterface; +use PHPUnit\Framework\TestCase; + +final class EqualityFilterTest extends TestCase +{ + private EqualityFilter $subject; + + protected function setUp(): void + { + $this->subject = new EqualityFilter( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + + $this->subject->setAttribute('foobar'); + + self::assertSame( + 'foobar', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(3, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::octetString('bar'), + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new EqualityFilter('foo', 'bar'), + EqualityFilter::fromAsn1((new EqualityFilter('foo', 'bar'))->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo=bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo=bar)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new EqualityFilter('foo', ')(bar=foo'); + + self::assertSame( + '(foo=\29\28bar=foo)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/GreaterThanOrEqualFilterTest.php b/tests/unit/Search/Filter/GreaterThanOrEqualFilterTest.php new file mode 100644 index 00000000..fd429e76 --- /dev/null +++ b/tests/unit/Search/Filter/GreaterThanOrEqualFilterTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; +use PHPUnit\Framework\TestCase; + +final class GreaterThanOrEqualFilterTest extends TestCase +{ + private GreaterThanOrEqualFilter $subject; + + protected function setUp(): void + { + $this->subject = new GreaterThanOrEqualFilter( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(5, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::octetString('bar'), + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new GreaterThanOrEqualFilter('foo', 'bar'), + GreaterThanOrEqualFilter::fromAsn1((new GreaterThanOrEqualFilter('foo', 'bar'))->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo>=bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo>=bar)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new GreaterThanOrEqualFilter( + 'foo', + ')(bar=*5', + ); + + self::assertSame( + '(foo>=\29\28bar=\2a5)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/LessThanOrEqualFilterTest.php b/tests/unit/Search/Filter/LessThanOrEqualFilterTest.php new file mode 100644 index 00000000..b8572b76 --- /dev/null +++ b/tests/unit/Search/Filter/LessThanOrEqualFilterTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\LessThanOrEqualFilter; +use PHPUnit\Framework\TestCase; + +final class LessThanOrEqualFilterTest extends TestCase +{ + private LessThanOrEqualFilter $subject; + + protected function setUp(): void + { + $this->subject = new LessThanOrEqualFilter( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'bar', + $this->subject->getValue(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(6, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::octetString('bar'), + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + (new LessThanOrEqualFilter('foo', 'bar')), + LessThanOrEqualFilter::fromAsn1((new LessThanOrEqualFilter('foo', 'bar'))->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo<=bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo<=bar)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new LessThanOrEqualFilter( + 'foo', + ')(bar=*5', + ); + + self::assertSame( + '(foo<=\29\28bar=\2a5)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/MatchingRuleFilterTest.php b/tests/unit/Search/Filter/MatchingRuleFilterTest.php new file mode 100644 index 00000000..51ce71b7 --- /dev/null +++ b/tests/unit/Search/Filter/MatchingRuleFilterTest.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\MatchingRuleFilter; +use PHPUnit\Framework\TestCase; + +final class MatchingRuleFilterTest extends TestCase +{ + private MatchingRuleFilter $subject; + + protected function setUp(): void + { + $this->subject = new MatchingRuleFilter( + 'foo', + 'bar', + 'foobar', + ); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'bar', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_get_the_value(): void + { + self::assertSame( + 'foobar', + $this->subject->getValue(), + ); + } + + public function test_it_should_not_use_dn_attributes_by_default(): void + { + self::assertFalse($this->subject->getUseDnAttributes()); + } + + public function test_it_should_set_whether_to_use_dn_attributes_by_default(): void + { + $this->subject->setUseDnAttributes(true); + + self::assertTrue($this->subject->getUseDnAttributes()); + } + + public function test_it_should_set_the_matching_rule(): void + { + $this->subject->setMatchingRule('bleep'); + + self::assertSame( + 'bleep', + $this->subject->getMatchingRule(), + ); + } + + public function test_it_should_be_able_to_set_the_attribute_to_null(): void + { + $this->subject->setAttribute(null); + + self::assertNull($this->subject->getAttribute()); + } + + public function test_it_should_be_able_to_set_the_matching_rule_to_null(): void + { + $this->subject->setMatchingRule(null); + + self::assertNull($this->subject->getMatchingRule()); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(9, Asn1::sequence( + Asn1::context(1, Asn1::octetString('foo')), + Asn1::context(2, Asn1::octetString('bar')), + Asn1::context(3, Asn1::octetString('foobar')), + Asn1::context(4, Asn1::boolean(false)), + )), + $this->subject->toAsn1(), + ); + + $this->subject->setUseDnAttributes(true); + self::assertEquals( + Asn1::context(9, Asn1::sequence( + Asn1::context(1, Asn1::octetString('foo')), + Asn1::context(2, Asn1::octetString('bar')), + Asn1::context(3, Asn1::octetString('foobar')), + Asn1::context(4, Asn1::boolean(true)), + )), + $this->subject->toAsn1(), + ); + + $this->subject->setMatchingRule(null); + self::assertEquals( + Asn1::context(9, Asn1::sequence( + Asn1::context(2, Asn1::octetString('bar')), + Asn1::context(3, Asn1::octetString('foobar')), + Asn1::context(4, Asn1::boolean(true)) + )), + $this->subject->toAsn1(), + ); + + + $this->subject->setAttribute(null); + self::assertEquals( + Asn1::context(9, Asn1::sequence( + Asn1::context(3, Asn1::octetString('foobar')), + Asn1::context(4, Asn1::boolean(true)) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $rule = new MatchingRuleFilter('foo', 'foo', 'bar', true); + + self::assertEquals( + $rule, + MatchingRuleFilter::fromAsn1($rule->toAsn1()), + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(bar:foo:=foobar)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_the_filter_representation_with_a_dn_match(): void + { + $this->subject->setUseDnAttributes(true); + + self::assertSame( + '(bar:foo:dn:=foobar)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(bar:foo:=foobar)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new MatchingRuleFilter( + 'foo', 'bar', + ')(bar=*5', + ); + + self::assertSame( + '(bar:foo:=\29\28bar=\2a5)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/NotFilterTest.php b/tests/unit/Search/Filter/NotFilterTest.php new file mode 100644 index 00000000..7be758fc --- /dev/null +++ b/tests/unit/Search/Filter/NotFilterTest.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filter\NotFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class NotFilterTest extends TestCase +{ + private NotFilter $subject; + + protected function setUp(): void + { + $this->subject = new NotFilter(Filters::equal( + attribute: 'foo', + value: 'bar', + )); + } + + public function test_it_should_set_the_filter(): void + { + + $this->subject->set(Filters::gte( + 'foobar', + 'foo', + )); + + self::assertEquals( + Filters::gte('foobar', 'foo'), + $this->subject->get(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(2, Asn1::sequence( + Filters::equal('foo', 'bar')->toAsn1(), + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + new NotFilter(new EqualityFilter('foo', 'bar')), + NotFilter::fromAsn1( + (new NotFilter(Filters::equal('foo', 'bar')))->toAsn1(), + ), + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(!(foo=bar))', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(!(foo=bar))', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new NotFilter(Filters::equal('foo', '*bar')); + + self::assertSame( + '(!(foo=\2abar))', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/Filter/OrFilterTest.php b/tests/unit/Search/Filter/OrFilterTest.php new file mode 100644 index 00000000..42dc9295 --- /dev/null +++ b/tests/unit/Search/Filter/OrFilterTest.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Asn1\Type\AbstractType; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filter\OrFilter; +use FreeDSx\Ldap\Search\Filter\SubstringFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class OrFilterTest extends TestCase +{ + private OrFilter $subject; + + protected function setUp(): void + { + $this->subject = new OrFilter( + Filters::equal('foo', 'bar'), + Filters::gte('foo', '2'), + ); + } + + public function test_it_should_get_the_filters_it_contains(): void + { + self::assertEquals( + [ + Filters::equal('foo', 'bar'), + Filters::gte('foo', '2'), + ], + $this->subject->get(), + ); + } + + public function test_it_should_set_the_filters(): void + { + $this->subject->set(Filters::equal( + 'bar', + 'foo' + )); + + self::assertEquals( + [Filters::equal('bar', 'foo')], + $this->subject->get(), + ); + } + + public function test_it_should_add_to_the_filters(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + $this->subject->add($filter); + + self::assertContains( + $filter, + $this->subject->get(), + ); + } + + public function test_it_should_remove_from_the_filters(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + $this->subject->add($filter); + self::assertContains( + $filter, + $this->subject->get(), + ); + + $this->subject->remove($filter); + self::assertNotContains( + $filter, + $this->subject->get(), + ); + } + + public function test_it_should_check_if_a_filter_exists(): void + { + $filter = Filters::equal('foobar', 'foobar'); + + self::assertFalse($this->subject->has($filter)); + + $this->subject->add($filter); + + self::assertTrue($this->subject->has($filter)); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(1, Asn1::setOf( + Filters::equal('foo', 'bar')->toAsn1(), + Filters::gte('foo', '2')->toAsn1() + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $or = new OrFilter( + new EqualityFilter('foo', 'bar'), + new SubstringFilter('bar', 'foo'), + ); + + self::assertEquals( + $or, + OrFilter::fromAsn1($or->toAsn1()), + ); + } + + /** + * @dataProvider malformedAsn1DataProvider + */ + public function test_it_should_not_be_constructed_from_invalid_asn1(AbstractType $type): void + { + $this->expectException(ProtocolException::class); + + $this->subject->fromAsn1($type);; + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(|(foo=bar)(foo>=2))', + $this->subject->toString(), + ); + } + + public function test_it_should_get_the_string_filter_representation_with_nested_containers(): void + { + $this->subject->add( + Filters::and(Filters::equal('foo', 'bar')) + ); + + self::assertSame( + '(|(foo=bar)(foo>=2)(&(foo=bar)))', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(|(foo=bar)(foo>=2))', + (string) $this->subject, + ); + } + + public function test_it_should_get_the_count(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + /** + * @return array + */ + public static function malformedAsn1DataProvider(): array + { + return [ + [Asn1::sequence()], + [Asn1::octetString('foo')], + ]; + } +} diff --git a/tests/unit/Search/Filter/PresentFilterTest.php b/tests/unit/Search/Filter/PresentFilterTest.php new file mode 100644 index 00000000..e37a954d --- /dev/null +++ b/tests/unit/Search/Filter/PresentFilterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Search\Filter\FilterInterface; +use FreeDSx\Ldap\Search\Filter\PresentFilter; +use PHPUnit\Framework\TestCase; + +class PresentFilterTest extends TestCase +{ + private PresentFilter $subject; + + protected function setUp(): void + { + $this->subject = new PresentFilter('foo'); + } + + public function test_it_should_get_the_attribute_name(): void + { + self::assertSame( + 'foo', + $this->subject->getAttribute(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(7, Asn1::octetString('foo')), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + self::assertEquals( + (new PresentFilter('foo')), + PresentFilter::fromAsn1((new PresentFilter('foo'))->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo=*)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo=*)', + (string) $this->subject, + ); + } +} diff --git a/tests/unit/Search/Filter/SubstringFilterTest.php b/tests/unit/Search/Filter/SubstringFilterTest.php new file mode 100644 index 00000000..9bbf5248 --- /dev/null +++ b/tests/unit/Search/Filter/SubstringFilterTest.php @@ -0,0 +1,271 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Filter; + +use FreeDSx\Asn1\Asn1; +use FreeDSx\Ldap\Exception\RuntimeException; +use FreeDSx\Ldap\Search\Filter\SubstringFilter; +use PHPUnit\Framework\TestCase; + +final class SubstringFilterTest extends TestCase +{ + private SubstringFilter $subject; + + protected function setUp(): void + { + $this->subject = new SubstringFilter( + 'foo', + 'f', + 'o', + 'o', + 'bar' + ); + } + + public function test_it_should_get_the_starts_with_value(): void + { + self::assertEquals( + 'f', + $this->subject->getStartsWith(), + ); + + $this->subject->setStartsWith(null); + + self::assertNull($this->subject->getStartsWith()); + } + + public function test_it_should_get_the_ends_with_value(): void + { + self::assertEquals( + 'o', + $this->subject->getEndsWith(), + ); + + $this->subject->setEndsWith(null); + + self::assertNull($this->subject->getEndsWith()); + } + + public function test_it_should_get_the_contains_value(): void + { + self::assertSame( + ['o', 'bar'], + $this->subject->getContains(), + ); + + $this->subject->setContains(); + + self::assertSame( + [], + $this->subject->getContains(), + ); + } + + public function test_it_should_generate_correct_asn1(): void + { + self::assertEquals( + Asn1::context(4, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequenceOf( + Asn1::context(0, Asn1::octetString('f')), + Asn1::context(1, Asn1::octetString('o')), + Asn1::context(1, Asn1::octetString('bar')), + Asn1::context(2, Asn1::octetString('o')) + ) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setStartsWith(null); + self::assertEquals( + Asn1::context(4, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequenceOf( + Asn1::context(1, Asn1::octetString('o')), + Asn1::context(1, Asn1::octetString('bar')), + Asn1::context(2, Asn1::octetString('o')) + ) + )), + $this->subject->toAsn1(), + ); + + $this->subject->setEndsWith(null); + self::assertEquals( + Asn1::context(4, Asn1::sequence( + Asn1::octetString('foo'), + Asn1::sequenceOf( + Asn1::context(1, Asn1::octetString('o')), + Asn1::context(1, Asn1::octetString('bar')) + ) + )), + $this->subject->toAsn1(), + ); + } + + public function test_it_should_error_if_no_starts_with_ends_with_or_contains_was_supplied(): void + { + self::expectException(RuntimeException::class); + + $this->subject->setStartsWith(null); + $this->subject->setEndsWith(null); + $this->subject->setContains(); + + $this->subject->toAsn1(); + } + + public function test_it_should_be_constructed_from_asn1(): void + { + $substring = new SubstringFilter( + 'foo', + 'foo', + 'bar', + 'foobar', + 'wee', + ); + self::assertEquals( + $substring, + SubstringFilter::fromAsn1($substring->toAsn1()) + ); + + $substring = new SubstringFilter( + 'foo', + 'foo', + 'bar', + ); + self::assertEquals( + $substring, + SubstringFilter::fromAsn1($substring->toAsn1()) + ); + + $substring = new SubstringFilter( + 'foo', + 'foo', + ); + self::assertEquals( + $substring, + SubstringFilter::fromAsn1($substring->toAsn1()) + ); + + $substring = new SubstringFilter( + 'foo', + null, + 'foo' + ); + self::assertEquals( + $substring, + SubstringFilter::fromAsn1($substring->toAsn1()) + ); + + $substring = new SubstringFilter( + 'foo', + null, + null, + 'foo', + 'bar' + ); + self::assertEquals( + $substring, + SubstringFilter::fromAsn1($substring->toAsn1()) + ); + } + + public function test_it_should_get_the_string_filter_representation(): void + { + self::assertSame( + '(foo=f*o*bar*o)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_the_filter_representation_with_a_starts_with(): void + { + $this->subject->setStartsWith('bar'); + $this->subject->setEndsWith(null); + $this->subject->setContains(); + + self::assertSame( + '(foo=bar*)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_the_filter_representation_with_an_ends_with(): void + { + $this->subject->setStartsWith(null); + $this->subject->setEndsWith('bar'); + $this->subject->setContains(); + + self::assertSame( + '(foo=*bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_a_filter_representation_with_a_start_and_end(): void + { + $this->subject->setStartsWith('foo'); + $this->subject->setEndsWith('bar'); + $this->subject->setContains(); + + self::assertSame( + '(foo=foo*bar)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_a_filter_representation_with_a_start_and_contains(): void + { + $this->subject->setStartsWith('foo'); + $this->subject->setEndsWith(null); + $this->subject->setContains('b', 'a', 'r'); + + self::assertSame( + '(foo=foo*b*a*r*)', + $this->subject->toString(), + ); + } + + public function test_it_should_get_a_filter_representation_with_an_end_and_contains(): void + { + $this->subject->setStartsWith(null); + $this->subject->setEndsWith('foo'); + $this->subject->setContains('b', 'a', 'r'); + + self::assertSame( + '(foo=*b*a*r*foo)', + $this->subject->toString(), + ); + } + + public function test_it_should_have_a_filter_as_a_toString_representation(): void + { + self::assertSame( + '(foo=f*o*bar*o)', + (string) $this->subject, + ); + } + + public function test_it_should_escape_values_on_the_string_representation(): void + { + $this->subject = new SubstringFilter('foo', ')(bar=*5'); + $this->subject->setStartsWith('*'); + $this->subject->setEndsWith(')(o=*'); + $this->subject->setContains('fo*'); + + self::assertSame( + '(foo=\2a*fo\2a*\29\28o=\2a)', + $this->subject->toString(), + ); + } +} diff --git a/tests/unit/Search/FilterParserTest.php b/tests/unit/Search/FilterParserTest.php new file mode 100644 index 00000000..7865916e --- /dev/null +++ b/tests/unit/Search/FilterParserTest.php @@ -0,0 +1,326 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Exception\FilterParseException; +use FreeDSx\Ldap\Search\FilterParser; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class FilterParserTest extends TestCase +{ + public function test_it_should_parse_an_equals_filter(): void + { + self::assertEquals( + Filters::equal('foo', 'bar'), + FilterParser::parse('(foo=bar)'), + ); + } + + public function test_it_should_parse_an_approximate_filter(): void + { + self::assertEquals( + Filters::approximate('foo', 'bar'), + FilterParser::parse('(foo~=bar)'), + ); + } + + public function test_it_should_parse_a_greater_than_or_equals_filter(): void + { + self::assertEquals( + Filters::gte('foo', '1'), + FilterParser::parse('(foo>=1)'), + ); + } + + public function test_it_should_parse_a_less_than_or_equals_filter(): void + { + self::assertEquals( + Filters::lte('foo', '1'), + FilterParser::parse('(foo<=1)'), + ); + } + + public function test_it_should_parse_a_present_filter(): void + { + self::assertEquals( + Filters::present('foo'), + FilterParser::parse('(foo=*)'), + ); + } + + public function test_it_should_parse_a_substring_starts_with_filter(): void + { + self::assertEquals( + Filters::startsWith('foo', 'bar'), + FilterParser::parse('(foo=bar*)'), + ); + } + + public function test_it_should_parse_a_substring_ends_with_filter(): void + { + self::assertEquals( + Filters::endsWith('foo', 'bar'), + FilterParser::parse('(foo=*bar)'), + ); + } + + public function test_it_should_parse_a_substring_contains_filter(): void + { + self::assertEquals( + Filters::contains('foo', 'bar'), + FilterParser::parse('(foo=*bar*)'), + ); + } + + public function test_it_should_parse_a_mixed_substring_filter(): void + { + self::assertEquals( + Filters::substring('foo', 'this', 'filter', 'is', 'a'), + FilterParser::parse('(foo=this*is*a*filter)'), + ); + } + + public function test_it_should_parse_an_extensible_match_filter_with_an_attribute_only(): void + { + self::assertEquals( + Filters::extensible('foo', 'bar', null), + FilterParser::parse('foo:=bar'), + ); + } + + public function test_it_should_parse_an_extensible_match_filter_with_a_dn_and_matching_rule(): void + { + self::assertEquals( + Filters::extensible(null, 'Chad', '1.2.3') + ->setUseDnAttributes(true), + FilterParser::parse(':dn:1.2.3:=Chad'), + ); + } + + public function test_it_should_parse_an_extensible_match_filter_with_a_dn_and_attribute_type(): void + { + self::assertEquals( + Filters::extensible('o', 'Chad', null) + ->setUseDnAttributes(true), + FilterParser::parse('o:dn:=Chad'), + ); + } + + /** + * @dataProvider invalidMatchingRuleDataProvider + */ + public function test_it_should_error_on_invalid_matching_rule_syntax(string $filter): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse($filter); + } + + public function test_it_should_error_when_parsing_an_extensible_match_with_no_matching_rule_or_type(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse(':dn:=Chad'); + } + + public function test_it_should_parse_a_simple_comparison_filter_without_parenthesis(): void + { + self::assertEquals( + Filters::equal('foo', 'bar'), + FilterParser::parse('foo=bar'), + ); + } + + public function test_it_should_parse_an_and_filter(): void + { + self::assertEquals( + Filters::and( + Filters::equal('foo', 'bar'), + Filters::approximate('bar', 'foo'), + ), + FilterParser::parse('(&(foo=bar)(bar~=foo))'), + ); + } + + public function test_it_should_parse_an_or_filter(): void + { + self::assertEquals( + Filters::or( + Filters::equal('foo', 'bar'), + Filters::approximate('bar', 'foo'), + ), + FilterParser::parse('(|(foo=bar)(bar~=foo))'), + ); + } + + public function test_it_should_parse_filter_containers_with_nested_containers(): void + { + self::assertEquals( + Filters::and( + Filters::or( + Filters::endsWith('foo', 'bar'), + Filters::lte('bar', '5'), + ), + Filters::approximate('foo', 'bar'), + ), + FilterParser::parse('(&(|(foo=*bar)(bar<=5))(foo~=bar))'), + ); + + self::assertEquals( + Filters::and( + Filters::or( + Filters::endsWith('foo', 'bar'), + Filters::lte('bar', '5'), + ), + Filters::approximate('foo', 'bar'), + Filters::and( + Filters::equal('foo', 'bar'), + Filters::or( + Filters::present('bar'), + Filters::equal('cn', 'Chad'), + ) + ) + ), + FilterParser::parse('(&(|(foo=*bar)(bar<=5))(foo~=bar)(&(foo=bar)(|(bar=*)(cn=Chad))))'), + ); + } + + public function test_it_should_parse_a_not_filter(): void + { + self::assertEquals( + Filters::not(Filters::equal('foo', 'bar')), + FilterParser::parse('(!(foo=bar))'), + ); + } + + public function test_it_should_not_allow_a_not_filter_to_contain_more_than_one_filter(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(!(foo=bar)(bar=baz))'); + } + + public function test_it_should_decode_hex_encoded_values(): void + { + self::assertEquals( + Filters::equal('o', 'Parens R Us (for all your parenthetical needs)'), + FilterParser::parse('(o=Parens R Us \28for all your parenthetical needs\29)'), + ); + + self::assertEquals( + Filters::contains('cn', '*'), + FilterParser::parse('(cn=*\2A*)'), + ); + + self::assertEquals( + Filters::equal('bin', "\x00\x00\x00\x04"), + FilterParser::parse('(bin=\00\00\00\04)'), + ); + + self::assertEquals( + Filters::equal('sn', 'Lučić'), + FilterParser::parse('(sn=Lu\c4\8di\c4\87)'), + ); + } + + public function test_it_should_error_on_nested_unmatched_parenthesis(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(&(foo=bar)(|(&(foo=bar)))'); + } + + public function test_it_should_error_on_an_unmatched_parenthesis(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('foo=bar)'); + } + + /** + * @dataProvider malformedFilterDataProvider + */ + public function test_it_should_error_on_unrecognized_values_at_the_end_of_the_filter(string $filter): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse($filter); + } + + public function test_it_should_error_on_empty_values(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(foo=)'); + } + + public function test_it_should_error_on_unrecognized_operators(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(foo>4)'); + } + + public function test_it_should_error_on_an_empty_attribute_in_the_filter(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(=bar)'); + } + + public function test_it_should_error_on_empty_containers(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(&)'); + } + + public function test_it_should_error_on_an_empty_filter(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse(''); + } + + public function test_it_should_error_on_a_malformed_container(): void + { + self::expectException(FilterParseException::class); + + FilterParser::parse('(&'); + } + + /** + * @return array + */ + public static function malformedFilterDataProvider(): array + { + return [ + ['(foo=bar)(foo)'], + ['(foo=bar)foo'] + ]; + } + + /** + * @return array + */ + public static function invalidMatchingRuleDataProvider(): array + { + return [ + [':dn::=Chad'], + ['&^]:=Chad'], + ['?:=Chad'] + ]; + } +} diff --git a/tests/unit/Search/FiltersTest.php b/tests/unit/Search/FiltersTest.php new file mode 100644 index 00000000..75bf72c4 --- /dev/null +++ b/tests/unit/Search/FiltersTest.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Search\Filter\AndFilter; +use FreeDSx\Ldap\Search\Filter\ApproximateFilter; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; +use FreeDSx\Ldap\Search\Filter\LessThanOrEqualFilter; +use FreeDSx\Ldap\Search\Filter\MatchingRuleFilter; +use FreeDSx\Ldap\Search\Filter\NotFilter; +use FreeDSx\Ldap\Search\Filter\OrFilter; +use FreeDSx\Ldap\Search\Filter\PresentFilter; +use FreeDSx\Ldap\Search\Filter\SubstringFilter; +use FreeDSx\Ldap\Search\Filters; +use PHPUnit\Framework\TestCase; + +final class FiltersTest extends TestCase +{ + public function test_it_should_create_an_and_filter(): void + { + self::assertEquals( + new AndFilter( + new EqualityFilter('foo', 'bar'), + new EqualityFilter('bar', 'foo') + ), + Filters::and( + new EqualityFilter('foo', 'bar'), + new EqualityFilter('bar', 'foo'), + ) + ); + } + + public function test_it_should_create_an_or_filter(): void + { + self::assertEquals( + new OrFilter( + new EqualityFilter('foo', 'bar'), + new EqualityFilter('bar', 'foo'), + ), + Filters::or( + new EqualityFilter('foo', 'bar'), + new EqualityFilter('bar', 'foo'), + ) + ); + } + + public function test_it_should_create_an_equality_filter(): void + { + self::assertEquals( + new EqualityFilter('foo', 'bar'), + Filters::equal('foo', 'bar'), + ); + } + + public function test_it_should_create_an_approximate_filter(): void + { + self::assertEquals( + new ApproximateFilter('foo', 'bar'), + Filters::approximate('foo', 'bar'), + ); + } + + public function test_it_should_create_a_greater_than_or_equal_filter(): void + { + self::assertEquals( + new GreaterThanOrEqualFilter('foo', 'bar'), + Filters::gte('foo', 'bar'), + ); + } + + public function test_it_should_have_gte_as_an_alias(): void + { + self::assertEquals( + new GreaterThanOrEqualFilter('foo', 'bar'), + Filters::gte('foo', 'bar'), + ); + } + + public function test_it_should_create_a_less_than_or_equal_filter(): void + { + self::assertEquals( + new LessThanOrEqualFilter('foo', 'bar'), + Filters::lte('foo', 'bar'), + ); + } + + public function test_it_should_have_lte_as_an_alias(): void + { + self::assertEquals( + new LessThanOrEqualFilter('foo', 'bar'), + Filters::lte('foo', 'bar'), + ); + } + + public function test_it_should_create_a_substring_filter(): void + { + self::assertEquals( + new SubstringFilter('foo', 'fo', 'ob', 'ar'), + Filters::substring('foo', 'fo', 'ob', 'ar'), + ); + } + + public function test_it_should_create_a_substring_starts_with_filter(): void + { + self::assertEquals( + new SubstringFilter('foo', 'bar'), + Filters::startsWith('foo', 'bar'), + ); + } + + public function test_it_should_create_a_substring_ends_with_filter(): void + { + self::assertEquals( + new SubstringFilter('foo', null, 'bar'), + Filters::endsWith('foo', 'bar'), + ); + } + + public function test_it_should_create_a_substring_contains_filter(): void + { + self::assertEquals( + new SubstringFilter('foo', null, null, 'bar'), + Filters::contains('foo', 'bar'), + ); + } + + public function test_it_should_create_an_extensible_filter(): void + { + self::assertEquals( + new MatchingRuleFilter('foobar', 'foo', 'bar'), + Filters::extensible('foo', 'bar', 'foobar'), + ); + } + + public function test_it_should_create_a_not_filter(): void + { + self::assertEquals( + new NotFilter(new EqualityFilter('foo', 'bar')), + Filters::not(new EqualityFilter('foo', 'bar')), + ); + } + + public function test_it_should_create_a_filter_from_a_raw_string_filter(): void + { + self::assertEquals( + new EqualityFilter('foo', 'bar'), + Filters::raw('foo=bar'), + ); + self::assertEquals( + new PresentFilter('foo'), + Filters::raw('(foo=*)'), + ); + } +} diff --git a/tests/unit/Search/PagingTest.php b/tests/unit/Search/PagingTest.php new file mode 100644 index 00000000..d452a11d --- /dev/null +++ b/tests/unit/Search/PagingTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Paging; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class PagingTest extends TestCase +{ + use TestFactoryTrait; + + private Paging $subject; + + private LdapClient&MockObject $client; + + private SearchRequest&MockObject $search; + + protected function setUp(): void + { + $this->client = $this->createMock(LdapClient::class); + $this->search = $this->createMock(SearchRequest::class); + + $this->subject = new Paging( + $this->client, + $this->search, + 1000 + ); + } + + public function test_it_should_check_whether_paging_has_entries_left_and_return_true_on_start(): void + { + self::assertTrue($this->subject->hasEntries()); + } + + public function test_it_should_return_true_for_entries_when_the_cookie_is_not_empty(): void + { + $this->expectPagingControl( + new PagingControl(1000, ''), + self::makeSearchResponseFromEntries( + entries: new Entries( + Entry::create('foo'), + Entry::create('bar'), + ), + controls: [new PagingControl(100, 'foo')], + ) + ); + $this->subject->getEntries(); + + self::assertTrue($this->subject->hasEntries()); + } + + public function test_it_should_return_false_for_entries_when_the_cookie_is_empty(): void + { + $this->expectPagingControl( + new PagingControl(100, ''), + self::makeSearchResponseFromEntries( + controls: [new PagingControl(0, '')] + ) + ); + + $this->subject->getEntries(100); + + self::assertFalse($this->subject->hasEntries()); + } + + public function test_it_should_abort_a_paging_operation_if_end_is_called(): void + { + $this->client + ->expects($this->atMost(2)) + ->method('sendAndReceive') + ->with($this->search, $this->anything()) + ->will($this->onConsecutiveCalls( + self::makeSearchResponseFromEntries( + controls: [new PagingControl(100, 'foo')] + ), + self::makeSearchResponseFromEntries(), + )); + + $this->subject->getEntries(); + $this->subject->end(); + + self::assertFalse($this->subject->hasEntries()); + } + + public function test_it_should_get_the_size_estimate_from_the_server_response(): void + { + $this->expectPagingControl( + new PagingControl(1000, ''), + self::makeSearchResponseFromEntries( + entries: new Entries( + Entry::create('foo'), + Entry::create('bar'), + ), + controls: [new PagingControl(100, 'foo')], + ) + ); + + self::assertNull($this->subject->sizeEstimate()); + + $this->subject->getEntries(); + + self::assertSame( + 100, + $this->subject->sizeEstimate() + ); + } + + public function test_it_should_get_the_entries_from_the_response(): void + { + $this->expectPagingControl( + new PagingControl(1000, ''), + self::makeSearchResponseFromEntries( + entries: new Entries( + Entry::create('foo'), + Entry::create('bar'), + ), + controls: [new PagingControl(100, 'foo')], + ) + ); + + self::assertEquals( + new Entries(Entry::create('foo'), Entry::create('bar')), + $this->subject->getEntries(), + ); + } + + public function test_it_should_get_marked_as_ended_if_not_critical_and_no_control_is_returned(): void + { + $this->expectPagingControl( + (new PagingControl(1000, ''))->setCriticality(false), + self::makeSearchResponseFromEntries( + entries: new Entries( + Entry::create('foo'), + Entry::create('bar') + ), + ) + ); + + self::assertEquals( + new Entries(Entry::create('foo'), Entry::create('bar')), + $this->subject->getEntries(), + ); + self::assertFalse($this->subject->hasEntries()); + } + + + public function test_it_should_throw_an_exception_if_marked_as_critical_and_no_control_is_received(): void + { + self::expectException(ProtocolException::class); + self::expectExceptionMessage('Expected a paging control, but received none.'); + + $this->expectPagingControl( + (new PagingControl(1000, ''))->setCriticality(true), + self::makeSearchResponseFromEntries( + entries: new Entries( + Entry::create('foo'), + Entry::create('bar') + ), + ) + ); + + $this->subject->isCritical(); + $this->subject->getEntries(); + } + + private function expectPagingControl( + PagingControl $control, + ?LdapMessageResponse $response = null, + ): void { + $response ??= $this::makeSearchResponseFromEntries( + controls: [new PagingControl(0, 'foo')] + ); + + $this->client + ->expects($this->once()) + ->method('sendAndReceive') + ->with($this->search, $this->equalTo($control)) + ->willReturn($response); + } +} diff --git a/tests/unit/Search/RangeRetrievalTest.php b/tests/unit/Search/RangeRetrievalTest.php new file mode 100644 index 00000000..0878e9df --- /dev/null +++ b/tests/unit/Search/RangeRetrievalTest.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Entry\Attribute; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Search\RangeRetrieval; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class RangeRetrievalTest extends TestCase +{ + private RangeRetrieval $subject; + + private LdapClient&MockObject $mockClient; + + protected function setUp(): void + { + $this->mockClient = $this->createMock(LdapClient::class); + $this->subject = new RangeRetrieval($this->mockClient);; + } + + public function test_it_should_get_a_specific_ranged_attribute_from_an_entry_if_it_exists(): void + { + self::assertEquals( + 'member', + $this->subject->getRanged(Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => []] + ), 'member')?->getName(), + ); + } + + public function test_it_should_return_null_on_a_request_for_a_specific_ranged_attribute_that_does_not_exist(): void + { + self::assertNull($this->subject->getRanged( + Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => []] + ), + 'bar' + )); + } + + public function test_it_should_get_all_ranged_attributes_for_an_entry_as_an_array(): void + { + self::assertCount( + 2, + $this->subject->getAllRanged(Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => [], 'bar;range=0-1000' => []] + )) + ); + } + + public function test_it_should_return_whether_an_entry_has_an_ranged_attributes(): void + { + self::assertFalse($this->subject->hasRanged( + Entry::create( + 'dc=foo', + ['member' => [], 'foo' => []] + ) + )); + self::assertTrue($this->subject->hasRanged( + Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => []] + ) + )); + } + + public function test_it_should_return_whether_an_entry_has_a_specific_ranged_attribute(): void + { + self::assertFalse($this->subject->hasRanged( + Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => []], + ), + 'foo' + )); + self::assertTrue($this->subject->hasRanged( + Entry::create( + 'dc=foo', + ['member;range=0-1500' => [], 'foo' => []], + ), + 'member', + )); + } + + public function test_it_should_check_if_a_ranged_attribute_has_more_values_to_retrieve(): void + { + self::assertFalse($this->subject->hasMoreValues(new Attribute('member'))); + self::assertFalse($this->subject->hasMoreValues(new Attribute('member;range=0-*'))); + } + + public function test_it_should_get_more_values_for_a_ranged_attribute(): void + { + $attrResult = new Attribute('member;range=1501-2000'); + $entry = new Entry('dc=foo', $attrResult); + + $this->mockClient + ->expects($this->once()) + ->method('readOrFail') + ->with( + 'dc=foo', + $this->callback(function ($attr) { + return $attr[0]->getOptions()->first()->getLowRange() == '1501' + && $attr[0]->getOptions()->first()->getHighRange() == '*'; + }) + ) + ->willReturn($entry); + + self::assertEquals( + $attrResult, + $this->subject->getMoreValues( + 'dc=foo', + new Attribute('member;range=0-1500') + ) + ); + } + + public function test_it_should_use_a_specific_ranged_amount_of_values_to_retrieve_if_specified(): void + { + $attrResult = new Attribute('member;range=1501-1600'); + $entry = new Entry('dc=foo', $attrResult); + + $this->mockClient + ->expects($this->once()) + ->method('readOrFail') + ->with( + 'dc=foo', + $this->callback(function ($attr) { + return $attr[0]->getOptions()->first()->getLowRange() === '1501' + && $attr[0]->getOptions()->first()->getHighRange() === '1600'; + }) + ) + ->willReturn($entry); + + self::assertEquals( + $attrResult, + $this->subject->getMoreValues( + 'dc=foo', + new Attribute('member;range=0-1500'), + 100, + ) + ); + } + + public function test_it_should_retrieve_all_values_for_a_specific_attribute(): void + { + $entry1 = new Entry('dc=foo', new Attribute('member;range=0-1500', 'foo')); + $entry2 = new Entry('dc=foo', new Attribute('member;range=1501-*', 'bar')); + + $this->mockClient + ->expects($this->exactly(2)) + ->method('readOrFail') + ->will($this->onConsecutiveCalls( + $entry1, + $entry2, + )); + + self::assertEquals( + ['foo', 'bar'], + $this->subject->getAllValues( + 'dc=foo', + 'member', + )->getValues(), + ); + } +} diff --git a/tests/unit/Search/Result/EntryResultTest.php b/tests/unit/Search/Result/EntryResultTest.php new file mode 100644 index 00000000..f1bad39c --- /dev/null +++ b/tests/unit/Search/Result/EntryResultTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Result; + +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\UnexpectedValueException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Result\EntryResult; +use PHPUnit\Framework\TestCase; + +final class EntryResultTest extends TestCase +{ + private EntryResult $subject; + + protected function setUp(): void + { + $this->subject = new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry( + new Entry('cn=foo') + ), + ) + ); + } + + public function test_it_should_get_the_entry(): void + { + self::assertEquals( + new Entry('cn=foo'), + $this->subject->getEntry(), + ); + } + + public function test_it_should_get_the_raw_message(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new SearchResultEntry( + new Entry('cn=foo') + ), + ), + $this->subject->getMessage(), + ); + } + + public function test_it_should_have_a_string_representation_if_the_dn_of_the_entry(): void + { + self::assertSame( + 'cn=foo', + (string) $this->subject, + ); + } + + public function test_it_must_have_a_SearchEntryResponse(): void + { + self::expectException(UnexpectedValueException::class); + self::expectExceptionMessage(sprintf( + 'Expected an instance of "%s", but got "%s".', + SearchResultEntry::class, + SearchResultReference::class, + )); + + $this->subject = new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultReference( + new LdapUrl('foo') + ), + ) + ); + $this->subject->getEntry(); + } +} diff --git a/tests/unit/Search/Result/ReferralResultTest.php b/tests/unit/Search/Result/ReferralResultTest.php new file mode 100644 index 00000000..a95dcfa4 --- /dev/null +++ b/tests/unit/Search/Result/ReferralResultTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search\Result; + +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\UnexpectedValueException; +use FreeDSx\Ldap\LdapUrl; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Operation\Response\SearchResultReference; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Result\ReferralResult; +use PHPUnit\Framework\TestCase; + +final class ReferralResultTest extends TestCase +{ + private ReferralResult $subject; + + protected function setUp(): void + { + $this->subject = new ReferralResult( + new LdapMessageResponse( + 1, + new SearchResultReference( + new LdapUrl('foo'), + ), + ) + ); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEquals( + [new LdapUrl('foo')], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_get_the_number_of_referrals(): void + { + self::assertCount( + 1, + $this->subject, + ); + } + + public function test_it_should_iterate_the_referrals(): void + { + self::assertEquals( + new \ArrayIterator([ + new LdapUrl('foo') + ]), + $this->subject->getIterator(), + ); + } + + public function test_it_should_have_a_string_representation_of_the_string_referral(): void + { + self::assertSame( + 'ldap://foo/', + (string) $this->subject, + ); + } + + public function test_it_must_have_a_SearchReferenceResponse(): void + { + self::expectException(UnexpectedValueException::class); + self::expectExceptionMessage(sprintf( + 'Expected an instance of "%s", but got "%s".', + SearchResultReference::class, + SearchResultEntry::class, + )); + + $this->subject = new ReferralResult( + new LdapMessageResponse( + 1, + new SearchResultEntry( + new Entry('cn=foo') + ), + ) + ); + + $this->subject->getReferrals(); + } +} diff --git a/tests/unit/Search/VlvTest.php b/tests/unit/Search/VlvTest.php new file mode 100644 index 00000000..a482ea03 --- /dev/null +++ b/tests/unit/Search/VlvTest.php @@ -0,0 +1,371 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Search; + +use FreeDSx\Ldap\Control\Sorting\SortingControl; +use FreeDSx\Ldap\Control\Sorting\SortKey; +use FreeDSx\Ldap\Control\Vlv\VlvControl; +use FreeDSx\Ldap\Control\Vlv\VlvResponseControl; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Search\Vlv; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Tests\Unit\FreeDSx\Ldap\TestFactoryTrait; + +final class VlvTest extends TestCase +{ + use TestFactoryTrait; + + private Vlv $subject; + + private LdapClient&MockObject $mockClient; + + private SearchRequest&MockObject $mockSearch; + + protected function setUp(): void + { + $this->mockClient = $this->createMock(LdapClient::class); + $this->mockSearch = $this->createMock(SearchRequest::class); + + $this->subject = new Vlv( + $this->mockClient, + $this->mockSearch, + 'cn', + ); + } + + public function test_it_should_accept_a_sort_key_as_a_sort_argument(): void + { + $this->subject = new Vlv( + $this->mockClient, + $this->mockSearch, + new SortKey('foo'), + ); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->anything(), + $this->equalTo(new SortingControl(new SortKey('foo'))) + ) + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(50, 150, 0, 'foo')] + )); + + $this->subject->getEntries(); + } + + public function test_it_should_accept_a_sort_control_as_a_sort_argument(): void + { + $this->subject = new Vlv( + $this->mockClient, + $this->mockSearch, + new SortingControl( + new SortKey('foo'), + new SortKey('bar'), + ), + ); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->anything(), + $this->equalTo(new SortingControl( + new SortKey('foo'), + new SortKey('bar'), + )) + )->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(50, 150, 0, 'foo')] + )); + + $this->subject->getEntries(); + } + + public function test_it_should_set_the_offset_using_startAt(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->equalTo(new VlvControl( + 0, + 100, + 1000, + 0, + null, + null + )), + $this->anything(), + )->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(50, 150, 0, 'foo')] + )); + + $this->subject->startAt(1000); + $this->subject->getEntries(); + } + + public function test_it_should_set_the_offset_using_moveTo(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->equalTo(new VlvControl( + 0, + 100, + 1000, + 0, + null, + null + )), + $this->anything(), + )->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(50, 150, 0, 'foo')] + )); + + $this->subject->moveTo(1000); + $this->subject->getEntries(); + } + + public function test_it_should_return_null_on_position_if_nothing_has_happened(): void + { + self::assertNull($this->subject->position()); + } + + public function test_it_should_return_the_offset_on_a_call_to_position(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(250, 150, 0, 'foo')] + )); + + $this->subject->getEntries(); + + self::assertSame( + 250, + $this->subject->position(), + ); + } + + public function test_it_should_return_the_size_of_the_list_returned_from_the_server(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(0, 200, 0, 'foo')] + )); + + $this->subject->getEntries(); + + self::assertSame( + 200, + $this->subject->listSize(), + ); + } + + public function test_it_should_get_the_offset_returned_by_the_server_when_calling_list_offset(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(10, 200, 0, 'foo')] + )); + + $this->subject->getEntries(); + + self::assertSame( + 10, + $this->subject->listOffset(), + ); + } + + public function test_it_should_check_if_we_are_at_the_start_of_the_list(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(1, 200, 0, 'foo')], + )); + + self::assertFalse($this->subject->isAtStartOfList()); + + $this->subject->getEntries(); + + self::assertTrue($this->subject->isAtStartOfList()); + } + + public function test_it_should_check_if_we_are_at_the_start_of_the_list_based_on_the_offset_and_before_value(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(101, 200, 0, 'foo')], + )); + + $this->subject->beforePosition(100); + + self::assertFalse($this->subject->isAtStartOfList()); + + $this->subject->getEntries(); + + self::assertTrue($this->subject->isAtStartOfList()); + } + + public function test_it_should_check_if_we_are_at_the_end_of_the_list(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(200, 200, 0, 'foo')], + )); + + self::assertFalse($this->subject->isAtEndOfList()); + + $this->subject->getEntries(); + + self::assertTrue($this->subject->isAtEndOfList()); + } + + public function test_it_should_check_if_we_are_at_the_end_of_the_list_based_on_the_offset_and_after_value(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(101, 200, 0, 'foo')], + )); + + self::assertFalse($this->subject->isAtEndOfList()); + + $this->subject->getEntries(); + + self::assertTrue($this->subject->isAtEndOfList()); + } + + public function test_it_should_set_the_before_and_after_positions(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->equalTo(new VlvControl(25, 75, 1, 0)), + $this->anything(), + )->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(1, 200, 0, 'foo')], + )); + + $this->subject->beforePosition(25); + $this->subject->afterPosition(75); + + $this->subject->getEntries(); + } + + public function test_it_should_indicate_the_position_as_a_percentage_if_specified(): void + { + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with( + $this->anything(), + $this->equalTo(new VlvControl(0, 100, 1, 100)), + $this->anything(), + )->willReturn($this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(150, 200, 0, 'foo')], + )); + + $this->subject->asPercentage(); + $this->subject->getEntries(); + + self::assertSame( + 75, + $this->subject->position(), + ); + } + + public function test_it_should_move_forward_as_a_percentage_if_specified(): void + { + $this->mockClient + ->expects($this->atMost(2)) + ->method('sendAndReceive') + ->will($this->onConsecutiveCalls( + $this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(1, 200, 0, 'foo')], + ), + $this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(20, 200, 0, 'foo')], + ) + )); + + $this->subject->asPercentage(); + $this->subject->getEntries(); + + $this->subject->moveForward(9); + $this->subject->getEntries(); + + self::assertSame( + 10, + $this->subject->position(), + ); + self::assertSame( + 20, + $this->subject->listOffset(), + ); + } + + public function test_it_should_move_backward_as_a_percentage_if_specified(): void + { + $this->mockClient + ->expects($this->atMost(2)) + ->method('sendAndReceive') + ->will($this->onConsecutiveCalls( + $this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(100, 200, 0, 'foo')], + ), + $this::makeSearchResponseFromEntries( + controls: [new VlvResponseControl(80, 200, 0, 'foo')], + ) + )); + + $this->subject->asPercentage(true); + $this->subject->startAt(50); + + $this->subject->getEntries(); + $this->subject->moveBackward(10); + $this->subject->getEntries(); + + self::assertSame( + 40, + $this->subject->position(), + ); + self::assertSame( + 80, + $this->subject->listOffset(), + ); + } +} diff --git a/tests/unit/Server/ChildProcessTest.php b/tests/unit/Server/ChildProcessTest.php new file mode 100644 index 00000000..178f10b5 --- /dev/null +++ b/tests/unit/Server/ChildProcessTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server; + +use FreeDSx\Ldap\Server\ChildProcess; +use FreeDSx\Socket\Socket; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ChildProcessTest extends TestCase +{ + private ChildProcess $subject; + + private Socket&MockObject $mockSocket; + + protected function setUp(): void + { + $this->mockSocket = $this->createMock(Socket::class); + + $this->subject = new ChildProcess( + 9001, + $this->mockSocket, + ); + } + + public function test_it_should_get_the_pid(): void + { + self::assertSame( + 9001, + $this->subject->getPid(), + ); + } + + public function test_it_should_get_the_socket(): void + { + self::assertSame( + $this->mockSocket, + $this->subject->getSocket(), + ); + } + + public function test_it_should_close_the_socket(): void + { + $this->mockSocket + ->expects(self::once()) + ->method('close'); + + $this->subject->closeSocket(); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestComparatorSpec.php b/tests/unit/Server/Paging/PagingRequestComparatorTest.php similarity index 73% rename from tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestComparatorSpec.php rename to tests/unit/Server/Paging/PagingRequestComparatorTest.php index 241c68a6..0e56a625 100644 --- a/tests/spec/FreeDSx/Ldap/Server/Paging/PagingRequestComparatorSpec.php +++ b/tests/unit/Server/Paging/PagingRequestComparatorTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Server\Paging; +namespace Tests\Unit\FreeDSx\Ldap\Server\Paging; use FreeDSx\Ldap\Control\Ad\SdFlagsControl; use FreeDSx\Ldap\Control\ControlBag; @@ -19,11 +19,19 @@ use FreeDSx\Ldap\Operation\Request\SearchRequest; use FreeDSx\Ldap\Search\Filter\EqualityFilter; use FreeDSx\Ldap\Server\Paging\PagingRequest; -use PhpSpec\ObjectBehavior; +use FreeDSx\Ldap\Server\Paging\PagingRequestComparator; +use PHPUnit\Framework\TestCase; -class PagingRequestComparatorSpec extends ObjectBehavior +final class PagingRequestComparatorTest extends TestCase { - public function it_compares_true_when_they_are_the_same(): void + private PagingRequestComparator $subject; + + protected function setUp(): void + { + $this->subject = new PagingRequestComparator(); + } + + public function test_it_compares_true_when_they_are_the_same(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -38,10 +46,10 @@ public function it_compares_true_when_they_are_the_same(): void 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(true); + self::assertTrue($this->subject->compare($old, $new)); } - public function it_compares_false_when_the_search_is_different(): void + public function test_it_compares_false_when_the_search_is_different(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -56,10 +64,10 @@ public function it_compares_false_when_the_search_is_different(): void 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(false); + self::assertFalse($this->subject->compare($old, $new)); } - public function it_compares_false_when_the_cookie_is_different(): void + public function test_it_compares_false_when_the_cookie_is_different(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -74,10 +82,10 @@ public function it_compares_false_when_the_cookie_is_different(): void 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(false); + self::assertFalse($this->subject->compare($old, $new)); } - public function it_compares_false_when_the_controls_are_different_in_count(): void + public function test_it_compares_false_when_the_controls_are_different_in_count(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -92,10 +100,10 @@ public function it_compares_false_when_the_controls_are_different_in_count(): vo 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(false); + self::assertFalse($this->subject->compare($old, $new)); } - public function it_compares_false_when_the_paging_criticality_is_different(): void + public function test_it_compares_false_when_the_paging_criticality_is_different(): void { $old = new PagingRequest( (new PagingControl(100, 'foo'))->setCriticality(true), @@ -110,10 +118,10 @@ public function it_compares_false_when_the_paging_criticality_is_different(): vo 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(false); + self::assertFalse($this->subject->compare($old, $new)); } - public function it_compares_false_when_the_controls_are_different_in_value(): void + public function test_it_compares_false_when_the_controls_are_different_in_value(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -128,10 +136,10 @@ public function it_compares_false_when_the_controls_are_different_in_value(): vo 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(false); + self::assertFalse($this->subject->compare($old, $new)); } - public function it_compares_true_when_the_controls_are_the_same_in_value(): void + public function test_it_compares_true_when_the_controls_are_the_same_in_value(): void { $old = new PagingRequest( new PagingControl(100, 'foo'), @@ -146,6 +154,6 @@ public function it_compares_true_when_the_controls_are_the_same_in_value(): void 'foo' ); - $this->compare($old, $new)->shouldBeEqualTo(true); + self::assertTrue($this->subject->compare($old, $new)); } } diff --git a/tests/unit/Server/Paging/PagingRequestTest.php b/tests/unit/Server/Paging/PagingRequestTest.php new file mode 100644 index 00000000..12d78fcd --- /dev/null +++ b/tests/unit/Server/Paging/PagingRequestTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\Paging; + +use DateTime; +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Server\Paging\PagingRequest; +use PHPUnit\Framework\TestCase; + +final class PagingRequestTest extends TestCase +{ + private PagingRequest $subject; + + protected function setUp(): void + { + $this->subject = new PagingRequest( + new PagingControl(100, ''), + new SearchRequest(new EqualityFilter('foo', 'bar')), + new ControlBag(), + 'bar', + new DateTime('01-01-2021') + ); + } + + public function test_it_should_be_true_for_is_paging_starting_before_it_has_been_processed(): void + { + self::assertTrue($this->subject->isPagingStart()); + } + + public function test_it_should_be_false_for_is_paging_starting_after_it_has_been_processed(): void + { + $this->subject->markProcessed(); + + self::assertFalse($this->subject->isPagingStart()); + } + + public function test_it_should_have_a_created_at(): void + { + self::assertEquals( + new DateTime('01-01-2021'), + $this->subject->createdAt(), + ); + } + + public function test_it_should_increase_iteration_after_being_processed(): void + { + $this->subject->markProcessed(); + + self::assertSame( + 2, + $this->subject->getIteration(), + ); + } + + public function test_it_should_have_a_last_processed_time_when_it_is_processed(): void + { + $this->subject->markProcessed(); + + self::assertNotNull($this->subject->lastProcessedAt()); + } + + public function test_it_should_have_an_initial_iteration_of_1(): void + { + self::assertSame( + 1, + $this->subject->getIteration(), + ); + } + + public function test_it_should_get_the_size_of_the_paging_control(): void + { + self::assertSame( + 100, + $this->subject->getSize(), + ); + } + + public function test_it_should_get_the_cookie_from_the_paging_control(): void + { + self::assertSame( + '', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_the_next_cookie(): void + { + self::assertSame( + 'bar', + $this->subject->getNextCookie(), + ); + } + + public function test_it_should_get_the_controls(): void + { + self::assertEquals( + new ControlBag(), + $this->subject->controls(), + ); + } + + public function test_it_should_get_the_search_request(): void + { + self::assertEquals( + new SearchRequest(new EqualityFilter( + 'foo', + 'bar' + )), + $this->subject->getSearchRequest(), + ); + } + + public function test_it_should_update_the_next_cookie(): void + { + $this->subject->updateNextCookie('new'); + + self::assertSame( + 'new', + $this->subject->getNextCookie(), + ); + } + + public function test_it_should_update_the_paging_control(): void + { + $this->subject->updatePagingControl(new PagingControl(50, 'new')); + + self::assertSame( + 'new', + $this->subject->getCookie(), + ); + self::assertSame( + 50, + $this->subject->getSize(), + ); + } + + public function test_it_should_return_false_when_not_an_abandon_request(): void + { + self::assertFalse($this->subject->isAbandonRequest()); + } + + public function test_it_should_return_true_when_it_is_an_abandon_request(): void + { + $this->subject = new PagingRequest( + new PagingControl(0, 'foo'), + new SearchRequest(new EqualityFilter('foo', 'bar')), + new ControlBag(), + 'bar', + new DateTime('01-01-2021') + ); + + self::assertTrue($this->subject->isAbandonRequest()); + } + + public function test_it_should_not_have_a_last_processed_time_when_it_is_not_yet_processed(): void + { + self::assertNull($this->subject->lastProcessedAt()); + } + + public function test_it_should_get_a_unique_id(): void + { + self::assertNotEmpty($this->subject->getUniqueId()); + } + + public function test_it_should_get_the_criticality_of_the_control(): void + { + self::assertFalse($this->subject->isCritical());; + } +} diff --git a/tests/unit/Server/Paging/PagingRequestsTest.php b/tests/unit/Server/Paging/PagingRequestsTest.php new file mode 100644 index 00000000..329d3ef0 --- /dev/null +++ b/tests/unit/Server/Paging/PagingRequestsTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\Paging; + +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Search\Filter\EqualityFilter; +use FreeDSx\Ldap\Server\Paging\PagingRequest; +use FreeDSx\Ldap\Server\Paging\PagingRequests; +use PHPUnit\Framework\TestCase; + +final class PagingRequestsTest extends TestCase +{ + private PagingRequest $pagingRequest; + + private PagingRequests $subject; + + protected function setUp(): void + { + $this->pagingRequest = new PagingRequest( + new PagingControl(1, 'foo'), + new SearchRequest(new EqualityFilter('cn', 'foo')), + new ControlBag(), + 'bar' + ); + + $this->subject = new PagingRequests([ + $this->pagingRequest, + ]); + } + + public function test_it_should_return_true_when_it_has_the_paging_request(): void + { + self::assertTrue($this->subject->has('bar')); + } + + public function test_it_should_return_false_when_it_does_not_have_the_paging_request(): void + { + self::assertFalse($this->subject->has('foo')); + } + + public function test_it_should_return_the_paging_request_when_it_exists(): void + { + self::assertSame( + $this->pagingRequest, + $this->subject->findByNextCookie('bar'), + ); + } + + public function test_it_should_remove_the_paging_request(): void + { + $this->subject->remove($this->pagingRequest); + + self::assertFalse( $this->subject->has('bar')); + } + + public function test_it_should_add_the_paging_request(): void + { + $new = new PagingRequest( + new PagingControl(1, 'bar'), + new SearchRequest(new EqualityFilter('cn', 'foo')), + new ControlBag(), + 'foo' + ); + $this->subject->add($new); + + self::assertEquals( + $new, + $this->subject->findByNextCookie('foo'), + ); + } + + public function test_it_should_throw_an_exception_when_the_request_does_not_exist(): void + { + $this->expectException(ProtocolException::class); + + $this->subject->findByNextCookie('ohno'); + } +} diff --git a/tests/unit/Server/Paging/PagingResponseTest.php b/tests/unit/Server/Paging/PagingResponseTest.php new file mode 100644 index 00000000..4fdfa0b9 --- /dev/null +++ b/tests/unit/Server/Paging/PagingResponseTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\Paging; + +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Server\Paging\PagingResponse; +use PHPUnit\Framework\TestCase; + +final class PagingResponseTest extends TestCase +{ + private PagingResponse $subject; + + protected function setUp(): void + { + $this->subject = new PagingResponse(new Entries()); + } + + public function test_it_should_not_be_complete_by_default(): void + { + self::assertFalse($this->subject->isComplete()); + } + + public function test_it_should_have_the_size_remaining(): void + { + self::assertSame( + 0, + $this->subject->getRemaining(), + ); + } + + public function test_it_should_get_the_entries(): void + { + self::assertEquals( + new Entries(), + $this->subject->getEntries(), + ); + } + + public function test_it_should_make_a_complete_response(): void + { + $this->subject = PagingResponse::makeFinal(new Entries()); + + self::assertTrue($this->subject->isComplete());; + } + + public function test_it_should_make_a_regular_response(): void + { + $this->subject = PagingResponse::make(new Entries()); + + self::assertFalse($this->subject->isComplete()); + } +} diff --git a/tests/unit/Server/RequestContextTest.php b/tests/unit/Server/RequestContextTest.php new file mode 100644 index 00000000..3730c0f4 --- /dev/null +++ b/tests/unit/Server/RequestContextTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server; + +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\Token\AnonToken; +use PHPUnit\Framework\TestCase; + +final class RequestContextTest extends TestCase +{ + private RequestContext $subject; + + protected function setUp(): void + { + $this->subject = new RequestContext( + new ControlBag(), + new AnonToken(null) + ); + } + + public function test_it_should_get_the_token(): void + { + self::assertEquals( + new AnonToken(null), + $this->subject->token(), + ); + } + + public function test_it_should_get_the_controls(): void + { + self::assertEquals( + new ControlBag(), + $this->subject->controls(), + ); + } +} diff --git a/tests/unit/Server/RequestHandler/GenericRequestHandlerTest.php b/tests/unit/Server/RequestHandler/GenericRequestHandlerTest.php new file mode 100644 index 00000000..8b7b8714 --- /dev/null +++ b/tests/unit/Server/RequestHandler/GenericRequestHandlerTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\RequestHandler; + +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Operation\Request\RequestInterface; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class GenericRequestHandlerTest extends TestCase +{ + private GenericRequestHandler $subject; + + private RequestContext&MockObject $mockContext; + + protected function setUp(): void + { + $this->mockContext = $this->createMock(RequestContext::class); + + $this->subject = new GenericRequestHandler(); + } + + + /** + * @dataProvider unsupportedRequestDataProvider + */ + public function test_it_should_throw_an_operations_exception_on_unsupported_requests( + callable $method, + RequestInterface $request + ): void { + self::expectException(OperationException::class); + + $method( + $this->mockContext, + $request, + ); + } + + public function test_it_should_return_false_on_a_bind_request(): void + { + self::assertFalse( + $this->subject->bind( + 'foo', + 'bar', + ), + ); + } + + /** + * @return array + */ + public static function unsupportedRequestDataProvider(): array + { + return [ + [(new GenericRequestHandler())->add(...), Operations::add(Entry::fromArray(''))], + [(new GenericRequestHandler())->delete(...), Operations::delete('foo')], + [(new GenericRequestHandler())->modify(...), Operations::modify('foo', Change::reset('foo'))], + [(new GenericRequestHandler())->modifyDn(...), Operations::rename('foo', 'cn=bar')], + [(new GenericRequestHandler())->search(...), Operations::search(Filters::equal('foo', 'bar'))], + [(new GenericRequestHandler())->compare(...), Operations::compare('foo', 'bar', 'baz')], + [(new GenericRequestHandler())->extended(...), Operations::extended('foo')], + ]; + } +} diff --git a/tests/unit/Server/RequestHandler/HandlerFactoryTest.php b/tests/unit/Server/RequestHandler/HandlerFactoryTest.php new file mode 100644 index 00000000..85f39b6a --- /dev/null +++ b/tests/unit/Server/RequestHandler/HandlerFactoryTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\RequestHandler; + +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; +use FreeDSx\Ldap\Server\RequestHandler\HandlerFactory; +use FreeDSx\Ldap\Server\RequestHandler\PagingHandlerInterface; +use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; +use FreeDSx\Ldap\ServerOptions; +use PHPUnit\Framework\TestCase; + +final class HandlerFactoryTest extends TestCase +{ + private HandlerFactory $subject; + + public function test_it_should_allow_a_request_handler_as_an_object(): void + { + $handler = new GenericRequestHandler(); + $this->subject = new HandlerFactory((new ServerOptions())->setRequestHandler($handler)); + + self::assertSame( + $handler, + $this->subject->makeRequestHandler() + ); + } + + public function test_it_should_allow_a_rootdse_handler_as_an_object(): void + { + $rootDseHandler = new ProxyHandler(new LdapClient()); + $this->subject = new HandlerFactory((new ServerOptions())->setRootDseHandler($rootDseHandler));; + + self::assertSame( + $rootDseHandler, + $this->subject->makeRootDseHandler() + ); + } + + public function test_it_should_allow_a_null_rootdse_handler(): void + { + $this->subject = new HandlerFactory( + (new ServerOptions())->setRootDseHandler(null) + ); + + self::assertNull($this->subject->makeRootDseHandler()); + } + + public function test_it_should_allow_a_paging_handler_as_an_object(): void + { + $pagingHandler = $this->createMock(PagingHandlerInterface::class); + $this->subject = new HandlerFactory( + (new ServerOptions())->setPagingHandler($pagingHandler) + ); + + self::assertSame( + $pagingHandler, + $this->subject->makePagingHandler() + ); + } + + public function test_it_should_allow_a_null_paging_handler(): void + { + $this->subject = new HandlerFactory( + (new ServerOptions())->setPagingHandler(null) + ); + + self::assertNull($this->subject->makePagingHandler()); + } +} diff --git a/tests/unit/Server/RequestHandler/ProxyHandlerTest.php b/tests/unit/Server/RequestHandler/ProxyHandlerTest.php new file mode 100644 index 00000000..5d83e47c --- /dev/null +++ b/tests/unit/Server/RequestHandler/ProxyHandlerTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\RequestHandler; + +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\RequestHandler\ProxyHandler; +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ProxyHandlerTest extends TestCase +{ + private ProxyHandler $subject; + + private LdapClient&MockObject $mockClient; + + private RequestContext&MockObject $mockContext; + + protected function setUp(): void + { + $this->mockClient = $this->createMock(LdapClient::class); + $this->mockContext = $this->createMock(RequestContext::class); + + $this->mockContext + ->method('controls') + ->willReturn(new ControlBag()); + $this->mockContext + ->method('token') + ->willReturn(new BindToken('foo', 'bar')); + + $this->subject = new ProxyHandler($this->mockClient);; + } + + public function test_it_should_handle_a_root_dse_request(): void + { + $rootDse = new Entry(''); + + $this->mockClient + ->expects(self::once()) + ->method('search') + ->willReturn(new Entries($rootDse)); + + self::assertSame( + $rootDse, + $this->subject->rootDse( + $this->mockContext, + $this->createMock(SearchRequest::class), + new Entry('') + ) + ); + } + + public function test_it_should_handle_a_root_dse_request_when_non_is_returned(): void + { + self::expectException(OperationException::class); + self::expectExceptionMessage('Entry not found.'); + + $this->mockClient + ->method('search') + ->willReturn(new Entries()); + + $this->subject->rootDse( + $this->mockContext, + $this->createMock(SearchRequest::class), + new Entry('') + ); + } +} diff --git a/tests/unit/Server/RequestHandler/ProxyPagingHandlerTest.php b/tests/unit/Server/RequestHandler/ProxyPagingHandlerTest.php new file mode 100644 index 00000000..3e385392 --- /dev/null +++ b/tests/unit/Server/RequestHandler/ProxyPagingHandlerTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\RequestHandler; + +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Control\PagingControl; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Search\Paging; +use FreeDSx\Ldap\Server\Paging\PagingRequest; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\RequestHandler\ProxyPagingHandler; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ProxyPagingHandlerTest extends TestCase +{ + private ProxyPagingHandler $subject; + + private LdapClient&MockObject $mockClient; + + private RequestContext&MockObject $mockContext; + + protected function setUp(): void + { + $this->mockContext = $this->createMock(RequestContext::class); + $this->mockClient = $this->createMock(LdapClient::class); + + $this->mockContext + ->method('controls') + ->willReturn(new ControlBag()); + + $this->subject = new ProxyPagingHandler($this->mockClient); + } + + public function test_it_should_handle_a_paging_request_when_paging_is_still_going(): void + { + $paging = $this->createMock(Paging::class); + + $request = new SearchRequest(Filters::equal('cn', 'foo')); + $entries = new Entries(new Entry('cn=foo,dc=foo,dc=bar')); + + $this->mockClient + ->expects($this->once()) + ->method('paging') + ->willReturn($paging); + + $paging->method('isCritical') + ->willReturn($paging); + $paging->method('getEntries') + ->with(25) + ->willReturn($entries); + $paging->method('hasEntries') + ->willReturn(true); + $paging->method('sizeEstimate') + ->willReturn(25); + + $pagingRequest = new PagingRequest( + new PagingControl(25, ''), + $request, + new ControlBag(), + 'foo' + ); + + self::assertFalse( + $this->subject->page($pagingRequest, $this->mockContext)->isComplete(), + ); + self::assertEquals( + $entries, + $this->subject->page($pagingRequest, $this->mockContext)->getEntries(), + ); + self::assertSame( + 25, + $this->subject->page($pagingRequest, $this->mockContext)->getRemaining(), + ); + } + + public function test_it_should_handle_a_paging_request_when_paging_is_complete(): void + { + $paging = $this->createMock(Paging::class); + + $request = new SearchRequest(Filters::equal('cn', 'foo')); + $entries = new Entries(new Entry('cn=foo,dc=foo,dc=bar')); + + $this->mockClient + ->expects($this->once()) + ->method('paging') + ->willReturn($paging); + + $paging->method('isCritical') + ->willReturn($paging); + $paging->method('getEntries') + ->with(25) + ->willReturn($entries); + $paging->method('hasEntries') + ->willReturn(false); + + $pagingRequest = new PagingRequest( + new PagingControl(25, ''), + $request, + new ControlBag(), + 'foo' + ); + + self::assertTrue($this->subject->page($pagingRequest, $this->mockContext)->isComplete()); + self::assertEquals( + $entries, + $this->subject->page($pagingRequest, $this->mockContext)->getEntries(), + ); + self::assertSame( + 0, + $this->subject->page($pagingRequest, $this->mockContext)->getRemaining(), + ); + } +} diff --git a/tests/unit/Server/RequestHandler/ProxyRequestHandlerTest.php b/tests/unit/Server/RequestHandler/ProxyRequestHandlerTest.php new file mode 100644 index 00000000..d756738a --- /dev/null +++ b/tests/unit/Server/RequestHandler/ProxyRequestHandlerTest.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\RequestHandler; + +use FreeDSx\Ldap\ClientOptions; +use FreeDSx\Ldap\Control\ControlBag; +use FreeDSx\Ldap\Entry\Change; +use FreeDSx\Ldap\Entry\Entries; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\BindException; +use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\LdapResult; +use FreeDSx\Ldap\Operation\Response\BindResponse; +use FreeDSx\Ldap\Operation\Response\CompareResponse; +use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Operations; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Server\RequestContext; +use FreeDSx\Ldap\Server\RequestHandler\ProxyRequestHandler; +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ProxyRequestHandlerTest extends TestCase +{ + private ProxyRequestHandler $subject; + + private LdapClient&MockObject $mockClient; + + private RequestContext&MockObject $mockContext; + + protected function setUp(): void + { + $this->mockClient = $this->createMock(LdapClient::class); + $this->mockContext = $this->createMock(RequestContext::class); + + $this->mockContext + ->method('controls') + ->willReturn(new ControlBag()); + + $this->mockContext + ->method('token') + ->willReturn(new BindToken('foo', 'bar')); + + $this->subject = new ProxyRequestHandler(new ClientOptions()); + $this->subject->setLdapClient($this->mockClient);; + } + + public function test_it_should_send_an_add_request(): void + { + $add = Operations::add(Entry::create('cn=foo,dc=freedsx,dc=local')); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($add); + + $this->subject->add( + $this->mockContext, + $add, + ); + } + + public function test_it_should_send_a_delete_request(): void + { + $delete = Operations::delete('cn=foo,dc=freedsx,dc=local'); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($delete); + + $this->subject->delete( + $this->mockContext, + $delete + ); + } + + public function test_it_should_send_a_modify_request(): void + { + $modify = Operations::modify('cn=foo,dc=freedsx,dc=local', Change::add('foo', 'bar')); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($modify); + + $this->subject->modify( + $this->mockContext, + $modify, + ); + } + + public function test_it_should_send_a_modify_dn_request(): void + { + $modifyDn = Operations::rename('cn=foo,dc=freedsx,dc=local', 'cn=bar'); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($modifyDn); + + $this->subject->modifyDn( + $this->mockContext, + $modifyDn, + ); + } + + public function test_it_should_send_a_search_request(): void + { + $search = Operations::search( + Filters::present('objectClass'), + 'cn' + )->base('dc=foo'); + $entries = new Entries(Entry::create('dc=foo')); + + $this->mockClient + ->expects($this->once()) + ->method('search') + ->willReturn($entries); + + self::assertEquals( + $entries, + $this->subject->search($this->mockContext, $search), + ); + } + + public function test_it_should_send_a_compare_request_and_return_false_on_no_match(): void + { + $compare = Operations::compare('foo', 'foo', 'bar'); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($compare) + ->willReturn(new LdapMessageResponse( + 1, + new CompareResponse(ResultCode::COMPARE_FALSE) + )); + + self::assertFalse( + $this->subject->compare( + $this->mockContext, + $compare + ) + ); + } + + public function test_it_should_send_a_compare_request_and_return_true_on_match(): void + { + $compare = Operations::compare('foo', 'foo', 'bar'); + + $this->mockClient + ->expects($this->once()) + ->method('sendAndReceive') + ->with($compare) + ->willReturn(new LdapMessageResponse( + 1, + new CompareResponse(ResultCode::COMPARE_TRUE) + )); + + self::assertTrue( + $this->subject->compare( + $this->mockContext, + $compare, + ) + ); + } + + public function test_it_should_send_an_extended_request(): void + { + $extended = Operations::extended('foo', 'bar'); + + $this->mockClient + ->expects($this->once()) + ->method('send') + ->with($extended); + + $this->subject->extended( + $this->mockContext, + $extended, + ); + } + + public function test_it_should_handle_a_bind_request(): void + { + $this->mockClient + ->expects($this->once()) + ->method('bind') + ->willReturn(new LdapMessageResponse( + 1, + new BindResponse(new LdapResult(0)), + )); + + self::assertTrue($this->subject->bind( + 'foo', + 'bar' + )); + } + + public function test_it_should_handle_a_bind_request_failure(): void + { + self::expectException(OperationException::class); + + $this->mockClient + ->expects($this->once()) + ->method('bind') + ->willThrowException(new BindException('Foo!', 49)); + + $this->subject->bind( + 'foo', + 'bar', + ); + } +} diff --git a/tests/unit/Server/RequestHistoryTest.php b/tests/unit/Server/RequestHistoryTest.php new file mode 100644 index 00000000..c11be99b --- /dev/null +++ b/tests/unit/Server/RequestHistoryTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server; + +use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Server\Paging\PagingRequests; +use FreeDSx\Ldap\Server\RequestHistory; +use PHPUnit\Framework\TestCase; + +final class RequestHistoryTest extends TestCase +{ + private RequestHistory $subject; + + protected function setUp(): void + { + $this->subject = new RequestHistory(); + } + + public function test_it_should_add_a_valid_id(): void + { + $this->subject->addId(1); + + self::assertSame( + [1], + $this->subject->getIds() + ); + } + + public function test_it_should_throw_when_adding_an_existing_id(): void + { + self::expectException(ProtocolException::class); + + $this->subject->addId(1); + $this->subject->addId(1); + } + + public function test_it_should_throw_when_adding_an_invalid_id(): void + { + self::expectException(ProtocolException::class); + + $this->subject->addId(0); + } + + public function test_it_should_get_the_paging_requests(): void + { + + self::assertInstanceOf( + PagingRequests::class, + $this->subject->pagingRequest() + ); + } +} diff --git a/tests/unit/Server/ServerProtocolFactoryTest.php b/tests/unit/Server/ServerProtocolFactoryTest.php new file mode 100644 index 00000000..c8fddda5 --- /dev/null +++ b/tests/unit/Server/ServerProtocolFactoryTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server; + +use FreeDSx\Ldap\Protocol\ServerAuthorization; +use FreeDSx\Ldap\Protocol\ServerProtocolHandler; +use FreeDSx\Ldap\Server\HandlerFactoryInterface; +use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; +use FreeDSx\Ldap\Server\ServerProtocolFactory; +use FreeDSx\Ldap\ServerOptions; +use FreeDSx\Socket\Socket; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class ServerProtocolFactoryTest extends TestCase +{ + private ServerProtocolFactory $subject; + + private HandlerFactoryInterface&MockObject $mockHandlerFactory; + + private ServerAuthorization&MockObject $mockServerAuthorization; + + protected function setUp(): void + { + $this->mockHandlerFactory = $this->createMock(HandlerFactoryInterface::class); + $this->mockServerAuthorization = $this->createMock(ServerAuthorization::class); + + $this->subject = new ServerProtocolFactory( + $this->mockHandlerFactory, + new ServerOptions(), + $this->mockServerAuthorization, + ); + } + + public function test_it_should_make_a_ServerProtocolInstance(): void + { + $mockSocket = $this->createMock(Socket::class); + + $this->mockHandlerFactory + ->expects($this->once()) + ->method('makeRequestHandler') + ->willReturn(new GenericRequestHandler()); + + self::assertInstanceOf( + ServerProtocolHandler::class, + $this->subject->make($mockSocket) + ); + } +} diff --git a/tests/unit/Server/SocketServerFactoryTest.php b/tests/unit/Server/SocketServerFactoryTest.php new file mode 100644 index 00000000..eb2f9f36 --- /dev/null +++ b/tests/unit/Server/SocketServerFactoryTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server; + +use FreeDSx\Ldap\Server\SocketServerFactory; +use FreeDSx\Ldap\ServerOptions; +use FreeDSx\Socket\SocketServer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use RuntimeException; + +final class SocketServerFactoryTest extends TestCase +{ + /** + * @var resource + */ + private $tmpUnixSocketResource; + + private string $tmpUnixSocketFilePath; + + private SocketServerFactory $subject; + + private LoggerInterface&MockObject $mockLogger; + + protected function setUp(): void + { + $this->tmpUnixSocketResource = $this->makeTempFile();; + $this->tmpUnixSocketFilePath = stream_get_meta_data($this->tmpUnixSocketResource)['uri']; + $this->mockLogger = $this->createMock(LoggerInterface::class); + + $this->subject = new SocketServerFactory( + (new ServerOptions()) + ->setPort(3390), + $this->mockLogger, + ); + } + + protected function tearDown(): void + { + fclose($this->tmpUnixSocketResource); + } + + public function test_it_should_make_and_bind_the_socket_server(): void + { + self::assertInstanceOf( + SocketServer::class, + $this->subject->makeAndBind(), + ); + } + + public function test_it_should_make_a_unix_based_socket_server(): void + { + if (str_starts_with(strtoupper(PHP_OS), 'WIN')) { + $this->markTestSkipped('Cannot construct unix based socket on Windows.'); + } + + $this->subject = new SocketServerFactory( + (new ServerOptions()) + ->setUnixSocket($this->tmpUnixSocketFilePath) + ->setTransport('unix'), + $this->mockLogger, + ); + + self::assertInstanceOf( + SocketServer::class, + $this->subject->makeAndBind(), + ); + } + + /** + * @return resource + */ + private function makeTempFile() + { + $tempFile = tmpfile(); + + if ($tempFile === false) { + throw new RuntimeException('Unable to create temporary file.'); + } + + return $tempFile; + } +} diff --git a/tests/unit/Server/Token/AnonTokenTest.php b/tests/unit/Server/Token/AnonTokenTest.php new file mode 100644 index 00000000..ea92170a --- /dev/null +++ b/tests/unit/Server/Token/AnonTokenTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\Token; + +use FreeDSx\Ldap\Server\Token\AnonToken; +use PHPUnit\Framework\TestCase; + +final class AnonTokenTest extends TestCase +{ + private AnonToken $subject; + + protected function setUp(): void + { + $this->subject = new AnonToken('foo'); + } + + public function test_it_should_get_the_username(): void + { + self::assertSame( + 'foo', + $this->subject->getUsername(), + ); + } + + public function test_it_should_get_a_null_password(): void + { + self::assertNull($this->subject->getPassword());; + } + + public function test_it_should_get_the_version(): void + { + self::assertSame( + 3, + $this->subject->getVersion(), + ); + } +} diff --git a/tests/unit/Server/Token/BindTokenTest.php b/tests/unit/Server/Token/BindTokenTest.php new file mode 100644 index 00000000..45f4f68d --- /dev/null +++ b/tests/unit/Server/Token/BindTokenTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Server\Token; + +use FreeDSx\Ldap\Server\Token\BindToken; +use PHPUnit\Framework\TestCase; + +final class BindTokenTest extends TestCase +{ + private BindToken $subject; + + protected function setUp(): void + { + $this->subject = new BindToken( + 'foo', + 'bar', + ); + } + + public function test_it_should_get_the_username(): void + { + self::assertSame( + 'foo', + $this->subject->getUsername(), + ); + } + + public function test_it_should_get_the_password(): void + { + self::assertSame( + 'bar', + $this->subject->getPassword(), + ); + } + + public function test_it_should_get_the_version(): void + { + self::assertSame( + 3, + $this->subject->getVersion(), + ); + } +} diff --git a/tests/unit/Sync/Result/SyncEntryResultTest.php b/tests/unit/Sync/Result/SyncEntryResultTest.php new file mode 100644 index 00000000..add5313a --- /dev/null +++ b/tests/unit/Sync/Result/SyncEntryResultTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Sync\Result; + +use FreeDSx\Ldap\Control\Sync\SyncStateControl; +use FreeDSx\Ldap\Entry\Entry; +use FreeDSx\Ldap\Exception\RuntimeException; +use FreeDSx\Ldap\Operation\Response\SearchResultEntry; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Result\EntryResult; +use FreeDSx\Ldap\Sync\Result\SyncEntryResult; +use PHPUnit\Framework\TestCase; + +final class SyncEntryResultTest extends TestCase +{ + private SyncEntryResult $subject; + + protected function setUp(): void + { + $this->subject = new SyncEntryResult(new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_ADD, + 'foo', + 'bar' + ) + ) + )); + } + + public function test_it_should_get_the_entry(): void + { + self::assertEquals( + new Entry('cn=foo'), + $this->subject->getEntry(), + ); + } + + public function test_it_should_get_the_sync_state(): void + { + self::assertSame( + SyncStateControl::STATE_ADD, + $this->subject->getState(), + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'bar', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_the_entry_uuid(): void + { + self::assertSame( + 'foo', + $this->subject->getEntryUuid(), + ); + } + + public function test_it_should_be_able_to_check_what_the_state_is(): void + { + self::assertTrue( + $this->subject->isState(SyncStateControl::STATE_ADD) + ); + } + + public function test_it_should_be_able_to_check_what_the_state_is_not(): void + { + self::assertFalse( + $this->subject->isState(SyncStateControl::STATE_MODIFY) + ); + } + + + public function test_it_should_tell_if_it_is_for_a_present_state(): void + { + $this->subject = new SyncEntryResult( + new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_PRESENT, + 'foo', + 'bar' + ) + ) + ) + ); + + self::assertTrue($this->subject->isPresent()); + } + + public function test_it_should_tell_if_it_is_for_a_add_state(): void + { + $this->subject = new SyncEntryResult(new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_ADD, + 'foo', + 'bar' + ) + ) + )); + + self::assertTrue($this->subject->isAdd()); + } + + public function test_it_should_tell_if_it_is_for_a_modify_state(): void + { + $this->subject = new SyncEntryResult( + new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_MODIFY, + 'foo', + 'bar' + ) + ) + ) + ); + + self::assertTrue($this->subject->isModify()); + } + + public function test_it_should_tell_if_it_is_for_a_delete_state(): void + { + $this->subject = new SyncEntryResult( + new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_DELETE, + 'foo', + 'bar' + ) + ) + ) + ); + + self::assertTrue($this->subject->isDelete()); + } + + public function test_it_should_get_the_raw_message(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + new SyncStateControl( + SyncStateControl::STATE_ADD, + 'foo', + 'bar' + ) + ), + $this->subject->getMessage(), + ); + } + + public function test_it_should_throw_an_error_if_there_is_no_sync_state_control(): void + { + self::expectException(RuntimeException::class); + self::expectExceptionMessage('Expected a SyncStateControl, but none was found.'); + + $this->subject = new SyncEntryResult(new EntryResult( + new LdapMessageResponse( + 1, + new SearchResultEntry(new Entry('cn=foo')), + ) + )); + + $this->subject->getState(); + } +} diff --git a/tests/unit/Sync/Result/SyncIdSetResultTest.php b/tests/unit/Sync/Result/SyncIdSetResultTest.php new file mode 100644 index 00000000..06169ab3 --- /dev/null +++ b/tests/unit/Sync/Result/SyncIdSetResultTest.php @@ -0,0 +1,140 @@ +subject = new SyncIdSetResult( + new LdapMessageResponse( + 1, + new SyncIdSet( + ['foo', 'bar'], + true, + 'tasty' + ), + ) + ); + } + + public function test_it_should_get_the_entry_uuids(): void + { + self::assertSame( + [ + 'foo', + 'bar', + ], + $this->subject->getEntryUuids(), + ); + } + + public function test_it_should_get_the_count_of_the_set(): void + { + self::assertCount( + 2, + $this->subject, + ); + } + + public function test_it_should_get_the_iterable_set(): void + { + self::assertEquals( + new \ArrayIterator([ + 'foo', + 'bar', + ]), + $this->subject->getIterator(), + ); + } + + public function test_it_should_get_the_raw_message(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new SyncIdSet( + ['foo', 'bar'], + true, + 'tasty' + ), + ), + $this->subject->getMessage(), + ); + } + + public function test_it_should_get_the_cookie(): void + { + self::assertSame( + 'tasty', + $this->subject->getCookie(), + ); + } + + public function test_it_should_get_if_this_is_for_entry_deletes(): void + { + $this->subject = new SyncIdSetResult( + new LdapMessageResponse( + 1, + new SyncIdSet( + ['foo', 'bar'], + true, + 'tasty' + ), + ) + ); + + self::assertTrue($this->subject->isDeleted()); + self::assertFalse($this->subject->isPresent()); + } + + public function test_it_should_get_if_this_is_for_entry_that_are_present(): void + { + $this->subject = new SyncIdSetResult( + new LdapMessageResponse( + 1, + new SyncIdSet( + ['foo', 'bar'], + false, + 'tasty' + ), + ) + ); + + self::assertTrue($this->subject->isPresent()); + self::assertFalse($this->subject->isDeleted()); + } + + public function test_it_must_have_a_SearchEntryResponse(): void + { + $this->subject = new SyncIdSetResult( + new LdapMessageResponse( + 1, + new SearchResultReference( + new LdapUrl('foo') + ), + ) + ); + + self::expectException(UnexpectedValueException::class); + self::expectExceptionMessage(sprintf( + 'Expected an instance of "%s", but got "%s".', + SyncIdSet::class, + SearchResultReference::class, + )); + + $this->subject->getEntryUuids(); + } +} diff --git a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncReferralResultSpec.php b/tests/unit/Sync/Result/SyncReferralResultTest.php similarity index 62% rename from tests/spec/FreeDSx/Ldap/Sync/Result/SyncReferralResultSpec.php rename to tests/unit/Sync/Result/SyncReferralResultTest.php index 8ca19660..f04b53da 100644 --- a/tests/spec/FreeDSx/Ldap/Sync/Result/SyncReferralResultSpec.php +++ b/tests/unit/Sync/Result/SyncReferralResultTest.php @@ -11,42 +11,24 @@ * file that was distributed with this source code. */ -namespace spec\FreeDSx\Ldap\Sync\Result; +namespace Tests\Unit\FreeDSx\Ldap\Sync\Result; use FreeDSx\Ldap\Control\Sync\SyncStateControl; use FreeDSx\Ldap\LdapUrl; use FreeDSx\Ldap\Operation\Response\SearchResultReference; use FreeDSx\Ldap\Protocol\LdapMessageResponse; use FreeDSx\Ldap\Search\Result\ReferralResult; -use PhpSpec\ObjectBehavior; +use FreeDSx\Ldap\Sync\Result\SyncReferralResult; +use PHPUnit\Framework\TestCase; -class SyncReferralResultSpec extends ObjectBehavior +final class SyncReferralResultTest extends TestCase { - public function let(): void - { - $this->beConstructedWith(new ReferralResult( - new LdapMessageResponse( - 1, - new SearchResultReference(new LdapUrl('ldap://foo')), - new SyncStateControl( - SyncStateControl::STATE_DELETE, - 'foo', - 'bar' - ) - ) - )); - } + private SyncReferralResult $subject; - public function it_should_get_the_referrals(): void + protected function setUp(): void { - $this->getReferrals() - ->shouldBeLike([new LdapUrl('ldap://foo')]); - } - - public function it_should_get_the_raw_nessage(): void - { - $this->getMessage() - ->shouldBeLike( + $this->subject = new SyncReferralResult( + new ReferralResult( new LdapMessageResponse( 1, new SearchResultReference(new LdapUrl('ldap://foo')), @@ -56,6 +38,31 @@ public function it_should_get_the_raw_nessage(): void 'bar' ) ) - ); + ) + ); + } + + public function test_it_should_get_the_referrals(): void + { + self::assertEquals( + [new LdapUrl('ldap://foo')], + $this->subject->getReferrals(), + ); + } + + public function test_it_should_get_the_raw_message(): void + { + self::assertEquals( + new LdapMessageResponse( + 1, + new SearchResultReference(new LdapUrl('ldap://foo')), + new SyncStateControl( + SyncStateControl::STATE_DELETE, + 'foo', + 'bar' + ) + ), + $this->subject->getMessage(), + ); } } diff --git a/tests/unit/Sync/SessionTest.php b/tests/unit/Sync/SessionTest.php new file mode 100644 index 00000000..e4ae90ce --- /dev/null +++ b/tests/unit/Sync/SessionTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Sync; + +use FreeDSx\Ldap\Sync\Session; +use PHPUnit\Framework\TestCase; + +final class SessionTest extends TestCase +{ + private Session $subject; + + protected function setUp(): void + { + $this->subject = new Session( + Session::MODE_POLL, + null + ); + } + + public function test_it_should_get_the_phase_when_it_is_not_set(): void + { + self::assertNull($this->subject->getPhase()); + } + + public function test_it_should_get_the_phase_when_it_is_set(): void + { + $this->subject->updatePhase(Session::PHASE_DELETE); + + self::assertSame( + Session::PHASE_DELETE, + $this->subject->getPhase(), + ); + } + + public function test_it_should_get_the_cookie_when_it_is_not_set(): void + { + self::assertNull($this->subject->getCookie()); + } + + public function test_it_should_get_the_cookie_when_it_is_set(): void + { + $this->subject->updateCookie('foo'); + + self::assertSame( + 'foo', + $this->subject->getCookie(), + ); + } +} diff --git a/tests/unit/Sync/SyncReplTest.php b/tests/unit/Sync/SyncReplTest.php new file mode 100644 index 00000000..a2a4bec8 --- /dev/null +++ b/tests/unit/Sync/SyncReplTest.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\Unit\FreeDSx\Ldap\Sync; + +use FreeDSx\Ldap\Control\Control; +use FreeDSx\Ldap\Control\Sync\SyncDoneControl; +use FreeDSx\Ldap\Control\Sync\SyncRequestControl; +use FreeDSx\Ldap\LdapClient; +use FreeDSx\Ldap\Operation\Request\SearchRequest; +use FreeDSx\Ldap\Operation\Request\SyncRequest; +use FreeDSx\Ldap\Operation\Response\SearchResultDone; +use FreeDSx\Ldap\Protocol\LdapMessageResponse; +use FreeDSx\Ldap\Search\Filters; +use FreeDSx\Ldap\Sync\Result\SyncEntryResult; +use FreeDSx\Ldap\Sync\Result\SyncIdSetResult; +use FreeDSx\Ldap\Sync\Result\SyncReferralResult; +use FreeDSx\Ldap\Sync\SyncRepl; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +final class SyncReplTest extends TestCase +{ + private SyncRepl $subject; + + private LdapClient&MockObject $mockClient; + + protected function setUp(): void + { + $this->mockClient = $this->createMock(LdapClient::class); + + $this->subject = new SyncRepl($this->mockClient); + } + + public function test_it_should_poll_for_changes(): void + { + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::anything(), + self::callback( + fn (SyncRequestControl $control) => $control->getMode() === SyncRequestControl::MODE_REFRESH_ONLY + ), + self::anything(), + ) + ->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_listen_for_changes(): void + { + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::anything(), + self::callback( + fn (SyncRequestControl $control) => $control->getMode() === SyncRequestControl::MODE_REFRESH_AND_PERSIST + ), + self::anything(), + ) + ->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->listen(); + } + + public function test_it_should_use_a_filter_if_specified(): void + { + $this->subject->useFilter(Filters::present('foo')); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::callback( + fn (SyncRequest $request) => + $request->getFilter()->toString() === Filters::present('foo')->toString() + ), + self::anything(), + self::anything(), + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_use_added_controls_if_specified(): void + { + $control = new Control('foo'); + + $this->subject + ->controls() + ->add($control); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::anything(), + self::anything(), + self::anything(), + $control, + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_use_the_cookie_if_specified(): void + { + $this->subject->useCookie('tasty'); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::anything(), + self::callback( + fn (SyncRequestControl $control) => $control->getCookie() === 'tasty' + ), + self::anything(), + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_use_the_entry_handler_specified(): void + { + $handler = fn (SyncEntryResult $result) => $result->getEntry(); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::callback( + fn (SyncRequest $request) => $request->getEntryHandler() === $handler + ), + self::anything(), + self::anything(), + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll($handler); + } + + public function test_it_should_use_the_referral_handler_specified(): void + { + $handler = fn (SyncReferralResult $result) => $result->getReferrals(); + + $this->subject->useReferralHandler($handler); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::callback( + fn (SyncRequest $request) => $request->getReferralHandler() === $handler + ), + self::anything(), + self::anything(), + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_use_the_idSet_handler_specified(): void + { + $handler = fn (SyncIdSetResult $result) => $result->getEntryUuids(); + + $this->subject->useIdSetHandler($handler); + + $this->mockClient + ->expects(self::once()) + ->method('sendAndReceive') + ->with( + self::callback( + fn (SyncRequest $request) => $request->getIdSetHandler() === $handler + ), + self::anything(), + self::anything(), + )->willReturn(new LdapMessageResponse( + 1, + new SearchResultDone(0), + new SyncDoneControl('foo') + )); + + $this->subject->poll(); + } + + public function test_it_should_use_the_continue_strategy_if_specified(): void + { + $this->subject->useContinueOnCancel(); + + self::assertSame( + SearchRequest::CANCEL_CONTINUE, + $this->subject + ->request() + ->getCancelStrategy() + ); + } +} diff --git a/tests/spec/FreeDSx/Ldap/TestFactoryTrait.php b/tests/unit/TestFactoryTrait.php similarity index 98% rename from tests/spec/FreeDSx/Ldap/TestFactoryTrait.php rename to tests/unit/TestFactoryTrait.php index 04d29e2d..01901d54 100644 --- a/tests/spec/FreeDSx/Ldap/TestFactoryTrait.php +++ b/tests/unit/TestFactoryTrait.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace spec\FreeDSx\Ldap; +namespace Tests\Unit\FreeDSx\Ldap; use FreeDSx\Ldap\Control\Control; use FreeDSx\Ldap\Entry\Entries; From c44e6af4077179166ecf8eb3ba77f7823f9f9b8b Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:42:56 -0500 Subject: [PATCH 03/12] Remove the null-coalesce; it wasn't doing anything. --- src/FreeDSx/Ldap/Control/Sync/SyncDoneControl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FreeDSx/Ldap/Control/Sync/SyncDoneControl.php b/src/FreeDSx/Ldap/Control/Sync/SyncDoneControl.php index cfb72c3d..cc567f07 100644 --- a/src/FreeDSx/Ldap/Control/Sync/SyncDoneControl.php +++ b/src/FreeDSx/Ldap/Control/Sync/SyncDoneControl.php @@ -67,7 +67,7 @@ public function toAsn1(): AbstractType )); } $this->controlValue->addChild(Asn1::boolean( - $this->refreshDeletes ?? false + $this->refreshDeletes )); return parent::toAsn1(); From 133fd4b573f50b4c81bc7e657d2951c4cc8195c3 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:43:33 -0500 Subject: [PATCH 04/12] Add a way to set the vlv filter. This was somehow overlooked and discovered via static analysis. --- src/FreeDSx/Ldap/Search/Vlv.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/FreeDSx/Ldap/Search/Vlv.php b/src/FreeDSx/Ldap/Search/Vlv.php index 4e4cb163..61c3153b 100644 --- a/src/FreeDSx/Ldap/Search/Vlv.php +++ b/src/FreeDSx/Ldap/Search/Vlv.php @@ -57,6 +57,18 @@ public function __construct( : Controls::sort($sort); } + /** + * Use a matching rule filter assertion to specify the target entry within a sorted LDAP result set. + * + * It is used as an alternative to specifying the target by a numerical offset. + */ + public function useFilter(?GreaterThanOrEqualFilter $filter): self + { + $this->filter = $filter; + + return $this; + } + /** * As a percentage the moveTo, moveForward, moveBackward, and position methods work with numbers 0 - 100 and should * be interpreted as percentages. From d743b998e869adf4edb5ccf54f2d9fd50a93e429 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:43:45 -0500 Subject: [PATCH 05/12] Remove old phpspec files. --- phpspec.cov.yml | 13 ------------- phpspec.yml | 5 ----- 2 files changed, 18 deletions(-) delete mode 100644 phpspec.cov.yml delete mode 100644 phpspec.yml diff --git a/phpspec.cov.yml b/phpspec.cov.yml deleted file mode 100644 index 5271b7f3..00000000 --- a/phpspec.cov.yml +++ /dev/null @@ -1,13 +0,0 @@ -suites: - default: - spec_path: tests/ - src_path: src/ -formatter.name: progress -extensions: - FriendsOfPhpSpec\PhpSpec\CodeCoverage\CodeCoverageExtension: - format: - - clover - output: - clover: coverage.xml - whitelist: - - src diff --git a/phpspec.yml b/phpspec.yml deleted file mode 100644 index fcb0523c..00000000 --- a/phpspec.yml +++ /dev/null @@ -1,5 +0,0 @@ -suites: - default: - spec_path: tests/ - src_path: src/ -formatter.name: progress From 2d46410a32c9c6c34be859e2219aa5bd5401a23e Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:46:05 -0500 Subject: [PATCH 06/12] Set the PHPStan level at 9 for now, which was the max for v1. I'm upgrading it as part of the move to phpunit. I will revisit and fix the necessary items at level 10 before bumping it. --- phpstan.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 5fa5de99..1b26d416 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: max + level: 9 paths: - %currentWorkingDirectory%/src treatPhpDocTypesAsCertain: false From 07164645f43d0c2755a3cac9496ca8c2226c6ee5 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:46:26 -0500 Subject: [PATCH 07/12] Add additional PHP versions for the CI runs. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f019d704..ba450887 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: fail-fast: false matrix: operating-system: [ubuntu-latest, windows-latest] - php-versions: ['8.1', '8.2'] + php-versions: ['8.1', '8.2', '8.3', '8.4', '8.5'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: - name: Checkout From b4ec2829d6dc606138c10abd0a4a9c549a26fdd0 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:52:15 -0500 Subject: [PATCH 08/12] Fix coverage references for phpspec. --- .github/workflows/analysis.yml | 4 ++-- composer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 93b215a3..86567094 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -43,11 +43,11 @@ jobs: COMPOSER_ALLOW_SUPERUSER: 1 run: sudo composer run-script --timeout=0 test-coverage - - name: Upload Spec Coverage to Codecov + - name: Upload Unit Coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml + file: ./coverage-unit.xml name: FreeDSx-LDAP-Unit - name: Upload Integration Coverage to Codecov diff --git a/composer.json b/composer.json index d7daa268..6c94038f 100644 --- a/composer.json +++ b/composer.json @@ -52,8 +52,8 @@ }, "scripts": { "test-coverage": [ - "phpspec run --no-interaction -c phpspec.cov.yml", - "phpunit --coverage-clover=coverage-integration.xml" + "phpunit --testsuite unit --coverage-clover=coverage-unit.xml", + "phpunit --testsuite integration --coverage-clover=coverage-integration.xml" ], "test-unit": [ "phpunit --testsuite unit" From 2934ca69e5d24e56bfe40c5650ee570b1633c85c Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 29 Nov 2025 16:55:44 -0500 Subject: [PATCH 09/12] Fix missing source reference in phpunit config. --- phpunit.xml.dist | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index eb16731e..39be95e8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,6 +12,11 @@ bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" > + + + ./src + + Date: Sun, 30 Nov 2025 10:24:46 -0500 Subject: [PATCH 10/12] Correct the server test path(s) since the directory structure changed. --- tests/integration/LdapTestCase.php | 4 ++-- tests/integration/ServerTestCase.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/LdapTestCase.php b/tests/integration/LdapTestCase.php index 09eb679d..f2cd7bb7 100644 --- a/tests/integration/LdapTestCase.php +++ b/tests/integration/LdapTestCase.php @@ -29,7 +29,7 @@ protected function makeOptions(): ClientOptions ->setServers([(string) $_ENV['LDAP_SERVER']]) ->setSslCaCert( $_ENV['LDAP_CA_CERT'] === '' - ? __DIR__ . '/../../../resources/cert/ca.crt' + ? __DIR__ . '/../resources/cert/ca.crt' : (string) $_ENV['LDAP_CA_CERT'] ) ->setBaseDn((string) $_ENV['LDAP_BASE_DN']); @@ -56,7 +56,7 @@ protected function isActiveDirectory(): bool try { self::$isActiveDirectory = $client->readOrFail('') ->has('forestFunctionality'); - } catch (Throwable $e) { + } catch (Throwable) { self::$isActiveDirectory = false; } finally { $client->unbind(); diff --git a/tests/integration/ServerTestCase.php b/tests/integration/ServerTestCase.php index dfa52de0..8e2138ed 100644 --- a/tests/integration/ServerTestCase.php +++ b/tests/integration/ServerTestCase.php @@ -74,7 +74,7 @@ protected function createServerProcess( ): void { $processArgs = [ 'php', - __DIR__ . '/../../../bin/' . $this->serverMode . '.php', + __DIR__ . '/../bin/' . $this->serverMode . '.php', $transport, ]; From 63085f7d013ce74dd353aa33a1b5fcac0942512d Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sun, 30 Nov 2025 10:45:06 -0500 Subject: [PATCH 11/12] Remove deprecated composer switches from CI run. --- .github/workflows/analysis.yml | 2 +- .github/workflows/build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 86567094..6fb11ac2 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -24,7 +24,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Install Composer Dependencies - run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Cache Dependencies uses: actions/cache@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba450887..37c131ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Install Composer dependencies - run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Cache dependencies uses: actions/cache@v3 From 0d23f7f5053c6aca66a57f0cd20b1c4ba780e3be Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sun, 30 Nov 2025 11:07:50 -0500 Subject: [PATCH 12/12] Update the changelog for the future upgrade. --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08fd8fd0..9d421646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ CHANGELOG ========= -1.0.0 (2023-xx-xx) +1.0.0 (202x-xx-xx) ------------------ * Updated the minimum version of PHP to version 8.1. * All classes now use "strict_types=1". This internal change should not impact external usage. @@ -10,6 +10,9 @@ CHANGELOG * Added entry handler processing of searches. This allows immediate processing of entries via anonymous functions. * Added the ability to cancel search or sync requests when using entry handler processing for searches. * Added support for the SensitiveParameter attribute to mask bind related credentials from stack traces. +* Removed various deprecations for PHP >= 8.1 (implicit nullability, return type declarations, etc.). +* Added PHP 8.3, 8.4, and 8.5 to normal CI runs. +* Migrated all unit tests from PHPSpec to PHPUnit. 0.8.0 (2022-05-21) ------------------