diff --git a/agent/deploy/erasure_codec_test.go b/agent/deploy/erasure_codec_test.go new file mode 100644 index 0000000..9fdbd99 --- /dev/null +++ b/agent/deploy/erasure_codec_test.go @@ -0,0 +1,76 @@ +package deploy + +import ( + "strings" + "testing" + + "github.com/klauspost/reedsolomon" +) + +func TestErasureParamsNormalizeDefaults(t *testing.T) { + p := erasureParams{} + got, err := p.normalize() + if err != nil { + t.Fatal(err) + } + if got.DataShards != erasureDefaultDataShards || got.ParityShards != erasureDefaultParityShards { + t.Fatalf("defaults=%+v", got) + } + if got.minShards() != erasureDefaultDataShards { + t.Fatalf("minShards=%d", got.minShards()) + } + if got.totalShards() != erasureDefaultDataShards+erasureDefaultParityShards { + t.Fatalf("totalShards=%d", got.totalShards()) + } +} + +func TestDecodeErasureShardsInsufficientPresent(t *testing.T) { + payload := []byte("codec-insufficient") + p := erasureParams{DataShards: 2, ParityShards: 2} + enc, err := reedsolomon.New(p.DataShards, p.ParityShards) + if err != nil { + t.Fatal(err) + } + shards, err := enc.Split(payload) + if err != nil { + t.Fatal(err) + } + if err := enc.Encode(shards); err != nil { + t.Fatal(err) + } + shards[0] = nil + shards[1] = nil + shards[2] = nil + _, err = decodeErasureShards(shards, len(payload), p) + if err == nil || !strings.Contains(err.Error(), "need 2 shards") { + t.Fatalf("err=%v", err) + } +} + +func TestDecodeErasureShardsSliceTooShort(t *testing.T) { + _, err := decodeErasureShards([][]byte{{1}, {2}}, 4, erasureParams{DataShards: 2, ParityShards: 2}) + if err == nil || !strings.Contains(err.Error(), "shard slice too short") { + t.Fatalf("err=%v", err) + } +} + +func TestDecodeErasureShardsVerificationFailed(t *testing.T) { + payload := []byte("codec-bad-shard") + p := erasureParams{DataShards: 2, ParityShards: 1} + enc, err := reedsolomon.New(p.DataShards, p.ParityShards) + if err != nil { + t.Fatal(err) + } + shards, err := enc.Split(payload) + if err != nil { + t.Fatal(err) + } + if err := enc.Encode(shards); err != nil { + t.Fatal(err) + } + shards[2][0] ^= 0xff + _, err = decodeErasureShards(shards, len(payload), p) + if err == nil || !strings.Contains(err.Error(), "verification failed") { + t.Fatalf("err=%v", err) + } +} diff --git a/agent/deploy/erasure_launch_unix.go b/agent/deploy/erasure_launch_unix.go index ff4bec5..763b8de 100644 --- a/agent/deploy/erasure_launch_unix.go +++ b/agent/deploy/erasure_launch_unix.go @@ -4,7 +4,6 @@ package deploy import ( "fmt" - "strings" ) func launchErasureStagedBinary(dest, launch, dllExport string, deferMining, spreadInstall bool) (string, error) { diff --git a/agent/deploy/natpunch.go b/agent/deploy/natpunch.go index 7078ab6..70bbfea 100644 --- a/agent/deploy/natpunch.go +++ b/agent/deploy/natpunch.go @@ -336,7 +336,13 @@ func ScanLocalSubnet(maxHosts int) string { return b.String() } +// probePortsFn overrides TCP port probes in tests (nil = live dial). +var probePortsFn func(host string, ports []int) []int + func probePorts(host string, ports []int) []int { + if probePortsFn != nil { + return probePortsFn(host, ports) + } var open []int for _, p := range ports { conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(p)), 800*time.Millisecond) diff --git a/agent/deploy/scale_limits_test.go b/agent/deploy/scale_limits_test.go index e3ada46..3de0cbf 100644 --- a/agent/deploy/scale_limits_test.go +++ b/agent/deploy/scale_limits_test.go @@ -14,6 +14,10 @@ func TestScaleLimitConstants(t *testing.T) { } func TestScanLocalSubnetClampsToMaxHosts(t *testing.T) { + prev := probePortsFn + probePortsFn = func(host string, ports []int) []int { return nil } + defer func() { probePortsFn = prev }() + _ = ScanLocalSubnet(999) _ = ScanLocalSubnet(0) } diff --git a/tests/README.md b/tests/README.md index eed8328..06ab8bf 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,6 +1,6 @@ # AetherForge Test Suite -**Current counts (2026-06-07):** Go server **793** `Test*` · Go agent **608** `Test*` · Vitest **791** tests in **95** files · Playwright **25** tests in **8** spec files. Refresh: `go test ./... -list .` (server/agent), `npm run test -- --run` (Vitest), `npx playwright test --list` (E2E). Full suite: `test.bat` → `scripts/test-suite.ps1`. +**Current counts (2026-06-07):** Go server **793** `Test*` · Go agent **612** `Test*` · Vitest **791** tests in **95** files · Playwright **25** tests in **8** spec files. Refresh: `go test ./... -list .` (server/agent), `npm run test -- --run` (Vitest), `npx playwright test --list` (E2E). Full suite: `test.bat` → `scripts/test-suite.ps1`. ## Master validation (operator commands)