Add genre feature; extract ConfirmModal for reusable confirm dialogs

Genres seed on boot, are editable per project, and gate agent config
errors more gracefully. ConfirmModal replaces ad-hoc confirm prompts
across chapters, tags, and characters pages.
This commit is contained in:
James Wampler
2026-08-15 11:25:13 -07:00
parent ffb476a81a
commit 27c287b9c8
27 changed files with 1428 additions and 80 deletions
@@ -0,0 +1,44 @@
using Novelly.Api.Projects;
namespace Novelly.Api.Tests;
[TestFixture]
public class GenreServiceTests : ServiceTestFixture
{
[Test]
public async Task The_genre_list_arrives_seeded_and_alphabetical()
{
var listed = await Genres.ListAsync();
Assert.Multiple(() =>
{
Assert.That(listed, Is.Not.Empty);
Assert.That(listed.Select(g => g.Name), Has.Member("Fantasy").And.Member("Literary Fiction"));
Assert.That(listed.Select(g => g.Name), Is.Ordered);
Assert.That(listed.Select(g => g.Id), Is.Unique);
});
}
[Test]
public async Task A_project_can_be_filed_under_a_genre_off_the_list()
{
var fantasy = (await Genres.ListAsync()).First(g => g.Name == "Fantasy");
var project = await Projects.CreateAsync(new CreateProjectRequest("The Salt Road", Genre: fantasy.Name));
Assert.That(project.Genre, Is.EqualTo("Fantasy"));
}
[Test]
public async Task A_project_can_still_carry_a_genre_that_is_not_on_the_list()
{
var project = await Projects.CreateAsync(
new CreateProjectRequest("The Salt Road", Genre: "Nautical Gothic"));
Assert.Multiple(async () =>
{
Assert.That(project.Genre, Is.EqualTo("Nautical Gothic"));
Assert.That((await Genres.ListAsync()).Select(g => g.Name), Has.No.Member("Nautical Gothic"));
});
}
}