This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package m7gate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/itworx/pulse/internal/host"
|
||||
"github.com/itworx/pulse/internal/network"
|
||||
"github.com/itworx/pulse/internal/probe"
|
||||
"github.com/itworx/pulse/internal/service"
|
||||
)
|
||||
|
||||
type outageFixture struct {
|
||||
ID string
|
||||
Timeline []struct {
|
||||
Action string
|
||||
Payload struct {
|
||||
Probe string
|
||||
ProbeGroup string
|
||||
Success bool
|
||||
}
|
||||
}
|
||||
ExpectedOutcomes []struct {
|
||||
Assertion string
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSOutageFixtureProjectsOneUpstreamAndSuppressedDownstreamInputs(t *testing.T) {
|
||||
content, err := os.ReadFile(filepath.Join("..", "..", "fixtures", "scenarios", "dns-outage-suppression.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var fixture outageFixture
|
||||
if err := json.Unmarshal(content, &fixture); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fixture.ID != "dns-outage-suppression" || len(fixture.Timeline) != 2 || len(fixture.ExpectedOutcomes) != 2 {
|
||||
t.Fatalf("unexpected DNS outage fixture: %+v", fixture)
|
||||
}
|
||||
if fixture.Timeline[0].Payload.Success || fixture.Timeline[0].Payload.Probe != "dns-primary" || fixture.Timeline[1].Payload.Success || fixture.Timeline[1].Payload.ProbeGroup != "dependent-http" {
|
||||
t.Fatalf("fixture does not describe the expected upstream/downstream outage: %+v", fixture.Timeline)
|
||||
}
|
||||
|
||||
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
|
||||
dns := service.ServiceStatus{ID: "dns-primary", State: service.StateDown, Probes: []service.ProbeConfig{{ID: "dns-probe", Type: probe.TypeDNS}}, History: []probe.Result{{ProbeID: "dns-probe", State: service.StateDown, ErrorClass: "timeout", ObservedAt: now}}}
|
||||
dependencies := make([]service.Dependency, 20)
|
||||
services := make([]service.ServiceStatus, 20)
|
||||
for index := range services {
|
||||
serviceID := "dependent-" + string(rune('a'+index))
|
||||
services[index] = service.ServiceStatus{ID: serviceID, State: service.StateDown, Reason: "dns"}
|
||||
dependencies[index] = service.Dependency{ID: "dependency-" + serviceID, ServiceID: serviceID, DependsOnServiceID: dns.ID, RelationType: service.RelationDependsOn, SourceID: "scenario", Confidence: 1, Confirmed: true}
|
||||
}
|
||||
serviceSnapshot := service.Snapshot{ObservedAt: now, Services: append([]service.ServiceStatus{dns}, services...), Total: 21}
|
||||
networkSnapshot, err := network.BuildSnapshot(now, host.Snapshot{Source: host.Source{ID: "fixture-host", Freshness: host.Fresh, State: host.StatusHealthy, ObservedAt: now}, Status: host.Status{State: host.StatusHealthy}, ObservedAt: now}, serviceSnapshot, nil, nil, network.Limits{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var dnsHealth network.Health
|
||||
for _, health := range networkSnapshot.Health {
|
||||
if health.Scope == network.ScopeDNS {
|
||||
dnsHealth = health
|
||||
}
|
||||
}
|
||||
if dnsHealth.State != network.StateDown || dnsHealth.Reason != "dns_probe_failed" {
|
||||
t.Fatalf("DNS outage was not projected independently: %+v", dnsHealth)
|
||||
}
|
||||
states := map[string]string{dns.ID: service.StateDown}
|
||||
for _, item := range services {
|
||||
states[item.ID] = item.State
|
||||
}
|
||||
inputs, err := service.BuildSuppressionInputs(dependencies, states)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(inputs) != 20 {
|
||||
t.Fatalf("expected 20 downstream suppression inputs, got %d", len(inputs))
|
||||
}
|
||||
for _, input := range inputs {
|
||||
if !input.Suppress || input.UpstreamState != service.StateDown || input.Reason != "confirmed_dependency_failure" {
|
||||
t.Fatalf("downstream failure was not grouped/suppressed by DNS dependency: %+v", input)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user