Files
Deal/src/ai-service/Deal.Ai.Tests/Support/JsonExtractorTests.cs
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
2026-09-11 23:56:47 +03:00

99 lines
3.1 KiB
C#

using System.Text.Json.Nodes;
using Deal.Ai.Llm;
namespace Deal.Ai.Tests.Support;
/// <summary>
/// Unit-тесты извлечения JSON из ответа модели
/// </summary>
public sealed class JsonExtractorTests
{
/// <summary>
/// Чистая JSON-строка объекта разбирается как есть.
/// </summary>
[Fact]
public void TryExtractObject_PlainObject_Parses()
{
JsonObject? json = JsonExtractor.TryExtractObject("""{"pass": true, "reason": "ок"}""");
Assert.NotNull(json);
Assert.True((bool)json!["pass"]!);
}
/// <summary>
/// Markdown-обёртка ```json снимается
/// </summary>
[Fact]
public void TryExtractObject_JsonFence_Unwraps()
{
JsonObject? json = JsonExtractor.TryExtractObject("```json\n{\"fit\": 1}\n```");
Assert.NotNull(json);
Assert.Equal(1, (int)json!["fit"]!);
}
/// <summary>
/// Обёртка без метки языка и с прозой вокруг — тоже снимается.
/// </summary>
[Fact]
public void TryExtractObject_PlainFenceWithProse_Unwraps()
{
const string answer = "Вот результат:\n```\n{\"keywords\": [\"php\"]}\n```\nНадеюсь, поможет.";
JsonObject? json = JsonExtractor.TryExtractObject(answer);
Assert.NotNull(json);
Assert.Single((JsonArray)json!["keywords"]!);
}
/// <summary>
/// JSON внутри произвольного текста берётся срезом между первой «{» и последней «}».
/// </summary>
[Fact]
public void TryExtractObject_JsonInsideProse_Slices()
{
JsonObject? json = JsonExtractor.TryExtractObject("модель решила: {\"pass\": false} конец");
Assert.NotNull(json);
Assert.False((bool)json!["pass"]!);
}
/// <summary>
/// Текст без JSON — null
/// </summary>
[Fact]
public void TryExtractObject_NoJson_ReturnsNull()
{
Assert.Null(JsonExtractor.TryExtractObject("извините, не могу ответить"));
}
/// <summary>
/// Незакрытая обёртка и битый JSON — null, а не исключение.
/// </summary>
[Fact]
public void TryExtractObject_BrokenJson_ReturnsNull()
{
Assert.Null(JsonExtractor.TryExtractObject("```json\n{\"pass\": tru"));
Assert.Null(JsonExtractor.TryExtractObject("{\"pass\": "));
}
/// <summary>
/// JSON верхнего уровня не объект
/// </summary>
[Fact]
public void TryExtractObject_NonObjectRoot_ReturnsNull()
{
Assert.Null(JsonExtractor.TryExtractObject("[\"php\", \"c#\"]"));
Assert.Null(JsonExtractor.TryExtractObject("\"просто строка\""));
}
/// <summary>
/// Пустой/пробельный текст — null.
/// </summary>
[Fact]
public void TryExtractObject_Empty_ReturnsNull()
{
Assert.Null(JsonExtractor.TryExtractObject(string.Empty));
Assert.Null(JsonExtractor.TryExtractObject(" "));
}
}