Files
ITWorx-Pulse-Public/internal/service/topology_test.go
T
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

88 lines
4.4 KiB
Go

package service
import (
"fmt"
"strings"
"testing"
"time"
)
func TestBuildTopologyIsBoundedAndPreservesUnknownNodes(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
snapshot := Snapshot{ObservedAt: now, Services: []ServiceStatus{{ID: "service-b", Name: "Backend", State: StateDown}, {ID: "service-a", Name: "Frontend", State: StateUp}}}
dependencies := []Dependency{{ID: "edge-2", ServiceID: "service-a", DependsOnServiceID: "missing", RelationType: RelationDependsOn, SourceID: "compose", Confidence: .6}, {ID: "edge-1", ServiceID: "service-a", DependsOnServiceID: "service-b", RelationType: RelationDependsOn, SourceID: "manual", Confidence: .9, Confirmed: true}}
topology, err := BuildTopology(snapshot, dependencies, 3, 2)
if err != nil {
t.Fatal(err)
}
if topology.ObservedAt != now || len(topology.Nodes) != 3 || len(topology.Edges) != 2 || topology.TotalEdges != 2 {
t.Fatalf("unexpected topology: %+v", topology)
}
if topology.Nodes[0].ID != "missing" || topology.Nodes[0].Known {
t.Fatalf("nodes are not deterministic or missing node is known: %+v", topology.Nodes)
}
if topology.Edges[0].ID != "edge-2" || topology.Edges[0].Confirmed || !topology.Edges[0].Inferred {
t.Fatalf("inferred edge was not preserved: %+v", topology.Edges[0])
}
if topology.Edges[1].ID != "edge-1" || !topology.Edges[1].Confirmed || topology.Edges[1].Inferred {
t.Fatalf("confirmed edge was not preserved: %+v", topology.Edges[1])
}
}
func TestBuildTopologyPreservesSourceState(t *testing.T) {
now := time.Date(2026, 8, 12, 1, 0, 0, 0, time.UTC)
topology, err := BuildTopology(Snapshot{ObservedAt: now, CapabilityState: CapabilityAvailable, ConfigurationState: ConfigurationNotConfigured, Reason: "no_services_configured"}, nil, 10, 10)
if err != nil {
t.Fatal(err)
}
if topology.CapabilityState != CapabilityAvailable || topology.ConfigurationState != ConfigurationNotConfigured || topology.Reason != "no_services_configured" {
t.Fatalf("topology source state was lost: %+v", topology)
}
}
func TestBuildTopologyRejectsUnsafeBoundsAndCycles(t *testing.T) {
if _, err := BuildTopology(Snapshot{}, nil, 0, 1); err == nil {
t.Fatal("expected bound error")
}
cycle := []Dependency{{ID: "a", ServiceID: "one", DependsOnServiceID: "two", RelationType: RelationDependsOn}, {ID: "b", ServiceID: "two", DependsOnServiceID: "one", RelationType: RelationDependsOn}}
_, err := BuildTopology(Snapshot{}, cycle, 10, 10)
if err == nil || !strings.Contains(err.Error(), "cycle") {
t.Fatalf("expected cycle error, got %v", err)
}
}
func TestBuildTopologyTruncatesEdgesDeterministically(t *testing.T) {
snapshot := Snapshot{ObservedAt: time.Now().UTC(), Services: []ServiceStatus{{ID: "a", Name: "A", State: StateUnknown}, {ID: "b", Name: "B", State: StateUnknown}, {ID: "c", Name: "C", State: StateUnknown}}}
dependencies := []Dependency{{ID: "z", ServiceID: "a", DependsOnServiceID: "c", RelationType: RelationBacks}, {ID: "a", ServiceID: "a", DependsOnServiceID: "b", RelationType: RelationExposes}}
topology, err := BuildTopology(snapshot, dependencies, 3, 1)
if err != nil {
t.Fatal(err)
}
if !topology.Truncated || len(topology.Edges) != 1 || topology.Edges[0].ID != "a" {
t.Fatalf("unexpected truncation: %+v", topology)
}
}
func TestBuildTopologyTargetScaleRemainsBounded(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
services := make([]ServiceStatus, 0, 300)
dependencies := make([]Dependency, 0, 600)
for index := 0; index < 300; index++ {
id := fmt.Sprintf("service-%03d", index)
services = append(services, ServiceStatus{ID: id, Name: id, State: StateUnknown})
if index < 299 {
dependencies = append(dependencies, Dependency{ID: fmt.Sprintf("edge-%03d", index), ServiceID: id, DependsOnServiceID: fmt.Sprintf("service-%03d", index+1), RelationType: RelationBacks})
}
if index < 298 {
dependencies = append(dependencies, Dependency{ID: fmt.Sprintf("edge-extra-%03d", index), ServiceID: id, DependsOnServiceID: fmt.Sprintf("service-%03d", index+2), RelationType: RelationExposes})
}
}
topology, err := BuildTopology(Snapshot{ObservedAt: now, Services: services}, dependencies, 100, 200)
if err != nil {
t.Fatal(err)
}
if len(topology.Nodes) > 100 || len(topology.Edges) > 200 || !topology.Truncated {
t.Fatalf("topology exceeded target bounds: nodes=%d edges=%d truncated=%v", len(topology.Nodes), len(topology.Edges), topology.Truncated)
}
}