Adds hundreds of unit/integration/e2e tests, fixes WS bcrypt auth, config merge, fleet analytics, agent schedule/log tail, and documents stale PROBLEMS items. Updates PROBLEMS.md, README, and test scripts; ignores local spread-kits and coverage dirs.
215 lines
5.5 KiB
Go
215 lines
5.5 KiB
Go
package builder
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCancelBuild(t *testing.T) {
|
|
h := &Handler{}
|
|
if h.CancelBuild("missing") {
|
|
t.Fatal("unknown token should return false")
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
h.registerCancel("tok-1", cancel)
|
|
if !h.CancelBuild("tok-1") {
|
|
t.Fatal("expected cancel success")
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
default:
|
|
t.Fatal("context should be cancelled")
|
|
}
|
|
}
|
|
|
|
func TestUnregisterCancelEmptyToken(t *testing.T) {
|
|
h := &Handler{activeCancels: map[string]context.CancelFunc{"x": func() {}}}
|
|
h.unregisterCancel("")
|
|
if _, ok := h.activeCancels["x"]; !ok {
|
|
t.Fatal("empty token unregister should be no-op")
|
|
}
|
|
}
|
|
|
|
func TestServeHTTPMethodNotAllowed(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/builder", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status: %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestServeHTTPInvalidJSON(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder", strings.NewReader("{bad"))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status: %d body: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestServeHTTPMissingWallet(t *testing.T) {
|
|
h := &Handler{}
|
|
body := `{"worker_name":"pc","server_url":"http://127.0.0.1:8989"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder", strings.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status: %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "wallet") {
|
|
t.Fatalf("body: %s", rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestServeEstimateMethodNotAllowed(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/builder/estimate", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeEstimate(rec, req)
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status: %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestServeEstimateRequiresMultipart(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/estimate", strings.NewReader(`{}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
h.ServeEstimate(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status: %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "multipart") {
|
|
t.Fatalf("body: %s", rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestStrictWallet(t *testing.T) {
|
|
h := &Handler{policy: BuildPolicy{StrictWalletValidation: true}}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "not-a-wallet",
|
|
}
|
|
err := h.normalizeRequest(req)
|
|
if err == nil || !strings.Contains(err.Error(), "wallet") {
|
|
t.Fatalf("expected wallet error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestCustomInstallBase(t *testing.T) {
|
|
h := &Handler{}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "48abc",
|
|
InstallBase: "custom",
|
|
}
|
|
if err := h.normalizeRequest(req); err == nil {
|
|
t.Fatal("expected install_custom_base error")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestInvalidOutputDir(t *testing.T) {
|
|
h := &Handler{}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "48abc",
|
|
OutputDir: "../escape",
|
|
}
|
|
if err := h.normalizeRequest(req); err == nil {
|
|
t.Fatal("expected output_dir error")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestSpreadKit(t *testing.T) {
|
|
h := &Handler{}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "48abc",
|
|
SpreadKit: true,
|
|
FusionEnabled: true,
|
|
}
|
|
if err := h.normalizeRequest(req); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if req.FusionEnabled {
|
|
t.Fatal("spread kit should disable fusion")
|
|
}
|
|
if req.TargetOS != "universal" {
|
|
t.Fatalf("target os: %q", req.TargetOS)
|
|
}
|
|
if !req.Persistence || !req.AutoStart {
|
|
t.Fatal("spread kit should force persistence")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestAIDefaults(t *testing.T) {
|
|
h := &Handler{}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "48abc",
|
|
AIEnabled: true,
|
|
}
|
|
if err := h.normalizeRequest(req); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if req.AIOllamaEndpoint == "" || req.AIModel == "" {
|
|
t.Fatal("AI defaults should be set")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeRequestThreadPercentCap(t *testing.T) {
|
|
h := &Handler{}
|
|
req := &BuildRequest{
|
|
WorkerName: "pc",
|
|
ServerURL: "http://127.0.0.1:8989",
|
|
Wallet: "48abc",
|
|
ThreadPercent: 150,
|
|
}
|
|
if err := h.normalizeRequest(req); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if req.ThreadPercent != 100 {
|
|
t.Fatalf("capped at 100, got %d", req.ThreadPercent)
|
|
}
|
|
}
|
|
|
|
func TestExportBuildArtifactsInvalidDir(t *testing.T) {
|
|
h := &Handler{projectRoot: t.TempDir(), dataDir: t.TempDir()}
|
|
_, _, err := h.exportBuildArtifacts("a", "b.exe", "c", "d.ps1", "..")
|
|
if err == nil {
|
|
t.Fatal("expected invalid output_dir error")
|
|
}
|
|
}
|
|
|
|
func TestPublishRootExecutableNoProjectRoot(t *testing.T) {
|
|
dir := t.TempDir()
|
|
src := filepath.Join(dir, "out.exe")
|
|
if err := os.WriteFile(src, []byte("bin"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h := &Handler{projectRoot: "."}
|
|
got, err := h.publishRootExecutable(src, "out.exe")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != src {
|
|
t.Fatalf("expected source path %q, got %q", src, got)
|
|
}
|
|
}
|