Covers schema migration, small-module verification, 224 stub fills, 401 new unit tests, 2126 deferred server-integration tests, and NatsServerBehaviorTests integration baseline.
1325 lines
43 KiB
Markdown
1325 lines
43 KiB
Markdown
# Phase 7: Porting Verification — Implementation Plan
|
||
|
||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development to implement this plan task-by-task.
|
||
|
||
**Goal:** Verify all ported .NET code by writing and running targeted unit tests, then mark the ~2,126 server-integration tests as `deferred` so the database reflects ground truth.
|
||
|
||
**Architecture:** Ten sequential sessions: schema migration → small-module verification → stub-filling sessions (opts, jwt, auth, signal/log) → new test writing sessions (stores, filestore, jetstream) → final deferred-marking and integration tests. Each session ends with a passing test run, DB update, and commit.
|
||
|
||
**Tech Stack:** .NET 10, xUnit 3, Shouldly, NSubstitute, SQLite (porting.db), PortTracker CLI
|
||
|
||
---
|
||
|
||
## Reference: DB State Entering Phase 7
|
||
|
||
| Category | Count |
|
||
|----------|-------|
|
||
| Small module tests (complete) | 114 |
|
||
| Server module tests (complete) | 205 |
|
||
| Server module tests (stub) | 224 — unit tests, bodies to be written |
|
||
| Server module tests (not_started → unit) | 401 — new tests to write |
|
||
| Server module tests (not_started → deferred) | 2,126 — server-integration, to be marked |
|
||
| thw benchmarks (not_started → n/a) | 6 — performance only, not applicable |
|
||
|
||
## Reference: Key File Paths
|
||
|
||
| Purpose | Path |
|
||
|---------|------|
|
||
| Go reference tests | `golang/nats-server/server/<file>_test.go` |
|
||
| .NET unit test project | `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/` |
|
||
| .NET integration test project | `dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/` |
|
||
| PortTracker CLI | `tools/NatsNet.PortTracker/` |
|
||
| SQLite database | `porting.db` |
|
||
|
||
## Reference: Useful Commands
|
||
|
||
```bash
|
||
# Run all unit tests
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
|
||
# Run tests by class
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~<ClassName>"
|
||
|
||
# PortTracker: mark test complete
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status complete --db porting.db
|
||
|
||
# PortTracker: mark module verified
|
||
dotnet run --project tools/NatsNet.PortTracker -- module update <id> --status verified --db porting.db
|
||
|
||
# PortTracker: mark all features in module verified
|
||
dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status verified --all-in-module <module_id> --db porting.db
|
||
|
||
# PortTracker: report
|
||
dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
|
||
|
||
# SQLite bulk update (for batch operations)
|
||
sqlite3 porting.db "UPDATE unit_tests SET status='deferred' WHERE ..."
|
||
```
|
||
|
||
---
|
||
|
||
## Task 1: Schema Migration — Add `deferred` Status
|
||
|
||
**Files:**
|
||
- Modify: `porting-schema.sql` (update CHECK constraint)
|
||
- Modify: `porting.db` (recreate unit_tests table via migration SQL)
|
||
|
||
### Step 1: Verify current constraint
|
||
|
||
```bash
|
||
sqlite3 porting.db ".schema unit_tests"
|
||
```
|
||
|
||
Expected: shows `CHECK (status IN ('not_started', 'stub', 'complete', 'verified', 'n_a'))`.
|
||
|
||
### Step 2: Run migration SQL
|
||
|
||
```bash
|
||
sqlite3 porting.db << 'EOF'
|
||
BEGIN;
|
||
|
||
CREATE TABLE unit_tests_new (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
module_id INTEGER NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
|
||
feature_id INTEGER REFERENCES features(id) ON DELETE SET NULL,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
go_file TEXT,
|
||
go_class TEXT,
|
||
go_method TEXT,
|
||
go_line_number INTEGER,
|
||
go_line_count INTEGER,
|
||
status TEXT NOT NULL DEFAULT 'not_started'
|
||
CHECK (status IN ('not_started', 'stub', 'complete', 'verified', 'n_a', 'deferred')),
|
||
dotnet_project TEXT,
|
||
dotnet_class TEXT,
|
||
dotnet_method TEXT,
|
||
notes TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
INSERT INTO unit_tests_new SELECT * FROM unit_tests;
|
||
DROP TABLE unit_tests;
|
||
ALTER TABLE unit_tests_new RENAME TO unit_tests;
|
||
|
||
-- Recreate indexes
|
||
CREATE INDEX IF NOT EXISTS idx_unit_tests_module ON unit_tests(module_id);
|
||
CREATE INDEX IF NOT EXISTS idx_unit_tests_feature ON unit_tests(feature_id);
|
||
CREATE INDEX IF NOT EXISTS idx_unit_tests_status ON unit_tests(status);
|
||
|
||
-- Recreate trigger
|
||
CREATE TRIGGER IF NOT EXISTS trg_unit_tests_updated AFTER UPDATE ON unit_tests
|
||
BEGIN
|
||
UPDATE unit_tests SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||
END;
|
||
|
||
COMMIT;
|
||
EOF
|
||
```
|
||
|
||
### Step 3: Verify migration
|
||
|
||
```bash
|
||
sqlite3 porting.db ".schema unit_tests"
|
||
# Confirm 'deferred' is in the CHECK constraint
|
||
|
||
sqlite3 porting.db "SELECT COUNT(*) FROM unit_tests;"
|
||
# Confirm row count matches original (3257)
|
||
|
||
# Verify existing data is intact
|
||
sqlite3 porting.db "SELECT status, COUNT(*) FROM unit_tests GROUP BY status;"
|
||
```
|
||
|
||
Expected status counts:
|
||
- complete: 319
|
||
- n_a: 181
|
||
- not_started: 2533
|
||
- stub: 224
|
||
|
||
### Step 4: Update porting-schema.sql
|
||
|
||
Edit `porting-schema.sql` to add `'deferred'` to the unit_tests CHECK constraint:
|
||
|
||
```sql
|
||
-- Find this line:
|
||
status TEXT NOT NULL DEFAULT 'not_started'
|
||
CHECK (status IN ('not_started', 'stub', 'complete', 'verified', 'n_a')),
|
||
|
||
-- Change to:
|
||
status TEXT NOT NULL DEFAULT 'not_started'
|
||
CHECK (status IN ('not_started', 'stub', 'complete', 'verified', 'n_a', 'deferred')),
|
||
```
|
||
|
||
### Step 5: Commit
|
||
|
||
```bash
|
||
git add porting-schema.sql porting.db
|
||
git commit -m "feat(p7-01): add 'deferred' status to unit_tests schema"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 2: P7-01 — Small Module Verification
|
||
|
||
**Modules to verify:** ats (id=1), avl (id=2), certidp (id=3), certstore (id=4), elastic (id=5), gsl (id=6), pse (id=7), stree (id=9), sysmem (id=10), thw (id=11), tpm (id=12)
|
||
|
||
**Test counts:** 114 complete tests across all small modules. 6 thw benchmarks → mark `n_a`.
|
||
|
||
### Step 1: Run all small module tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.Internal|FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.Auth.CertificateIdentityProvider|FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.Auth.TpmKeyProvider|FullyQualifiedName~TimeHashWheelTests|FullyQualifiedName~SubjectTreeTests|FullyQualifiedName~GenericSubjectListTests|FullyQualifiedName~ProcessStatsProviderTests|FullyQualifiedName~AccessTimeServiceTests"
|
||
```
|
||
|
||
Or run all unit tests and verify none fail:
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
```
|
||
|
||
Expected: all existing tests pass (currently ~635 tests).
|
||
|
||
### Step 2: Mark thw benchmarks as n/a
|
||
|
||
The 6 thw benchmarks (IDs 3250–3255) are Go benchmark functions, not correctness tests. Mark `n_a`:
|
||
|
||
```bash
|
||
for id in 3250 3251 3252 3253 3254 3255; do
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update $id --status n_a --db porting.db
|
||
done
|
||
```
|
||
|
||
### Step 3: Mark all small module tests as verified
|
||
|
||
All complete tests in small modules (114 tests) pass — mark them verified. Use direct SQL for efficiency:
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests
|
||
SET status = 'verified'
|
||
WHERE status = 'complete' AND module_id IN (1,2,3,4,5,6,7,9,10,11,12);"
|
||
```
|
||
|
||
Verify:
|
||
```bash
|
||
sqlite3 porting.db "SELECT module_id, status, COUNT(*) FROM unit_tests WHERE module_id IN (1,2,3,4,5,6,7,9,10,11,12) GROUP BY module_id, status;"
|
||
```
|
||
|
||
### Step 4: Mark all features in small modules as verified
|
||
|
||
```bash
|
||
for mod_id in 1 2 3 4 5 6 7 9 10 11 12; do
|
||
dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status verified --all-in-module $mod_id --db porting.db
|
||
done
|
||
```
|
||
|
||
### Step 5: Mark all small modules as verified
|
||
|
||
```bash
|
||
for mod_id in 1 2 3 4 5 6 7 9 10 11 12; do
|
||
dotnet run --project tools/NatsNet.PortTracker -- module update $mod_id --status verified --db porting.db
|
||
done
|
||
```
|
||
|
||
### Step 6: Verify and commit
|
||
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
|
||
./reports/generate-report.sh
|
||
git add -A
|
||
git commit -m "feat(p7-01): verify 11 small modules (114 tests), mark thw benchmarks n/a"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3: P7-02 — Opts Stubs (77 tests)
|
||
|
||
**Go source:** `golang/nats-server/server/opts_test.go`
|
||
**Target .NET file:** `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerOptionsTests.cs`
|
||
**DB IDs:** 2514–2597 (77 stubs in `server/opts_test.go`)
|
||
|
||
These test config file parsing and option binding. The Go tests at these line numbers use `ProcessConfigFile`, `MergeOptions`, and `SetBaselineOptions` — all of which are ported in `Config/ServerOptionsConfiguration.cs` and `ServerOptions.cs`.
|
||
|
||
### Step 1: Read existing file and Go reference
|
||
|
||
```bash
|
||
# View existing ServerOptionsTests.cs (333 lines, 9 complete tests already written)
|
||
cat dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerOptionsTests.cs
|
||
|
||
# List all 77 stubs from DB with Go line numbers
|
||
sqlite3 porting.db "
|
||
SELECT id, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests
|
||
WHERE module_id=8 AND go_file='server/opts_test.go' AND status='stub'
|
||
ORDER BY go_line_number;"
|
||
```
|
||
|
||
### Step 2: Classify stubs
|
||
|
||
Before writing tests, check each Go test to determine if it requires a running server:
|
||
- Tests using `RunServer()` or `RunBasicJetStreamServer()` → mark `deferred` instead
|
||
- Tests that only call `ProcessConfigFile()`, `MergeOptions()`, `SetBaseline()`, or validate option structs → write as unit test
|
||
|
||
**Expected classification:** Most opts stubs are config-parsing tests (unit). Some may be server-integration (mark `deferred`).
|
||
|
||
For each stub that needs a running server, run:
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status deferred --db porting.db
|
||
```
|
||
|
||
### Step 3: Write test bodies
|
||
|
||
For each remaining stub, add a test method to `ServerOptionsTests.cs`.
|
||
|
||
Pattern for a config file test:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestConfigFile — verify ProcessConfigFile loads host/port from JSON.
|
||
/// </summary>
|
||
[Fact] // T:2514
|
||
public void ConfigFile_ShouldSucceed()
|
||
{
|
||
var json = """{"host":"localhost","port":4222}""";
|
||
var tmp = Path.GetTempFileName() + ".json";
|
||
File.WriteAllText(tmp, json);
|
||
try
|
||
{
|
||
var opts = ServerOptionsConfiguration.ProcessConfigFile(tmp);
|
||
opts.Host.ShouldBe("localhost");
|
||
opts.Port.ShouldBe(4222);
|
||
}
|
||
finally { File.Delete(tmp); }
|
||
}
|
||
```
|
||
|
||
Pattern for a flag-override test:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestRouteFlagOverride — flag opts override file opts.
|
||
/// </summary>
|
||
[Fact] // T:2517
|
||
public void RouteFlagOverride_ShouldSucceed()
|
||
{
|
||
var fileOpts = new ServerOptions { Routes = ["nats://server1:5222"] };
|
||
var flagOpts = new ServerOptions { Routes = ["nats://server2:5222"] };
|
||
fileOpts.MergeOptions(flagOpts);
|
||
fileOpts.Routes.ShouldContain("nats://server2:5222");
|
||
}
|
||
```
|
||
|
||
For Go tests that use config files, create a temp file with JSON content matching what the Go test uses, call `ProcessConfigFile`, and assert the resulting `ServerOptions` fields.
|
||
|
||
### Step 4: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~ServerOptionsTests"
|
||
```
|
||
|
||
Fix any failures by comparing Go reference at the line numbers from Step 1.
|
||
|
||
### Step 5: Mark complete in DB
|
||
|
||
For each test whose body was written and is passing:
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status complete --db porting.db
|
||
```
|
||
|
||
Or in bulk via SQLite if all 77 passed:
|
||
```bash
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE module_id=8 AND go_file='server/opts_test.go' AND status='stub';"
|
||
```
|
||
|
||
### Step 6: Run full suite and commit
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerOptionsTests.cs porting.db reports/
|
||
git commit -m "feat(p7-02): fill opts_test.go stubs (77 tests) — ServerOptionsTests"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 4: P7-03 — JWT Stubs (88 tests)
|
||
|
||
**Go source:** `golang/nats-server/server/jwt_test.go`
|
||
**Target .NET file:** `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs`
|
||
**DB IDs:** 1809–1896 (88 stubs)
|
||
|
||
These test JWT decode, validate, and claims extraction. The Go tests at these line numbers test pure JWT functions in `jwt.go` — they do NOT require a running server.
|
||
|
||
### Step 1: List stubs
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests
|
||
WHERE module_id=8 AND go_file='server/jwt_test.go' AND status='stub'
|
||
ORDER BY go_line_number;"
|
||
```
|
||
|
||
### Step 2: Read existing test file
|
||
|
||
```bash
|
||
cat dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs
|
||
```
|
||
|
||
The file has complete tests for `JwtPrefix`, `WipeSlice`, `ValidateSrc`, etc. Stubs cover the remaining JWT functions.
|
||
|
||
### Step 3: Write test bodies
|
||
|
||
Look up each Go test by line number in `golang/nats-server/server/jwt_test.go`. Write the corresponding .NET test method. The .NET JWT functions live in `Auth/JwtProcessor.cs`.
|
||
|
||
Pattern for a claims decode test:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestJwtHeader — verify header prefix detection.
|
||
/// </summary>
|
||
[Fact] // T:1809
|
||
public void JwtHeader_ValidJwt_ReturnsTrue()
|
||
{
|
||
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.payload.sig";
|
||
JwtProcessor.IsJwt(token).ShouldBeTrue();
|
||
}
|
||
```
|
||
|
||
Pattern for a validation error test:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestDecodeAccountClaims_InvalidToken.
|
||
/// </summary>
|
||
[Fact] // T:1820
|
||
public void DecodeAccountClaims_InvalidToken_ReturnsError()
|
||
{
|
||
var (claims, err) = JwtProcessor.DecodeAccountClaims("notajwt");
|
||
claims.ShouldBeNull();
|
||
err.ShouldNotBeNull();
|
||
}
|
||
```
|
||
|
||
### Step 4: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~JwtProcessorTests"
|
||
```
|
||
|
||
### Step 5: Mark complete and commit
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE module_id=8 AND go_file='server/jwt_test.go' AND status='stub';"
|
||
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs porting.db reports/
|
||
git commit -m "feat(p7-03): fill jwt_test.go stubs (88 tests) — JwtProcessorTests"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 5: P7-04 — Auth & Config-Check Stubs (40 tests)
|
||
|
||
**Go sources:**
|
||
- `golang/nats-server/server/auth_test.go` (6 stubs, IDs 147–153, class `AuthHandlerTests`)
|
||
- `golang/nats-server/server/auth_callout_test.go` (31 stubs, IDs 111–141, class `AuthCalloutTests`)
|
||
- `golang/nats-server/server/config_check_test.go` (3 stubs, IDs 271–273, class `ConfigCheckTests`)
|
||
|
||
**Target .NET files:**
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/AuthHandlerTests.cs` (add 6 methods)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/AuthCalloutTests.cs` (create or add 31 methods)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Config/ConfigCheckTests.cs` (create 3 methods)
|
||
|
||
### Step 1: List stubs per file
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_file, go_method, go_line_number, dotnet_class, dotnet_method
|
||
FROM unit_tests
|
||
WHERE id IN (111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
|
||
147,148,149,150,151,152,153,
|
||
271,272,273)
|
||
ORDER BY go_file, go_line_number;"
|
||
```
|
||
|
||
### Step 2: Auth handler stubs (6 tests)
|
||
|
||
Read existing `AuthHandlerTests.cs`, then add these 6 test methods:
|
||
|
||
- **T:147** `TestUserUnknownAllowedConnectionType` → test that passing an unrecognized connection type string to `ValidateAllowedConnections` returns an error
|
||
- **T:149** `TestNoAuthUser` → test that `ProcessClientOrLeafAuthentication` with no auth configured returns true when `AuthRequired=false`
|
||
- **T:150** `TestNoAuthUserNkey` → test that an NKey user with valid credentials authenticates when NKey list has one entry
|
||
- **T:151** `TestUserConnectionDeadline` → test `User.ConnectionDeadline` parsing from Unix timestamp in claims
|
||
- **T:152** `TestNoAuthUserNoConnectProto` → test that missing connect protocol fields get default handling
|
||
- **T:153** `TestAuthProxyRequired` → test that `GetAuthErrClosedState(new AuthProxyRequiredException())` returns `ClosedState.ProxyRequired`
|
||
|
||
Pattern:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestAuthProxyRequired.
|
||
/// </summary>
|
||
[Fact] // T:153
|
||
public void AuthProxyRequired_ShouldReturnProxyRequiredState()
|
||
{
|
||
var state = AuthHandler.GetAuthErrClosedState(new AuthProxyRequiredException("proxy required"));
|
||
state.ShouldBe(ClosedState.ProxyRequired);
|
||
}
|
||
```
|
||
|
||
### Step 3: Auth callout stubs (31 tests, class `AuthCalloutTests`)
|
||
|
||
**Important:** Check Go tests at lines 212, 329, etc. in `auth_callout_test.go`. Most use `RunBasicJetStreamServer()`. If they do, mark them **`deferred`** instead:
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status deferred --db porting.db
|
||
```
|
||
|
||
For those that test pure helper types/functions (e.g., `FillClientInfo`, `FillConnectOpts`, serialization), write the unit test body.
|
||
|
||
Check which file `AuthCalloutTests` lives in:
|
||
```bash
|
||
grep -rn "AuthCalloutTests\|class AuthCallout" dotnet/tests/ | grep -v obj
|
||
```
|
||
|
||
If the file doesn't exist, create `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/AuthCalloutTests.cs`:
|
||
```csharp
|
||
// Copyright 2012-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server;
|
||
using ZB.MOM.NatsNet.Server.Auth;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.Auth;
|
||
|
||
/// <summary>
|
||
/// Tests for AuthCallout helper types.
|
||
/// Mirrors type/helper tests from server/auth_callout_test.go.
|
||
/// </summary>
|
||
public class AuthCalloutTests
|
||
{
|
||
// test methods here
|
||
}
|
||
```
|
||
|
||
### Step 4: Config check stubs (3 tests, class `ConfigCheckTests`)
|
||
|
||
Go tests at lines corresponding to IDs 271–273:
|
||
- **T:271** `TestConfigCheck` → test that a valid config passes validation
|
||
- **T:272** `TestConfigCheckIncludes` → test that includes are resolved
|
||
- **T:273** `TestConfigCheckMultipleErrors` → test that multiple validation errors are all reported
|
||
|
||
Create `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Config/ConfigCheckTests.cs`:
|
||
```csharp
|
||
// Copyright 2012-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server;
|
||
using ZB.MOM.NatsNet.Server.Config;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.Config;
|
||
|
||
/// <summary>
|
||
/// Tests for configuration validation.
|
||
/// Mirrors server/config_check_test.go.
|
||
/// </summary>
|
||
public class ConfigCheckTests
|
||
{
|
||
// test methods here
|
||
}
|
||
```
|
||
|
||
### Step 5: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~AuthHandlerTests|FullyQualifiedName~AuthCalloutTests|FullyQualifiedName~ConfigCheckTests"
|
||
```
|
||
|
||
### Step 6: Mark complete (or deferred) and commit
|
||
|
||
For each test marked complete:
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status complete --db porting.db
|
||
```
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-04): fill auth & config_check stubs — AuthHandlerTests, AuthCalloutTests, ConfigCheckTests"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 6: P7-05 — Signal & Log Stubs (19 tests)
|
||
|
||
**Go sources:**
|
||
- `golang/nats-server/server/signal_test.go` (16 stubs, IDs 2913–2928, class `SignalHandlerTests`)
|
||
- `golang/nats-server/server/log_test.go` (3 stubs, IDs 2020–2022, class `ServerLoggerTests`)
|
||
|
||
**Target .NET files:**
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/SignalHandlerTests.cs` (add 16 methods)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ServerLoggerTests.cs` (create 3 methods)
|
||
|
||
### Step 1: List stubs
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_file, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests WHERE id BETWEEN 2913 AND 2928
|
||
ORDER BY go_line_number;"
|
||
|
||
sqlite3 porting.db "
|
||
SELECT id, go_file, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests WHERE id IN (2020, 2021, 2022);"
|
||
```
|
||
|
||
### Step 2: Signal stubs (16 tests)
|
||
|
||
Most signal tests involve `ProcessSignal(command, pidExpression)`. Key tests:
|
||
|
||
- **T:2913–2928** cover: multi-process glob, pgrep errors, resolve single process, invalid command/PID, sending QUIT/TERM/Reopen/Reload to specific PID, lame duck mode, interrupt/term exit codes.
|
||
|
||
Read each Go test at the given line in `golang/nats-server/server/signal_test.go`. For tests that start a real server or spawn processes, mark `deferred`.
|
||
|
||
Pattern for pure logic tests:
|
||
```csharp
|
||
/// <summary>
|
||
/// Mirrors TestProcessSignalInvalidCommand.
|
||
/// </summary>
|
||
[Fact] // T:2919
|
||
public void ProcessSignalInvalidCommand_ShouldReturnError()
|
||
{
|
||
var err = SignalHandler.ProcessSignal((ServerCommand)99, "0");
|
||
err.ShouldNotBeNull();
|
||
}
|
||
```
|
||
|
||
Add test methods to the existing `SignalHandlerTests.cs` file.
|
||
|
||
### Step 3: Log stubs (3 tests)
|
||
|
||
The 3 stubs test password/token masking in protocol trace output:
|
||
- **T:2020** `TestNoPasswordsFromConnectTrace` — connect string with `pass=secret` should have password redacted in trace
|
||
- **T:2021** `TestRemovePassFromTrace` — `RemovePassFromTrace("... pass: secret ...")` returns string without the password
|
||
- **T:2022** `TestRemoveAuthTokenFromTrace` — `RemoveAuthTokenFromTrace("... auth_token: tok ...")` removes the token
|
||
|
||
Create `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ServerLoggerTests.cs`:
|
||
```csharp
|
||
// Copyright 2012-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server.Internal;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.Internal;
|
||
|
||
/// <summary>
|
||
/// Tests for password/token masking in server logging.
|
||
/// Mirrors server/log_test.go tests for trace sanitization.
|
||
/// </summary>
|
||
public class ServerLoggerTests
|
||
{
|
||
[Fact] // T:2020
|
||
public void NoPasswordsFromConnectTrace_ShouldRedactPass()
|
||
{
|
||
// See golang/nats-server/server/log_test.go:TestNoPasswordsFromConnectTrace
|
||
// Call the relevant NatsServer or ServerLogging method that strips passwords from CONNECT traces
|
||
// Assert the returned string does not contain the literal password
|
||
}
|
||
|
||
[Fact] // T:2021
|
||
public void RemovePassFromTrace_ShouldRemovePassword()
|
||
{
|
||
var input = """{"verbose":false,"pedantic":false,"user":"derek","pass":"s3cr3t!","tls_required":false,"name":""}""";
|
||
var result = ServerLogging.RemovePassFromTrace(input);
|
||
result.ShouldNotContain("s3cr3t!");
|
||
result.ShouldContain("\"pass\":\"\"");
|
||
}
|
||
|
||
[Fact] // T:2022
|
||
public void RemoveAuthTokenFromTrace_ShouldRemoveToken()
|
||
{
|
||
var input = """{"verbose":false,"auth_token":"mytoken","tls_required":false}""";
|
||
var result = ServerLogging.RemoveAuthTokenFromTrace(input);
|
||
result.ShouldNotContain("mytoken");
|
||
result.ShouldContain("\"auth_token\":\"\"");
|
||
}
|
||
}
|
||
```
|
||
|
||
Check that `ServerLogging.RemovePassFromTrace` and `RemoveAuthTokenFromTrace` exist. If not, add them to `Internal/ServerLogging.cs` by reading the Go source in `server/log.go`.
|
||
|
||
### Step 4: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~SignalHandlerTests|FullyQualifiedName~ServerLoggerTests"
|
||
```
|
||
|
||
### Step 5: Mark complete and commit
|
||
|
||
```bash
|
||
# Mark signal stubs complete (adjust IDs for any that were deferred instead)
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE id BETWEEN 2913 AND 2928 AND status='stub';"
|
||
|
||
for id in 2020 2021 2022; do
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update $id --status complete --db porting.db
|
||
done
|
||
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-05): fill signal & log stubs (19 tests) — SignalHandlerTests, ServerLoggerTests"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 7: P7-06 — Memory Store & Store Interface Tests (58 tests)
|
||
|
||
**Go sources:**
|
||
- `golang/nats-server/server/memstore_test.go` (41 tests, IDs 2023–2063)
|
||
- `golang/nats-server/server/store_test.go` (17 tests, IDs 2941–2957)
|
||
|
||
**Target .NET files:**
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamMemoryStoreTests.cs` (create, 41 tests)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/StorageEngineTests.cs` (create, 17 tests)
|
||
|
||
**Important:** These test the in-memory JetStream message store. The Go `memstore.go` is a pure data structure with no server dependency. Verify the .NET port exists before writing tests:
|
||
|
||
```bash
|
||
find dotnet/src -name "*.cs" | xargs grep -l "MemStore\|MemoryStore\|MsgStore" | grep -v obj
|
||
```
|
||
|
||
### Step 1: Understand memstore interface
|
||
|
||
Read the Go store interface at the top of `golang/nats-server/server/store.go`. The .NET equivalent should implement a similar `IMsgStore` interface. If it hasn't been ported yet, note the missing implementation and write tests that call methods as they should exist.
|
||
|
||
### Step 2: Classify memstore tests
|
||
|
||
From `golang/nats-server/server/memstore_test.go`, check each test:
|
||
- Tests using `NewMemStore()` only → unit tests (write .NET equivalent)
|
||
- Tests using `RunServer()` or requiring cluster → mark `deferred`
|
||
|
||
### Step 3: Create JetStreamMemoryStoreTests.cs
|
||
|
||
```csharp
|
||
// Copyright 2018-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server.JetStream;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
||
|
||
/// <summary>
|
||
/// Tests for the in-memory JetStream message store.
|
||
/// Mirrors server/memstore_test.go.
|
||
/// </summary>
|
||
public class JetStreamMemoryStoreTests
|
||
{
|
||
[Fact] // T:2023
|
||
public void MemStoreBasics_ShouldStoreAndRetrieve()
|
||
{
|
||
// NewMemStore() → store message → retrieve → verify payload matches
|
||
// Reference: golang/nats-server/server/memstore_test.go:TestMemStoreBasics
|
||
}
|
||
|
||
// ... remaining test methods
|
||
}
|
||
```
|
||
|
||
### Step 4: Create StorageEngineTests.cs
|
||
|
||
```csharp
|
||
// Copyright 2018-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server.JetStream;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
||
|
||
/// <summary>
|
||
/// Tests for the JetStream store interface contracts.
|
||
/// Mirrors server/store_test.go.
|
||
/// </summary>
|
||
public class StorageEngineTests
|
||
{
|
||
// test methods
|
||
}
|
||
```
|
||
|
||
### Step 5: Map tests in DB
|
||
|
||
For each new test method, update the DB to mark it complete and fill in the dotnet_method:
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- test update <id> \
|
||
--status complete --db porting.db
|
||
|
||
# Also update dotnet_method if not already set:
|
||
dotnet run --project tools/NatsNet.PortTracker -- test map <id> \
|
||
--class JetStreamMemoryStoreTests \
|
||
--method <MethodName> --db porting.db
|
||
```
|
||
|
||
### Step 6: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~JetStreamMemoryStoreTests|FullyQualifiedName~StorageEngineTests"
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
```
|
||
|
||
### Step 7: Commit
|
||
|
||
```bash
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-06): port memstore & store interface tests (58 tests)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 8: P7-07 — File Store Unit Tests, First Half (~125 tests)
|
||
|
||
**Go source:** `golang/nats-server/server/filestore_test.go`
|
||
**Target .NET file:** `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs`
|
||
**DB IDs:** 351–474 (first ~125 of 249 total, `server/filestore_test.go`)
|
||
|
||
### Step 1: List first-half IDs and Go line numbers
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests
|
||
WHERE module_id=8 AND go_file='server/filestore_test.go'
|
||
ORDER BY go_line_number
|
||
LIMIT 125;"
|
||
```
|
||
|
||
The split point for P7-07 vs P7-08 is approximately test ID 474 (the first 125 tests by Go line order).
|
||
|
||
### Step 2: Verify filestore.go is ported
|
||
|
||
```bash
|
||
find dotnet/src -name "*.cs" | xargs grep -l "FileStore\|fileStore" | grep -v obj
|
||
```
|
||
|
||
If `FileStore` is not yet ported, this task must port the implementation first, then write tests. Check Phase 6 session notes for filestore porting status.
|
||
|
||
### Step 3: Classify file store tests
|
||
|
||
From `golang/nats-server/server/filestore_test.go`, each test that uses `newFileStore()` or `newFileStoreWithCreated()` is a unit test. Tests that use `RunServer()` are deferred.
|
||
|
||
### Step 4: Create JetStreamFileStoreTests.cs (first half)
|
||
|
||
```csharp
|
||
// Copyright 2018-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using Shouldly;
|
||
using ZB.MOM.NatsNet.Server.JetStream;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
||
|
||
/// <summary>
|
||
/// Unit tests for JetStream file store read/write/purge operations.
|
||
/// Mirrors server/filestore_test.go (first half).
|
||
/// </summary>
|
||
public class JetStreamFileStoreTests
|
||
{
|
||
private static string TempDir() => Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||
|
||
[Fact] // T:351
|
||
public void FileStoreBasics_ShouldStoreAndRetrieve()
|
||
{
|
||
// newFileStore(config) → store message → load → verify
|
||
// Reference: golang/nats-server/server/filestore_test.go:TestFileStoreBasics
|
||
}
|
||
|
||
// ... remaining first-half tests
|
||
}
|
||
```
|
||
|
||
### Step 5: Run, mark, commit
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~JetStreamFileStoreTests"
|
||
|
||
# Mark first-half tests complete via SQLite
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE module_id=8 AND go_file='server/filestore_test.go' AND status='not_started'
|
||
AND id <= 474;" -- adjust ID boundary based on actual split
|
||
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-07): port filestore tests first half (~125 tests)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 9: P7-08 — File Store Unit Tests, Second Half (~124 tests)
|
||
|
||
**Go source:** `golang/nats-server/server/filestore_test.go`
|
||
**Target .NET file:** `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs`
|
||
**DB IDs:** 475–599 (remaining ~124 of 249 total)
|
||
|
||
### Step 1: List second-half IDs
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_method, go_line_number, dotnet_method
|
||
FROM unit_tests
|
||
WHERE module_id=8 AND go_file='server/filestore_test.go' AND status='not_started'
|
||
ORDER BY go_line_number;"
|
||
```
|
||
|
||
### Step 2: Add remaining test methods
|
||
|
||
Continue adding methods to `JetStreamFileStoreTests.cs` for IDs 475–599.
|
||
|
||
### Step 3: Run, mark, commit
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~JetStreamFileStoreTests"
|
||
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE module_id=8 AND go_file='server/filestore_test.go' AND status='not_started';"
|
||
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-08): port filestore tests second half (~124 tests)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 10: P7-09 — JetStream Unit Tests (94 tests)
|
||
|
||
**Go sources:**
|
||
- `golang/nats-server/server/jetstream_errors_test.go` (4 tests, IDs 1381–1384)
|
||
- `golang/nats-server/server/jetstream_versioning_test.go` (18 tests, IDs 1791–1808)
|
||
- `golang/nats-server/server/jetstream_batching_test.go` (29 tests, IDs 716–744)
|
||
- `golang/nats-server/server/dirstore_test.go` (12 tests, IDs 285–296)
|
||
- `golang/nats-server/server/accounts_test.go` (31 tests, IDs 80–110)
|
||
|
||
**Target .NET files:**
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamErrorsTests.cs` (create)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamVersioningTests.cs` (create)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamBatchingTests.cs` (create)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/DirectoryStoreTests.cs` (create)
|
||
- `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Accounts/AccountTests.cs` (add 31 methods to existing)
|
||
|
||
### Step 1: List all IDs
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
SELECT id, go_file, go_method, go_line_number, dotnet_class, dotnet_method
|
||
FROM unit_tests
|
||
WHERE id IN (
|
||
SELECT id FROM unit_tests WHERE go_file='server/jetstream_errors_test.go' AND status='not_started'
|
||
UNION SELECT id FROM unit_tests WHERE go_file='server/jetstream_versioning_test.go' AND status='not_started'
|
||
UNION SELECT id FROM unit_tests WHERE go_file='server/jetstream_batching_test.go' AND status='not_started'
|
||
UNION SELECT id FROM unit_tests WHERE go_file='server/dirstore_test.go' AND status='not_started'
|
||
UNION SELECT id FROM unit_tests WHERE go_file='server/accounts_test.go' AND status='not_started'
|
||
)
|
||
ORDER BY go_file, go_line_number;"
|
||
```
|
||
|
||
### Step 2: JetStream errors (4 tests)
|
||
|
||
These test error type checking — pure type assertions:
|
||
```csharp
|
||
// JetStreamErrorsTests.cs
|
||
[Fact] // T:1381
|
||
public void IsApiError_WithApiError_ReturnsTrue()
|
||
{
|
||
var err = new ApiError(404, "not found");
|
||
JetStreamErrors.IsApiError(err).ShouldBeTrue();
|
||
}
|
||
```
|
||
|
||
### Step 3: JetStream versioning (18 tests)
|
||
|
||
These test version compatibility checks:
|
||
```csharp
|
||
// JetStreamVersioningTests.cs
|
||
[Fact] // T:1791
|
||
public void ServerVersion_BelowMinimum_ReturnsError()
|
||
{
|
||
var err = JetStreamVersioning.CheckServerVersion("2.1.0", "2.2.0");
|
||
err.ShouldNotBeNull();
|
||
}
|
||
```
|
||
|
||
### Step 4: JetStream batching (29 tests)
|
||
|
||
These test batch message accumulation logic (pure data structure, no server):
|
||
```csharp
|
||
// JetStreamBatchingTests.cs
|
||
[Fact] // T:716
|
||
public void BatchBasics_ShouldAccumulateMessages()
|
||
{
|
||
// Reference: golang/nats-server/server/jetstream_batching_test.go
|
||
}
|
||
```
|
||
|
||
### Step 5: Directory store (12 tests)
|
||
|
||
These test the JWT directory store (reading/writing JWT files):
|
||
```csharp
|
||
// DirectoryStoreTests.cs
|
||
[Fact] // T:285
|
||
public void DirStore_StoreAndFetch_ShouldRoundtrip()
|
||
{
|
||
var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||
Directory.CreateDirectory(dir);
|
||
try
|
||
{
|
||
// create DirStore, store a JWT, fetch it back, verify match
|
||
}
|
||
finally { Directory.Delete(dir, recursive: true); }
|
||
}
|
||
```
|
||
|
||
### Step 6: Account tests (31 tests)
|
||
|
||
These test account route mapping, imports, exports — pure account logic:
|
||
|
||
Read existing `AccountTests.cs`, then add 31 methods. Example:
|
||
```csharp
|
||
[Fact] // T:80
|
||
public void AccountMultipleServiceImports_SameSubject_ShouldSucceed()
|
||
{
|
||
// Reference: golang/nats-server/server/accounts_test.go line 2535
|
||
// Test that an account can have multiple service imports for the same subject
|
||
// from different remote accounts
|
||
}
|
||
```
|
||
|
||
### Step 7: Run tests
|
||
|
||
```bash
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~JetStreamErrorsTests|FullyQualifiedName~JetStreamVersioningTests|FullyQualifiedName~JetStreamBatchingTests|FullyQualifiedName~DirectoryStoreTests"
|
||
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
|
||
--filter "FullyQualifiedName~AccountTests"
|
||
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
|
||
```
|
||
|
||
### Step 8: Mark complete and commit
|
||
|
||
```bash
|
||
# Mark all P7-09 tests complete
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='complete'
|
||
WHERE module_id=8 AND status='not_started' AND go_file IN (
|
||
'server/jetstream_errors_test.go',
|
||
'server/jetstream_versioning_test.go',
|
||
'server/jetstream_batching_test.go',
|
||
'server/dirstore_test.go',
|
||
'server/accounts_test.go'
|
||
);"
|
||
|
||
./reports/generate-report.sh
|
||
git add dotnet/tests/ porting.db reports/
|
||
git commit -m "feat(p7-09): port JetStream error/versioning/batching/dirstore/accounts tests (94 tests)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 11: P7-10 — Mark Deferred, Integration Tests, Phase Close
|
||
|
||
This session has three parts: (A) mark 2,126 server-integration tests `deferred`, (B) replace integration test placeholder, (C) close DB + milestones.
|
||
|
||
### Part A: Mark server-integration tests as deferred
|
||
|
||
All `not_started` tests that require a running NATS server are marked `deferred`:
|
||
|
||
```bash
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='deferred'
|
||
WHERE status='not_started'
|
||
AND go_file NOT IN (
|
||
'server/accounts_test.go',
|
||
'server/dirstore_test.go',
|
||
'server/memstore_test.go',
|
||
'server/store_test.go',
|
||
'server/filestore_test.go',
|
||
'server/jetstream_errors_test.go',
|
||
'server/jetstream_versioning_test.go',
|
||
'server/jetstream_batching_test.go',
|
||
'server/opts_test.go'
|
||
);"
|
||
```
|
||
|
||
Verify:
|
||
```bash
|
||
sqlite3 porting.db "SELECT status, COUNT(*) FROM unit_tests GROUP BY status ORDER BY status;"
|
||
```
|
||
|
||
Expected after this update:
|
||
- complete: ~1,256 (319 old + ~937 new from P7-02 through P7-09)
|
||
- deferred: ~2,126
|
||
- n_a: ~187 (181 old + 6 thw benchmarks)
|
||
- stub: 0 (all filled)
|
||
- not_started: 0
|
||
- verified: ~114 (small modules)
|
||
|
||
### Part B: Replace integration test placeholder
|
||
|
||
Replace `dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/UnitTest1.cs` with `NatsServerBehaviorTests.cs`.
|
||
|
||
These tests run against the **Go NATS server** (not the .NET server). They establish a behavioral baseline.
|
||
|
||
**Prerequisites:** Go server must be running: `cd golang/nats-server && go run . -p 4222`
|
||
|
||
Create `dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/NatsServerBehaviorTests.cs`:
|
||
|
||
```csharp
|
||
// Copyright 2012-2025 The NATS Authors
|
||
// Licensed under the Apache License, Version 2.0
|
||
|
||
using NATS.Client.Core;
|
||
using Shouldly;
|
||
|
||
namespace ZB.MOM.NatsNet.Server.IntegrationTests;
|
||
|
||
/// <summary>
|
||
/// Behavioral baseline tests against the reference Go NATS server.
|
||
/// These tests require a running Go NATS server on localhost:4222.
|
||
/// Start with: cd golang/nats-server && go run . -p 4222
|
||
/// </summary>
|
||
[Trait("Category", "Integration")]
|
||
public class NatsServerBehaviorTests : IAsyncLifetime
|
||
{
|
||
private NatsConnection? _nats;
|
||
|
||
public async Task InitializeAsync()
|
||
{
|
||
_nats = new NatsConnection(new NatsOpts { Url = "nats://localhost:4222" });
|
||
await _nats.ConnectAsync();
|
||
}
|
||
|
||
public async Task DisposeAsync()
|
||
{
|
||
if (_nats is not null)
|
||
await _nats.DisposeAsync();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task BasicPubSub_ShouldDeliverMessage()
|
||
{
|
||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||
var received = new TaskCompletionSource<string>();
|
||
|
||
await using var sub = await _nats!.SubscribeAsync<string>("test.hello", cancellationToken: cts.Token);
|
||
_ = Task.Run(async () =>
|
||
{
|
||
await foreach (var msg in sub.Msgs.ReadAllAsync(cts.Token))
|
||
{
|
||
received.TrySetResult(msg.Data ?? "");
|
||
break;
|
||
}
|
||
}, cts.Token);
|
||
|
||
await _nats.PublishAsync("test.hello", "world");
|
||
var result = await received.Task.WaitAsync(cts.Token);
|
||
result.ShouldBe("world");
|
||
}
|
||
|
||
[Fact]
|
||
public async Task WildcardSubscription_DotStar_ShouldMatch()
|
||
{
|
||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||
var received = new TaskCompletionSource<string>();
|
||
|
||
await using var sub = await _nats!.SubscribeAsync<string>("foo.*", cancellationToken: cts.Token);
|
||
_ = Task.Run(async () =>
|
||
{
|
||
await foreach (var msg in sub.Msgs.ReadAllAsync(cts.Token))
|
||
{
|
||
received.TrySetResult(msg.Subject);
|
||
break;
|
||
}
|
||
}, cts.Token);
|
||
|
||
await _nats.PublishAsync("foo.bar", "payload");
|
||
var subject = await received.Task.WaitAsync(cts.Token);
|
||
subject.ShouldBe("foo.bar");
|
||
}
|
||
|
||
[Fact]
|
||
public async Task WildcardSubscription_GreaterThan_ShouldMatchMultiLevel()
|
||
{
|
||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||
var received = new TaskCompletionSource<string>();
|
||
|
||
await using var sub = await _nats!.SubscribeAsync<string>("foo.>", cancellationToken: cts.Token);
|
||
_ = Task.Run(async () =>
|
||
{
|
||
await foreach (var msg in sub.Msgs.ReadAllAsync(cts.Token))
|
||
{
|
||
received.TrySetResult(msg.Subject);
|
||
break;
|
||
}
|
||
}, cts.Token);
|
||
|
||
await _nats.PublishAsync("foo.bar.baz", "payload");
|
||
var subject = await received.Task.WaitAsync(cts.Token);
|
||
subject.ShouldBe("foo.bar.baz");
|
||
}
|
||
|
||
[Fact]
|
||
public async Task QueueGroup_ShouldDeliverToOnlyOneSubscriber()
|
||
{
|
||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||
var count1 = 0;
|
||
var count2 = 0;
|
||
|
||
await using var sub1 = await _nats!.SubscribeAsync<string>("qg.test", queueGroup: "workers", cancellationToken: cts.Token);
|
||
await using var sub2 = await _nats!.SubscribeAsync<string>("qg.test", queueGroup: "workers", cancellationToken: cts.Token);
|
||
|
||
_ = Task.Run(async () =>
|
||
{
|
||
await foreach (var _ in sub1.Msgs.ReadAllAsync(cts.Token))
|
||
Interlocked.Increment(ref count1);
|
||
}, cts.Token);
|
||
_ = Task.Run(async () =>
|
||
{
|
||
await foreach (var _ in sub2.Msgs.ReadAllAsync(cts.Token))
|
||
Interlocked.Increment(ref count2);
|
||
}, cts.Token);
|
||
|
||
for (var i = 0; i < 10; i++)
|
||
await _nats.PublishAsync("qg.test", $"msg{i}");
|
||
|
||
await Task.Delay(500, cts.Token);
|
||
(count1 + count2).ShouldBe(10);
|
||
count1.ShouldBeGreaterThan(0);
|
||
count2.ShouldBeGreaterThan(0);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task ConnectDisconnect_ShouldNotThrow()
|
||
{
|
||
var nats2 = new NatsConnection(new NatsOpts { Url = "nats://localhost:4222" });
|
||
await Should.NotThrowAsync(async () =>
|
||
{
|
||
await nats2.ConnectAsync();
|
||
await nats2.DisposeAsync();
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
Delete the placeholder:
|
||
```bash
|
||
rm dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/UnitTest1.cs
|
||
```
|
||
|
||
Run the integration tests (requires Go server running):
|
||
```bash
|
||
# In terminal 1: start Go server
|
||
cd golang/nats-server && go run . -p 4222
|
||
|
||
# In terminal 2: run integration tests
|
||
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/
|
||
```
|
||
|
||
### Part C: Final DB verification and milestone close
|
||
|
||
#### Mark server module verified (features and module)
|
||
|
||
After all unit tests are written and verified:
|
||
```bash
|
||
# Mark all complete tests in server module as verified
|
||
sqlite3 porting.db "
|
||
UPDATE unit_tests SET status='verified'
|
||
WHERE module_id=8 AND status='complete';"
|
||
|
||
# Mark all features in server module verified
|
||
dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status verified --all-in-module 8 --db porting.db
|
||
|
||
# Mark server module verified
|
||
dotnet run --project tools/NatsNet.PortTracker -- module update 8 --status verified --db porting.db
|
||
```
|
||
|
||
#### Run phase check
|
||
|
||
```bash
|
||
dotnet run --project tools/NatsNet.PortTracker -- phase check 7 --db porting.db
|
||
```
|
||
|
||
#### Generate final report
|
||
|
||
```bash
|
||
./reports/generate-report.sh
|
||
dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
|
||
```
|
||
|
||
#### Close Gitea issues #45–#52
|
||
|
||
```bash
|
||
for issue in 45 46 47 48 49 50 51 52; do
|
||
curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/issues/${issue}" \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-d '{"state":"closed"}'
|
||
done
|
||
```
|
||
|
||
#### Close milestones 7 and 8
|
||
|
||
```bash
|
||
for milestone in 7 8; do
|
||
curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/milestones/${milestone}" \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-d '{"state":"closed"}'
|
||
done
|
||
```
|
||
|
||
#### Final commit
|
||
|
||
```bash
|
||
git add porting.db reports/ dotnet/tests/
|
||
git commit -m "feat(p7-10): mark deferred tests, add integration tests, close Phase 7
|
||
|
||
- 2126 server-integration tests marked deferred
|
||
- NatsServerBehaviorTests.cs replaces UnitTest1.cs placeholder
|
||
- Server module and all features marked verified
|
||
- Gitea issues #45-#52 and milestones 7 & 8 closed"
|
||
```
|
||
|
||
---
|
||
|
||
## Completion Checklist
|
||
|
||
- [ ] Schema: `unit_tests.status` includes `'deferred'`
|
||
- [ ] 11 small modules: status `verified`
|
||
- [ ] All 224 stubs: status `complete` or `deferred` (none remain as `stub`)
|
||
- [ ] ~401 unit tests: status `complete` (new test methods written)
|
||
- [ ] ~2,126 server-integration tests: status `deferred`
|
||
- [ ] 6 thw benchmarks: status `n_a`
|
||
- [ ] `dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/` passes
|
||
- [ ] `dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/` passes
|
||
- [ ] `dotnet run --project tools/NatsNet.PortTracker -- phase check 7 --db porting.db` passes
|
||
- [ ] Gitea issues #45–#52 closed
|
||
- [ ] Gitea milestones 7 and 8 closed
|