135 lines
3.2 KiB
Go
135 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"forge-mesh/internal/api/types"
|
|
"forge-mesh/internal/mining"
|
|
)
|
|
|
|
func startMockStratum(t *testing.T) (host, port string) {
|
|
t.Helper()
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = ln.Close() })
|
|
go func() {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
go func(c net.Conn) {
|
|
defer c.Close()
|
|
buf := make([]byte, 4096)
|
|
_, _ = c.Read(buf)
|
|
}(conn)
|
|
}
|
|
}()
|
|
host, port, _ = net.SplitHostPort(ln.Addr().String())
|
|
return host, port
|
|
}
|
|
|
|
func TestMinerTierChainStateMachine(t *testing.T) {
|
|
host, port := startMockStratum(t)
|
|
|
|
profile := types.MiningProfile{
|
|
WalletAddress: "chain-wallet",
|
|
Tiers: []types.MiningTierSpec{
|
|
{Type: "stratum", Duration: 0, Config: map[string]string{"algo": "rx/0"}},
|
|
{Type: "stratum", Duration: 0, Config: map[string]string{"algo": "rx/0"}},
|
|
},
|
|
}
|
|
|
|
cfg := mining.DefaultChainConfig()
|
|
cfg.HostID = "chain-host"
|
|
cfg.StratumHost = host
|
|
cfg.StratumXMRPort = port
|
|
cfg.GateWindow = 100 * time.Millisecond
|
|
cfg.PollInterval = 40 * time.Millisecond
|
|
cfg.DefaultProbe = 800 * time.Millisecond
|
|
cfg.MinHashrateHps = 500
|
|
|
|
chain := mining.NewChain(profile, cfg)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
|
defer cancel()
|
|
|
|
go func() { _ = chain.Run(ctx) }()
|
|
|
|
seen := map[string]bool{}
|
|
deadline := time.Now().Add(10 * time.Second)
|
|
|
|
for time.Now().Before(deadline) {
|
|
st := chain.Status()
|
|
if st.TierState != "" {
|
|
seen[st.TierState] = true
|
|
}
|
|
if st.CurrentTier >= 1 && st.HashrateHps >= cfg.MinHashrateHps && st.TierState == mining.TierStateActive {
|
|
if st.Wallet != "chain-wallet" {
|
|
t.Fatalf("wallet not pinned: %q", st.Wallet)
|
|
}
|
|
break
|
|
}
|
|
time.Sleep(80 * time.Millisecond)
|
|
}
|
|
|
|
chain.Stop()
|
|
cancel()
|
|
|
|
if !seen[mining.TierStateProbing] {
|
|
t.Fatal("expected probing state in tier chain")
|
|
}
|
|
if !seen[mining.TierStateActive] {
|
|
t.Fatal("expected active state during mock stratum tier")
|
|
}
|
|
|
|
st := chain.Status()
|
|
if st.CurrentTier < 1 {
|
|
t.Fatalf("expected tier progression, got tier %d", st.CurrentTier)
|
|
}
|
|
}
|
|
|
|
func TestMinerTierChainMockHashrateGate(t *testing.T) {
|
|
host, port := startMockStratum(t)
|
|
|
|
profile := types.MiningProfile{
|
|
WalletAddress: "mock-wallet",
|
|
Tiers: []types.MiningTierSpec{
|
|
{Type: "stratum", Duration: 0, Config: map[string]string{"algo": "rx/0"}},
|
|
},
|
|
}
|
|
|
|
cfg := mining.DefaultChainConfig()
|
|
cfg.HostID = "mock-host"
|
|
cfg.StratumHost = host
|
|
cfg.StratumXMRPort = port
|
|
cfg.GateWindow = 50 * time.Millisecond
|
|
cfg.PollInterval = 25 * time.Millisecond
|
|
cfg.DefaultProbe = 2 * time.Second
|
|
cfg.MinHashrateHps = 100
|
|
|
|
chain := mining.NewChain(profile, cfg)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
|
|
defer cancel()
|
|
|
|
go func() { _ = chain.Run(ctx) }()
|
|
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
st := chain.Status()
|
|
if st.HashrateHps >= cfg.MinHashrateHps && st.TierState == mining.TierStateActive {
|
|
if !st.Simulated {
|
|
t.Log("using real stratum path (non-simulated)")
|
|
}
|
|
chain.Stop()
|
|
return
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
}
|
|
t.Fatal("mock stratum tier did not reach hashrate gate")
|
|
}
|