mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-08 09:53:25 +00:00
201 lines
5.3 KiB
Go
201 lines
5.3 KiB
Go
package unified
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestUnifiedFindingJSONCanonicalOutput(t *testing.T) {
|
|
finding := UnifiedFinding{
|
|
ID: "f1",
|
|
Source: SourceThreshold,
|
|
Severity: SeverityWarning,
|
|
Category: CategoryPerformance,
|
|
ResourceID: "res-1",
|
|
Title: "High CPU",
|
|
AlertIdentifier: "instance:node:100::metric/cpu",
|
|
DetectedAt: time.Now(),
|
|
LastSeenAt: time.Now(),
|
|
}
|
|
|
|
raw, err := json.Marshal(finding)
|
|
if err != nil {
|
|
t.Fatalf("marshal unified finding: %v", err)
|
|
}
|
|
|
|
var payload map[string]interface{}
|
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
t.Fatalf("decode unified finding 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["alert_id"]; ok {
|
|
t.Fatalf("did not expect legacy alert_id in canonical payload, got %#v", payload["alert_id"])
|
|
}
|
|
|
|
var decoded UnifiedFinding
|
|
if err := json.Unmarshal([]byte(`{
|
|
"id":"f1",
|
|
"source":"threshold",
|
|
"severity":"warning",
|
|
"category":"performance",
|
|
"resource_id":"res-1",
|
|
"title":"High CPU",
|
|
"detected_at":"2026-03-11T00:00:00Z",
|
|
"last_seen_at":"2026-03-11T00:00:00Z",
|
|
"alert_identifier":"instance:node:100::metric/cpu"
|
|
}`), &decoded); err != nil {
|
|
t.Fatalf("unmarshal canonical unified finding: %v", err)
|
|
}
|
|
if decoded.AlertIdentifier != "instance:node:100::metric/cpu" {
|
|
t.Fatalf("expected canonical alert_identifier to load, got %q", decoded.AlertIdentifier)
|
|
}
|
|
}
|
|
|
|
func TestFilePersistence_SaveLoad(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewFilePersistence(dir)
|
|
|
|
findings := map[string]*UnifiedFinding{
|
|
"f1": {
|
|
ID: "f1",
|
|
Source: SourceThreshold,
|
|
ResourceID: "res-1",
|
|
DetectedAt: time.Now(),
|
|
},
|
|
}
|
|
|
|
if err := p.SaveFindings(findings); err != nil {
|
|
t.Fatalf("unexpected save error: %v", err)
|
|
}
|
|
info, err := os.Stat(filepath.Join(dir, "unified_findings.json"))
|
|
if err != nil {
|
|
t.Fatalf("stat failed: %v", err)
|
|
}
|
|
if got := info.Mode().Perm(); got != 0600 {
|
|
t.Fatalf("expected mode 0600, got %o", got)
|
|
}
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected load error: %v", err)
|
|
}
|
|
if len(loaded) != 1 {
|
|
t.Fatalf("expected 1 finding, got %d", len(loaded))
|
|
}
|
|
}
|
|
|
|
func TestFilePersistence_LoadMissingAndInvalid(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewFilePersistence(dir)
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(loaded) != 0 {
|
|
t.Fatalf("expected empty map for missing file")
|
|
}
|
|
|
|
path := filepath.Join(dir, "unified_findings.json")
|
|
if err := os.WriteFile(path, []byte("{bad json"), 0644); err != nil {
|
|
t.Fatalf("write failed: %v", err)
|
|
}
|
|
|
|
loaded, err = p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(loaded) != 0 {
|
|
t.Fatalf("expected empty map for invalid json")
|
|
}
|
|
}
|
|
|
|
func TestVersionedPersistence_SaveLoad(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewVersionedPersistence(dir)
|
|
|
|
findings := map[string]*UnifiedFinding{
|
|
"f1": {ID: "f1", Source: SourceAIPatrol, ResourceID: "res-1"},
|
|
}
|
|
|
|
if err := p.SaveFindings(findings); err != nil {
|
|
t.Fatalf("unexpected save error: %v", err)
|
|
}
|
|
info, err := os.Stat(filepath.Join(dir, "unified_findings.json"))
|
|
if err != nil {
|
|
t.Fatalf("stat failed: %v", err)
|
|
}
|
|
if got := info.Mode().Perm(); got != 0600 {
|
|
t.Fatalf("expected mode 0600, got %o", got)
|
|
}
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected load error: %v", err)
|
|
}
|
|
if len(loaded) != 1 {
|
|
t.Fatalf("expected 1 finding, got %d", len(loaded))
|
|
}
|
|
}
|
|
|
|
func TestVersionedPersistence_LoadLegacy(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewVersionedPersistence(dir)
|
|
|
|
path := filepath.Join(dir, "unified_findings.json")
|
|
legacy := `[{"id":"f1","source":"ai-patrol","resource_id":"res-1"}]`
|
|
if err := os.WriteFile(path, []byte(legacy), 0644); err != nil {
|
|
t.Fatalf("write failed: %v", err)
|
|
}
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected load error: %v", err)
|
|
}
|
|
if len(loaded) != 1 {
|
|
t.Fatalf("expected 1 finding, got %d", len(loaded))
|
|
}
|
|
}
|
|
|
|
func TestFilePersistence_LoadFindings_SkipsNullEntries(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewFilePersistence(dir)
|
|
|
|
path := filepath.Join(dir, "unified_findings.json")
|
|
legacy := `[null,{"id":"f1","source":"ai-patrol","resource_id":"res-1"}]`
|
|
if err := os.WriteFile(path, []byte(legacy), 0o644); err != nil {
|
|
t.Fatalf("write failed: %v", err)
|
|
}
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected load error: %v", err)
|
|
}
|
|
if len(loaded) != 1 {
|
|
t.Fatalf("expected 1 finding after skipping null entry, got %d", len(loaded))
|
|
}
|
|
}
|
|
|
|
func TestVersionedPersistence_LoadFindings_SkipsNullEntries(t *testing.T) {
|
|
dir := t.TempDir()
|
|
p := NewVersionedPersistence(dir)
|
|
|
|
path := filepath.Join(dir, "unified_findings.json")
|
|
versioned := `{"version":1,"findings":[null,{"id":"f1","source":"ai-patrol","resource_id":"res-1"}]}`
|
|
if err := os.WriteFile(path, []byte(versioned), 0o644); err != nil {
|
|
t.Fatalf("write failed: %v", err)
|
|
}
|
|
|
|
loaded, err := p.LoadFindings()
|
|
if err != nil {
|
|
t.Fatalf("unexpected load error: %v", err)
|
|
}
|
|
if len(loaded) != 1 {
|
|
t.Fatalf("expected 1 finding after skipping null entry, got %d", len(loaded))
|
|
}
|
|
}
|