Files
novelly/tests/Novelly.Api.Tests/ServiceTestFixture.cs
T
James Wampler 40f93e40a8 Stop throwing for not-found; add Guard and request validation
Not-found lookups return null/false instead of throwing NotFoundException
across all services — a missing row is expected control flow, not an
exceptional condition. NotFoundException stays for embedded precondition
checks inside mutations (missing parent, invalid foreign reference).

Guard (copied from mic-check) enforces required arguments at the top of
every service method. A ported IModelValidator<T> framework validates
every request DTO at the API layer via a new ValidationEndpointFilter,
returning a 400 with field-level messages; services re-run the same
validator and throw for direct callers that bypass the API.

Endpoints translate null/false into 404 via a new ToApiResult() helper.
The agent toolset boundary translates the same nullable/bool results
into the tool-error text the model already expected.
2026-08-06 15:13:36 -07:00

83 lines
4.0 KiB
C#

using Novelly.Api.Beats;
using Novelly.Api.Chapters;
using Novelly.Api.Characters;
using Novelly.Api.Projects;
using Novelly.Api.Questions;
using Novelly.Api.Scenes;
using Novelly.Api.Tags;
namespace Novelly.Api.Tests;
/// <summary>
/// Shared plumbing for the service tests: a fresh in-memory database and a matching set
/// of services per test.
/// </summary>
/// <remarks>
/// The setup lives in <c>[SetUp]</c> rather than a constructor or field initialisers
/// because NUnit builds one fixture instance for the whole class — anything created once
/// would leak state from one test into the next.
/// </remarks>
public abstract class ServiceTestFixture
{
protected TestDatabase Db { get; private set; } = null!;
protected TagService Tags { get; private set; } = null!;
protected ProjectService Projects { get; private set; } = null!;
protected CharacterService Characters { get; private set; } = null!;
protected ChapterService Chapters { get; private set; } = null!;
protected SceneService Scenes { get; private set; } = null!;
protected BeatService Beats { get; private set; } = null!;
protected CharacterArcService Arcs { get; private set; } = null!;
protected OpenQuestionService Questions { get; private set; } = null!;
protected CapturingLogger<ProjectService> ProjectLogs { get; private set; } = null!;
protected CapturingLogger<CharacterService> CharacterLogs { get; private set; } = null!;
protected CapturingLogger<ChapterService> ChapterLogs { get; private set; } = null!;
protected CapturingLogger<SceneService> SceneLogs { get; private set; } = null!;
protected CapturingLogger<BeatService> BeatLogs { get; private set; } = null!;
protected CapturingLogger<TagService> TagLogs { get; private set; } = null!;
protected CapturingLogger<CharacterArcService> ArcLogs { get; private set; } = null!;
protected CapturingLogger<OpenQuestionService> QuestionLogs { get; private set; } = null!;
[SetUp]
public void SetUpFixture()
{
Db = new TestDatabase();
TagLogs = new CapturingLogger<TagService>();
ProjectLogs = new CapturingLogger<ProjectService>();
CharacterLogs = new CapturingLogger<CharacterService>();
ChapterLogs = new CapturingLogger<ChapterService>();
SceneLogs = new CapturingLogger<SceneService>();
BeatLogs = new CapturingLogger<BeatService>();
ArcLogs = new CapturingLogger<CharacterArcService>();
QuestionLogs = new CapturingLogger<OpenQuestionService>();
Tags = new TagService(Db.Context, TagLogs, new CreateTagRequestValidator(), new UpdateTagRequestValidator());
Projects = new ProjectService(Db.Context, ProjectLogs, new CreateProjectRequestValidator(), new UpdateProjectRequestValidator());
Characters = new CharacterService(
Db.Context, Tags, CharacterLogs,
new CreateCharacterRequestValidator(), new UpdateCharacterRequestValidator(), new CreateRelationshipRequestValidator());
Chapters = new ChapterService(Db.Context, Tags, ChapterLogs, new CreateChapterRequestValidator(), new UpdateChapterRequestValidator());
Scenes = new SceneService(Db.Context, SceneLogs, new CreateSceneRequestValidator(), new UpdateSceneRequestValidator());
Beats = new BeatService(
Db.Context, Tags, BeatLogs,
new CreateBeatRequestValidator(), new UpdateBeatRequestValidator(), new ReorderBeatsRequestValidator());
Arcs = new CharacterArcService(
Db.Context, ArcLogs,
new CreateArcStageRequestValidator(), new UpdateArcStageRequestValidator(), new ReorderArcStagesRequestValidator());
Questions = new OpenQuestionService(
Db.Context, QuestionLogs,
new CreateOpenQuestionRequestValidator(), new UpdateOpenQuestionRequestValidator(), new ResolveOpenQuestionRequestValidator());
OnSetUp();
}
/// <summary>Runs after the services exist, for per-class seed data.</summary>
protected virtual void OnSetUp()
{
}
[TearDown]
public void TearDownFixture() => Db.Dispose();
}