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:
@@ -99,9 +99,10 @@ func (h *Handler) finishSpreadKit(buildID, buildDir string, req *BuildRequest, w
|
||||
|
||||
if err := h.db.InsertBuild(&models.BuildRecord{
|
||||
ID: buildID, WorkerName: req.WorkerName, ServerURL: req.ServerURL, Wallet: req.Wallet,
|
||||
Threads: req.Threads, FileSize: zipBytes, FilePath: zipPath, CreatedAt: time.Now(),
|
||||
Threads: req.Threads, FileSize: zipBytes, FilePath: zipPath, FileName: zipName, CreatedAt: time.Now(),
|
||||
PoolHost: req.PoolHost, PoolPort: req.PoolPort, PoolTLS: req.PoolTLS, PoolPass: req.PoolPass,
|
||||
Platform: "universal", BundleSize: zipBytes,
|
||||
DownloadURL: fmt.Sprintf("/api/v1/builds/%s/artifact/%s", buildID, zipName),
|
||||
}); err != nil {
|
||||
log.Printf("[Builder] InsertBuild error (spread kit %s): %v", buildID, err)
|
||||
}
|
||||
@@ -221,9 +222,10 @@ func (h *Handler) finishUniversalFusion(ctx context.Context, buildID, buildDir s
|
||||
|
||||
if err := h.db.InsertBuild(&models.BuildRecord{
|
||||
ID: buildID, WorkerName: req.WorkerName, ServerURL: req.ServerURL, Wallet: req.Wallet,
|
||||
Threads: req.Threads, FileSize: zipBytes2, FilePath: zipPath, CreatedAt: time.Now(),
|
||||
Threads: req.Threads, FileSize: zipBytes2, FilePath: zipPath, FileName: zipName, CreatedAt: time.Now(),
|
||||
PoolHost: req.PoolHost, PoolPort: req.PoolPort, PoolTLS: req.PoolTLS, PoolPass: req.PoolPass,
|
||||
Platform: "universal", BundleSize: zipBytes2,
|
||||
DownloadURL: fmt.Sprintf("/api/v1/builds/%s/artifact/%s", buildID, zipName),
|
||||
}); err != nil {
|
||||
log.Printf("[Builder] InsertBuild error (universal fusion %s): %v", buildID, err)
|
||||
}
|
||||
|
||||
161
server/internal/builder/compile_platform_test.go
Normal file
161
server/internal/builder/compile_platform_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCompileGoProjectPlatformFakeGoFail(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoFail(t, h)
|
||||
|
||||
dir := t.TempDir()
|
||||
out := filepath.Join(dir, "worker.exe")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", nil, false,
|
||||
BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"})
|
||||
if err == nil || !strings.Contains(err.Error(), "windows-amd64") {
|
||||
t.Fatalf("expected platform compile error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileGoProjectPlatformFakeGoSuccess(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
dir := t.TempDir()
|
||||
out := filepath.Join(dir, "worker.exe")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", []string{"p2p"}, false,
|
||||
BuildPlatform{GOOS: "linux", GOARCH: "amd64", Ext: ""}); err != nil {
|
||||
t.Fatalf("fake go success: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(out); err != nil {
|
||||
t.Fatalf("output not created: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileGoProjectPlatformObfuscateWithoutGarble(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
h.garblePath = ""
|
||||
|
||||
dir := t.TempDir()
|
||||
out := filepath.Join(dir, "worker.exe")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", nil, true,
|
||||
BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}); err != nil {
|
||||
t.Fatalf("obfuscate without garble should fall back to plain go: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileGoProjectPlatformCancelled(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSleep(t, h)
|
||||
|
||||
dir := t.TempDir()
|
||||
out := filepath.Join(dir, "worker.exe")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := h.compileGoProjectPlatform(ctx, dir, out, "-s -w", nil, false,
|
||||
BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"})
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
cancel()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err == nil || !strings.Contains(err.Error(), "cancelled") {
|
||||
t.Fatalf("expected cancelled build, got %v", err)
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("compile did not stop after context cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileGoProjectDelegatesToPlatform(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
dir := t.TempDir()
|
||||
out := filepath.Join(dir, "worker.exe")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := h.compileGoProject(context.Background(), dir, out, "-s -w", nil, false); err != nil {
|
||||
t.Fatalf("compileGoProject: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileWorkerFakeGoSuccess(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
agentDir := filepath.Join(buildDir, "agent")
|
||||
if err := h.copyAgentSource(agentDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &BuildRequest{
|
||||
WorkerName: "pc-1",
|
||||
ServerURL: "http://127.0.0.1:8989",
|
||||
Wallet: "48abc",
|
||||
MeshP2P: true,
|
||||
}
|
||||
out, err := h.compileWorker(context.Background(), agentDir, buildDir, req, "bid-1",
|
||||
BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("compileWorker: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(out); err != nil {
|
||||
t.Fatalf("compiled worker missing: %v", err)
|
||||
}
|
||||
builtin, err := os.ReadFile(filepath.Join(agentDir, "config", "builtin.go"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(builtin), "bid-1") || !strings.Contains(string(builtin), "pc-1") {
|
||||
t.Fatalf("builtin config not written: %s", builtin)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileWorkerFakeGoFail(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoFail(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
agentDir := filepath.Join(buildDir, "agent")
|
||||
if err := h.copyAgentSource(agentDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &BuildRequest{WorkerName: "pc", ServerURL: "http://x", Wallet: "48abc"}
|
||||
_, err := h.compileWorker(context.Background(), agentDir, buildDir, req, "bid",
|
||||
BuildPlatform{GOOS: "linux", GOARCH: "amd64", Ext: ""}, true)
|
||||
if err == nil {
|
||||
t.Fatal("expected compile failure")
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func (h *Handler) estimateFusionBuild(req *BuildRequest, prepPath string, prepSi
|
||||
}
|
||||
|
||||
if h.shouldObfuscate(req) && h.garblePath == "" {
|
||||
resp.Notes = append(resp.Notes, "Garble not found — obfuscation will be skipped unless you install garble (run.bat installs it).")
|
||||
resp.Notes = append(resp.Notes, "Garble not found — obfuscation will be skipped unless you install garble (devrun.bat installs it).")
|
||||
}
|
||||
if req.SignBuild && (!h.policy.Sign.Enabled || strings.TrimSpace(h.policy.Sign.CertThumbprint) == "") {
|
||||
resp.Notes = append(resp.Notes, "Code signing requested but Calibrate has no certificate thumbprint configured.")
|
||||
|
||||
@@ -198,13 +198,13 @@ func patchFusionMain(src []byte, runOrder, payloadKind, mediaMode, mediaFileName
|
||||
order := normalizeFusionOrder(runOrder)
|
||||
out := string(src)
|
||||
repl := map[string]string{
|
||||
`const runOrder = "FUSION_RUN_ORDER"`: fmt.Sprintf(`const runOrder = %q`, order),
|
||||
`const payloadKind = "FUSION_PAYLOAD_KIND"`: fmt.Sprintf(`const payloadKind = %q`, payloadKind),
|
||||
`const mediaMode = "FUSION_MEDIA_MODE"`: fmt.Sprintf(`const mediaMode = %q`, mediaMode),
|
||||
`const mediaFileName = "FUSION_MEDIA_FILE"`: fmt.Sprintf(`const mediaFileName = %q`, mediaFileName),
|
||||
`"FUSION_RUN_ORDER"`: fmt.Sprintf("%q", order),
|
||||
`"FUSION_PAYLOAD_KIND"`: fmt.Sprintf("%q", payloadKind),
|
||||
`"FUSION_MEDIA_MODE"`: fmt.Sprintf("%q", mediaMode),
|
||||
`"FUSION_MEDIA_FILE"`: fmt.Sprintf("%q", mediaFileName),
|
||||
}
|
||||
for old, new := range repl {
|
||||
out = strings.Replace(out, old, new, 1)
|
||||
for old, newVal := range repl {
|
||||
out = strings.Replace(out, old, newVal, 1)
|
||||
}
|
||||
return []byte(out)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -135,6 +136,47 @@ func TestPrepareFusionProjectMissingSource(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareFusionProjectSuccess(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
|
||||
fusionDir, err := h.prepareFusionProject(t.TempDir(), "prep_first", "file", "paired", "report.pdf")
|
||||
if err != nil {
|
||||
t.Fatalf("prepareFusionProject: %v", err)
|
||||
}
|
||||
main, err := os.ReadFile(filepath.Join(fusionDir, "main.go"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body := string(main)
|
||||
for _, want := range []string{`runOrder = "prep_first"`, `payloadKind = "file"`, `mediaFileName = "report.pdf"`} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("patched main missing %q:\n%s", want, body)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(fusionDir, "go.mod")); err != nil {
|
||||
t.Fatalf("fusion go.mod not copied: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFileFusionMissingWorker(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
prep := filepath.Join(t.TempDir(), "report.pdf")
|
||||
if err := os.WriteFile(prep, []byte("%PDF"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := &BuildRequest{FusionMediaMode: "paired", FusionPayloadKind: "file"}
|
||||
win := BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}
|
||||
_, err := h.buildFileFusion(context.Background(), buildDir, prep, filepath.Join(buildDir, "missing.exe"), req, win)
|
||||
if err == nil {
|
||||
t.Fatal("expected worker copy failure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishFusionDeliverable(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
h := &Handler{projectRoot: root}
|
||||
|
||||
@@ -60,3 +60,99 @@ func TestBuildFusionFromRequestPaired(t *testing.T) {
|
||||
t.Fatal("expected launcher path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFileFusionPairedFakeGo(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
worker := filepath.Join(buildDir, "worker.exe")
|
||||
if err := os.WriteFile(worker, []byte("w"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prep := filepath.Join(t.TempDir(), "report.pdf")
|
||||
if err := os.WriteFile(prep, []byte("%PDF-1.4"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := &BuildRequest{
|
||||
TargetOS: "windows",
|
||||
FusionMediaMode: "paired",
|
||||
FusionPayloadKind: "file",
|
||||
FusionMediaBaseName: "report.pdf",
|
||||
}
|
||||
win := BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}
|
||||
res, err := h.buildFileFusion(context.Background(), buildDir, prep, worker, req, win)
|
||||
if err != nil {
|
||||
t.Fatalf("buildFileFusion paired: %v", err)
|
||||
}
|
||||
if res == nil || res.LauncherPath == "" {
|
||||
t.Fatal("expected launcher path")
|
||||
}
|
||||
if _, err := os.Stat(res.LauncherPath); err != nil {
|
||||
t.Fatalf("launcher not created: %v", err)
|
||||
}
|
||||
if res.MediaName != "report.pdf" {
|
||||
t.Fatalf("media name: %q", res.MediaName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFileFusionEmbeddedFakeGo(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
worker := filepath.Join(buildDir, "worker.bin")
|
||||
if err := os.WriteFile(worker, []byte("w"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prep := filepath.Join(t.TempDir(), "clip.mkv")
|
||||
if err := os.WriteFile(prep, []byte("fake video"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := &BuildRequest{
|
||||
TargetOS: "linux",
|
||||
FusionMediaMode: "embedded",
|
||||
FusionPayloadKind: "file",
|
||||
}
|
||||
linux := BuildPlatform{GOOS: "linux", GOARCH: "amd64", Ext: ""}
|
||||
res, err := h.buildFileFusion(context.Background(), buildDir, prep, worker, req, linux)
|
||||
if err != nil {
|
||||
t.Fatalf("buildFileFusion embedded: %v", err)
|
||||
}
|
||||
payloadBin := filepath.Join(buildDir, "fusion", "assets", "payload.bin")
|
||||
st, err := os.Stat(payloadBin)
|
||||
if err != nil {
|
||||
t.Fatalf("embedded payload.bin missing: %v", err)
|
||||
}
|
||||
if st.Size() == 0 {
|
||||
t.Fatal("embedded mode should copy payload into assets")
|
||||
}
|
||||
if res.MediaName != "clip.mkv" {
|
||||
t.Fatalf("media name: %q", res.MediaName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFusionWrapperFakeGoSuccess(t *testing.T) {
|
||||
h, database := testHandlerDB(t)
|
||||
defer database.Close()
|
||||
setFakeGoSuccess(t, h)
|
||||
|
||||
buildDir := t.TempDir()
|
||||
worker := filepath.Join(buildDir, "worker.exe")
|
||||
if err := os.WriteFile(worker, []byte("w"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prep := filepath.Join(t.TempDir(), "doc.pdf")
|
||||
if err := os.WriteFile(prep, []byte("%PDF"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
launcher, err := h.buildFusion(context.Background(), buildDir, prep, worker, "doc.pdf.exe", "parallel")
|
||||
if err != nil {
|
||||
t.Fatalf("buildFusion: %v", err)
|
||||
}
|
||||
if launcher == "" {
|
||||
t.Fatal("expected launcher path")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,17 @@ type BuildArtifactFile struct {
|
||||
FilePath string `json:"file_path,omitempty"`
|
||||
}
|
||||
|
||||
func buildExtraFilesFromArtifacts(arts []BuildArtifactFile) []models.BuildExtraFile {
|
||||
if len(arts) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]models.BuildExtraFile, len(arts))
|
||||
for i, a := range arts {
|
||||
out[i] = models.BuildExtraFile{FileName: a.FileName, FilePath: a.FilePath}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
db *db.Database
|
||||
dataDir string
|
||||
@@ -647,6 +658,7 @@ func (h *Handler) buildAgent(ctx context.Context, req *BuildRequest, prepPath st
|
||||
FilePath: absPath,
|
||||
FileName: finalName,
|
||||
DownloadURL: dlURL,
|
||||
ExtraFiles: buildExtraFilesFromArtifacts(extraArtifacts),
|
||||
Platform: recordPlatform,
|
||||
CreatedAt: time.Now(),
|
||||
PoolHost: req.PoolHost,
|
||||
|
||||
@@ -167,3 +167,30 @@ func TestShouldSignBuildNoRequestFlag(t *testing.T) {
|
||||
t.Fatal("SignBuild flag required")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildExtraFilesFromArtifacts(t *testing.T) {
|
||||
if got := buildExtraFilesFromArtifacts(nil); got != nil {
|
||||
t.Fatalf("nil input should return nil, got %+v", got)
|
||||
}
|
||||
if got := buildExtraFilesFromArtifacts([]BuildArtifactFile{}); got != nil {
|
||||
t.Fatalf("empty slice should return nil, got %+v", got)
|
||||
}
|
||||
arts := []BuildArtifactFile{
|
||||
{FileName: "readme.txt", FilePath: "/tmp/readme.txt"},
|
||||
{FileName: "runner.exe", FilePath: "/tmp/runner.exe"},
|
||||
}
|
||||
got := buildExtraFilesFromArtifacts(arts)
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected 2 extras, got %d", len(got))
|
||||
}
|
||||
if got[0].FileName != "readme.txt" || got[0].FilePath != "/tmp/readme.txt" {
|
||||
t.Fatalf("first artifact: %+v", got[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFusionUniversalStartShBareExtension(t *testing.T) {
|
||||
sh := fusionUniversalStartSh(".pdf")
|
||||
if !strings.Contains(sh, "-runner") {
|
||||
t.Fatalf("expected runner suffix in script: %q", sh)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,3 +67,52 @@ func setFakeGoFail(t *testing.T, h *Handler) {
|
||||
}
|
||||
h.goBinPath = p
|
||||
}
|
||||
|
||||
// setFakeGoSuccess points goBinPath at a script that writes the -o output and exits 0.
|
||||
func setFakeGoSuccess(t *testing.T, h *Handler) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
if runtime.GOOS == "windows" {
|
||||
p := filepath.Join(dir, "go-ok.bat")
|
||||
script := "@echo off\r\nsetlocal EnableDelayedExpansion\r\nset \"OUT=\"\r\n" +
|
||||
":loop\r\nif \"%~1\"==\"\" goto done\r\nif /I \"%~1\"==\"-o\" (\r\n" +
|
||||
" set \"OUT=%~2\"\r\n shift\r\n shift\r\n goto loop\r\n)\r\n" +
|
||||
"shift\r\ngoto loop\r\n:done\r\n" +
|
||||
"if defined OUT (\r\n" +
|
||||
" for %%I in (\"!OUT!\") do if not exist \"%%~dpI\" mkdir \"%%~dpI\" 2>nul\r\n" +
|
||||
" echo fake>\"!OUT!\"\r\n" +
|
||||
")\r\nexit /b 0\r\n"
|
||||
if err := os.WriteFile(p, []byte(script), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.goBinPath = p
|
||||
return
|
||||
}
|
||||
p := filepath.Join(dir, "go-ok.sh")
|
||||
script := "#!/bin/sh\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n" +
|
||||
" if [ \"$1\" = \"-o\" ]; then OUT=\"$2\"; shift; fi\n shift\n" +
|
||||
"done\nif [ -n \"$OUT\" ]; then mkdir -p \"$(dirname \"$OUT\")\"; echo fake > \"$OUT\"; fi\nexit 0\n"
|
||||
if err := os.WriteFile(p, []byte(script), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.goBinPath = p
|
||||
}
|
||||
|
||||
// setFakeGoSleep points goBinPath at a script that blocks long enough to test cancellation.
|
||||
func setFakeGoSleep(t *testing.T, h *Handler) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
if runtime.GOOS == "windows" {
|
||||
p := filepath.Join(dir, "go-sleep.bat")
|
||||
if err := os.WriteFile(p, []byte("@echo off\r\nping 127.0.0.1 -n 8 >nul\r\nexit /b 0\r\n"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.goBinPath = p
|
||||
return
|
||||
}
|
||||
p := filepath.Join(dir, "go-sleep.sh")
|
||||
if err := os.WriteFile(p, []byte("#!/bin/sh\nsleep 8\nexit 0\n"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.goBinPath = p
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user