Files
Luna/Luna.Channels.Tests/IntegrationTests.cs
T
darman f21d55d87d Add channel system unit and integration tests
Comprehensive NUnit test suite covering ChannelManager, CliChannel,
TelegramChannel, DI registration, and integration scenarios. Uses
Moq for mocking with test stubs for isolated unit testing.
2026-04-04 04:12:24 +02:00

193 lines
8.1 KiB
C#

using Luna.Channels.Abstractions;
using Luna.Channels.Tests.Stubs;
using Luna.Core.Abstractions;
using Luna.Shared;
using Moq;
namespace Luna.Channels.Tests;
[TestFixture]
[Category("Unit")]
public class IntegrationTests
{
private Mock<ISessionManager> _sessionManagerMock = null!;
private Mock<IChannel> _channelMock = null!;
private ChannelManagerStub _manager = null!;
[SetUp]
public void SetUp()
{
_sessionManagerMock = new Mock<ISessionManager>();
_channelMock = new Mock<IChannel>();
_channelMock.Setup(c => c.ChannelId).Returns("test-ch-1");
_channelMock.Setup(c => c.ChannelType).Returns("test");
_channelMock.Setup(c => c.SendStreamingMessageAsync(
It.IsAny<IAsyncEnumerable<ChatStreamUpdate>>(),
It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
_manager = new ChannelManagerStub(_sessionManagerMock.Object);
}
private static async IAsyncEnumerable<ChatStreamUpdate> BuildStream(params ChatStreamUpdate[] updates)
{
foreach (var u in updates)
{
yield return u;
await Task.Yield();
}
}
[Test]
public async Task MessageReceived_RoutesToSessionManager_WithCorrectContent()
{
var message = new ChannelMessage(
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "user-1", "User One", "Hello Luna!", DateTimeOffset.UtcNow);
_sessionManagerMock
.Setup(s => s.RouteMessagesAsync(
message.Content,
message.ConversationId.ToString(),
It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.Returns(BuildStream(new ChatStreamUpdate(ChatStreamUpdateType.Text, "Hi!", null, false)));
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
var args = new ChannelMessageReceivedEventArgs { Message = message, Channel = _channelMock.Object };
_channelMock.Raise(c => c.MessageReceived += null, _channelMock.Object, args);
await Task.Delay(50);
_sessionManagerMock.Verify(s => s.RouteMessagesAsync(
message.Content,
message.ConversationId.ToString(),
It.IsAny<string>(),
It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task MessageReceived_PipesSessionResponseToChannel_SendStreamingMessageAsync()
{
var message = new ChannelMessage(
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "user-2", "User Two", "What time is it?", DateTimeOffset.UtcNow);
var responseStream = BuildStream(
new ChatStreamUpdate(ChatStreamUpdateType.Text, "It's ", null, false),
new ChatStreamUpdate(ChatStreamUpdateType.Text, "noon.", null, false),
new ChatStreamUpdate(ChatStreamUpdateType.Complete, null, null, true));
_sessionManagerMock
.Setup(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(responseStream);
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
var args = new ChannelMessageReceivedEventArgs { Message = message, Channel = _channelMock.Object };
_channelMock.Raise(c => c.MessageReceived += null, _channelMock.Object, args);
await Task.Delay(50);
_channelMock.Verify(c => c.SendStreamingMessageAsync(
It.IsAny<IAsyncEnumerable<ChatStreamUpdate>>(),
It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task MessageReceived_WhenIsHandled_True_DoesNotRouteToSessionManager()
{
var message = new ChannelMessage(
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "user-3", "User Three", "/start", DateTimeOffset.UtcNow);
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
var args = new ChannelMessageReceivedEventArgs { Message = message, Channel = _channelMock.Object, IsHandled = true };
_channelMock.Raise(c => c.MessageReceived += null, _channelMock.Object, args);
await Task.Delay(50);
_sessionManagerMock.Verify(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()),
Times.Never);
}
[Test]
public async Task TwoChannels_ReceiveMessagesIndependently_BothRoutedToSessionManager()
{
var channel2Mock = new Mock<IChannel>();
channel2Mock.Setup(c => c.ChannelId).Returns("test-ch-2");
channel2Mock.Setup(c => c.ChannelType).Returns("test");
channel2Mock.Setup(c => c.SendStreamingMessageAsync(
It.IsAny<IAsyncEnumerable<ChatStreamUpdate>>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);
_sessionManagerMock
.Setup(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(BuildStream(new ChatStreamUpdate(ChatStreamUpdateType.Text, "ok", null, true)));
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
_manager.RegisterChannel(channel2Mock.Object);
var msg1 = new ChannelMessage(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "u1", "U1", "Msg1", DateTimeOffset.UtcNow);
var msg2 = new ChannelMessage(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "u2", "U2", "Msg2", DateTimeOffset.UtcNow);
_channelMock.Raise(c => c.MessageReceived += null, _channelMock.Object,
new ChannelMessageReceivedEventArgs { Message = msg1, Channel = _channelMock.Object });
channel2Mock.Raise(c => c.MessageReceived += null, channel2Mock.Object,
new ChannelMessageReceivedEventArgs { Message = msg2, Channel = channel2Mock.Object });
await Task.Delay(100);
_sessionManagerMock.Verify(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()),
Times.Exactly(2));
}
[Test]
public async Task SessionManager_RouteMessagesAsync_ReceivesCancellationToken()
{
using var cts = new CancellationTokenSource();
var message = new ChannelMessage(
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "user-4", "User Four", "Cancel me", DateTimeOffset.UtcNow);
_sessionManagerMock
.Setup(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), cts.Token))
.Returns(BuildStream(new ChatStreamUpdate(ChatStreamUpdateType.Text, "x", null, true)));
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
var args = new ChannelMessageReceivedEventArgs { Message = message, Channel = _channelMock.Object };
await _channelMock.RaiseAsync(c => c.MessageReceived += null, _channelMock.Object, args);
await Task.Delay(50, cts.Token);
_sessionManagerMock.Verify(s => s.RouteMessagesAsync(
message.Content,
message.ConversationId.ToString(),
It.IsAny<string>(),
It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task SessionManager_Throws_ChannelRemainsActive_NotDisconnected()
{
var message = new ChannelMessage(
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "user-5", "User Five", "Boom", DateTimeOffset.UtcNow);
_sessionManagerMock
.Setup(s => s.RouteMessagesAsync(
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Throws(new InvalidOperationException("Simulated routing failure"));
_manager.RegisterChannel(new CliChannelStub { ChannelId = "test-1" });
Assert.DoesNotThrowAsync(async () =>
{
var args = new ChannelMessageReceivedEventArgs { Message = message, Channel = _channelMock.Object };
await _channelMock.RaiseAsync(c => c.MessageReceived += null, _channelMock.Object, args);
await Task.Delay(50);
});
}
}