From 24f090e81ed014c8abb381a1988a7374d38e0f32 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 23 Jul 2026 03:18:34 -0700 Subject: [PATCH] Fix network hangs in recon unit tests and update README setup documentation --- README.md | 10 ++++++++++ server/internal/recon/recon_stream_test.go | 7 +++++++ server/internal/recon/recon_test.go | 14 ++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/README.md b/README.md index a0d4459..c7e3999 100644 --- a/README.md +++ b/README.md @@ -809,6 +809,16 @@ AetherForge is optimized for large-scale fleet management: --- +## Testing & Validation Setup + +AetherForge includes a full cross-platform test suite (`test.bat` or `scripts/test-suite.ps1`) covering Go backend services, Go agent modules, Fusion bundler, and Vite/React frontend components: + +- **Full Suite Execution:** Run `.\test.bat` from PowerShell / CMD to validate all unit, compilation, and E2E test phases. +- **Fast Unit Testing:** Run `.\test.bat -SkipE2E -SkipBuild` to execute all Go and Vitest unit tests in seconds without waiting for production binary builds or Playwright browser runs. +- **Offline Network Isolation:** `recon` module unit tests utilize stubbed banner hooks (`SetBannerHooks`, `SetPortDialHook`, `SetFetchPageHook`) to isolate tests from real DNS resolutions and external HTTP requests. + +--- + ## License Private use. Monero mining uses the RandomX algorithm (BSD-3-Clause) via `git.gammaspectra.live/P2Pool/go-randomx`. diff --git a/server/internal/recon/recon_stream_test.go b/server/internal/recon/recon_stream_test.go index 389d901..e1ee860 100644 --- a/server/internal/recon/recon_stream_test.go +++ b/server/internal/recon/recon_stream_test.go @@ -29,6 +29,13 @@ func TestScanStreamEmitsPortsFirst(t *testing.T) { return 200, `Home`, nil }) t.Cleanup(func() { SetFetchPageHook(nil) }) + SetBannerHooks( + func(_ string, p int) string { if p == 22 { return "SSH" }; return "" }, + func(_ string, p int) (string, string) { if p == 80 { return "t", "s" }; return "", "" }, + func(_ string, p int) string { if p == 5985 { return "w" }; return "" }, + nil, + ) + t.Cleanup(func() { SetBannerHooks(nil, nil, nil, nil) }) var events []string report, err := ScanStream(ScanRequest{Host: "stream.lab", Profile: ProfileQuick, Port: 80, Scheme: "http"}, "scan-1", func(eventType string, _ map[string]interface{}) { diff --git a/server/internal/recon/recon_test.go b/server/internal/recon/recon_test.go index 3ac09e1..b92fbb0 100644 --- a/server/internal/recon/recon_test.go +++ b/server/internal/recon/recon_test.go @@ -143,6 +143,13 @@ func TestScanOwnedTarget(t *testing.T) { return 200, `
`, nil }) t.Cleanup(func() { SetFetchPageHook(nil) }) + SetBannerHooks( + func(_ string, p int) string { if p == 22 { return "SSH" }; return "" }, + func(_ string, p int) (string, string) { if p == 80 { return "t", "s" }; return "", "" }, + func(_ string, p int) string { if p == 5985 { return "w" }; return "" }, + nil, + ) + t.Cleanup(func() { SetBannerHooks(nil, nil, nil, nil) }) report, err := Scan(ScanRequest{Host: "owned.lab", Port: 80, Scheme: "http"}) if err != nil { t.Fatal(err) @@ -261,6 +268,13 @@ func TestScanReportIncludesAdminSurfaceJSON(t *testing.T) { return resp.StatusCode, body, nil }) t.Cleanup(func() { SetFetchPageHook(nil) }) + SetBannerHooks( + func(_ string, p int) string { if p == 22 { return "SSH" }; return "" }, + func(_ string, p int) (string, string) { if p == 80 { return "t", "s" }; return "", "" }, + func(_ string, p int) string { if p == 5985 { return "w" }; return "" }, + nil, + ) + t.Cleanup(func() { SetBannerHooks(nil, nil, nil, nil) }) report, err := Scan(ScanRequest{Host: u.Hostname(), Port: port, Scheme: u.Scheme}) if err != nil { t.Fatal(err)