Files
Wiki/test/Wiki.Application.Tests/FoldArticleUseCaseTests.cs
T

145 lines
5.5 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using Wiki.Application.Encyclopedia;
using Wiki.Core.Model;
namespace Wiki.Application.Tests;
public class FoldArticleUseCaseTests
{
private static readonly EncyclopediaSettings Settings = new(16000, 10, 300_000, [], null) { CandidateArticles = 2 };
private static FoldArticleUseCase UseCase(FakeTextModel model) =>
new(model, Settings, NullLogger<FoldArticleUseCase>.Instance);
private static PlannedArticle Article(string slug, params string[] sources) =>
new(slug, "Titel " + slug, "Title " + slug, ArticleCategory.System, "Scope " + slug, sources);
private static Dictionary<string, Document> Notes(params string[] slugs) =>
slugs.ToDictionary(s => s, s => TestNotes.Note(s));
[Fact]
public async Task Fold_Into()
{
var thin = Article("valve", "a");
var model = new FakeTextModel().Reply("""{"into":"heating"}""");
var target = await UseCase(model).FoldAsync(thin, Notes("a"), [Article("heating", "h"), Article("garden", "g")], CancellationToken.None);
Assert.Equal("heating", target);
Assert.Equal(Settings.FoldMaxTokens, model.Calls[0].MaxTokens);
Assert.StartsWith("[fold]", model.Calls[0].System);
}
[Fact]
public async Task Fold_ListsAtMostFoldMinSources()
{
var model = new FakeTextModel().Reply("""{"keep":true}""");
var thin = Article("valve", "a", "b", "c", "d");
var notes = new Dictionary<string, Document>
{
["a"] = TestNotes.Note("a", title: "Na"), ["b"] = TestNotes.Note("b", title: "Nb"),
["c"] = TestNotes.Note("c", title: "Nc"), ["d"] = TestNotes.Note("d", title: "Nd"),
};
await UseCase(model).FoldAsync(thin, notes, [Article("heating", "h")], CancellationToken.None);
Assert.Contains("Nc", model.Calls[0].User);
Assert.DoesNotContain("Nd", model.Calls[0].User);
}
[Fact]
public async Task Fold_Keep_IsNull()
{
var model = new FakeTextModel().Reply("""{"keep":true}""");
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_TargetNotNearby_IsNull()
{
var model = new FakeTextModel().Reply("""{"into":"elsewhere"}""");
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_TargetBeyondCandidateCap_IsNull()
{
var nearby = new[] { Article("n1", "1"), Article("n2", "2"), Article("n3", "3") };
var model = new FakeTextModel().Reply("""{"into":"n3"}""");
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), nearby, CancellationToken.None));
Assert.DoesNotContain("n3 | ", model.Calls[0].User);
}
[Fact]
public async Task Fold_IntoItself_IsNull()
{
var model = new FakeTextModel().Reply("""{"into":"valve"}""");
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("valve", "a"), Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_NoNearby_NoCall()
{
var model = new FakeTextModel();
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [], CancellationToken.None));
Assert.Empty(model.Calls);
}
[Fact]
public async Task Fold_Truncated_IsNull()
{
var model = new FakeTextModel().Reply("""{"into":"heating"}""", truncated: true);
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_NotJson_IsNull()
{
var model = new FakeTextModel().Reply("hm");
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_BadRequest_IsNull()
{
var model = new FakeTextModel().Throw(new HttpRequestException("bad", null, System.Net.HttpStatusCode.BadRequest));
Assert.Null(await UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public async Task Fold_ModelUnavailable_Propagates()
{
var model = new FakeTextModel().Throw(new ModelUnavailableException("down"));
await Assert.ThrowsAsync<ModelUnavailableException>(() =>
UseCase(model).FoldAsync(Article("valve", "a"), Notes("a"), [Article("heating", "h")], CancellationToken.None));
}
[Fact]
public void IsThin_Thresholds()
{
var s = new EncyclopediaSettings(16000, 10, 300_000, [], null);
var notes = new Dictionary<string, Document>
{
["a"] = TestNotes.Note("a", 1000),
["b"] = TestNotes.Note("b", 1000),
["c"] = TestNotes.Note("c", 1000),
["big"] = TestNotes.Note("big", 4000),
};
Assert.True(FoldArticleUseCase.IsThin(Article("x", "a", "b"), notes, s));
Assert.False(FoldArticleUseCase.IsThin(Article("x", "a", "b", "c"), notes, s));
Assert.False(FoldArticleUseCase.IsThin(Article("x", "big"), notes, s));
Assert.False(FoldArticleUseCase.IsThin(Article("x", "a", "b", "ghost"), notes, s));
Assert.True(FoldArticleUseCase.IsThin(Article("x", "a"), notes, s));
}
}