Improve portable launch, forge persistence, and operator auth UX.

Persist build extra_files for Build Manager history, print dashboard login on every start, add libp2p for Mesh P2P forge, defer WebSocket until login, and split devrun.bat from LAUNCH.bat with USB deck auto-detection.
This commit is contained in:
AetherForge
2026-05-31 18:56:43 -07:00
parent 2f528229f2
commit feba06e008
80 changed files with 2897 additions and 675 deletions

View File

@@ -0,0 +1,74 @@
package miner
import (
"testing"
"crypto-miner-agent/config"
"crypto-miner-agent/stats"
)
func TestPoolResourcesOKBlocksHighMinFreeRAM(t *testing.T) {
r := stats.NewReporter()
free := r.FreeMemoryMB()
if free == 0 {
t.Skip("free memory unavailable on this platform")
}
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
MiningMode: "always",
MinFreeRAM: int(free + 1_000_000),
MaxCPUUsage: 0,
MaxMemoryPct: 0,
}}
p := NewPool(1, cfg, r, nil)
if p.resourcesOK() {
t.Fatal("MinFreeRAM above available free RAM should block mining")
}
}
func TestPoolResourcesOKBlocksLowMaxMemoryPct(t *testing.T) {
r := stats.NewReporter()
total := r.TotalMemoryMB()
if total == 0 {
t.Skip("total memory unavailable on this platform")
}
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
MiningMode: "always",
MaxMemoryPct: 1,
MaxCPUUsage: 0,
MinFreeRAM: 0,
}}
p := NewPool(1, cfg, r, nil)
if p.resourcesOK() {
t.Fatal("MaxMemoryPct=1 should block when system memory use exceeds 1%")
}
}
func TestPoolResourcesOKAllowsHighMaxCPU(t *testing.T) {
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
MiningMode: "always",
MaxCPUUsage: 100,
MinFreeRAM: 0,
}}
p := NewPool(1, cfg, stats.NewReporter(), nil)
if !p.resourcesOK() {
t.Fatal("MaxCPUUsage=100 should allow mining when CPU is at most 100%")
}
}
func TestPoolMiningAllowedRequiresResourcesAndSchedule(t *testing.T) {
r := stats.NewReporter()
free := r.FreeMemoryMB()
if free == 0 {
t.Skip("free memory unavailable on this platform")
}
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
MiningMode: "always",
MinFreeRAM: int(free + 1_000_000),
MaxCPUUsage: 0,
MaxMemoryPct: 0,
}}
p := NewPool(1, cfg, r, nil)
if p.miningAllowed() {
t.Fatal("miningAllowed should deny when resource guard fails even in always mode")
}
}

View File

@@ -147,3 +147,102 @@ func TestStratumMsgJobNotification(t *testing.T) {
t.Fatalf("job notification: %+v", sj)
}
}
func TestBuildStratumEndpointsMultipleBackups(t *testing.T) {
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
PoolHost: "primary.pool",
PoolPort: 3333,
BackupPools: []config.BackupPool{
{Host: "backup1.pool", Port: 4444, Pass: "a"},
{Host: "backup2.pool", Port: 5555, TLS: true},
},
}}
eps := buildStratumEndpoints(cfg)
if len(eps) != 3 {
t.Fatalf("expected primary + 2 backups, got %d", len(eps))
}
if eps[2].Host != "backup2.pool" || eps[2].Port != 5555 || !eps[2].TLS {
t.Fatalf("third endpoint mismatch: %+v", eps[2])
}
}
func TestStratumLoginErrorResponse(t *testing.T) {
line := `{"id":1,"jsonrpc":"2.0","error":{"code":-1,"message":"invalid wallet"}}`
var loginResp stratumMsg
if err := json.Unmarshal([]byte(line), &loginResp); err != nil {
t.Fatal(err)
}
if loginResp.Error == nil {
t.Fatal("expected login error field")
}
if loginResp.Result != nil {
t.Fatal("error response should not carry a result")
}
}
func TestStratumKeepaliveMsgRoundTrip(t *testing.T) {
params := mustMarshal(map[string]string{"id": "sess-abc"})
msg := stratumMsg{ID: 3, JSONRPC: "2.0", Method: "keepalived", Params: params}
b, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
var decoded stratumMsg
if err := json.Unmarshal(b, &decoded); err != nil {
t.Fatal(err)
}
if decoded.Method != "keepalived" || decoded.JSONRPC != "2.0" {
t.Fatalf("keepalive msg: %+v", decoded)
}
var p map[string]string
if err := json.Unmarshal(decoded.Params, &p); err != nil {
t.Fatal(err)
}
if p["id"] != "sess-abc" {
t.Fatalf("keepalive params: %v", p)
}
}
func TestStratumSubmitRequestRoundTrip(t *testing.T) {
params, err := json.Marshal(submitParams{
ID: "sess-1", JobID: "42", Nonce: "04030201", Hash: "deadbeef",
})
if err != nil {
t.Fatal(err)
}
msg := stratumMsg{ID: 4, JSONRPC: "2.0", Method: "submit", Params: params}
b, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
var decoded stratumMsg
if err := json.Unmarshal(b, &decoded); err != nil {
t.Fatal(err)
}
if decoded.Method != "submit" {
t.Fatalf("method=%q", decoded.Method)
}
var sp submitParams
if err := json.Unmarshal(decoded.Params, &sp); err != nil {
t.Fatal(err)
}
if sp.ID != "sess-1" || sp.JobID != "42" || sp.Nonce != "04030201" || sp.Hash != "deadbeef" {
t.Fatalf("submit params: %+v", sp)
}
}
func TestStratumJobParamsInvalidJSON(t *testing.T) {
msg := stratumMsg{Method: "job", Params: json.RawMessage(`{"blob":`)}
var sj stratumJob
if err := json.Unmarshal(msg.Params, &sj); err == nil {
t.Fatal("malformed job params should fail unmarshal")
}
}
func TestStratumLineInvalidJSONIgnored(t *testing.T) {
line := "not-json\n"
var msg stratumMsg
if err := json.Unmarshal([]byte(line), &msg); err == nil {
t.Fatal("invalid stratum line should not parse as msg")
}
}