This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package reconciliation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/itworx/pulse/internal/datasource"
|
||||
)
|
||||
|
||||
func TestRepeatedSnapshotIsIdempotent(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
obs := []Observation{{SourceID: "source", ExternalType: "container", ExternalID: "abc", EntityType: "container", CanonicalName: "app", DisplayName: "App", ObservedAt: now}}
|
||||
first, err := Reconcile("source", datasource.HealthHealthy, obs, nil, nil, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := Reconcile("source", datasource.HealthHealthy, obs, first.Entities, nil, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(second.Added) != 0 || len(second.Updated) != 1 || first.Aliases["source\x00container\x00abc"] != second.Aliases["source\x00container\x00abc"] {
|
||||
t.Fatalf("not idempotent: first=%+v second=%+v", first, second)
|
||||
}
|
||||
}
|
||||
func TestSourceOutageDoesNotTombstone(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
previous := []Existing{{EntityID: StableEntityID("source", "container", "abc"), SourceID: "source", ExternalType: "container", ExternalID: "abc"}}
|
||||
result, err := Reconcile("source", datasource.HealthUnknown, nil, previous, nil, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(result.Tombstoned) != 0 || len(result.Entities) != 1 {
|
||||
t.Fatalf("outage changed inventory: %+v", result)
|
||||
}
|
||||
}
|
||||
func TestOverrideSurvivesDiscovery(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
id := StableEntityID("source", "container", "abc")
|
||||
result, err := Reconcile("source", datasource.HealthHealthy, []Observation{{SourceID: "source", ExternalType: "container", ExternalID: "abc", EntityType: "container", CanonicalName: "app", DisplayName: "Discovered", ObservedAt: now}}, nil, []Override{{EntityID: id, FieldName: "displayName", Value: "Manual"}}, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Aliases["source\x00container\x00abc"] != id || result.Entities[0].DisplayName != "Manual" {
|
||||
t.Fatalf("override was not preserved: %+v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissingObservationStampsTombstoneTime(t *testing.T) {
|
||||
now := time.Date(2026, time.August, 1, 22, 0, 0, 0, time.UTC)
|
||||
obs := []Observation{{SourceID: "source", ExternalType: "container", ExternalID: "abc", EntityType: "container", CanonicalName: "app", DisplayName: "App", ObservedAt: now}}
|
||||
first, err := Reconcile("source", datasource.HealthHealthy, obs, nil, nil, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !first.Entities[0].TombstonedAt.IsZero() {
|
||||
t.Fatalf("live entity carries a tombstone time: %+v", first.Entities[0])
|
||||
}
|
||||
later := now.Add(time.Hour).In(time.FixedZone("CEST", 2*60*60))
|
||||
second, err := Reconcile("source", datasource.HealthHealthy, nil, first.Entities, nil, later)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(second.Tombstoned) != 1 || len(second.Entities) != 1 {
|
||||
t.Fatalf("expected a single tombstone: %+v", second)
|
||||
}
|
||||
entity := second.Entities[0]
|
||||
if !entity.Tombstoned || !entity.TombstonedAt.Equal(later) || entity.TombstonedAt.Location() != time.UTC {
|
||||
t.Fatalf("tombstone time was not stamped in UTC: %+v", entity)
|
||||
}
|
||||
third, err := Reconcile("source", datasource.HealthHealthy, nil, second.Entities, nil, later.Add(time.Hour))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(third.Tombstoned) != 0 {
|
||||
t.Fatalf("already tombstoned entity was tombstoned again: %+v", third)
|
||||
}
|
||||
revived, err := Reconcile("source", datasource.HealthHealthy, obs, second.Entities, nil, later.Add(2*time.Hour))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if revived.Entities[0].Tombstoned || !revived.Entities[0].TombstonedAt.IsZero() {
|
||||
t.Fatalf("revived entity kept its tombstone: %+v", revived.Entities[0])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user