Files
ConfigKit/internal/engine/resolution_index_test.go

172 lines
7.0 KiB
Go

package engine
import (
"reflect"
"testing"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
func TestCoalescedResolutionSnapshotsMatchOrderedScan(t *testing.T) {
parsedDependency := parseOverrideTestPath(t, "/dependencies/item")
snapshots := []resolutionSnapshot{
{change: Change{Path: Keys("a", "b"), Reasons: []string{"child", "shared", "child"}, Dependencies: []Path{Keys("dependencies", "item")}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Keys("a", "c"), Reasons: []string{"other"}, Dependencies: []Path{Keys("other")}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Keys("a"), Reasons: []string{"parent"}, Dependencies: []Path{Keys("parent")}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Keys("a", "b", "grand"), Reasons: []string{"deep", "shared"}, Dependencies: []Path{parsedDependency, Keys("deep")}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Path{}, Reasons: []string{"root"}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Keys("outside"), Reasons: []string{"root child"}, Origin: Origin{Operation: ChangeDerivedDefault}}},
{change: Change{Path: Keys("a", "b", "normalization"), Reasons: []string{"normalized"}, Origin: Origin{Operation: ChangeNormalization}}},
{change: Change{Path: Keys("a", "none"), Reasons: []string{"parent child"}, Origin: Origin{Operation: ChangeDerivedDefault}}},
}
want := coalescedResolutionSnapshotsByScan(snapshots)
got := coalescedResolutionSnapshots(snapshots)
if !reflect.DeepEqual(got, want) {
t.Fatalf("coalesced snapshots differ from ordered scan:\n got: %#v\nwant: %#v", got, want)
}
if len(got) != 4 || got[0].change.Origin.Operation != ChangeNormalization ||
!reflect.DeepEqual(got[0].change.Reasons, []string{"child", "shared", "child", "deep", "normalized"}) ||
len(got[0].change.Dependencies) != 2 || got[3].change.Path.String() != "" {
t.Fatalf("coalesced ordering or unions changed: %#v", got)
}
}
func coalescedResolutionSnapshotsByScan(snapshots []resolutionSnapshot) []resolutionSnapshot {
groups := make([]resolutionSnapshot, 0, len(snapshots))
for _, snapshot := range snapshots {
merged := false
for i := range groups {
if !pathContains(groups[i].change.Path, snapshot.change.Path) {
continue
}
for _, reason := range snapshot.change.Reasons {
groups[i].change.Reasons = appendUniqueString(groups[i].change.Reasons, reason)
}
for _, dependency := range snapshot.change.Dependencies {
groups[i].change.Dependencies = appendUniquePath(groups[i].change.Dependencies, dependency)
}
if snapshot.change.Origin.Operation == ChangeNormalization {
groups[i].change.Origin.Operation = ChangeNormalization
}
merged = true
break
}
if !merged {
snapshot.change = cloneChange(snapshot.change)
groups = append(groups, snapshot)
}
}
return groups
}
func TestResolutionCoverageIndexMatchesOrderedScan(t *testing.T) {
paths := []Path{
Keys("group"),
Keys("group", "field"),
Keys("group", "field"),
Keys("group", "field", "nested"),
Keys("other"),
}
index := newResolutionCoverageIndex(paths)
queries := []Path{
Path{},
Keys("group"),
Keys("group", "field"),
Keys("group", "field", "nested", "leaf"),
Keys("group", "missing"),
Keys("other", "child"),
Keys("group").Index(-1),
}
for _, query := range queries {
wantID, wantDepth, wantFound := 0, -1, false
for id, candidate := range paths {
if pathContains(candidate, query) && (!wantFound || len(candidate.segments) > wantDepth) {
wantID, wantDepth, wantFound = id, len(candidate.segments), true
}
}
gotID, gotFound := index.deepestCoveringID(query)
if gotID != wantID || gotFound != wantFound {
t.Errorf("deepestCoveringID(%s) = %d, %v; want %d, %v", query.String(), gotID, gotFound, wantID, wantFound)
}
}
}
func TestDocumentLookupMatchesPathScan(t *testing.T) {
name := &document.Node{Kind: document.String, Text: "item"}
sequence := &document.Node{Kind: document.Sequence, Sequence: []*document.Node{
{Kind: document.String, Text: "zero"},
{Kind: document.Mapping, Mapping: []document.Entry{{Key: "name", Value: name}}},
}}
root := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{
{Key: "0", Value: &document.Node{Kind: document.String, Text: "numeric key"}},
{Key: "01", Value: &document.Node{Kind: document.String, Text: "leading zero key"}},
{Key: "items", Value: sequence},
}}
lookup := buildDocumentLookup(root)
paths := []Path{
Path{},
Keys("0"),
Keys("01"),
Keys("items"),
Keys("items").Index(0),
Keys("items").Index(1).Key("name"),
Keys("items").Index(2),
Keys("items").Key("name"),
Keys("missing"),
Keys("items").Index(-1),
(Path{}).Index(0),
}
for _, path := range paths {
wantNode, wantFound := documentAtPath(root, path)
gotNode, gotFound := documentAtPathWithLookup(root, path, lookup)
if gotNode != wantNode || gotFound != wantFound {
t.Errorf("indexed lookup %s = (%p, %v), want (%p, %v)", path.String(), gotNode, gotFound, wantNode, wantFound)
}
}
}
func TestApplyResolutionMetadataPreservesOverlappingDeltaOrder(t *testing.T) {
oldOrigin := Origin{Layer: LayerBase}
parentOrigin := Origin{Layer: LayerResolution, Operation: ChangeNormalization}
childOrigin := Origin{Layer: LayerResolution, Operation: ChangeDerivedDefault}
leaf := &document.Node{Kind: document.String, Text: "value"}
final := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
Key: "group",
Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
Key: "item",
Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{Key: "leaf", Value: leaf}}},
}}},
}}}
metadata := &Metadata{
fields: map[string]FieldInfo{"/group/item/leaf": {Origin: oldOrigin}},
history: make(map[string][]Change),
}
resolved := resolutionResult{
final: final,
deltas: []documentDelta{{path: Keys("group")}, {path: Keys("group", "item")}},
snapshots: []resolutionSnapshot{
{change: Change{Path: Keys("group"), Origin: parentOrigin}},
{change: Change{Path: Keys("group", "item"), Origin: childOrigin}},
},
}
applyResolutionMetadata(metadata, resolved)
field := metadata.fields["/group/item/leaf"]
if field.Origin.Operation != childOrigin.Operation {
t.Fatalf("final leaf origin operation = %q, want %q", field.Origin.Operation, childOrigin.Operation)
}
changes := metadata.history["/group/item/leaf"]
if len(changes) != 2 {
t.Fatalf("leaf history has %d entries, want 2: %#v", len(changes), changes)
}
if changes[0].Origin.Operation != parentOrigin.Operation || changes[1].Origin.Operation != childOrigin.Operation {
t.Fatalf("leaf history operations = %q, %q; want %q, %q", changes[0].Origin.Operation, changes[1].Origin.Operation, parentOrigin.Operation, childOrigin.Operation)
}
for index, change := range changes {
if len(change.Previous) != 1 || change.Previous[0].Layer != oldOrigin.Layer {
t.Errorf("leaf history[%d] Previous = %#v, want original origin", index, change.Previous)
}
}
}