Bitai.LDAPHelper.LdapAdapters.LdapHelperMock
10.2.0
dotnet add package Bitai.LDAPHelper.LdapAdapters.LdapHelperMock --version 10.2.0
NuGet\Install-Package Bitai.LDAPHelper.LdapAdapters.LdapHelperMock -Version 10.2.0
<PackageReference Include="Bitai.LDAPHelper.LdapAdapters.LdapHelperMock" Version="10.2.0" />
<PackageVersion Include="Bitai.LDAPHelper.LdapAdapters.LdapHelperMock" Version="10.2.0" />
<PackageReference Include="Bitai.LDAPHelper.LdapAdapters.LdapHelperMock" />
paket add Bitai.LDAPHelper.LdapAdapters.LdapHelperMock --version 10.2.0
#r "nuget: Bitai.LDAPHelper.LdapAdapters.LdapHelperMock, 10.2.0"
#:package Bitai.LDAPHelper.LdapAdapters.LdapHelperMock@10.2.0
#addin nuget:?package=Bitai.LDAPHelper.LdapAdapters.LdapHelperMock&version=10.2.0
#tool nuget:?package=Bitai.LDAPHelper.LdapAdapters.LdapHelperMock&version=10.2.0
Bitai.LDAPHelper.LdapAdapters.LdapHelperMock ![Logo]()
An in-memory LDAP adapter implementation designed for unit testing, integration testing, offline prototyping, local development, and CI/CD pipelines in the Bitai LDAP Helper ecosystem.
Table of Contents
- Overview
- Solution Architecture
- Key Features
- Project Structure & Class Taxonomy
- Quick Start
- Detailed Usage Scenarios
- Data Store & Seeding Infrastructure
- Build, Test & Package
- Observability & Diagnostic Assertions
- Troubleshooting
- Security & Operational Guidance
- License
Overview
Bitai.LDAPHelper.LdapAdapters.LdapHelperMock provides lightweight, fast, in-memory mock implementations of the LDAP adapter contracts defined in Bitai.LDAPHelper.
It enables developers to test LDAP-dependent components—such as authentication services (Authenticator), user search providers (Searcher), group membership validators (GroupMembershipValidator), and Active Directory account management routines (AccountManager)—without requiring access to a physical Active Directory, OpenLDAP, or containerized LDAP instance.
Target Framework & Specifications
- Target Framework: .NET 10.0 (
net10.0) - Nullable Context: Enabled (
<Nullable>enable</Nullable>) - Implicit Usings: Enabled (
<ImplicitUsings>enable</ImplicitUsings>) - Project Reference: Depends on Bitai.LDAPHelper
Solution Architecture
In the Bitai.LDAPHelper ecosystem, core services interact exclusively with LDAP contract abstractions (ILdapConnectionFactoryAdapter, ILdapConnectionAdapter, ILdapEntryAdapter, etc.) rather than concrete directory implementations. LdapHelperMock acts as a drop-in replacement for production adapters such as Bitai.LDAPHelper.LdapAdapters.Novell.
┌─────────────────────────────────────────────────────────────────┐
│ Consumer Application │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Bitai.LDAPHelper │
│ (Authenticator, Searcher, AccountManager, Validator) │
└─────────────────────────────────────────────────────────────────┘
│
ILdapConnectionFactoryAdapter
ILdapConnectionAdapter
│
┌───────────────────────┴───────────────────────┐
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ LdapAdapters.Novell │ │ LdapHelperMock │
│ (Production / Live LDAP) │ │ (In-Memory / Test Suite) │
└──────────────────────────────┘ └──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ MockLdapDataStore │
│ (In-Memory Directory) │
└──────────────────────────────┘
Repository Projects Overview
| Project | Path | Role & Purpose |
|---|---|---|
Bitai.LDAPHelper.LdapAdapters.LdapHelperMock |
adapters/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock |
In-memory mock adapter implementation & test seeder |
Bitai.LDAPHelper |
src/Bitai.LDAPHelper |
Core helper library defining adapter interfaces & services |
Bitai.LDAPHelper.DTO |
src/Bitai.LDAPHelper.DTO |
Data transfer objects, credentials, and operation results |
Bitai.LDAPHelper.LdapAdapters.Novell |
adapters/Bitai.LDAPHelper.LdapAdapters.Novell |
Novell-backed LDAP connection adapter for live environments |
Bitai.LDAPHelper.Tests |
tests/Bitai.LDAPHelper.Tests |
Unit and integration test suite consuming mock adapters |
Key Features
- Persistent Shared State (
MockLdapPersistentConnectionAdapter)- Simulates
ConnectAsync,BindAsync,SearchAsync,AddEntryAsync,ModifyEntryAsync, andDeleteEntryAsync. - Supports registerable search results via
AddSearchResult(filterPattern, entries). - Backed by
MockLdapDataStoreto persist entry creations, modifications, and deletions across multiple connections. - Evaluates search filters dynamically against stored records.
- Simulates
- Deterministic Directory Seeder (
MockLdapDataSeeder)- Populates realistic Active Directory objects: domain roots, OUs (Users, Groups, Computers), standard accounts, service accounts, and nested group memberships.
- Includes thread-safe progressive RID generation and structured logging via
ILogger<MockLdapDataSeeder>.
- Side-Effect Inspection & Assertions
- Tracks created entries (
CreatedEntries), modifications (Modifications), and deletions (DeletedEntries) for assertion in unit tests.
- Tracks created entries (
- Multi-Attribute Filter Engine
- Evaluates LDAP search filter criteria including wildcard matching (
*) againstsAMAccountName,distinguishedName,cn,objectSid, andobjectClass.
- Evaluates LDAP search filter criteria including wildcard matching (
- Active Directory SID Conversion
- Includes binary SID parsing utilities (
ConvertByteToStringSid) to match Windows Active DirectoryobjectSidformatting (S-1-5-...).
- Includes binary SID parsing utilities (
Project Structure & Class Taxonomy
adapters/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock/
├── Bitai.LDAPHelper.LdapAdapters.LdapHelperMock.csproj
├── README.md
├── LICENSE.md
├── MockLdapPersistentConnectionAdapter.cs
├── MockLdapPersistentConnectionFactoryAdapter.cs
├── MockLdapEntryAdapter.cs
├── MockLdapAttributeSetAdapter.cs
├── MockLdapAttributeAdapter.cs
├── MockLdapModificationAdapter.cs
├── MockLdapMessageAdapter.cs
├── MockLdapSearchQueueAdapter.cs
└── LdapData/
├── MockLdapDataStore.cs
└── MockLdapDataSeeder.cs
Class Roles Breakdown
| Class | Implemented Interface | Primary Responsibility |
|---|---|---|
MockLdapPersistentConnectionAdapter |
ILdapConnectionAdapter |
Persistent mock connection connected to the central MockLdapDataStore. Supports configurable search results & side-effect trackers. |
MockLdapPersistentConnectionFactoryAdapter |
ILdapConnectionFactoryAdapter |
Factory producing MockLdapPersistentConnectionAdapter instances. |
MockLdapEntryAdapter |
ILdapEntryAdapter |
Represents an LDAP entry with a DN and attribute set. |
MockLdapAttributeSetAdapter |
ILdapAttributeSetAdapter |
Dictionary-backed container for entry attributes. Includes verification helpers. |
MockLdapAttributeAdapter |
ILdapAttributeAdapter |
Represents individual attribute key-value pairs (string, string array, or byte array). |
MockLdapModificationAdapter |
ILdapModificationAdapter |
Represents an LDAP entry modification (Add, Delete, Replace). |
MockLdapMessageAdapter |
ILdapMessageAdapter |
Wraps single search result entries for message queue delivery. |
MockLdapSearchQueueAdapter |
ILdapSearchQueueAdapter |
In-memory queue storing search result messages returned by SearchAsync. |
MockLdapDataStore |
N/A (Singleton) | Thread-safe in-memory directory store guarded by ReaderWriterLockSlim. |
MockLdapDataSeeder |
N/A | Seeds MockLdapDataStore with domain hierarchies, OUs, users, groups, and SIDs. |
Quick Start
Installation
To add Bitai.LDAPHelper.LdapAdapters.LdapHelperMock to a test project:
dotnet add package Bitai.LDAPHelper.LdapAdapters.LdapHelperMock
Or via project reference:
<ItemGroup>
<ProjectReference Include="..\..\adapters\Bitai.LDAPHelper.LdapAdapters.LdapHelperMock\Bitai.LDAPHelper.LdapAdapters.LdapHelperMock.csproj" />
</ItemGroup>
Basic Minimal Example
using Bitai.LDAPHelper;
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPHelper.LdapAdapters.LdapHelperMock;
// 1. Create a persistent mock connection
var mockConnection = new MockLdapPersistentConnectionAdapter();
var userEntry = new MockLdapEntryAdapter("CN=John Doe,OU=Users,DC=example,DC=com");
userEntry.AddAttribute("cn", "John Doe");
userEntry.AddAttribute("sAMAccountName", "jdoe");
userEntry.AddAttribute("userPrincipalName", "jdoe@example.com");
mockConnection.AddSearchResult("(sAMAccountName=jdoe)", new List<MockLdapEntryAdapter> { userEntry });
// 2. Wrap in a factory
var factory = new MockLdapPersistentConnectionFactoryAdapter();
// 3. Initialize LDAPHelper services using the mock factory
var connectionInfo = new ConnectionInfo("localhost", 389, useSSL: false, connectionTimeout: 15);
var credential = new LDAPDomainAccountCredential("EXAMPLE", "service.account", "SecretPassword!");
var searchLimits = new SearchLimits("DC=example,DC=com");
var searcher = new Searcher(connectionInfo, searchLimits, credential, factory);
// 4. Perform search via Searcher
var user = await searcher.GetLdapEntryBySamAccountNameAsync("jdoe");
Console.WriteLine($"Found User: {user?.CN} ({user?.DistinguishedName})");
Detailed Usage Scenarios
Scenario 1: Isolated Unit Testing with Custom Search Results
For targeted unit tests where you want exact control over returned entries without global directory state:
using Xunit;
using Bitai.LDAPHelper;
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPHelper.LdapAdapters.LdapHelperMock;
public class UserServiceTests
{
[Fact]
public async Task GetUser_ShouldReturnMatchingLdapEntry()
{
// Arrange
var mockConnection = new MockLdapPersistentConnectionAdapter();
var expectedDn = "CN=Alice Smith,OU=Engineering,DC=corp,DC=local";
var entry = new MockLdapEntryAdapter(expectedDn);
entry.AddAttribute("sAMAccountName", "asmith");
entry.AddAttribute("mail", "alice.smith@corp.local");
entry.AddAttribute("givenName", "Alice");
entry.AddAttribute("sn", "Smith");
mockConnection.AddSearchResult("asmith", new List<MockLdapEntryAdapter> { entry });
var factory = new MockLdapPersistentConnectionFactoryAdapter();
var searcher = new Searcher(
new ConnectionInfo("ldap.corp.local", 389, false, 10),
new SearchLimits("DC=corp,DC=local"),
new LDAPDomainAccountCredential("CORP", "admin", "pass"),
factory
);
// Act
var result = await searcher.GetLdapEntryBySamAccountNameAsync("asmith");
// Assert
Assert.NotNull(result);
Assert.Equal("asmith", result.SamAccountName);
Assert.Equal(expectedDn, result.DistinguishedName);
}
}
Scenario 2: Integration Testing with Persistent Seeded Data
When testing complex workflows that execute sequential searches, authentications, or group membership checks against a shared directory dataset:
using Xunit;
using Bitai.LDAPHelper;
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPHelper.LdapAdapters.LdapHelperMock;
using Bitai.LDAPHelper.LdapAdapters.LdapHelperMock.LdapData;
using Microsoft.Extensions.Logging.Abstractions;
public class AuthenticationIntegrationTests
{
public AuthenticationIntegrationTests()
{
// Seed the shared in-memory data store with realistic AD entities
var seeder = new MockLdapDataSeeder(NullLogger<MockLdapDataSeeder>.Instance);
seeder.SeedAllData();
}
[Fact]
public async Task AuthenticateDomainAccount_WithSeededUser_ShouldSucceed()
{
// Arrange - use persistent connection factory
var factory = new MockLdapPersistentConnectionFactoryAdapter();
var connectionInfo = new ConnectionInfo("dc01.domain.com", 389, false, 30);
var searchLimits = new SearchLimits("DC=domain,DC=com");
var adminCreds = new LDAPDomainAccountCredential("DOMAIN", "Administrator", "AdminP@ss123");
var authenticator = new Authenticator(connectionInfo, searchLimits, adminCreds, factory);
// Act - Authenticate a user populated by MockLdapDataSeeder
var targetUserCreds = new LDAPDomainAccountCredential("DOMAIN", "jdoe", "UserP@ssword1!");
var authResult = await authenticator.AuthenticateDomainAccountAsync(targetUserCreds);
// Assert
Assert.NotNull(authResult);
Assert.True(authResult.IsAuthenticated);
}
}
Scenario 3: Verifying Account Management Side Effects
When testing routines that create, modify, or delete directory objects (e.g. AccountManager), use MockLdapPersistentConnectionAdapter to verify generated modifications:
using Xunit;
using Bitai.LDAPHelper;
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPHelper.LdapAdapters;
using Bitai.LDAPHelper.LdapAdapters.LdapHelperMock;
public class AccountManagerTests
{
[Fact]
public async Task ModifyUserAttribute_ShouldRecordModification()
{
// Arrange
var mockConnection = new MockLdapPersistentConnectionAdapter();
var factory = new MockLdapPersistentConnectionFactoryAdapter();
var connectionInfo = new ConnectionInfo("localhost", 389, false, 10);
var searchLimits = new SearchLimits("DC=example,DC=com");
var adminCreds = new LDAPDomainAccountCredential("EXAMPLE", "admin", "pass");
var accountManager = new AccountManager(connectionInfo, searchLimits, adminCreds, factory);
var userDn = "CN=Bob Jones,OU=Users,DC=example,DC=com";
var modifications = new List<ILdapModificationAdapter>
{
new MockLdapModificationAdapter(LdapModificationType.Replace, "telephoneNumber", "+1-555-0199")
};
// Act
await accountManager.ModifyEntryAsync(userDn, modifications);
// Assert
Assert.Single(mockConnection.Modifications);
var recordedMod = mockConnection.Modifications[0];
Assert.Equal(userDn, recordedMod.DistinguishedName);
Assert.Equal("telephoneNumber", recordedMod.AttributeName);
Assert.Equal("+1-555-0199", recordedMod.Value);
}
}
Data Store & Seeding Infrastructure
MockLdapDataStore
MockLdapDataStore is a thread-safe singleton managing the in-memory LDAP hierarchy for persistent mock connections.
- Thread-Safety: Operations are protected by
ReaderWriterLockSlim. - Case-Insensitive Keys: Distinguished names are indexed using
StringComparer.OrdinalIgnoreCase. - API Surface:
AddOrUpdateEntry(MockLdapEntryAdapter entry)GetEntry(string distinguishedName)SearchEntries(Func<MockLdapEntryAdapter, bool> predicate)RemoveEntry(string distinguishedName)GetAllEntries()Clear()Count
MockLdapDataSeeder
MockLdapDataSeeder generates a complete, realistic directory structure inside MockLdapDataStore.
Seeding Operations Executed by SeedAllData()
- Domain Structure: Seeds root domain
DC=domain,DC=com. - Organizational Units:
OU=Users,OU=Groups,OU=Computers,OU=IT,OU=HR,OU=Finance. - Standard Users: Generates standard accounts with attributes (
cn,sAMAccountName,userPrincipalName,mail,givenName,sn,objectSid,userAccountControl). - Security Groups: Seeds domain groups (
Domain Admins,Domain Users,VPN Users,HR Staff,Finance Managers). - Computer Objects: Seeds workstation and server entries.
- Group Memberships: Links users to groups (
memberandmemberOfattributes). - RID Allocation: Thread-safe RID counters starting at:
- Users:
1000 - Groups:
2000 - Computers:
3000 - Other Objects:
4000
- Users:
Build, Test & Package
Prerequisites
- .NET SDK 10.0 or newer installed.
- PowerShell or standard Linux/macOS command shell.
Building the Project
dotnet build adapters/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock.csproj -c Release
Running the Test Suite
Run the full repository test suite that consumes LdapHelperMock:
dotnet test tests/Bitai.LDAPHelper.Tests/Bitai.LDAPHelper.Tests.csproj -c Release
Creating the NuGet Package
dotnet pack adapters/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock/Bitai.LDAPHelper.LdapAdapters.LdapHelperMock.csproj -c Release -o ./artifacts
Observability & Diagnostic Assertions
When debugging unit or integration tests, MockLdapPersistentConnectionAdapter provides diagnostic tracking properties:
// Inspect entries created during the test run
List<MockLdapEntryAdapter> created = mockConnection.CreatedEntries;
// Inspect all entry modifications executed
List<MockModification> mods = mockConnection.Modifications;
// Inspect deleted distinguished names
List<string> deleted = mockConnection.DeletedEntries;
MockLdapDataSeeder accepts an ILogger<MockLdapDataSeeder> parameter, emitting structured log statements during data population for full visibility in console outputs or test runner logs.
Troubleshooting
| Issue / Symptom | Probable Cause | Recommended Fix |
|---|---|---|
ConnectAsync throws Invalid server connection! |
host parameter is empty, "unknown", or "0.0.0.0", or port <= 0. |
Provide a valid non-empty host string and positive port number (e.g. "localhost", 389). |
BindAsync throws Invalid credentials! |
Credentials contain "hacker" or "123456", or are null/empty. |
Use valid non-blacklisted mock credentials. |
SearchAsync returns empty results in standard mode |
The filter string was not registered via AddSearchResult. |
Call mockConnection.AddSearchResult(filterPattern, entries) before executing queries. |
| Search returns empty results in persistent mode | MockLdapDataSeeder was not executed, or filter attribute is unsupported. |
Execute seeder.SeedAllData() prior to testing. Ensure filter matches supported attributes (sAMAccountName, distinguishedName, cn, objectSid, objectClass). |
ServerCertificateValidationByPass throws InvalidOperationException |
Method called on mock class. | Certificate validation bypass is not applicable in mock mode. Remove call in test setups. |
Security & Operational Guidance
- Mock Credentials: Never hardcode or commit production passwords or sensitive Active Directory credentials into test scripts or seeder logic.
- Deterministic Testing: Use fixed seeds or reset
MockLdapDataStore.Instance.Clear()between tests to prevent inter-test state contamination. - Non-Production Purpose: This package is explicitly designed for testing, CI/CD, and local prototyping. Do not deploy mock adapters to production environments.
License
This project is licensed under the MIT License. See LICENSE.md for details.
© 2026 BITAI. All rights reserved.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Bitai.LDAPHelper (>= 10.2.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.