mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-02 05:20:18 +00:00
217 lines
5.9 KiB
Go
217 lines
5.9 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
|
|
)
|
|
|
|
func TestDetectServiceName_Default(t *testing.T) {
|
|
t.Setenv("PATH", "")
|
|
|
|
if got := detectServiceName(); got != "pulse-backend" {
|
|
t.Fatalf("expected pulse-backend, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResponseCaptureWrites(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
rc := &responseCapture{ResponseWriter: rec}
|
|
|
|
rc.WriteHeader(http.StatusCreated)
|
|
if !rc.wrote {
|
|
t.Fatalf("expected wrote=true after WriteHeader")
|
|
}
|
|
if rec.Code != http.StatusCreated {
|
|
t.Fatalf("expected status %d, got %d", http.StatusCreated, rec.Code)
|
|
}
|
|
|
|
_, _ = rc.Write([]byte("ok"))
|
|
if !rc.wrote {
|
|
t.Fatalf("expected wrote=true after Write")
|
|
}
|
|
if rec.Body.String() != "ok" {
|
|
t.Fatalf("expected body 'ok', got %q", rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHandleRegenerateAPIToken_MissingEnvFile(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
cfg := &config.Config{
|
|
DataPath: dataDir,
|
|
ConfigPath: dataDir,
|
|
}
|
|
|
|
router := &Router{config: cfg}
|
|
handler := http.HandlerFunc(router.HandleRegenerateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.9")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/regenerate-token", nil)
|
|
req.RemoteAddr = "198.51.100.9:54321"
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("expected status %d, got %d", http.StatusNotFound, rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleValidateAPIToken_InvalidJSON(t *testing.T) {
|
|
router := &Router{config: &config.Config{}}
|
|
handler := http.HandlerFunc(router.HandleValidateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.10")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/validate-token", strings.NewReader("not-json"))
|
|
req.RemoteAddr = "198.51.100.10:54321"
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleValidateAPIToken_MissingToken(t *testing.T) {
|
|
router := &Router{config: &config.Config{}}
|
|
handler := http.HandlerFunc(router.HandleValidateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.11")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/validate-token", strings.NewReader(`{"token":""}`))
|
|
req.RemoteAddr = "198.51.100.11:54321"
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["message"] != "Token is required" {
|
|
t.Fatalf("unexpected message: %#v", payload["message"])
|
|
}
|
|
}
|
|
|
|
func TestHandleValidateAPIToken_NoTokensConfigured(t *testing.T) {
|
|
router := &Router{config: &config.Config{}}
|
|
handler := http.HandlerFunc(router.HandleValidateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.12")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/validate-token", strings.NewReader(`{"token":"abc"}`))
|
|
req.RemoteAddr = "198.51.100.12:54321"
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["message"] != "API token authentication is not configured" {
|
|
t.Fatalf("unexpected message: %#v", payload["message"])
|
|
}
|
|
}
|
|
|
|
func TestHandleValidateAPIToken_InvalidToken(t *testing.T) {
|
|
hashed, err := internalauth.HashPassword("secret")
|
|
if err != nil {
|
|
t.Fatalf("hash password: %v", err)
|
|
}
|
|
|
|
record, err := config.NewAPITokenRecord("good-token", "token", nil)
|
|
if err != nil {
|
|
t.Fatalf("new token record: %v", err)
|
|
}
|
|
|
|
cfg := &config.Config{
|
|
AuthUser: "admin",
|
|
AuthPass: hashed,
|
|
APITokens: []config.APITokenRecord{*record},
|
|
}
|
|
cfg.SortAPITokens()
|
|
|
|
router := &Router{config: cfg}
|
|
handler := http.HandlerFunc(router.HandleValidateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.13")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/validate-token", strings.NewReader(`{"token":"bad-token"}`))
|
|
req.RemoteAddr = "198.51.100.13:54321"
|
|
req.SetBasicAuth("admin", "secret")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["message"] != "Token is invalid" {
|
|
t.Fatalf("unexpected message: %#v", payload["message"])
|
|
}
|
|
}
|
|
|
|
func TestHandleValidateAPIToken_ValidToken(t *testing.T) {
|
|
hashed, err := internalauth.HashPassword("secret")
|
|
if err != nil {
|
|
t.Fatalf("hash password: %v", err)
|
|
}
|
|
|
|
record, err := config.NewAPITokenRecord("good-token", "token", nil)
|
|
if err != nil {
|
|
t.Fatalf("new token record: %v", err)
|
|
}
|
|
|
|
cfg := &config.Config{
|
|
AuthUser: "admin",
|
|
AuthPass: hashed,
|
|
APITokens: []config.APITokenRecord{*record},
|
|
}
|
|
cfg.SortAPITokens()
|
|
|
|
router := &Router{config: cfg}
|
|
handler := http.HandlerFunc(router.HandleValidateAPIToken)
|
|
|
|
authLimiter.Reset("198.51.100.14")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/security/validate-token", strings.NewReader(`{"token":"good-token"}`))
|
|
req.RemoteAddr = "198.51.100.14:54321"
|
|
req.SetBasicAuth("admin", "secret")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["message"] != "Token is valid" {
|
|
t.Fatalf("unexpected message: %#v", payload["message"])
|
|
}
|
|
}
|