178 lines
7.0 KiB
C#
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));
|
|
}
|
|
}
|