160 lines
4.9 KiB
Go
160 lines
4.9 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
func newTestBlueprintHandler(t *testing.T) (*BlueprintHandler, string) {
|
|
t.Helper()
|
|
dataDir := t.TempDir()
|
|
return NewBlueprintHandler(dataDir), dataDir
|
|
}
|
|
|
|
func TestSanitizeFilename(t *testing.T) {
|
|
if sanitizeFilename(" my preset ") != "my preset" {
|
|
t.Fatalf("trim failed: %q", sanitizeFilename(" my preset "))
|
|
}
|
|
if sanitizeFilename("../../../etc/passwd") == "" || strings.Contains(sanitizeFilename("../../../etc/passwd"), "..") {
|
|
t.Fatalf("traversal not sanitized: %q", sanitizeFilename("../../../etc/passwd"))
|
|
}
|
|
if sanitizeFilename("bad/name") != "badname" {
|
|
t.Fatalf("slashes removed: %q", sanitizeFilename("bad/name"))
|
|
}
|
|
}
|
|
|
|
func TestBlueprintListEmpty(t *testing.T) {
|
|
h, _ := newTestBlueprintHandler(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/blueprints", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
var list []BlueprintInfo
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &list); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(list) != 0 {
|
|
t.Fatalf("expected empty list, got %d", len(list))
|
|
}
|
|
}
|
|
|
|
func TestBlueprintSaveGetDelete(t *testing.T) {
|
|
h, dataDir := newTestBlueprintHandler(t)
|
|
|
|
saveBody, _ := json.Marshal(map[string]interface{}{
|
|
"name": "fleet-default",
|
|
"data": map[string]interface{}{"pool_host": "pool.example.com", "threads": 4},
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/blueprints", bytes.NewReader(saveBody))
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("save status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
|
|
filePath := filepath.Join(dataDir, "blueprints", "fleet-default.json")
|
|
if _, err := os.Stat(filePath); err != nil {
|
|
t.Fatalf("blueprint file missing: %v", err)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/v1/blueprints", nil)
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
var listed []BlueprintInfo
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &listed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(listed) != 1 || listed[0].Name != "fleet-default" {
|
|
t.Fatalf("list after save: %+v", listed)
|
|
}
|
|
|
|
r := chi.NewRouter()
|
|
r.Get("/blueprints/{name}", h.GetBlueprint)
|
|
req = httptest.NewRequest(http.MethodGet, "/blueprints/fleet-default", nil)
|
|
rec = httptest.NewRecorder()
|
|
r.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("get status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "pool.example.com") {
|
|
t.Fatalf("unexpected blueprint body: %s", rec.Body.String())
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodDelete, "/api/v1/blueprints?name=fleet-default", nil)
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("delete status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
|
|
t.Fatal("blueprint file should be removed")
|
|
}
|
|
}
|
|
|
|
func TestBlueprintSaveValidationErrors(t *testing.T) {
|
|
h, _ := newTestBlueprintHandler(t)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/blueprints", bytes.NewReader([]byte(`{"name":"","data":{}}`)))
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("empty name should be 400, got %d", rec.Code)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodPost, "/api/v1/blueprints", bytes.NewReader([]byte(`not-json`)))
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("invalid json should be 400, got %d", rec.Code)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodPost, "/api/v1/blueprints", bytes.NewReader([]byte(`{"name":"ok","data":}`)))
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("malformed data json should be 400, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestBlueprintGetNotFound(t *testing.T) {
|
|
h, _ := newTestBlueprintHandler(t)
|
|
r := chi.NewRouter()
|
|
r.Get("/blueprints/{name}", h.GetBlueprint)
|
|
req := httptest.NewRequest(http.MethodGet, "/blueprints/missing", nil)
|
|
rec := httptest.NewRecorder()
|
|
r.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("expected 404, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestBlueprintDeleteMissingName(t *testing.T) {
|
|
h, _ := newTestBlueprintHandler(t)
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/blueprints", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestBlueprintMethodNotAllowed(t *testing.T) {
|
|
h, _ := newTestBlueprintHandler(t)
|
|
req := httptest.NewRequest(http.MethodPatch, "/api/v1/blueprints", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("expected 405, got %d", rec.Code)
|
|
}
|
|
}
|