Files
EchoHub/docs/auriondocs/Code/src/EchoHub.Tests/Irc/TestHelpers.cs.md
T
Hue 607217b314 docs: Update documentation for 145 files
Generated by AurionDocs
Job ID: 934f8c39-8082-4942-8d17-72ed8f5f8d50
Source commit: 40aea9a
2026-07-23 11:44:20 +02:00

14 KiB

TestHelpers.cs

Source: src/EchoHub.Tests/Irc/TestHelpers.cs

Contents


FakeChannelService

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal sealed class FakeChannelService : IChannelService

A test double that implements IChannelService and lets tests control the results of channel-related operations by setting public properties. Use FakeChannelService in unit tests when you need a simple, configurable implementation of IChannelService without using a mocking framework.

Remarks

FakeChannelService is a lightweight, stateful fake intended for unit tests. Each operation returns the value of a corresponding public property (for example, CreateResult, UpdateTopicResult, DeleteResult), or a sensible default when a property is not set. This makes it easy to simulate success, failure, and edge cases for callers of IChannelService without wiring up a full service or external dependencies. EnsureSystemChannelAsync will return SystemChannelToReturn if set; otherwise it constructs a fallback ChannelDto with deterministic fields (a new Guid, the supplied channelName/topic, DateTimeOffset.UnixEpoch, and other boolean flags as shown in the implementation).

Example

// Arrange
var fake = new FakeChannelService();
var createdChannel = new ChannelDto(Guid.NewGuid(), "room", null, false, 0, DateTimeOffset.UtcNow, false, false, false);
fake.CreateResult = ChannelOperationResult.Success(createdChannel);

// Act
var result = await fake.CreateChannelAsync(Guid.NewGuid(), "room", null, true);

// Assert
if (!result.IsSuccess) throw new Exception("expected success");

Notes

  • The fake exposes mutable public properties; tests must set the appropriate property (for example CreateResult or UpdateTopicResult) before invoking the corresponding method. Properties are read/write and not thread-safe.
  • Several methods return a failure when their result property is unset: operations like CreateChannelAsync, UpdateTopicAsync, SetChannelPasswordAsync, RekeyChannelAsync, and DeleteChannelAsync return ChannelOperationResult.Fail(ChannelError.ValidationFailed, "Not configured") unless the corresponding property is provided. Tests that expect success must assign a matching ChannelOperationResult first.
  • The XML summary suggests the fake "records method calls," but the implementation only exposes configurable return properties and does not record call history. If call-count or argument inspection is required, extend the fake (or use a mocking library) to capture that information.

FakeChatService

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal sealed class FakeChatService : IChatService

Fake test double implementing IChatService that records every call into in-memory lists and returns configurable, pre-set responses. Use FakeChatService in unit or integration tests when you need to assert which IChatService methods were invoked or control what the hub/client sees without running a real chat backend.

Remarks

FakeChatService is a combined stub-and-spy: each IChatService method either appends a record to one of the public lists (for later assertions) and/or returns the values exposed on its configurable properties. This lets tests both (a) inject specific return values such as HistoryToReturn, JoinError, SendMessageError, ChannelsForUserToReturn, and OnlineUsersToReturn, and (b) verify side-effects by inspecting ConnectedUsers, DisconnectedConnections, JoinedChannels, LeftChannels, SentMessages, StatusUpdates, and JoinKeys. It implements the full IChatService surface so it can be passed anywhere the production service is expected without additional shimming.

Example

// Arrange
var fake = new FakeChatService();
var sampleMessage = new MessageDto(/* ... */); // construct as needed
fake.HistoryToReturn = new List<MessageDto> { sampleMessage };
fake.JoinPasswordRequired = true;

// Act
var joinResult = await fake.JoinChannelAsync("conn-1", Guid.NewGuid(), "alice", "general", "secret");
await fake.SendMessageAsync(Guid.NewGuid(), "alice", "general", "hello world");

// Assert (inspect recorded calls and configured return)
// joinResult.History contains the configured MessageDto
// fake.JoinedChannels contains ("general", "alice")
// fake.JoinKeys contains the supplied password "secret"
// fake.SentMessages contains ("general", "hello world")

Notes

  • FakeChatService is not thread-safe: all recorded collections are plain List<T> instances and are mutated without synchronization. Reset or recreate the instance between parallel tests.
  • Several methods ignore some input parameters: for example, GetChannelHistoryAsync always returns HistoryToReturn and does not use the count or offset arguments; tests relying on real paging behavior will not be exercised by this fake.
  • Default behaviors are simple and explicit: UserDisconnectedAsync returns null by default, SendMessageAsync returns whatever SendMessageError is set to, and the various Broadcast* methods are no-ops. Tests that need side-effects from broadcasts must simulate them explicitly.

FakeEncryptionService

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal sealed class FakeEncryptionService : IMessageEncryptionService

FakeEncryptionService is a compact, test-oriented implementation that simulates encryption by prefixing plaintext with a fixed marker. It implements IMessageEncryptionService and is intended for test scenarios where deterministic, invertible behavior is enough to exercise encryption flows without pulling in real cryptography. Encrypt("hello") produces "$ENC$hello", and Decrypt("$ENC$hello") returns the original text. If a value to decrypt does not start with the expected prefix, Decrypt simply returns the input unchanged. The nullable helpers EncryptNullable and DecryptNullable mirror the non-nullable versions, preserving null semantics. The EncryptDatabaseEnabled flag is always true in this fake, enabling components that check encryption per database usage to behave consistently in tests.

