feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge
This commit is contained in:
159
server/internal/api/blueprint_handler_test.go
Normal file
159
server/internal/api/blueprint_handler_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user