mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
184 lines
5 KiB
Go
184 lines
5 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
|
|
)
|
|
|
|
func TestHandleSetupScriptURL(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "pulse-setup-url-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
dummyCfg := &config.Config{
|
|
FrontendPort: 8080,
|
|
PublicURL: "https://pulse.example.com",
|
|
}
|
|
dummyCfg.DataPath = tempDir
|
|
handler := newTestConfigHandlers(t, dummyCfg)
|
|
|
|
tests := []struct {
|
|
name string
|
|
requestBody interface{}
|
|
expectedStatus int
|
|
verifyResponse func(*testing.T, map[string]interface{}, *ConfigHandlers)
|
|
}{
|
|
{
|
|
name: "success_valid_request",
|
|
requestBody: map[string]interface{}{
|
|
"type": "pve",
|
|
"host": "pve1.local",
|
|
},
|
|
expectedStatus: http.StatusOK,
|
|
verifyResponse: func(t *testing.T, resp map[string]interface{}, h *ConfigHandlers) {
|
|
url, ok := resp["url"].(string)
|
|
if !ok || url == "" {
|
|
t.Errorf("expected valid url, got %v", resp["url"])
|
|
}
|
|
token, ok := resp["setupToken"].(string)
|
|
if !ok || token == "" {
|
|
t.Errorf("expected valid setupToken, got %v", resp["setupToken"])
|
|
}
|
|
|
|
// Verify URL construction uses public URL host
|
|
if !strings.Contains(url, "pulse.example.com") {
|
|
t.Errorf("expected URL to contain public host, got %s", url)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "fail_invalid_json",
|
|
requestBody: "invalid-json", // Will fail JSON decoding
|
|
expectedStatus: http.StatusBadRequest,
|
|
verifyResponse: nil,
|
|
},
|
|
{
|
|
name: "success_missing_fields_defaults",
|
|
requestBody: map[string]interface{}{}, // Empty body might be valid if fields are optional?
|
|
// Looking at code: it just decodes. If type/host are empty, it proceeds to generate token.
|
|
// But type is used in setupCode struct.
|
|
expectedStatus: http.StatusOK,
|
|
verifyResponse: func(t *testing.T, resp map[string]interface{}, h *ConfigHandlers) {
|
|
// Should still return a valid response even with empty fields
|
|
if _, ok := resp["url"]; !ok {
|
|
t.Errorf("expected url field in response")
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var body []byte
|
|
var err error
|
|
|
|
if s, ok := tt.requestBody.(string); ok && s == "invalid-json" {
|
|
body = []byte(s)
|
|
} else {
|
|
body, err = json.Marshal(tt.requestBody)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
req := httptest.NewRequest("POST", "/api/setup/url", bytes.NewBuffer(body))
|
|
req.Host = "127.0.0.1:8080" // Simulate loopback request to trigger PublicURL usage logic
|
|
w := httptest.NewRecorder()
|
|
|
|
handler.HandleSetupScriptURL(w, req)
|
|
|
|
if w.Code != tt.expectedStatus {
|
|
t.Errorf("handler returned wrong status code: got %v want %v", w.Code, tt.expectedStatus)
|
|
}
|
|
|
|
if tt.verifyResponse != nil && w.Code == http.StatusOK {
|
|
var response map[string]interface{}
|
|
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
|
t.Errorf("failed to decode response: %v", err)
|
|
}
|
|
tt.verifyResponse(t, response, handler)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandleSetupScriptURL_MethodNotAllowed(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "pulse-setup-url-test-method")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
cfg := &config.Config{DataPath: tempDir}
|
|
handler := newTestConfigHandlers(t, cfg)
|
|
|
|
req := httptest.NewRequest("GET", "/api/setup/url", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
handler.HandleSetupScriptURL(w, req)
|
|
|
|
if w.Code != http.StatusMethodNotAllowed {
|
|
t.Errorf("expected method not allowed, got %v", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleSetupScriptURLIgnoresOrgContext(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "pulse-setup-url-test-org")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
cfg := &config.Config{
|
|
DataPath: tempDir,
|
|
FrontendPort: 8080,
|
|
PublicURL: "https://pulse.example.com",
|
|
}
|
|
handler := newTestConfigHandlers(t, cfg)
|
|
|
|
body := bytes.NewBufferString(`{"type":"pve","host":"delly"}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/setup-script-url", body)
|
|
req = req.WithContext(context.WithValue(req.Context(), OrgIDContextKey, "acme"))
|
|
req.Host = "127.0.0.1:8080"
|
|
w := httptest.NewRecorder()
|
|
|
|
handler.HandleSetupScriptURL(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
token, ok := resp["setupToken"].(string)
|
|
if !ok || token == "" {
|
|
t.Fatalf("expected setup token in response, got %#v", resp["setupToken"])
|
|
}
|
|
|
|
tokenHash := internalauth.HashAPIToken(token)
|
|
|
|
handler.codeMutex.RLock()
|
|
setupCode := handler.setupCodes[tokenHash]
|
|
handler.codeMutex.RUnlock()
|
|
|
|
if setupCode == nil {
|
|
t.Fatalf("expected setup code to be stored for token hash %q", tokenHash)
|
|
}
|
|
if setupCode.OrgID != "default" {
|
|
t.Fatalf("expected setup code org to be forced to default, got %q", setupCode.OrgID)
|
|
}
|
|
}
|