Remarks

Designed as a lightweight test double, this class enforces the IMessageEncryptionService contract while avoiding real crypto. It makes the encryption step observable through a constant prefix, enabling tests to locate and verify encrypted payloads, and to simulate database encryption paths via EncryptDatabaseEnabled. By keeping it internal and sealed, the implementation is deliberately opaque to prevent accidental misuse outside tests and to preserve a predictable test surface.

Notes

  • This is a non-secure stub and should never be used for production encryption or storage.
  • Because the type is internal, it is intended for test code within the same assembly (or a friend-accessible setup). If you need to reference it from production-like tests, ensure appropriate internals visibility is configured.

FakeUserService

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal sealed class FakeUserService : IUserService

Lightweight test double implementing IUserService that lets tests control return values and observe calls. Set the public properties AuthResult, RegisterResult, and ProfileToReturn to force specific outcomes; inspect RegisterInviteCodes to assert which inviteCode values were passed to RegisterUserAsync.

Remarks

FakeUserService exists as an in-memory test helper to avoid exercising real persistence or external systems. All IUserService methods return completed tasks via Task.FromResult, so calls are synchronous from the test's perspective and easy to arrange. Use SuccessResult to construct a successful UserOperationResult containing a UserProfileDto (it sets UserStatus.Online, ServerRole.Member, and timestamps using DateTimeOffset.UtcNow). The class is intended for unit tests where you need deterministic control over authentication/registration/profile responses and simple verification of arguments.

Example

// Arrange
var svc = new FakeUserService();
var userId = Guid.NewGuid();
svc.AuthResult = FakeUserService.SuccessResult(userId, "alice");
svc.RegisterResult = UserOperationResult.Fail(UserError.AlreadyExists, "already");

// Act
var auth = await svc.AuthenticateUserAsync("alice", "pw");
await svc.RegisterUserAsync("bob", "pw", inviteCode: "INV-123");

// Assert
if (!auth.IsSuccess) throw new Exception("expected success");
// Verify that the invite code passed to RegisterUserAsync was recorded
if (svc.RegisterInviteCodes.Count != 1 || svc.RegisterInviteCodes[0] != "INV-123")
    throw new Exception("invite code not recorded");

Notes

  • RegisterInviteCodes is a plain List<string?> that records the raw inviteCode argument (including null) in call order and is not synchronized; concurrent test runs must not share a single instance without synchronization.
  • SuccessResult populates timestamps using DateTimeOffset.UtcNow, so created UserProfileDto instances will have varying timestamp values; avoid strict equality checks against fixed timestamps.
  • UpdateProfileAsync and SetAvatarAsync always return a failure (UserError.NotFound with message "Not configured") unless the test replaces these behaviors; they are placeholders rather than functioning update/asset implementations.

TestDuplexStream

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal sealed class TestDuplexStream : Stream

A lightweight in-memory duplex Stream intended for tests: it exposes a readable input buffer (populated from the input constructor argument) and a separate writable output buffer that callers can inspect via GetOutput and GetOutputLines. Use TestDuplexStream when you need to inject deterministic input into code that reads from a Stream and capture what that code writes, without opening real network sockets or files.

Remarks

TestDuplexStream intentionally implements only the minimal Stream surface needed for typical read/write/flush scenarios in tests. Reads come from the private _readBuffer initialized from the constructor input, while writes are appended to the private _writeBuffer and later returned by GetOutput. The class is not seekable (seeking, Length, and Position throw NotSupportedException) because the read and write sides are logically independent buffers rather than a single random-access backing store. The implementation disposes both internal buffers in Dispose(bool) so test code should treat a disposed TestDuplexStream as unusable.

Notes

  • GetOutput strips a leading UTF-8 BOM (the code calls TrimStart('\uFEFF')); that handles writers that emit a BOM but also means a deliberate leading U+FEFF in written data will be removed.
  • GetOutputLines splits on the literal CR+LF sequence ("\r\n") and uses StringSplitOptions.RemoveEmptyEntries, so lone "\n" line endings or blank lines may not be handled as callers expect.
  • Seeking and length-related members are not supported: Seek, SetLength, Position and Length throw NotSupportedException.
  • Reading consumes the provided input buffer; once Read/ReadAsync drain the _readBuffer, subsequent reads return 0 (end-of-stream) unless a new instance is created.
  • The class is a test helper and does not provide synchronization for concurrent callers; concurrent reads/writes from multiple threads are not guaranteed to be safe.

TestIrcConnectionFactory

File: src/EchoHub.Tests/Irc/TestHelpers.cs
Kind: class

internal static class TestIrcConnectionFactory

TestIrcConnectionFactory is an internal static test helper that creates IrcClientConnection instances backed by a TestDuplexStream for unit testing. It provides two entry points: Create, which builds a new connection wired to an in-memory duplex stream seeded with the supplied input lines; and CreateAuthenticated, which builds a pre-authenticated, registered connection by populating identity fields and authentication flags. The input lines are joined with and a trailing is appended if any lines are provided, simulating lines received from an IRC server. The returned tuple gives tests both the IrcClientConnection and the TestDuplexStream, enabling observation of outgoing data and control of inbound data.