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 sessionManagerMock = null!; private Mock channelMock = null!; private ChannelManagerStub manager = null!; [SetUp] public void SetUp() { sessionManagerMock = new Mock(); channelMock = new Mock(); channelMock.Setup(c => c.ChannelId).Returns("test-ch-1"); channelMock.Setup(c => c.ChannelType).Returns("test"); channelMock.Setup(c => c.SendStreamingMessageAsync( It.IsAny>(), It.IsAny())).Returns(Task.CompletedTask); manager = new ChannelManagerStub(sessionManagerMock.Object); } private static async IAsyncEnumerable 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, It.IsAny(), It.IsAny())) .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, It.IsAny(), It.IsAny()), 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(), It.IsAny(), It.IsAny(), It.IsAny())) .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>(), It.IsAny()), 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(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Test] public async Task TwoChannels_ReceiveMessagesIndependently_BothRoutedToSessionManager() { var channel2Mock = new Mock(); channel2Mock.Setup(c => c.ChannelId).Returns("test-ch-2"); channel2Mock.Setup(c => c.ChannelType).Returns("test"); channel2Mock.Setup(c => c.SendStreamingMessageAsync( It.IsAny>(), It.IsAny())) .Returns(Task.CompletedTask); sessionManagerMock .Setup(s => s.RouteMessagesAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .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(), It.IsAny(), It.IsAny(), It.IsAny()), 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(), It.IsAny(), It.IsAny(), 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, It.IsAny(), It.IsAny()), 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(), It.IsAny(), It.IsAny(), It.IsAny())) .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); }); } }