diff --git a/docs/plans/phases/phase-4-dotnet-design.md b/docs/plans/phases/phase-4-dotnet-design.md new file mode 100644 index 0000000..2a8f8c8 --- /dev/null +++ b/docs/plans/phases/phase-4-dotnet-design.md @@ -0,0 +1,186 @@ +# Phase 4: .NET Solution Design + +Design the target .NET 10 solution structure and map every Go item to its .NET counterpart. This phase translates the Go codebase decomposition (from Phases 1-2) and library mappings (from Phase 3) into a concrete .NET implementation plan. + +## Objective + +Every module, feature, and test in the porting database must have either a .NET mapping (project, namespace, class, method) or a justified N/A status. The result is a complete blueprint for the porting work in Phase 6. + +## Prerequisites + +- Phases 1-3 complete: all Go items in the DB, all libraries mapped +- Verify with: `dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db` + +## Solution Structure + +Define the .NET solution layout following standard conventions: + +``` +src/ + NATS.Server/ # Main server library (all core logic) + Protocol/ # Wire protocol parsing, commands + Subscriptions/ # SubList trie, subject matching + JetStream/ # Stream management, consumers + Cluster/ # Routes, gateways, leaf nodes + Auth/ # Authentication, accounts, JWT + ... + NATS.Server.Host/ # Host/entry point (Program.cs, DI, config) + +tests/ + NATS.Server.Tests/ # Unit tests for NATS.Server + Protocol/ + Subscriptions/ + JetStream/ + ... + NATS.Server.IntegrationTests/ # Cross-module and end-to-end tests +``` + +The `NATS.Server` project holds all portable logic. `NATS.Server.Host` is the thin entry point that wires up dependency injection, configuration, and hosting. Tests mirror the source structure. + +## Naming Conventions + +Follow these rules consistently when mapping Go items to .NET: + +| Aspect | Convention | Example | +|--------|-----------|---------| +| Classes | PascalCase | `NatsParser`, `SubList`, `JetStreamController` | +| Methods | PascalCase | `TryParse`, `Match`, `ProcessMessage` | +| Namespaces | `NATS.Server.[Module]` | `NATS.Server.Protocol`, `NATS.Server.Subscriptions` | +| Test classes | `[ClassName]Tests` | `NatsParserTests`, `SubListTests` | +| Test methods | `[Method]_[Scenario]_[Expected]` | `TryParse_ValidInput_ReturnsTrue` | +| Interfaces | `I[Name]` | `IMessageRouter`, `ISubListAccess` | +| Projects | `NATS.Server[.Suffix]` | `NATS.Server`, `NATS.Server.Host` | + +Avoid abbreviations unless they are universally understood (e.g., `TCP`, `TLS`, `JWT`). Prefer descriptive names over short ones. + +## Steps + +### Step 1: Map modules + +For each module in the database, assign a .NET project, namespace, and class. The `--namespace` and `--class` options are optional but recommended. + +```bash +# List all modules to review +dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db + +# Map a module to its .NET target +dotnet run --project tools/NatsNet.PortTracker -- module map \ + --project "NATS.Server" \ + --namespace "NATS.Server.Protocol" \ + --class "NatsParser" \ + --db porting.db +``` + +Work through all modules systematically. Group related Go files into the same namespace: + +| Go package/file pattern | .NET namespace | +|------------------------|----------------| +| `server/parser.go` | `NATS.Server.Protocol` | +| `server/sublist.go` | `NATS.Server.Subscriptions` | +| `server/jetstream*.go` | `NATS.Server.JetStream` | +| `server/route.go`, `server/gateway.go` | `NATS.Server.Cluster` | +| `server/auth.go`, `server/accounts.go` | `NATS.Server.Auth` | +| `server/pse/` | Likely N/A (Go-specific platform code) | + +### Step 2: Map features + +For each feature (function/method), assign the .NET class and method name: + +```bash +# List features for a specific module +dotnet run --project tools/NatsNet.PortTracker -- feature list --module --db porting.db + +# Map a feature +dotnet run --project tools/NatsNet.PortTracker -- feature map \ + --project "NATS.Server" \ + --class "NatsParser" \ + --method "TryParse" \ + --db porting.db +``` + +When mapping Go functions to .NET methods: + +- Go free functions become static methods or instance methods on the appropriate class +- Go methods with receivers map to instance methods on the corresponding .NET class +- Go `init()` functions typically map to static constructors or initialization in DI setup +- Go `goroutine` launches map to `Task`-based async methods + +### Step 3: Map tests + +For each test function, assign the .NET test class and method: + +```bash +# List tests for a module +dotnet run --project tools/NatsNet.PortTracker -- test list --module --db porting.db + +# Map a test +dotnet run --project tools/NatsNet.PortTracker -- test map \ + --project "NATS.Server.Tests" \ + --class "NatsParserTests" \ + --method "TryParse_ValidInput_ReturnsTrue" \ + --db porting.db +``` + +Go test naming (`TestParserValid`) translates to .NET naming (`TryParse_ValidInput_ReturnsTrue`). Each Go `Test*` function maps to one or more `[Fact]` or `[Theory]` methods. Table-driven Go tests often become `[Theory]` with `[InlineData]` or `[MemberData]`. + +### Step 4: Mark N/A items + +Some Go code has no .NET equivalent. Mark these with a clear reason: + +```bash +# Mark a module as N/A +dotnet run --project tools/NatsNet.PortTracker -- module set-na \ + --reason "Go-specific platform code, not needed in .NET" \ + --db porting.db + +# Mark a feature as N/A +dotnet run --project tools/NatsNet.PortTracker -- feature set-na \ + --reason "Go signal handling, replaced by .NET host lifecycle" \ + --db porting.db +``` + +### Common N/A categories + +Items that typically do not need a .NET port: + +| Go item | Reason | +|---------|--------| +| `pse_darwin.go`, `pse_linux.go`, `pse_windows.go` | Go-specific platform syscall wrappers; use .NET `System.Diagnostics.Process` instead | +| `disk_avail_windows.go`, `disk_avail_linux.go` | Go-specific disk APIs; use .NET `System.IO.DriveInfo` instead | +| Custom logger (`logger.go`, `log.go`) | Replaced by Serilog via `NATS.Server.Host` | +| Signal handling (`signal.go`) | Replaced by .NET Generic Host `IHostLifetime` | +| Go `sync.Pool`, `sync.Map` wrappers | .NET has `ObjectPool`, `ConcurrentDictionary` built-in | +| Build tags / `_test.go` helpers specific to Go test infra | Replaced by xUnit attributes and test fixtures | +| `go:embed` directives | Replaced by embedded resources or `IFileProvider` | + +Every N/A must include a reason. Bare N/A status without explanation is not acceptable. + +## Verification + +After mapping all items, run a quick check: + +```bash +# Count unmapped items (should be 0) +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db + +# Review all modules — every row should show DotNet Project filled or status n_a +dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db + +# Review N/A items to confirm they all have reasons +dotnet run --project tools/NatsNet.PortTracker -- module list --status n_a --db porting.db +dotnet run --project tools/NatsNet.PortTracker -- feature list --status n_a --db porting.db +``` + +## Completion Criteria + +- Every module has `dotnet_project` and `dotnet_namespace` set, or status is `n_a` with a reason +- Every feature has `dotnet_project`, `dotnet_class`, and `dotnet_method` set, or status is `n_a` with a reason +- Every test has `dotnet_project`, `dotnet_class`, and `dotnet_method` set, or status is `n_a` with a reason +- Naming follows PascalCase and the namespace hierarchy described above +- No two features map to the same class + method combination (collisions) + +## Related Documentation + +- [Phase 3: Library Mapping](phase-3-library-mapping.md) -- library mappings inform .NET class choices +- [Phase 5: Mapping Verification](phase-5-mapping-verification.md) -- next phase, validates all mappings +- [Phase 6: Porting](phase-6-porting.md) -- uses these mappings as the implementation blueprint diff --git a/docs/plans/phases/phase-5-mapping-verification.md b/docs/plans/phases/phase-5-mapping-verification.md new file mode 100644 index 0000000..54768ea --- /dev/null +++ b/docs/plans/phases/phase-5-mapping-verification.md @@ -0,0 +1,193 @@ +# Phase 5: Mapping Verification + +Verify that every Go item in the porting database is either mapped to a .NET target or justified as N/A. This phase is a quality gate between design (Phase 4) and implementation (Phase 6). + +## Objective + +Confirm zero unmapped items, validate all N/A justifications, enforce naming conventions, and detect collisions. The porting database must be a complete, consistent blueprint before any code is written. + +## Prerequisites + +- Phase 4 complete: all items have .NET mappings or N/A status +- Verify with: `dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db` + +## Steps + +### Step 1: Confirm zero unmapped items + +Run the summary report and verify that no items remain in `not_started` status without a .NET mapping: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db +``` + +The output shows counts per status. All items should be in one of these categories: + +- `not_started` with .NET mapping fields populated (ready for Phase 6) +- `n_a` with a reason in the notes field + +If any items lack both a mapping and N/A status, go back to Phase 4 and address them. + +### Step 2: Review N/A items + +Every N/A item must have a justification. Review them by type: + +```bash +# Review N/A modules +dotnet run --project tools/NatsNet.PortTracker -- module list --status n_a --db porting.db + +# Review N/A features +dotnet run --project tools/NatsNet.PortTracker -- feature list --status n_a --db porting.db + +# Review N/A tests +dotnet run --project tools/NatsNet.PortTracker -- test list --status n_a --db porting.db +``` + +For each N/A item, verify: + +1. The reason is documented (check with `module show `, `feature show `, or `test show `) +2. The reason is valid (the item genuinely has no .NET equivalent or is replaced by a .NET facility) +3. No dependent items rely on this N/A item being ported + +```bash +# Check if anything depends on an N/A item +dotnet run --project tools/NatsNet.PortTracker -- dependency show module --db porting.db +dotnet run --project tools/NatsNet.PortTracker -- dependency show feature --db porting.db +``` + +If a non-N/A item depends on an N/A item, either the dependency needs to be resolved differently or the N/A classification is wrong. + +### Step 3: Verify naming conventions + +Walk through the mappings and check for naming compliance: + +**PascalCase check**: All `dotnet_class` and `dotnet_method` values must use PascalCase. No `snake_case`, no `camelCase`. + +```bash +# List all mapped modules and spot-check names +dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db + +# List all mapped features for a module and check class/method names +dotnet run --project tools/NatsNet.PortTracker -- feature list --module --db porting.db +``` + +**Namespace hierarchy check**: Namespaces must follow `NATS.Server.[Module]` pattern: + +| Valid | Invalid | +|-------|---------| +| `NATS.Server.Protocol` | `Protocol` (missing root) | +| `NATS.Server.JetStream` | `NATS.Server.jetstream` (wrong case) | +| `NATS.Server.Subscriptions` | `NATSServer.Subscriptions` (wrong root) | + +**Test naming check**: Test classes must end in `Tests`. Test methods must follow `[Method]_[Scenario]_[Expected]` pattern: + +| Valid | Invalid | +|-------|---------| +| `NatsParserTests` | `ParserTest` (wrong suffix) | +| `TryParse_ValidInput_ReturnsTrue` | `TestParserValid` (Go-style naming) | +| `Match_WildcardSubject_ReturnsSubscribers` | `test_match` (snake_case) | + +### Step 4: Check for collisions + +No two features should map to the same class + method combination. This would cause compile errors or overwrite conflicts. + +```bash +# Export the full mapping report for review +dotnet run --project tools/NatsNet.PortTracker -- report export --format md --output porting-mapping-report.md --db porting.db +``` + +Open `porting-mapping-report.md` and search for duplicate class + method pairs. If the database is large, run a targeted SQL query: + +```bash +sqlite3 porting.db " + SELECT dotnet_class, dotnet_method, COUNT(*) as cnt + FROM features + WHERE dotnet_class IS NOT NULL AND dotnet_method IS NOT NULL + GROUP BY dotnet_class, dotnet_method + HAVING cnt > 1; +" +``` + +If collisions are found, rename one of the conflicting methods. Common resolution: add a more specific suffix (`ParseHeaders` vs `ParseBody` instead of two `Parse` methods). + +### Step 5: Validate cross-references + +Verify that test mappings reference the correct test project: + +```bash +# All tests should target NATS.Server.Tests or NATS.Server.IntegrationTests +dotnet run --project tools/NatsNet.PortTracker -- test list --db porting.db +``` + +Check that: +- Unit tests point to `NATS.Server.Tests` +- Integration tests (if any) point to `NATS.Server.IntegrationTests` +- No tests accidentally point to `NATS.Server` (the library project) + +### Step 6: Run phase check + +Run the built-in phase verification: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- phase check 5 --db porting.db +``` + +This runs automated checks and reports any remaining issues. All checks must pass. + +### Step 7: Export final mapping report + +Generate the definitive mapping report that serves as the implementation reference for Phase 6: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- report export \ + --format md \ + --output porting-mapping-report.md \ + --db porting.db +``` + +Review the exported report for completeness. This document becomes the source of truth for the porting work. + +## Troubleshooting + +### Unmapped items found + +```bash +# Find features with no .NET mapping and not N/A +dotnet run --project tools/NatsNet.PortTracker -- feature list --status not_started --db porting.db +``` + +For each unmapped item, either map it (Phase 4 Step 2) or set it to N/A with a reason. + +### N/A item has dependents + +If a non-N/A feature depends on an N/A feature: + +1. Determine if the dependency is real or an artifact of the Go call graph +2. If real, the N/A classification is likely wrong -- map the item instead +3. If the dependency is Go-specific, remove or reclassify it + +### Naming collision detected + +Rename one of the colliding methods to be more specific: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature map \ + --method "ParseHeadersFromBuffer" \ + --db porting.db +``` + +## Completion Criteria + +- Zero items in `not_started` status without a .NET mapping +- All N/A items have a documented, valid reason +- All `dotnet_class` and `dotnet_method` values follow PascalCase +- All namespaces follow `NATS.Server.[Module]` hierarchy +- No two features map to the same class + method combination +- All tests target the correct test project +- `phase check 5` passes with no errors +- Mapping report exported and reviewed + +## Related Documentation + +- [Phase 4: .NET Solution Design](phase-4-dotnet-design.md) -- the mapping phase this verifies +- [Phase 6: Porting](phase-6-porting.md) -- uses the verified mappings for implementation diff --git a/docs/plans/phases/phase-6-porting.md b/docs/plans/phases/phase-6-porting.md new file mode 100644 index 0000000..bf86373 --- /dev/null +++ b/docs/plans/phases/phase-6-porting.md @@ -0,0 +1,252 @@ +# Phase 6: Initial Porting + +Port Go code to .NET 10 C#, working through the dependency graph bottom-up. This is the main implementation phase where the actual code is written. + +## Objective + +Implement every non-N/A module, feature, and test in the porting database. Work from leaf nodes (items with no unported dependencies) upward through the dependency graph. Keep the database current as work progresses. + +## Prerequisites + +- Phase 5 complete: all mappings verified, no collisions, naming validated +- .NET solution structure created: + - `src/NATS.Server/NATS.Server.csproj` + - `src/NATS.Server.Host/NATS.Server.Host.csproj` + - `tests/NATS.Server.Tests/NATS.Server.Tests.csproj` + - `tests/NATS.Server.IntegrationTests/NATS.Server.IntegrationTests.csproj` +- Library dependencies (NuGet packages) added per Phase 3 mappings +- Verify readiness: `dotnet run --project tools/NatsNet.PortTracker -- phase check 5 --db porting.db` + +## Porting Workflow + +This is the core loop. Repeat until all items are complete. + +### Step 1: Find ready items + +Query for items whose dependencies are all ported (status `complete`, `verified`, or `n_a`): + +```bash +dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db +``` + +This returns modules and features that have no unported dependencies. Start with these. + +### Step 2: Pick an item and mark as stub + +Choose an item from the ready list. Mark it as `stub` to signal work has begun: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature update --status stub --db porting.db +``` + +### Step 3: Create the skeleton + +In the .NET project, create the class and method skeleton based on the mapping: + +1. Look up the mapping: `dotnet run --project tools/NatsNet.PortTracker -- feature show --db porting.db` +2. Create the file at the correct path under `src/NATS.Server/` following the namespace hierarchy +3. Add the class declaration, method signature, and a `throw new NotImplementedException()` body + +For batch scaffolding of an entire module: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status stub \ + --all-in-module --db porting.db +``` + +### Step 4: Implement the logic + +Reference the Go source code. The database stores the Go file path and line number for each feature: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature show --db porting.db +``` + +The output includes `Go File`, `Go Line`, and `Go LOC` fields. Open the Go source at those coordinates and translate the logic to C#. + +Key translation patterns: + +| Go pattern | .NET equivalent | +|-----------|-----------------| +| `goroutine` + `channel` | `Task` + `Channel` or `async/await` | +| `sync.Mutex` | `lock` statement or `SemaphoreSlim` | +| `sync.RWMutex` | `ReaderWriterLockSlim` | +| `sync.WaitGroup` | `Task.WhenAll` or `CountdownEvent` | +| `defer` | `try/finally` or `using`/`IDisposable` | +| `interface{}` / `any` | `object` or generics | +| `[]byte` | `byte[]`, `ReadOnlySpan`, or `ReadOnlyMemory` | +| `map[K]V` | `Dictionary` or `ConcurrentDictionary` | +| `error` return | Exceptions or `Result` pattern | +| `panic/recover` | Exceptions (avoid `Environment.FailFast` for recoverable cases) | +| `select` on channels | `Task.WhenAny` or `Channel` reader patterns | +| `context.Context` | `CancellationToken` | +| `io.Reader/Writer` | `Stream`, `PipeReader/PipeWriter` | + +### Step 5: Mark complete + +Once the implementation compiles and the basic logic is in place: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature update --status complete --db porting.db +``` + +### Step 6: Run targeted tests + +If tests exist for this feature, run them: + +```bash +dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol" \ + tests/NATS.Server.Tests/ +``` + +Fix any failures before moving on. + +### Step 7: Check what is now unblocked + +Completing items may unblock others that depend on them: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db +``` + +Return to Step 2 with the newly available items. + +## DB Update Discipline + +The porting database must stay current. Update status at every transition: + +```bash +# Starting work on a feature +dotnet run --project tools/NatsNet.PortTracker -- feature update 42 --status stub --db porting.db + +# Feature implemented +dotnet run --project tools/NatsNet.PortTracker -- feature update 42 --status complete --db porting.db + +# Batch scaffolding for all features in a module +dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status stub \ + --all-in-module 3 --db porting.db + +# Module fully ported (all its features are complete) +dotnet run --project tools/NatsNet.PortTracker -- module update 3 --status complete --db porting.db + +# Check progress +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db +``` + +Status transitions follow this progression: + +``` +not_started -> stub -> complete -> verified (Phase 7) + \-> n_a (if determined during porting) +``` + +Never skip `stub` -- it signals that work is in progress and prevents duplicate effort. + +## Porting Order Strategy + +### Start with leaf modules + +Leaf modules have no dependencies on other unported modules. They are safe to port first because nothing they call is missing. + +```bash +# These are the leaves — port them first +dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db +``` + +Typical leaf modules include: +- Utility/helper code (string manipulation, byte buffer pools) +- Constants and enums +- Configuration types (options, settings) +- Error types and codes + +### Then work upward + +After leaves are done, modules that depended only on those leaves become ready. Continue up the dependency graph: + +``` +Leaf utilities -> Protocol types -> Parser -> Connection handler -> Server +``` + +### Port tests alongside features + +When porting a feature, also port its associated tests in the same pass. This provides immediate validation: + +```bash +# List tests for a feature +dotnet run --project tools/NatsNet.PortTracker -- test list --module --db porting.db + +# After porting a test +dotnet run --project tools/NatsNet.PortTracker -- test update --status complete --db porting.db +``` + +## Progress Tracking + +Check overall progress regularly: + +```bash +# Summary stats +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db + +# What is still blocked +dotnet run --project tools/NatsNet.PortTracker -- dependency blocked --db porting.db + +# Phase-level check +dotnet run --project tools/NatsNet.PortTracker -- phase check 6 --db porting.db +``` + +## Handling Discoveries During Porting + +During implementation, you may find: + +### Items that should be N/A + +If a feature turns out to be unnecessary in .NET (discovered during implementation): + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature set-na \ + --reason "Go-specific memory management, handled by .NET GC" --db porting.db +``` + +### Missing dependencies + +If the Go analyzer missed a dependency: + +```bash +# The dependency is tracked in the DB via the dependencies table +# For now, just ensure the target is ported before continuing +dotnet run --project tools/NatsNet.PortTracker -- dependency show feature --db porting.db +``` + +### Design changes + +If the .NET design needs to differ from the original mapping (e.g., splitting a large Go function into multiple .NET methods), update the mapping: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- feature map \ + --class "NewClassName" \ + --method "NewMethodName" \ + --db porting.db +``` + +## Tips + +1. **Keep the build green.** The solution should compile after each feature is completed. Do not leave unresolved references. +2. **Write idiomatic C#.** Do not transliterate Go line-by-line. Use .NET patterns (async/await, LINQ, Span, dependency injection) where they produce cleaner code. +3. **Use `CancellationToken` everywhere.** The Go code uses `context.Context` pervasively -- mirror this with `CancellationToken` parameters. +4. **Prefer `ReadOnlySpan` for hot paths.** The NATS parser processes bytes at high throughput. Use spans and avoid allocations in the critical path. +5. **Do not port Go comments verbatim.** Translate the intent into C# XML doc comments where appropriate. +6. **Run `dotnet build` frequently.** Catch compile errors early rather than accumulating them. + +## Completion Criteria + +- All non-N/A modules have status `complete` or better +- All non-N/A features have status `complete` or better +- All non-N/A tests have status `complete` or better +- The solution compiles without errors: `dotnet build` +- `dependency blocked` returns no items (or only items waiting for Phase 7 verification) +- `report summary` shows the expected completion counts + +## Related Documentation + +- [Phase 5: Mapping Verification](phase-5-mapping-verification.md) -- the verified mappings this phase implements +- [Phase 7: Porting Verification](phase-7-porting-verification.md) -- targeted testing of the ported code diff --git a/docs/plans/phases/phase-7-porting-verification.md b/docs/plans/phases/phase-7-porting-verification.md new file mode 100644 index 0000000..20dd70d --- /dev/null +++ b/docs/plans/phases/phase-7-porting-verification.md @@ -0,0 +1,233 @@ +# Phase 7: Porting Verification + +Verify all ported code through targeted testing per module. This phase does NOT run the full test suite as a single pass -- it systematically verifies each module, marks items as verified, and confirms behavioral equivalence with the Go server. + +## Objective + +Every ported module passes its targeted tests. Every item in the database reaches `verified` or `n_a` status. Cross-module integration tests pass. Key behavioral scenarios produce equivalent results between the Go and .NET servers. + +## Prerequisites + +- Phase 6 complete: all non-N/A items at `complete` or better +- All tests ported and compilable +- Verify readiness: `dotnet run --project tools/NatsNet.PortTracker -- phase check 6 --db porting.db` + +## Verification Workflow + +Work through modules one at a time. Do not move to the next module until the current one is fully verified. + +### Step 1: List modules to verify + +```bash +# Show all modules — look for status 'complete' (not yet verified) +dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db +``` + +Start with leaf modules (those with the fewest dependencies) and work upward, same order as the porting phase. + +### Step 2: List tests for the module + +For each module, identify all mapped tests: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- test list --module --db porting.db +``` + +This shows every test associated with the module, its status, and its .NET method name. + +### Step 3: Run targeted tests + +Run only the tests for this module using `dotnet test --filter`: + +```bash +# Filter by namespace (matches all tests in the module's namespace) +dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol" \ + tests/NATS.Server.Tests/ + +# Filter by test class +dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol.NatsParserTests" \ + tests/NATS.Server.Tests/ + +# Filter by specific test method +dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol.NatsParserTests.TryParse_ValidInput_ReturnsTrue" \ + tests/NATS.Server.Tests/ +``` + +The `--filter` flag uses partial matching on the fully qualified test name. Use the namespace pattern for module-wide runs, and the class or method pattern for debugging specific failures. + +### Step 4: Handle failures + +When tests fail: + +1. **Read the failure output.** The test runner prints the assertion that failed, the expected vs actual values, and the stack trace. +2. **Locate the Go reference.** Look up the test in the database to find the original Go test and source: + ```bash + dotnet run --project tools/NatsNet.PortTracker -- test show --db porting.db + ``` +3. **Compare Go and .NET logic.** Open the Go source at the stored line number. Check for translation errors: off-by-one, missing edge cases, different default values. +4. **Fix and re-run.** After fixing, re-run only the failing test: + ```bash + dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol.NatsParserTests.TryParse_EmptyInput_ReturnsFalse" \ + tests/NATS.Server.Tests/ + ``` +5. **Then re-run the full module.** Confirm no regressions: + ```bash + dotnet test --filter "FullyQualifiedName~NATS.Server.Tests.Protocol" \ + tests/NATS.Server.Tests/ + ``` + +Common failure causes: + +| Symptom | Likely cause | +|---------|-------------| +| Off-by-one in buffer parsing | Go slices are half-open `[start:end)`, C# spans match but array indexing might differ | +| Timeout in async test | Missing `CancellationToken`, or `Task` not awaited | +| Wrong byte sequence | Go uses `[]byte("string")` which is UTF-8; ensure C# uses `Encoding.UTF8` | +| Nil vs null behavior | Go nil checks behave differently from C# null; check for `default` values | +| Map iteration order | Go maps iterate in random order; if the test depends on order, sort first | + +### Step 5: Mark module as verified + +Once all tests pass for a module: + +```bash +# Mark the module itself as verified +dotnet run --project tools/NatsNet.PortTracker -- module update --status verified --db porting.db + +# Mark all features in the module as verified +dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status verified \ + --all-in-module --db porting.db + +# Mark individual tests as verified +dotnet run --project tools/NatsNet.PortTracker -- test update --status verified --db porting.db +``` + +### Step 6: Move to next module + +Repeat Steps 2-5 for each module. Track progress: + +```bash +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db +``` + +## Integration Testing + +After all modules are individually verified, run integration tests that exercise cross-module behavior. + +### Step 7: Run integration tests + +```bash +dotnet test tests/NATS.Server.IntegrationTests/ +``` + +Integration tests cover scenarios like: + +- Client connects, subscribes, receives published messages +- Multiple clients with wildcard subscriptions +- Connection lifecycle (connect, disconnect, reconnect) +- Protocol error handling (malformed commands, oversized payloads) +- Configuration loading and server startup + +Fix any failures by tracing through the modules involved and checking the interaction boundaries. + +### Step 8: Behavioral comparison + +Run both the Go server and the .NET server with the same workload and compare behavior. This catches semantic differences that unit tests might miss. + +**Setup:** + +1. Start the Go server: + ```bash + cd golang/nats-server && go run . -p 4222 + ``` +2. Start the .NET server: + ```bash + dotnet run --project src/NATS.Server.Host -- --port 4223 + ``` + +**Comparison scenarios:** + +| Scenario | What to compare | +|----------|----------------| +| Basic pub/sub | Publish a message, verify subscriber receives identical payload | +| Wildcard matching | Subscribe with `foo.*` and `foo.>`, publish to `foo.bar`, verify same match results | +| Queue groups | Multiple subscribers in a queue group, verify round-robin distribution | +| Protocol errors | Send malformed commands, verify same error responses | +| Connection info | Connect and check `INFO` response fields | +| Graceful shutdown | Send SIGTERM, verify clean disconnection | + +Use the `nats` CLI tool to drive traffic: + +```bash +# Subscribe on Go server +nats sub -s nats://localhost:4222 "test.>" + +# Subscribe on .NET server +nats sub -s nats://localhost:4223 "test.>" + +# Publish to both and compare +nats pub -s nats://localhost:4222 test.hello "payload" +nats pub -s nats://localhost:4223 test.hello "payload" +``` + +Document any behavioral differences. Some differences are expected (e.g., server name, version string) while others indicate bugs. + +### Step 9: Final verification + +Run the complete check: + +```bash +# Phase 7 check — all tests verified +dotnet run --project tools/NatsNet.PortTracker -- phase check 7 --db porting.db + +# Final summary — all items should be verified or n_a +dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db + +# Export final report +dotnet run --project tools/NatsNet.PortTracker -- report export \ + --format md \ + --output porting-final-report.md \ + --db porting.db +``` + +## Troubleshooting + +### Test passes individually but fails in module run + +Likely a test ordering dependency or shared state. Check for: +- Static mutable state not reset between tests +- Port conflicts if tests start servers +- File system artifacts from previous test runs + +Fix by adding proper test cleanup (`IDisposable`, `IAsyncLifetime`) and using unique ports/paths per test. + +### Module passes but integration test fails + +The issue is at a module boundary. Check: +- Interface implementations match expectations +- Serialization/deserialization is consistent across modules +- Thread safety at module interaction points +- Async patterns are correct (no fire-and-forget `Task` without error handling) + +### Behavioral difference with Go server + +1. Identify the specific protocol message or state that differs +2. Trace through both implementations step by step +3. Check the NATS protocol specification for the correct behavior +4. Fix the .NET implementation to match (the Go server is the reference) + +## Completion Criteria + +- All non-N/A modules have status `verified` +- All non-N/A features have status `verified` +- All non-N/A tests have status `verified` +- All targeted tests pass: `dotnet test tests/NATS.Server.Tests/` +- All integration tests pass: `dotnet test tests/NATS.Server.IntegrationTests/` +- Key behavioral scenarios produce equivalent results on Go and .NET servers +- `phase check 7` passes with no errors +- Final report exported and reviewed + +## Related Documentation + +- [Phase 6: Porting](phase-6-porting.md) -- the implementation phase this verifies +- [Phase 4: .NET Solution Design](phase-4-dotnet-design.md) -- the original design mappings