Files
tnsu-approval-route/backend/tests/Tnsu.ApprovalRoute.IntegrationTests/Infrastructure/SharePointClientParsingTests.cs
T
2026-08-12 15:12:11 +05:00

100 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.Json;
using Shouldly;
using Tnsu.ApprovalRoute.Infrastructure.SharePoint;
namespace Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
/// <summary>
/// Разбор ответов SharePoint REST — единственная часть SharePointClient, которую
/// можно проверить без живого тенанта: сам HTTP-вызов неотделим от аутентификации
/// по сертификату (ClientCertificateCredential ходит в Azure AD за токеном перед
/// каждым запросом), а вот разбор уже полученного JSON — чистая функция.
///
/// Это самое непроверенное место всей интеграции: форма ответа Search REST
/// (_api/search/query) задокументирована, но подтвердить её точное соответствие
/// без реального тенанта нельзя — только этими тестами на образцах ответа.
/// </summary>
public sealed class SharePointClientParsingTests
{
private static JsonElement Parse(string json) => JsonDocument.Parse(json).RootElement;
[Fact(DisplayName = "Search: с odata=nometadata Rows/Cells приходят голыми массивами")]
public void ParseSearchListItemIds_BareArrays_ReturnsIds()
{
const string response = """
{
"PrimaryQueryResult": {
"RelevantResults": {
"RowCount": 2,
"Table": {
"Rows": [
{ "Cells": [ { "Key": "Rank", "Value": "1" }, { "Key": "ListItemID", "Value": "11" } ] },
{ "Cells": [ { "Key": "ListItemID", "Value": "27" }, { "Key": "Rank", "Value": "2" } ] }
]
}
}
}
}
""";
var ids = SharePointClient.ParseSearchListItemIds(Parse(response));
ids.ShouldBe([11, 27]);
}
[Fact(DisplayName = "Search: без odata=nometadata Rows/Cells завёрнуты в {results: [...]}")]
public void ParseSearchListItemIds_WrappedResults_ReturnsIds()
{
const string response = """
{
"PrimaryQueryResult": {
"RelevantResults": {
"Table": {
"Rows": {
"results": [
{ "Cells": { "results": [ { "Key": "ListItemID", "Value": "42" } ] } }
]
}
}
}
}
}
""";
var ids = SharePointClient.ParseSearchListItemIds(Parse(response));
ids.ShouldBe([42]);
}
[Fact(DisplayName = "Search: нет совпадений — пустой список, а не исключение")]
public void ParseSearchListItemIds_NoRows_ReturnsEmpty()
{
const string response = """{ "PrimaryQueryResult": { "RelevantResults": { "RowCount": 0, "Table": { "Rows": [] } } } } """;
SharePointClient.ParseSearchListItemIds(Parse(response)).ShouldBeEmpty();
}
[Fact(DisplayName = "Search: неожиданная форма ответа не роняет разбор")]
public void ParseSearchListItemIds_UnexpectedShape_ReturnsEmpty()
{
SharePointClient.ParseSearchListItemIds(Parse("""{ "odata.error": "что-то не так" }""")).ShouldBeEmpty();
}
[Fact(DisplayName = "RootFolder/ServerRelativeUrl разбирается для условия Path в KQL")]
public void ParseServerRelativeUrl_ReturnsValue()
{
const string response = """
{ "RootFolder": { "ServerRelativeUrl": "/sites/requests/Lists/Заявка тест — согласование" } }
""";
SharePointClient.ParseServerRelativeUrl(Parse(response))
.ShouldBe("/sites/requests/Lists/Заявка тест — согласование");
}
[Fact(DisplayName = "Список без RootFolder в ответе — null, а не исключение")]
public void ParseServerRelativeUrl_Missing_ReturnsNull()
{
SharePointClient.ParseServerRelativeUrl(Parse("{}")).ShouldBeNull();
}
}