feat: add structured logging, Shouldly assertions, CPM, and project documentation

- Add Microsoft.Extensions.Logging + Serilog to NatsServer and NatsClient
- Convert all test assertions from xUnit Assert to Shouldly
- Add NSubstitute package for future mocking needs
- Introduce Central Package Management via Directory.Packages.props
- Add documentation_rules.md with style guide, generation/update rules, component map
- Generate 10 documentation files across 5 component folders (GettingStarted, Protocol, Subscriptions, Server, Configuration/Operations)
- Update CLAUDE.md with logging, testing, porting, agent model, CPM, and documentation guidance
This commit is contained in:
Joseph Doherty
2026-02-22 21:05:53 -05:00
parent b9f4dec523
commit 539b2b7588
25 changed files with 2734 additions and 110 deletions

View File

@@ -0,0 +1,183 @@
# Setup
This guide covers prerequisites, building, running, and testing the NATS .NET server.
## Prerequisites
### .NET 10 SDK
The project targets `net10.0` with `LangVersion preview` (configured in `Directory.Build.props`). Install the .NET 10 preview SDK from https://dotnet.microsoft.com/download.
Verify your installation:
```bash
dotnet --version
# Expected: 10.0.x
```
### Go Toolchain (optional)
The Go toolchain is only needed if you want to run the reference server from `golang/nats-server/` or execute Go test suites for cross-validation. It is not required for building or testing the .NET port.
```bash
go version
# Expected: go1.22 or later
```
---
## Building
The solution file uses the `.slnx` format (not `.sln`). Pass it explicitly if your tooling requires it.
```bash
# Build all projects
dotnet build
# Clean and rebuild
dotnet clean && dotnet build
```
The solution contains three projects:
| Project | Path |
|---------|------|
| `NATS.Server` | `src/NATS.Server/` |
| `NATS.Server.Host` | `src/NATS.Server.Host/` |
| `NATS.Server.Tests` | `tests/NATS.Server.Tests/` |
All projects share settings from `Directory.Build.props`: `net10.0` target framework, nullable reference types enabled, warnings treated as errors.
---
## Running
The server executable is `NATS.Server.Host`. With no arguments it binds to `0.0.0.0:4222`.
```bash
dotnet run --project src/NATS.Server.Host
```
### CLI Arguments
| Flag | Alias | Type | Default | Description |
|------|-------|------|---------|-------------|
| `-p` | `--port` | `int` | `4222` | TCP port to listen on |
| `-a` | `--addr` | `string` | `0.0.0.0` | Bind address |
| `-n` | `--name` | `string` | `nats-dotnet-<hostname>` | Server name reported in INFO |
```bash
# Custom port
dotnet run --project src/NATS.Server.Host -- -p 14222
# Custom address and name
dotnet run --project src/NATS.Server.Host -- -a 127.0.0.1 -p 4222 -n dev-server
```
The `--` separator is required to pass arguments through `dotnet run` to the application.
Startup log output (Serilog console sink):
```
[12:00:00 INF] Listening on 0.0.0.0:4222
```
---
## Testing
```bash
# Run all tests
dotnet test
# Run tests with verbose output
dotnet test -v normal
# Run a single test project
dotnet test tests/NATS.Server.Tests
# Run a specific test by name
dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubListTests"
```
### Test Stack
| Package | Version | Purpose |
|---------|---------|---------|
| `xunit` | 2.9.3 | Test framework |
| `xunit.runner.visualstudio` | 3.1.4 | VS/Rider test runner integration |
| `Shouldly` | 4.3.0 | Assertion library |
| `NSubstitute` | 5.3.0 | Mocking |
| `NATS.Client.Core` | 2.7.2 | Official NATS .NET client for integration tests |
| `coverlet.collector` | 6.0.4 | Code coverage |
Do not use FluentAssertions or Moq — the project uses Shouldly and NSubstitute exclusively.
### Test Files
| File | Covers |
|------|--------|
| `ParserTests.cs` | `NatsParser.TryParse` for each command type |
| `SubjectMatchTests.cs` | `SubjectMatch` validation and wildcard matching |
| `SubListTests.cs` | `SubList` trie insert, remove, match, and cache behaviour |
| `ClientTests.cs` | `NatsClient` command dispatch and subscription tracking |
| `ServerTests.cs` | `NatsServer` pub/sub, wildcards, queue groups |
| `IntegrationTests.cs` | End-to-end tests using `NATS.Client.Core` against a live server |
---
## NuGet: Central Package Management
Package versions are defined centrally in `Directory.Packages.props`. Individual `.csproj` files reference packages without a `Version` attribute:
```xml
<!-- Directory.Packages.props -->
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
```
```xml
<!-- NATS.Server.Tests.csproj -->
<PackageReference Include="Shouldly" />
<PackageReference Include="NSubstitute" />
```
To add a new dependency: add a `<PackageVersion>` entry in `Directory.Packages.props`, then add a `<PackageReference>` (without `Version`) in the relevant `.csproj`.
---
## Logging
The host configures Serilog via `Microsoft.Extensions.Logging` in `Program.cs`:
```csharp
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
using var loggerFactory = new Serilog.Extensions.Logging.SerilogLoggerFactory(Log.Logger);
var server = new NatsServer(options, loggerFactory);
```
`NatsServer` and `NatsClient` receive `ILoggerFactory` and `ILogger` respectively via constructor injection. The core library (`NATS.Server`) depends only on `Microsoft.Extensions.Logging.Abstractions` — it has no direct Serilog dependency. The Serilog packages are wired in `NATS.Server.Host`.
Per-client loggers are created with a scoped category name that includes the client ID:
```csharp
var clientLogger = _loggerFactory.CreateLogger($"NATS.Server.NatsClient[{clientId}]");
```
To adjust log levels at runtime, modify the `LoggerConfiguration` in `Program.cs`.
---
## Related Documentation
- [Architecture](./Architecture.md)
- [Configuration Overview](../Configuration/Overview.md)
- [Protocol Overview](../Protocol/Overview.md)
- [Server Overview](../Server/Overview.md)
<!-- Last verified against codebase: 2026-02-22 -->