mirror of
https://github.com/Shchoholiev/shopping-assistant-api.git
synced 2025-04-04 16:49:36 +00:00
added the initial implementation of the method SearchProductAsync and a rough test for it
This commit is contained in:
parent
c5fe0c1d5f
commit
ba116a3533
@ -10,6 +10,7 @@ using ShoppingAssistantApi.Application.Models.OpenAi;
|
||||
using ShoppingAssistantApi.Application.Models.ProductSearch;
|
||||
using ShoppingAssistantApi.Domain.Entities;
|
||||
using ShoppingAssistantApi.Domain.Enums;
|
||||
using ServerSentEvent = ShoppingAssistantApi.Application.Models.ProductSearch.ServerSentEvent;
|
||||
|
||||
namespace ShoppingAssistantApi.Infrastructure.Services;
|
||||
|
||||
@ -18,6 +19,7 @@ public class ProductService : IProductService
|
||||
private readonly IWishlistsService _wishlistsService;
|
||||
|
||||
private readonly IOpenAiService _openAiService;
|
||||
|
||||
|
||||
public ProductService(IOpenAiService openAiService, IWishlistsService wishlistsService)
|
||||
{
|
||||
@ -25,13 +27,127 @@ public class ProductService : IProductService
|
||||
_wishlistsService = wishlistsService;
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<ServerSentEvent> SearchProductAsync(string wishlistId, MessageCreateDto message, CancellationToken cancellationToken)
|
||||
public async IAsyncEnumerable<ServerSentEvent> SearchProductAsync(string wishlistId, MessageCreateDto message, CancellationToken cancellationToken)
|
||||
{
|
||||
// Documentation: https://shchoholiev.atlassian.net/l/cp/JizkynhU
|
||||
var chatRequest = new ChatCompletionRequest
|
||||
{
|
||||
Messages = new List<OpenAiMessage>
|
||||
{
|
||||
new OpenAiMessage
|
||||
{
|
||||
Role = "User",
|
||||
Content = PromptForProductSearch(message.Text)
|
||||
}
|
||||
},
|
||||
Stream = true
|
||||
};
|
||||
|
||||
var currentDataType = SearchEventType.Wishlist;
|
||||
var dataTypeHolder = string.Empty;
|
||||
|
||||
throw new NotImplementedException();
|
||||
await foreach (var data in _openAiService.GetChatCompletionStream(chatRequest, cancellationToken))
|
||||
{
|
||||
if (data.Contains("["))
|
||||
{
|
||||
dataTypeHolder = string.Empty;
|
||||
dataTypeHolder += data;
|
||||
}
|
||||
|
||||
else if (data.Contains("]"))
|
||||
{
|
||||
dataTypeHolder += data;
|
||||
currentDataType = DetermineDataType(dataTypeHolder);
|
||||
}
|
||||
|
||||
else if (dataTypeHolder=="[" && !data.Contains("["))
|
||||
{
|
||||
dataTypeHolder += data;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
switch (currentDataType)
|
||||
{
|
||||
case SearchEventType.Message:
|
||||
yield return new ServerSentEvent
|
||||
{
|
||||
Event = SearchEventType.Message,
|
||||
Data = data
|
||||
};
|
||||
break;
|
||||
|
||||
case SearchEventType.Suggestion:
|
||||
yield return new ServerSentEvent
|
||||
{
|
||||
Event = SearchEventType.Suggestion,
|
||||
Data = data
|
||||
};
|
||||
break;
|
||||
|
||||
case SearchEventType.Product:
|
||||
yield return new ServerSentEvent
|
||||
{
|
||||
Event = SearchEventType.Product,
|
||||
Data = data
|
||||
};
|
||||
break;
|
||||
|
||||
case SearchEventType.Wishlist:
|
||||
yield return new ServerSentEvent
|
||||
{
|
||||
Event = SearchEventType.Wishlist,
|
||||
Data = data
|
||||
};
|
||||
break;
|
||||
|
||||
}
|
||||
dataTypeHolder = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SearchEventType DetermineDataType(string dataTypeHolder)
|
||||
{
|
||||
if (dataTypeHolder.StartsWith("[Question]"))
|
||||
{
|
||||
return SearchEventType.Message;
|
||||
}
|
||||
else if (dataTypeHolder.StartsWith("[Options]"))
|
||||
{
|
||||
return SearchEventType.Suggestion;
|
||||
}
|
||||
else if (dataTypeHolder.StartsWith("[Message]"))
|
||||
{
|
||||
return SearchEventType.Message;
|
||||
}
|
||||
else if (dataTypeHolder.StartsWith("[Products]"))
|
||||
{
|
||||
return SearchEventType.Product;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SearchEventType.Wishlist;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: remove all methods below
|
||||
public async IAsyncEnumerable<(List<ProductName> ProductNames, WishlistDto Wishlist)> StartNewSearchAndReturnWishlist(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
@ -39,7 +155,7 @@ public class ProductService : IProductService
|
||||
{
|
||||
new OpenAiMessage()
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = PromptForProductSearch(message.Text)
|
||||
}
|
||||
};
|
||||
@ -73,7 +189,7 @@ public class ProductService : IProductService
|
||||
{
|
||||
new OpenAiMessage()
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = PromptForProductSearchWithQuestion(message.Text)
|
||||
}
|
||||
};
|
||||
@ -114,7 +230,7 @@ public class ProductService : IProductService
|
||||
{
|
||||
new OpenAiMessage()
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = PromptForRecommendationsForProductSearch(message.Text)
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Bson;
|
||||
using Moq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@ -28,13 +29,108 @@ public class ProductTests
|
||||
_productService = new ProductService(_openAiServiceMock.Object, _wishListServiceMock.Object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*[Fact]
|
||||
public async Task SearchProductAsync_WhenWishlistIdIsEmpty_CreatesWishlistAndReturnsEvent()
|
||||
{
|
||||
// Arrange
|
||||
string wishlistId = string.Empty; // Simulating an empty wishlist ID
|
||||
var message = new MessageCreateDto
|
||||
{
|
||||
Text = "Your message text here"
|
||||
};
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
// Define your expected new wishlist and event data
|
||||
var newWishlistId = "123"; // Example wishlist ID
|
||||
var expectedEvent = new ServerSentEvent
|
||||
{
|
||||
Event = SearchEventType.Wishlist,
|
||||
Data = newWishlistId
|
||||
};
|
||||
|
||||
// Mock the StartPersonalWishlistAsync method to return the expected wishlist
|
||||
_wishListServiceMock.Setup(x => x.StartPersonalWishlistAsync(It.IsAny<WishlistCreateDto>(), CancellationToken.None))
|
||||
.ReturnsAsync(new WishlistDto
|
||||
{
|
||||
Id = "123",
|
||||
Name = "MacBook",
|
||||
Type = WishlistTypes.Product.ToString(), // Use enum
|
||||
CreatedById = "someId"
|
||||
});
|
||||
|
||||
// Mock the GetChatCompletionStream method to provide SSE data
|
||||
var sseData = new List<string> { "[Question] What is your question?" };
|
||||
_openAiServiceMock.Setup(x => x.GetChatCompletionStream(It.IsAny<ChatCompletionRequest>(), cancellationToken))
|
||||
.Returns(sseData.ToAsyncEnumerable());
|
||||
|
||||
// Act
|
||||
var result = await _productService.SearchProductAsync(wishlistId, message, cancellationToken).ToListAsync();
|
||||
|
||||
// Assert
|
||||
// Check if the first item in the result is the expected wishlist creation event
|
||||
var firstEvent = result.FirstOrDefault();
|
||||
Assert.NotNull(firstEvent);
|
||||
Assert.Equal(expectedEvent.Event, firstEvent.Event);
|
||||
Assert.Equal(expectedEvent.Data, firstEvent.Data);
|
||||
|
||||
// You can add more assertions to verify the other SSE events as needed.
|
||||
}*/
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task SearchProductAsync_WhenWishlistExists_ReturnsExpectedEvents()
|
||||
{
|
||||
// Arrange
|
||||
string wishlistId = "existingWishlistId"; // Simulating an existing wishlist ID
|
||||
var message = new MessageCreateDto
|
||||
{
|
||||
Text = "Your message text here"
|
||||
};
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
// Define your expected SSE data for the test
|
||||
var expectedSseData = new List<string>
|
||||
{
|
||||
"[",
|
||||
"Question",
|
||||
"]",
|
||||
" What",
|
||||
" features",
|
||||
" are",
|
||||
" you",
|
||||
" looking",
|
||||
"?\n",
|
||||
"[",
|
||||
"Options",
|
||||
"]",
|
||||
" USB",
|
||||
"-C"
|
||||
};
|
||||
|
||||
// Mock the GetChatCompletionStream method to provide the expected SSE data
|
||||
_openAiServiceMock.Setup(x => x.GetChatCompletionStream(It.IsAny<ChatCompletionRequest>(), cancellationToken))
|
||||
.Returns(expectedSseData.ToAsyncEnumerable());
|
||||
|
||||
// Act
|
||||
var resultStream = _productService.SearchProductAsync(wishlistId, message, cancellationToken);
|
||||
|
||||
// Convert the result stream to a list of ServerSentEvent
|
||||
var actualSseEvents = await resultStream.ToListAsync();
|
||||
|
||||
// Assert
|
||||
// Check if the actual SSE events match the expected SSE events
|
||||
Assert.Equal(8, actualSseEvents.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StartNewSearchAndReturnWishlist_CreatesWishlistObject()
|
||||
{
|
||||
// Arrange
|
||||
var expectedOpenAiMessage = new OpenAiMessage
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = "{ \"Name\": [{ \"Name\": \"NVIDIA GeForce RTX 3080\" }, { \"Name\": \"AMD Radeon RX 6900 XT\" }] }"
|
||||
};
|
||||
|
||||
@ -93,7 +189,7 @@ public class ProductTests
|
||||
|
||||
var expectedOpenAiMessage = new OpenAiMessage
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = "{ \"Name\": [{ \"Name\": \"NVIDIA GeForce RTX 3080\" }, { \"Name\": \"AMD Radeon RX 6900 XT\" }] }"
|
||||
};
|
||||
|
||||
@ -132,7 +228,7 @@ public class ProductTests
|
||||
|
||||
var expectedOpenAiMessage = new OpenAiMessage
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = "{ \"AdditionalQuestion\": [{ \"QuestionText\": \"What specific MacBook model are you using?\" }," +
|
||||
" { \"QuestionText\": \"Do you have any preferences for brand or capacity?\" }] }"
|
||||
};
|
||||
@ -170,7 +266,7 @@ public class ProductTests
|
||||
|
||||
var expectedOpenAiMessage = new OpenAiMessage
|
||||
{
|
||||
Role = OpenAiRole.User,
|
||||
Role = "User",
|
||||
Content = "{ \"Recommendation\": [\"Recommendation 1\", \"Recommendation 2\"] }"
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Moq" Version="4.20.69" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
|
Loading…
Reference in New Issue
Block a user