88 lines
3.1 KiB
Go
88 lines
3.1 KiB
Go
package rule
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.ilapage.cn/ila/yovision/Bell/server/app/event"
|
|
"git.ilapage.cn/ila/yovision/Bell/server/internal/platform"
|
|
"git.ilapage.cn/ila/yovision/Bell/server/migrations"
|
|
)
|
|
|
|
func TestPostgresMatchingAndNavigationShape(t *testing.T) {
|
|
url := os.Getenv("BELL_TEST_DATABASE_URL")
|
|
if url == "" {
|
|
t.Skip("BELL_TEST_DATABASE_URL is not set")
|
|
}
|
|
ctx := context.Background()
|
|
db, err := platform.OpenDatabase(ctx, url)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
if err := migrations.Apply(ctx, db); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
service := Service{DB: db}
|
|
prefix := fmt.Sprintf("rule-test-%d", time.Now().UnixNano())
|
|
eventType := "rule-test-danger"
|
|
location := "north"
|
|
first, err := service.Create(ctx, CreateInput{Code: prefix + "-one", Name: "规则一", EventType: &eventType, MinimumSeverity: "medium", LocationContains: &location})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := service.Create(ctx, CreateInput{Code: prefix + "-two", Name: "规则二", EventType: &eventType, MinimumSeverity: "low"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
eventService := event.Service{DB: db, AfterPersist: service.Match}
|
|
base := event.Command{ProducerID: prefix, SourceEventID: "1", EventType: eventType, OccurredAt: time.Now().UTC(), Location: "north gate", Severity: "high", Attributes: map[string]any{}}
|
|
one, err := eventService.Ingest(ctx, base)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base.SourceEventID = "2"
|
|
two, err := eventService.Ingest(ctx, base)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var alertCount, linkCount int
|
|
if err := db.QueryRow(ctx, `SELECT count(DISTINCT m.alert_id),count(*) FROM bell_rule_matches m WHERE m.rule_id=ANY($1::uuid[])`, []string{first.ID, second.ID}).Scan(&alertCount, &linkCount); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if alertCount != 2 || linkCount != 4 {
|
|
t.Fatalf("alerts=%d links=%d", alertCount, linkCount)
|
|
}
|
|
var shared int
|
|
if err := db.QueryRow(ctx, `SELECT count(*) FROM bell_alert_events a JOIN bell_alert_events b ON b.alert_id=a.alert_id JOIN bell_alerts current ON current.id=a.alert_id WHERE a.event_id=$1 AND b.event_id=$2 AND current.primary_rule_id=ANY($3::uuid[])`, one.Event.ID, two.Event.ID, []string{first.ID, second.ID}).Scan(&shared); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if shared != 2 {
|
|
t.Fatalf("expected two correlated alerts, got %d", shared)
|
|
}
|
|
base.SourceEventID = "3"
|
|
base.EventType = "unmatched"
|
|
unmatched, err := eventService.Ingest(ctx, base)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var evaluations, matches int
|
|
if err := db.QueryRow(ctx, `SELECT count(*),count(*) FILTER(WHERE matched) FROM bell_rule_evaluations WHERE event_id=$1`, unmatched.Event.ID).Scan(&evaluations, &matches); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if evaluations < 2 || matches != 0 {
|
|
t.Fatalf("evaluations=%d matches=%d", evaluations, matches)
|
|
}
|
|
disabled, err := service.SetEnabled(ctx, first.ID, false)
|
|
if err != nil || disabled.Enabled || disabled.Version != 2 {
|
|
t.Fatalf("disable: %#v %v", disabled, err)
|
|
}
|
|
enabled, err := service.SetEnabled(ctx, first.ID, true)
|
|
if err != nil || !enabled.Enabled || enabled.Version != 3 {
|
|
t.Fatalf("enable: %#v %v", enabled, err)
|
|
}
|
|
}
|