Files
Pulse/internal/api/router_low_coverage_additional_test.go
rcourtman bfa648ddd5 Test: expand api feature test coverage
Add tests for AI intelligence, Docker/K8s agents, log redaction, and general router helper functions.
2026-02-02 22:02:22 +00:00

248 lines
7.9 KiB
Go

package api
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/knowledge"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
"github.com/rcourtman/pulse-go-rewrite/pkg/metrics"
)
func TestHandleMetricsStoreStats_MethodNotAllowed(t *testing.T) {
router := &Router{}
req := httptest.NewRequest(http.MethodPost, "/api/metrics/store/stats", nil)
rec := httptest.NewRecorder()
router.handleMetricsStoreStats(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
}
}
func TestHandleMetricsStoreStats_NoMonitor(t *testing.T) {
router := &Router{}
req := httptest.NewRequest(http.MethodGet, "/api/metrics/store/stats", nil)
rec := httptest.NewRecorder()
router.handleMetricsStoreStats(rec, req)
if rec.Code != http.StatusInternalServerError {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
}
}
func TestHandleMetricsStoreStats_NoStore(t *testing.T) {
monitor, _, _ := newTestMonitor(t)
router := &Router{monitor: monitor}
req := httptest.NewRequest(http.MethodGet, "/api/metrics/store/stats", nil)
rec := httptest.NewRecorder()
router.handleMetricsStoreStats(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["enabled"] != false {
t.Fatalf("expected enabled=false, got %#v", payload["enabled"])
}
}
func TestHandleMetricsStoreStats_WithStore(t *testing.T) {
monitor, _, _ := newTestMonitor(t)
store, err := metrics.NewStore(metrics.DefaultConfig(t.TempDir()))
if err != nil {
t.Fatalf("metrics.NewStore error: %v", err)
}
defer store.Close()
setUnexportedField(t, monitor, "metricsStore", store)
router := &Router{monitor: monitor}
req := httptest.NewRequest(http.MethodGet, "/api/metrics/store/stats", nil)
rec := httptest.NewRecorder()
router.handleMetricsStoreStats(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["enabled"] != true {
t.Fatalf("expected enabled=true, got %#v", payload["enabled"])
}
}
func TestHandleDiagnosticsDockerPrepareToken_MethodNotAllowed(t *testing.T) {
router := &Router{}
req := httptest.NewRequest(http.MethodGet, "/api/diagnostics/docker/prepare-token", nil)
rec := httptest.NewRecorder()
router.handleDiagnosticsDockerPrepareToken(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
}
}
func TestHandleDiagnosticsDockerPrepareToken_InvalidJSON(t *testing.T) {
router := &Router{monitor: &monitoring.Monitor{}, config: &config.Config{}}
req := httptest.NewRequest(http.MethodPost, "/api/diagnostics/docker/prepare-token", strings.NewReader("{"))
rec := httptest.NewRecorder()
router.handleDiagnosticsDockerPrepareToken(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
}
func TestHandleDiagnosticsDockerPrepareToken_MissingHostID(t *testing.T) {
router := &Router{monitor: &monitoring.Monitor{}, config: &config.Config{}}
body := bytes.NewBufferString(`{"hostId":""}`)
req := httptest.NewRequest(http.MethodPost, "/api/diagnostics/docker/prepare-token", body)
rec := httptest.NewRecorder()
router.handleDiagnosticsDockerPrepareToken(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
}
func TestHandleDiagnosticsDockerPrepareToken_HostNotFound(t *testing.T) {
monitor, _, _ := newTestMonitor(t)
router := &Router{monitor: monitor, config: &config.Config{}}
body := bytes.NewBufferString(`{"hostId":"missing"}`)
req := httptest.NewRequest(http.MethodPost, "/api/diagnostics/docker/prepare-token", body)
rec := httptest.NewRecorder()
router.handleDiagnosticsDockerPrepareToken(rec, req)
if rec.Code != http.StatusNotFound {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusNotFound)
}
}
func TestHandleDiagnosticsDockerPrepareToken_Success(t *testing.T) {
monitor, state, _ := newTestMonitor(t)
state.DockerHosts = []models.DockerHost{{ID: "host-1", DisplayName: "Docker Host"}}
router := &Router{monitor: monitor, config: &config.Config{PublicURL: "https://pulse.example.com"}}
body := bytes.NewBufferString(`{"hostId":"host-1","tokenName":""}`)
req := httptest.NewRequest(http.MethodPost, "/api/diagnostics/docker/prepare-token", body)
rec := httptest.NewRecorder()
router.handleDiagnosticsDockerPrepareToken(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if ok, _ := payload["success"].(bool); !ok {
t.Fatalf("expected success true, got %#v", payload["success"])
}
if payload["token"] == "" {
t.Fatalf("expected token in response")
}
host, _ := payload["host"].(map[string]interface{})
if host["id"] != "host-1" {
t.Fatalf("unexpected host id: %#v", host["id"])
}
if !strings.Contains(payload["installCommand"].(string), "https://pulse.example.com") {
t.Fatalf("expected install command to include base URL")
}
if len(router.config.APITokens) == 0 {
t.Fatalf("expected API token to be recorded")
}
}
func TestHandleDownloadDockerInstallerScript_MethodNotAllowed(t *testing.T) {
router := &Router{}
req := httptest.NewRequest(http.MethodPost, "/download/install-docker.sh", nil)
rec := httptest.NewRecorder()
router.handleDownloadDockerInstallerScript(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
}
}
func TestHandleDownloadDockerInstallerScript_ServesFile(t *testing.T) {
root := t.TempDir()
scriptPath := filepath.Join(root, "scripts", "install-docker.sh")
if err := os.MkdirAll(filepath.Dir(scriptPath), 0o755); err != nil {
t.Fatalf("mkdir scripts dir: %v", err)
}
if err := os.WriteFile(scriptPath, []byte("#!/bin/sh\necho docker\n"), 0o644); err != nil {
t.Fatalf("write script: %v", err)
}
router := &Router{projectRoot: root}
req := httptest.NewRequest(http.MethodGet, "/download/install-docker.sh", nil)
rec := httptest.NewRecorder()
router.handleDownloadDockerInstallerScript(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
}
if ct := rec.Header().Get("Content-Type"); ct != "text/x-shellscript" {
t.Fatalf("expected text/x-shellscript, got %q", ct)
}
}
func TestKnowledgeStoreProviderWrapper(t *testing.T) {
wrapper := &knowledgeStoreProviderWrapper{}
if err := wrapper.SaveNote("res-1", "note", "service"); err == nil {
t.Fatalf("expected error when store is nil")
}
if got := wrapper.GetKnowledge("res-1", ""); got != nil {
t.Fatalf("expected nil knowledge when store is nil")
}
store, err := knowledge.NewStore(t.TempDir())
if err != nil {
t.Fatalf("knowledge.NewStore error: %v", err)
}
wrapper.store = store
if err := wrapper.SaveNote("res-1", "hello", "service"); err != nil {
t.Fatalf("SaveNote error: %v", err)
}
entries := wrapper.GetKnowledge("res-1", "service")
if len(entries) != 1 {
t.Fatalf("expected 1 entry, got %d", len(entries))
}
if entries[0].Category != "service" || entries[0].Note != "hello" {
t.Fatalf("unexpected entry: %#v", entries[0])
}
all := wrapper.GetKnowledge("res-1", "")
if len(all) != 1 {
t.Fatalf("expected 1 entry from full query, got %d", len(all))
}
}