All 44 tests, same names, same coverage — verified by diffing the runner's test
list before and after.
The load-bearing change is fixture lifecycle. xUnit builds a new test-class
instance per test, so a `readonly TestDatabase _db = new()` field gave every
test its own database. NUnit reuses one instance for the whole class, so those
field initialisers and constructors would have shared a single database across a
class and let tests read each other's rows. Setup moved into [SetUp]/[TearDown]
via a new ServiceTestFixture base class, which also collapses the per-class
service wiring that was duplicated five times.
Assertions are now Assert.That with the constraint model:
Should().Be(x) -> Is.EqualTo(x)
Should().BeNull() -> Is.Null
Should().HaveCount(n) -> Has.Count.EqualTo(n)
Should().Equal(a, b) -> Is.EqualTo(new[] { a, b })
Should().BeEquivalentTo(..) -> Is.EquivalentTo(..)
Should().Contain("x") -> Does.Contain("x")
Should().OnlyHaveUniqueItems() -> Is.Unique
ThrowAsync<T>().WithMessage("*m*")
-> Throws.TypeOf<T>().With.Message.Contains("m")
FluentAssertions' `.Which` chains became plain indexed asserts, grouped in
Assert.Multiple so a failure reports every broken expectation in the case rather
than stopping at the first.
Because a framework migration can quietly produce vacuously-passing tests,
spot-checked four conversions by mutation — breaking the code under an async
Assert.Multiple block, a sync one, a Throws constraint, and a collection
ordering assert. All four failed as they should, confirming the assertions are
live and that NUnit bound the async lambdas to AsyncTestDelegate rather than
silently accepting them as async void.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
40 lines
1.4 KiB
C#
40 lines
1.4 KiB
C#
using Microsoft.Extensions.Options;
|
|
using NovelSoftware.Application;
|
|
using NovelSoftware.Application.Agent;
|
|
using NovelSoftware.Infrastructure.Anthropic;
|
|
|
|
namespace NovelSoftware.Tests;
|
|
|
|
[TestFixture]
|
|
public class AnthropicClientTests
|
|
{
|
|
[Test]
|
|
public void Constructing_without_a_key_does_not_throw() =>
|
|
// The agent service takes this as a dependency and also serves read-only endpoints
|
|
// (listing conversations, reading a transcript). Throwing at construction would
|
|
// take those down on any install that has not configured a key yet.
|
|
Assert.That(
|
|
() => new AnthropicAgentModelClient(Options.Create(new AgentOptions())),
|
|
Throws.Nothing);
|
|
|
|
[Test]
|
|
public void Sending_without_a_key_reports_a_configuration_problem()
|
|
{
|
|
var previous = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");
|
|
Environment.SetEnvironmentVariable("ANTHROPIC_API_KEY", null);
|
|
|
|
try
|
|
{
|
|
var client = new AnthropicAgentModelClient(Options.Create(new AgentOptions()));
|
|
|
|
Assert.That(
|
|
async () => await client.CompleteAsync("system", [], []),
|
|
Throws.TypeOf<AgentNotConfiguredException>().With.Message.Contains("ANTHROPIC_API_KEY"));
|
|
}
|
|
finally
|
|
{
|
|
Environment.SetEnvironmentVariable("ANTHROPIC_API_KEY", previous);
|
|
}
|
|
}
|
|
}
|