From 3942e1aa3354eed79c64237c016d92370b68e733 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Tue, 7 Oct 2025 10:07:07 +0300 Subject: [PATCH] Initial tests for AI package --- framework/Volo.Abp.slnx | 5 +- .../Volo.Abp.AI.Tests.abppkg | 3 + .../Volo.Abp.AI.Tests.csproj | 18 ++++ .../Volo/Abp/AI/AbpAITestModule.cs | 40 +++++++++ .../Volo/Abp/AI/ChatClient_Tests.cs | 89 +++++++++++++++++++ .../Volo/Abp/AI/Mocks/MockChatClient.cs | 44 +++++++++ .../Abp/AI/Mocks/MockDefaultChatClient.cs | 4 + .../Volo/Abp/AI/Workspaces/WordCounter.cs | 7 ++ 8 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.abppkg create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.csproj create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/AbpAITestModule.cs create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/ChatClient_Tests.cs create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockChatClient.cs create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockDefaultChatClient.cs create mode 100644 framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Workspaces/WordCounter.cs diff --git a/framework/Volo.Abp.slnx b/framework/Volo.Abp.slnx index ab63be61ea..1289991520 100644 --- a/framework/Volo.Abp.slnx +++ b/framework/Volo.Abp.slnx @@ -1,5 +1,7 @@ + + @@ -164,12 +166,11 @@ - - + diff --git a/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.abppkg b/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.abppkg new file mode 100644 index 0000000000..a686451fbc --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.abppkg @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.csproj b/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.csproj new file mode 100644 index 0000000000..c67a9b8a0d --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo.Abp.AI.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + net10.0 + Volo.Abp.AI.Tests + Volo.Abp.AI.Tests + + + + + + + + + + diff --git a/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/AbpAITestModule.cs b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/AbpAITestModule.cs new file mode 100644 index 0000000000..2de265db92 --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/AbpAITestModule.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.AI; +using Volo.Abp.AI; +using Volo.Abp.AI.Mocks; +using Volo.Abp.AI.Tests.Workspaces; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AutoMapper; + +[DependsOn( + typeof(AbpTestBaseModule), + typeof(AbpAIModule) +)] +public class AbpAITestModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(options => + { + options.Workspaces.ConfigureDefault(options => + { + options.ConfigureChatClient(clientOptions => + { + clientOptions.Builder = new ChatClientBuilder(new MockDefaultChatClient()); + }); + }); + + options.Workspaces.Configure(workspaceOptions => + { + workspaceOptions.ConfigureChatClient(clientOptions => + { + clientOptions.Builder = new ChatClientBuilder(new MockChatClient()); + }); + }); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + } +} diff --git a/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/ChatClient_Tests.cs b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/ChatClient_Tests.cs new file mode 100644 index 0000000000..6e5c0e02ee --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/ChatClient_Tests.cs @@ -0,0 +1,89 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.AI; +using Shouldly; +using Volo.Abp.AI.Mocks; +using Volo.Abp.AI.Tests.Workspaces; +using Volo.Abp.AutoMapper; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.AI.Tests; + +public class ChatClient_Tests : AbpIntegratedTest +{ + [Fact] + public void Should_Resolve_ChatClient_For_Workspace() + { + // Arrange & Act + var chatClient = GetRequiredService>(); + + // Assert + chatClient.ShouldNotBeNull(); + chatClient.ShouldNotBeOfType(); + } + + [Fact] + public void Should_Resolve_Keyed_ChatClient_For_Workspace() + { + // Arrange + var workspaceName = WorkspaceNameAttribute.GetWorkspaceName(); + var serviceName = AbpAIWorkspaceOptions.GetChatClientServiceKeyName(workspaceName); + + // Act + var chatClient = GetRequiredKeyedService( + serviceName + ); + + // Assert + chatClient.ShouldNotBeNull(); + } + + [Fact] + public void Should_Resolve_Default_ChatClient() + { + // Arrange & Act + var chatClient = GetRequiredService(); + + // Assert + chatClient.ShouldNotBeNull(); + chatClient.ShouldBeOfType(); + } + + [Fact] + public async Task Should_Get_Response_For_Workspace() + { + // Arrange + var chatClient = GetRequiredService>(); + + // Act + var response = await chatClient.GetResponseAsync(new[] + { + new ChatMessage(ChatRole.User, "Hello, how are you?") + }); + + // Assert + response.ShouldNotBeNull(); + response.Messages.ShouldNotBeEmpty(); + } + + [Fact] + public async Task Should_Get_Streaming_Response_For_Workspace() + { + // Arrange + var chatClient = GetRequiredService>(); + var messagesInput = new[] + { + new ChatMessage(ChatRole.User, "Hello, how are you?") + }; + + // Act + var responseParts = 0; + await foreach (var response in chatClient.GetStreamingResponseAsync(messagesInput)) + { + responseParts++; + } + + // Assert + responseParts.ShouldBe(MockChatClient.StreamingResponseParts); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockChatClient.cs b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockChatClient.cs new file mode 100644 index 0000000000..6e3b00f1a9 --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockChatClient.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; + +namespace Volo.Abp.AI.Mocks; + +public class MockChatClient : IChatClient +{ + public const int StreamingResponseParts = 12; + public void Dispose() + { + + } + + public Task GetResponseAsync(IEnumerable messages, ChatOptions options = null, CancellationToken cancellationToken = default) + { + var responseMessages = messages.ToList(); + responseMessages.Add(new ChatMessage(ChatRole.Assistant, "This is a mock response.")); + return Task.FromResult(new ChatResponse + { + Messages = responseMessages, + }); + } + + public object GetService(Type serviceType, object serviceKey = null) + { + return null; + } + + public async IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions options = null, CancellationToken cancellationToken = default) + { + for (var i = 0; i < StreamingResponseParts; i++) + { + await Task.Delay(25, cancellationToken); + yield return new ChatResponseUpdate + { + RawRepresentation = "This is a mock streaming response part " + (i + 1) + }; + } + } +} diff --git a/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockDefaultChatClient.cs b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockDefaultChatClient.cs new file mode 100644 index 0000000000..42ff57fd7e --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Mocks/MockDefaultChatClient.cs @@ -0,0 +1,4 @@ +namespace Volo.Abp.AI.Mocks; +public class MockDefaultChatClient : MockChatClient +{ +} diff --git a/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Workspaces/WordCounter.cs b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Workspaces/WordCounter.cs new file mode 100644 index 0000000000..0e0a4c81b1 --- /dev/null +++ b/framework/test/Volo.Abp.AI.Tests/Volo/Abp/AI/Workspaces/WordCounter.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AI.Tests.Workspaces; + +[WorkspaceName("WordCounter")] +public class WordCounter +{ + +} \ No newline at end of file