Public source validation / validate (push) Failing after 3m8s
87 lines
3.4 KiB
Go
87 lines
3.4 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|