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

178 lines
7.0 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using Wiki.Application.Encyclopedia;
using Wiki.Core.Model;
namespace Wiki.Application.Tests;
public class AssignNotesUseCaseTests
{
private static readonly EncyclopediaSettings Settings = new(16000, 10, 300_000, [], null);
private static AssignNotesUseCase UseCase(FakeTextModel model) =>
new(model, Settings, NullLogger<AssignNotesUseCase>.Instance);
private static PlannedArticle Article(string slug, params string[] sources) =>
new(slug, "Titel " + slug, "Title " + slug, ArticleCategory.System, "Scope " + slug, sources);
private static ArticlePlan PlanWith(params PlannedArticle[] articles)
{
var plan = ArticlePlan.Empty();
foreach (var a in articles) plan.AddArticle(a);
return plan;
}
private static Document[] Batch(params string[] slugs) =>
slugs.Select(s => TestNotes.Note(s)).ToArray();
[Fact]
public async Task Assign_ExistingAndNew()
{
var heating = Article("heating", "old");
var model = new FakeTextModel().Reply("""
{"new":[{"slug":"garden","titleDe":"Garten","titleEn":"Garden","category":"room","scope":"Der Garten."}],
"notes":[{"note":"a","articles":["heating","garden"]},{"note":"b","articles":["garden"]},{"note":"c","skip":true}]}
""");
var outcome = await UseCase(model).AssignAsync(Batch("a", "b", "c"), [heating], PlanWith(heating), CancellationToken.None);
Assert.False(outcome.Unusable);
var garden = Assert.Single(outcome.New);
Assert.Equal("garden", garden.Slug);
Assert.Equal(ArticleCategory.Room, garden.Category);
Assert.Equal(["a", "b"], garden.Sources);
Assert.Equal(["heating", "garden"], outcome.Assignments["a"]);
Assert.Empty(outcome.Assignments["c"]);
}
[Fact]
public async Task Assign_NoteOutsideBatch_Dropped()
{
var heating = Article("heating");
var model = new FakeTextModel().Reply("""{"new":[],"notes":[{"note":"a","articles":["heating"]},{"note":"zzz","articles":["heating"]}]}""");
var outcome = await UseCase(model).AssignAsync(Batch("a"), [heating], PlanWith(heating), CancellationToken.None);
Assert.Equal(["a"], outcome.Assignments.Keys);
}
[Fact]
public async Task Assign_ArticleOutsideCandidates_Dropped()
{
var heating = Article("heating");
var other = Article("other");
var model = new FakeTextModel().Reply("""{"new":[],"notes":[{"note":"a","articles":["heating","other"]},{"note":"b","articles":["other"]}]}""");
var outcome = await UseCase(model).AssignAsync(Batch("a", "b"), [heating], PlanWith(heating, other), CancellationToken.None);
Assert.Equal(["heating"], outcome.Assignments["a"]);
Assert.False(outcome.Assignments.ContainsKey("b"));
}
[Fact]
public async Task Assign_NewWithTakenSlug_DroppedAndItsNotesUnassigned()
{
var heating = Article("heating");
var plan = PlanWith(heating, Article("garden"));
var model = new FakeTextModel().Reply("""
{"new":[{"slug":"garden","titleDe":"G","titleEn":"G","category":"room","scope":"s"}],
"notes":[{"note":"a","articles":["garden"]},{"note":"b","articles":["heating"]}]}
""");
var outcome = await UseCase(model).AssignAsync(Batch("a", "b"), [heating], plan, CancellationToken.None);
Assert.Empty(outcome.New);
Assert.False(outcome.Assignments.ContainsKey("a"));
Assert.Equal(["heating"], outcome.Assignments["b"]);
}
[Fact]
public async Task Assign_UnreferencedNew_Dropped()
{
var heating = Article("heating");
var model = new FakeTextModel().Reply("""
{"new":[{"slug":"garden","titleDe":"G","titleEn":"G","category":"room","scope":"s"}],
"notes":[{"note":"a","articles":["heating"]}]}
""");
var outcome = await UseCase(model).AssignAsync(Batch("a"), [heating], PlanWith(heating), CancellationToken.None);
Assert.False(outcome.Unusable);
Assert.Empty(outcome.New);
}
[Fact]
public async Task Assign_Truncated_IsUnusable()
{
var model = new FakeTextModel().Reply("""{"new":[],"notes":[{"note":"a","skip":true}]}""", truncated: true);
var outcome = await UseCase(model).AssignAsync(Batch("a"), [], ArticlePlan.Empty(), CancellationToken.None);
Assert.True(outcome.Unusable);
Assert.Empty(outcome.Assignments);
}
[Fact]
public async Task Assign_PromptIsBounded()
{
var articles = Enumerable.Range(0, 500).Select(i => Article($"article-{i}", "n")).ToArray();
var plan = PlanWith(articles);
var batch = Batch(Enumerable.Range(0, 20).Select(i => $"note-{i:00}").ToArray());
var model = new FakeTextModel().Reply("""{"new":[],"notes":[]}""");
await UseCase(model).AssignAsync(batch, articles.Take(100).ToArray(), plan, CancellationToken.None);
var user = model.Calls[0].User;
Assert.Equal(20, user.Split('\n').Count(l => l.StartsWith("note-")));
Assert.Equal(20, user.Split('\n').Count(l => l.StartsWith("article-")));
Assert.DoesNotContain("article-20 ", user);
}
[Fact]
public async Task Assign_FirstParagraph_SkipsHeadingsAndBlankLines()
{
var note = TestNotes.Note("a") with { Body = "# T\n\n \nfirst line\ncontinued\n\nsecond para" };
var model = new FakeTextModel().Reply("""{"new":[],"notes":[]}""");
await UseCase(model).AssignAsync([note], [], ArticlePlan.Empty(), CancellationToken.None);
var line = model.Calls[0].User.Split('\n').Single(l => l.StartsWith("a | "));
Assert.EndsWith(" | first line continued", line);
}
[Fact]
public async Task Assign_FirstParagraph_CutTo300()
{
var note = TestNotes.Note("a") with { Body = new string('y', 500) };
var model = new FakeTextModel().Reply("""{"new":[],"notes":[]}""");
await UseCase(model).AssignAsync([note], [], ArticlePlan.Empty(), CancellationToken.None);
var line = model.Calls[0].User.Split('\n').Single(l => l.StartsWith("a | "));
Assert.EndsWith(" | " + new string('y', 300), line);
}
[Fact]
public async Task Assign_NewWithoutTitle_Dropped()
{
var heating = Article("heating");
var model = new FakeTextModel().Reply("""
{"new":[{"slug":"garden","titleDe":"G","category":"room","scope":"s"}],
"notes":[{"note":"a","articles":["garden","heating"]}]}
""");
var outcome = await UseCase(model).AssignAsync(Batch("a"), [heating], PlanWith(heating), CancellationToken.None);
Assert.Empty(outcome.New);
Assert.Equal(["heating"], outcome.Assignments["a"]);
}
[Fact]
public async Task Assign_ModelUnavailable_Propagates()
{
var model = new FakeTextModel().Throw(new ModelUnavailableException("down"));
await Assert.ThrowsAsync<ModelUnavailableException>(() =>
UseCase(model).AssignAsync(Batch("a"), [], ArticlePlan.Empty(), CancellationToken.None));
}
}