# Domain tests — entities & invariants

You test pure domain entities (no EF, no Moq, no DI). They live in `*.Domain/Entities`.
Tests go to `Tests/{Module}/Domain/{Entity}Tests.cs`.

## Rules

- Class `{Entity}Tests`, one file per entity. Method `Method_Scenario_Expected`.
- Group tests in a `#region` per method (`#region Create`, `#region Status transitions`).
- Build via the static factory `{Entity}.Create(...)` — **never** `new {Entity} { … }` (the ctor is private by design).
- Single case → `[Fact]`; parameterised → `[Theory]` + `[InlineData]`.
- GUIDs always `Guid.NewGuid()` — never a hardcoded literal.
- Timestamps: capture `var before = DateTime.UtcNow;` then `entity.CreatedAt.Should().BeOnOrAfter(before)`. **Never `BeGreaterThan` on DateTime.**
- Exceptions: `var act = () => {Entity}.Create(bad);` then `act.Should().Throw<ArgumentException>().WithMessage("*keyword*")` (wildcard, never an exact message).
- A `private static {Entity} CreateValid(...)` helper keeps bodies short.

## Canonical skeleton

```csharp
using Xunit;
using FluentAssertions;
using MyApp.Domain.Entities;

namespace MyApp.Tests.Orders.Domain;

[Trait("Category", "Domain")]
[Trait("Type", "Unit")]
public class OrderTests
{
    #region Create

    [Fact]
    public void Create_WithValidArgs_SetsPropertiesAndStampsCreatedAt()
    {
        var before = DateTime.UtcNow;

        var order = Order.Create("ORD-001", Guid.NewGuid(), 100m);

        order.Id.Should().NotBeEmpty();
        order.CreatedAt.Should().BeOnOrAfter(before);
        order.Code.Should().Be("ORD-001");
    }

    [Fact]
    public void Create_WithEmptyCode_Throws()
    {
        var act = () => Order.Create("", Guid.NewGuid(), 100m);

        act.Should().Throw<ArgumentException>().WithMessage("*code*");
    }

    #endregion

    #region Status transitions

    [Theory]
    [InlineData("draft", "submitted")]
    [InlineData("submitted", "approved")]
    public void Submit_FromValidState_Transitions(string from, string to)
    {
        var order = CreateValid(status: from);

        order.TransitionTo(to);

        order.Status.Should().Be(to);
    }

    #endregion

    private static Order CreateValid(string status = "draft") =>
        Order.Create("ORD-001", Guid.NewGuid(), 100m); // adjust to your factory
}
```

## Anti-patterns

- ❌ `new Order { … }` — use `Order.Create(...)`.
- ❌ Hardcoded GUID literal — `Guid.NewGuid()`.
- ❌ `BeGreaterThan` on DateTime — `BeOnOrAfter` / `BeBefore` / `BeCloseTo`.
- ❌ Exact exception message — wildcard `.WithMessage("*kw*")`.
- ❌ `using Moq;` / EF Core here — the domain has zero dependencies.
