Files
UniVerse/backend/UniVerse.Api.Tests/Reviews/ReviewPromptServiceTests.cs
T
serega404 935e4ed37a
Backend CI / build-and-test (push) Failing after 11m26s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Failing after 14m2s
Frontend CI / build-and-check (push) Failing after 19m55s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14m7s
🚀 Create and publish a Docker image / Build & publish backend image (push) Failing after 14m59s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Failing after 15m0s
feat: добавил изменение промта для админа
2026-05-21 21:58:33 +03:00

139 lines
4.9 KiB
C#

using System.Net;
using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using UniVerse.Application.DTOs.Reviews;
using UniVerse.Application.Interfaces;
using UniVerse.Application.Prompts;
using UniVerse.Domain.Exceptions;
using UniVerse.Infrastructure.Data;
using UniVerse.Infrastructure.ExternalServices;
using UniVerse.Infrastructure.Services;
using Xunit;
namespace UniVerse.Api.Tests.Reviews;
public class ReviewPromptServiceTests
{
[Fact]
public async Task GetAsync_ReturnsDefaultPrompt_WhenSettingDoesNotExist()
{
await using var db = CreateDbContext();
var service = new ReviewPromptService(db);
var result = await service.GetAsync();
Assert.Equal(ReviewPromptTemplate.Default, result.Prompt);
Assert.Null(result.UpdatedAt);
}
[Fact]
public async Task UpdateAsync_UpsertsSingletonPrompt()
{
await using var db = CreateDbContext();
var service = new ReviewPromptService(db);
await service.UpdateAsync(new UpdateReviewPromptRequest("First {lectureContext} {reviewText}"));
var result = await service.UpdateAsync(new UpdateReviewPromptRequest("Second {lectureContext} {reviewText}"));
Assert.Equal("Second {lectureContext} {reviewText}", result.Prompt);
Assert.NotNull(result.UpdatedAt);
Assert.Equal(1, await db.ReviewPromptSettings.CountAsync());
Assert.Equal("Second {lectureContext} {reviewText}", (await service.GetAsync()).Prompt);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("Prompt without placeholders")]
[InlineData("Only lecture {lectureContext}")]
[InlineData("Only review {reviewText}")]
public async Task UpdateAsync_RejectsInvalidPrompt(string prompt)
{
await using var db = CreateDbContext();
var service = new ReviewPromptService(db);
await Assert.ThrowsAsync<BadRequestException>(() =>
service.UpdateAsync(new UpdateReviewPromptRequest(prompt)));
}
[Fact]
public async Task AnalyzeReviewAsync_RendersCustomPrompt()
{
var handler = new CapturingHandler();
var http = new HttpClient(handler)
{
BaseAddress = new Uri("https://llm.test/")
};
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Llm:Model"] = "test-model",
["Llm:ApiKey"] = "test-key"
})
.Build();
var promptService = Substitute.For<IReviewPromptService>();
promptService.GetAsync().Returns(new ReviewPromptDto(
"Custom prompt. Context: {lectureContext}. Text: {reviewText}",
DateTime.UtcNow));
var client = new LlmClient(http, config, promptService, NullLogger<LlmClient>.Instance);
await client.AnalyzeReviewAsync("Very useful review", "Lecture: Algebra");
Assert.NotNull(handler.RequestBody);
using var requestJson = JsonDocument.Parse(handler.RequestBody!);
var content = requestJson.RootElement
.GetProperty("messages")[0]
.GetProperty("content")
.GetString();
Assert.Contains("Custom prompt", content);
Assert.Contains("Lecture: Algebra", content);
Assert.Contains("Very useful review", content);
Assert.DoesNotContain(ReviewPromptTemplate.LectureContextPlaceholder, content);
Assert.DoesNotContain(ReviewPromptTemplate.ReviewTextPlaceholder, content);
}
private static AppDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase($"ReviewPromptServiceTests_{Guid.NewGuid()}")
.Options;
return new AppDbContext(options);
}
private sealed class CapturingHandler : HttpMessageHandler
{
public string? RequestBody { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
RequestBody = request.Content is null
? null
: await request.Content.ReadAsStringAsync(cancellationToken);
const string responsePayload = """
{
"choices": [
{
"message": {
"content": "{\"qualityScore\":0.8,\"sentiment\":\"Positive\",\"tags\":[\"practice\"],\"isInformative\":true}"
}
}
]
}
""";
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responsePayload, Encoding.UTF8, "application/json")
};
}
}
}