mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 17:19:57 +00:00
214 lines
6.2 KiB
Go
214 lines
6.2 KiB
Go
package config_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/notifications"
|
|
)
|
|
|
|
func TestConfigPersistence_DataDir(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := config.NewConfigPersistence(tempDir)
|
|
if cp.DataDir() != tempDir {
|
|
t.Errorf("Expected %s, got %s", tempDir, cp.DataDir())
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistence_MigrateWebhooksIfNeeded(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := config.NewConfigPersistence(tempDir)
|
|
|
|
// 1. Create legacy file
|
|
legacyFile := filepath.Join(tempDir, "webhooks.json")
|
|
legacyWebhooks := []notifications.WebhookConfig{
|
|
{ID: "webhook-1", URL: "http://example.com/legacy"},
|
|
}
|
|
data, _ := json.Marshal(legacyWebhooks)
|
|
_ = os.WriteFile(legacyFile, data, 0644)
|
|
|
|
// 2. Migrate
|
|
if err := cp.MigrateWebhooksIfNeeded(); err != nil {
|
|
t.Fatalf("MigrateWebhooksIfNeeded failed: %v", err)
|
|
}
|
|
|
|
// 3. Verify encrypted file exists (since SaveWebhooks uses encryption if key exists)
|
|
// NewConfigPersistence generates a key if it doesn't exist, so encryption IS enabled by default.
|
|
loaded, err := cp.LoadWebhooks()
|
|
if err != nil {
|
|
t.Fatalf("LoadWebhooks failed: %v", err)
|
|
}
|
|
if len(loaded) != 1 || loaded[0].URL != "http://example.com/legacy" {
|
|
t.Errorf("Migration failed to preserve data: %+v", loaded)
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistence_PatrolRunHistory(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := config.NewConfigPersistence(tempDir)
|
|
|
|
runs := []config.PatrolRunRecord{
|
|
{
|
|
ID: "run-1",
|
|
StartedAt: time.Now().Add(-1 * time.Hour),
|
|
CompletedAt: time.Now().Add(-59 * time.Minute),
|
|
DurationMs: 60000,
|
|
Type: "quick",
|
|
AlertIdentifier: "instance:node:100::metric/cpu",
|
|
ResourcesChecked: 10,
|
|
NewFindings: 2,
|
|
},
|
|
}
|
|
|
|
if err := cp.SavePatrolRunHistory(runs); err != nil {
|
|
t.Fatalf("SavePatrolRunHistory failed: %v", err)
|
|
}
|
|
|
|
history, err := cp.LoadPatrolRunHistory()
|
|
if err != nil {
|
|
t.Fatalf("LoadPatrolRunHistory failed: %v", err)
|
|
}
|
|
|
|
if len(history.Runs) != 1 || history.Runs[0].ID != "run-1" {
|
|
t.Errorf("Patrol history mismatch: %+v", history)
|
|
}
|
|
if history.Runs[0].AlertIdentifier != "instance:node:100::metric/cpu" {
|
|
t.Errorf("Expected canonical alert identifier after load, got %q", history.Runs[0].AlertIdentifier)
|
|
}
|
|
|
|
// Test non-existent file
|
|
cp2 := config.NewConfigPersistence(t.TempDir())
|
|
history2, err := cp2.LoadPatrolRunHistory()
|
|
if err != nil {
|
|
t.Fatalf("LoadPatrolRunHistory on empty dir failed: %v", err)
|
|
}
|
|
if len(history2.Runs) != 0 {
|
|
t.Errorf("Expected 0 runs, got %d", len(history2.Runs))
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistence_AIChatSessionsMigratesPlaintextFile(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := config.NewConfigPersistence(tempDir)
|
|
chatFile := filepath.Join(tempDir, "ai_chat_sessions.json")
|
|
|
|
plaintext := config.AIChatSessionsData{
|
|
Version: 1,
|
|
LastSaved: time.Now().UTC(),
|
|
Sessions: map[string]*config.AIChatSession{
|
|
"session-1": {
|
|
ID: "session-1",
|
|
Title: "Legacy chat",
|
|
UpdatedAt: time.Now().UTC(),
|
|
},
|
|
},
|
|
}
|
|
raw, err := json.MarshalIndent(plaintext, "", " ")
|
|
if err != nil {
|
|
t.Fatalf("marshal plaintext chat sessions: %v", err)
|
|
}
|
|
if err := os.WriteFile(chatFile, raw, 0o600); err != nil {
|
|
t.Fatalf("WriteFile: %v", err)
|
|
}
|
|
|
|
loaded, err := cp.LoadAIChatSessions()
|
|
if err != nil {
|
|
t.Fatalf("LoadAIChatSessions failed: %v", err)
|
|
}
|
|
if len(loaded.Sessions) != 1 {
|
|
t.Fatalf("expected 1 session, got %d", len(loaded.Sessions))
|
|
}
|
|
|
|
rewritten, err := os.ReadFile(chatFile)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile rewritten chat sessions: %v", err)
|
|
}
|
|
if bytes.Equal(rewritten, raw) {
|
|
t.Fatalf("expected plaintext ai chat sessions file to be rewritten encrypted")
|
|
}
|
|
}
|
|
|
|
func TestPatrolRunRecordJSONCanonicalOutput(t *testing.T) {
|
|
record := config.PatrolRunRecord{
|
|
ID: "run-1",
|
|
AlertIdentifier: "instance:node:100::metric/cpu",
|
|
}
|
|
|
|
raw, err := json.Marshal(record)
|
|
if err != nil {
|
|
t.Fatalf("marshal patrol run record: %v", err)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
t.Fatalf("decode patrol run payload: %v", err)
|
|
}
|
|
if payload["alert_identifier"] != "instance:node:100::metric/cpu" {
|
|
t.Fatalf("expected canonical alert_identifier, got %#v", payload["alert_identifier"])
|
|
}
|
|
if _, ok := payload["legacy_alert_id"]; ok {
|
|
t.Fatalf("did not expect legacy_alert_id in canonical payload, got %#v", payload["legacy_alert_id"])
|
|
}
|
|
if _, ok := payload["alert_id"]; ok {
|
|
t.Fatalf("did not expect alert_id in canonical payload, got %#v", payload["alert_id"])
|
|
}
|
|
|
|
var decoded config.PatrolRunRecord
|
|
if err := json.Unmarshal([]byte(`{
|
|
"id":"run-1",
|
|
"alert_identifier":"instance:node:100::metric/cpu"
|
|
}`), &decoded); err != nil {
|
|
t.Fatalf("unmarshal canonical patrol run: %v", err)
|
|
}
|
|
if decoded.AlertIdentifier != "instance:node:100::metric/cpu" {
|
|
t.Fatalf("expected canonical alert_identifier to load, got %q", decoded.AlertIdentifier)
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistence_UpdateEnvFile(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
envFile := filepath.Join(tempDir, ".env")
|
|
|
|
initialContent := `UPDATE_CHANNEL=stable
|
|
AUTO_UPDATE_ENABLED=false
|
|
POLLING_INTERVAL=10
|
|
CUSTOM_VAR=value`
|
|
_ = os.WriteFile(envFile, []byte(initialContent), 0644)
|
|
|
|
cp := config.NewConfigPersistence(tempDir)
|
|
|
|
settings := config.SystemSettings{
|
|
UpdateChannel: "beta",
|
|
AutoUpdateEnabled: true,
|
|
AutoUpdateCheckInterval: 3600,
|
|
}
|
|
|
|
if err := cp.SaveSystemSettings(settings); err != nil {
|
|
t.Fatalf("SaveSystemSettings failed: %v", err)
|
|
}
|
|
|
|
updatedData, err := os.ReadFile(envFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read .env file: %v", err)
|
|
}
|
|
updatedContent := string(updatedData)
|
|
|
|
if !strings.Contains(updatedContent, "UPDATE_CHANNEL=beta") {
|
|
t.Errorf("UPDATE_CHANNEL not updated. Content: %s", updatedContent)
|
|
}
|
|
if !strings.Contains(updatedContent, "AUTO_UPDATE_ENABLED=true") {
|
|
t.Errorf("AUTO_UPDATE_ENABLED not updated. Content: %s", updatedContent)
|
|
}
|
|
if strings.Contains(updatedContent, "POLLING_INTERVAL=") {
|
|
t.Error("POLLING_INTERVAL should have been removed")
|
|
}
|
|
if !strings.Contains(updatedContent, "CUSTOM_VAR=value") {
|
|
t.Error("CUSTOM_VAR should have been preserved")
|
|
}
|
|
}
|