146 lines
5.9 KiB
Go
146 lines
5.9 KiB
Go
package engine
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestMovedRemovedFieldHasConsistentHistory(t *testing.T) {
|
|
type config struct {
|
|
Items []map[string]any `yaml:"items"`
|
|
}
|
|
definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{
|
|
Resolve: func(value *config, r *Resolution) error {
|
|
delete(value.Items[0], "extra")
|
|
value.Items[0], value.Items[1] = value.Items[1], value.Items[0]
|
|
r.RecordNormalization(Keys("items"), "remove field and reorder")
|
|
return nil
|
|
},
|
|
}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}})
|
|
result, err := testLoad(Bytes("records", []byte("items: [{id: a, extra: own}, {id: b}]\n")), Options[config]{Definition: definition})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := Keys("items").Index(1).Key("extra")
|
|
if _, found := result.Metadata().Lookup(path); found {
|
|
t.Fatal("removed field is effective")
|
|
}
|
|
explanation, found := result.Inspection.Explain(path)
|
|
if !found || len(explanation.History) != 2 {
|
|
t.Fatalf("explanation = %#v, %t", explanation, found)
|
|
}
|
|
history := result.Metadata().History(path)
|
|
if len(history) != 2 || history[0].Origin.Layer != LayerMain || history[1].Origin.Operation != ChangeNormalization {
|
|
t.Fatalf("metadata history = %#v", history)
|
|
}
|
|
last := explanation.History[1]
|
|
if last.Before.YAML != "own\n" || last.After.Present {
|
|
t.Fatalf("removal = %#v", last)
|
|
}
|
|
for _, unknown := range []Path{Keys("items", "1", "extra"), Keys("items").Index(1).Key("unknown")} {
|
|
if history := result.Metadata().History(unknown); len(history) != 0 {
|
|
t.Fatalf("unexpected history for %s: %#v", unknown.String(), history)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMovedInheritedHistoryUsesFinalRecordPrefix(t *testing.T) {
|
|
type config struct {
|
|
Items []map[string]any `yaml:"items"`
|
|
}
|
|
field := Keys("items").Index(0).Key("nested").Key("more").Key("value")
|
|
definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{
|
|
Resolve: func(value *config, r *Resolution) error {
|
|
value.Items[0], value.Items[1] = value.Items[1], value.Items[0]
|
|
r.RecordNormalization(Keys("items"), "reorder")
|
|
r.RecordNormalization(field, "retain inherited field")
|
|
return nil
|
|
},
|
|
}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}})
|
|
result, err := testLoad(Bytes("records", []byte("items:\n - id: parent\n nested: {more: {value: old}}\n - id: child\n extends: {id: parent}\n")), Options[config]{Definition: definition})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
physical := result.Metadata().History(Keys("items"))
|
|
sourceID := result.Inspection.Sources()[0].ID
|
|
for _, query := range []Path{field, Keys("items").Index(0)} {
|
|
inherited, recorded := false, false
|
|
for _, change := range result.Metadata().History(query) {
|
|
if change.Origin.Operation == ChangeInheritance {
|
|
inherited = true
|
|
if !pathContains(Keys("items").Index(0), change.Path) {
|
|
t.Errorf("inheritance path for %s = %s", query.String(), change.Path.String())
|
|
}
|
|
if query.String() == field.String() && change.Path.String() != "/items/0" {
|
|
t.Errorf("container operation replaced by leaf path: %s", change.Path.String())
|
|
}
|
|
for _, location := range change.Origin.Locations {
|
|
if location.SourceID != sourceID || (query.String() == field.String() && location.Line != 3) {
|
|
t.Errorf("source coordinates changed: %#v", location)
|
|
}
|
|
}
|
|
}
|
|
if change.Origin.Layer == LayerResolution && len(change.Reasons) == 1 && change.Reasons[0] == "retain inherited field" {
|
|
recorded = true
|
|
if change.Path.String() != field.String() {
|
|
t.Errorf("final resolution path translated twice: %s", change.Path.String())
|
|
}
|
|
}
|
|
}
|
|
if !inherited || !recorded {
|
|
t.Errorf("missing inherited or recorded evidence for %s", query.String())
|
|
}
|
|
}
|
|
for _, change := range physical {
|
|
if change.Origin.Operation == ChangeInheritance && !pathContains(Keys("items").Index(1), change.Path) {
|
|
t.Errorf("collection history lost physical coordinates: %s", change.Path.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCyclicMatchingProtectsRemovedAndDerivedValues(t *testing.T) {
|
|
type config struct {
|
|
Items []map[string]any `yaml:"items"`
|
|
Derived string `yaml:"derived"`
|
|
}
|
|
removed := Keys("items").Index(1).Key("extra")
|
|
definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{
|
|
Secret: func(path Path) bool { return path.String() == "/items/0/extra" },
|
|
Resolve: func(value *config, r *Resolution) error {
|
|
value.Derived = value.Items[0]["extra"].(string)
|
|
delete(value.Items[0], "extra")
|
|
last := value.Items[len(value.Items)-1]
|
|
copy(value.Items[1:], value.Items[:len(value.Items)-1])
|
|
value.Items[0] = last
|
|
r.RecordNormalization(Keys("items"), "remove field and rotate")
|
|
r.RecordDerivedDefault(Keys("derived"), "derive from removed value", removed)
|
|
return nil
|
|
},
|
|
}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}})
|
|
result, err := testLoad(Bytes("records", []byte("items: [{id: a, extra: CYCLIC_PRIVATE_CANARY}, {id: b}, {id: c}, {id: d}]\n")), Options[config]{Definition: definition})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, path := range []Path{Keys("items"), removed, Keys("derived")} {
|
|
explanation, found := result.Inspection.Explain(path)
|
|
if !found {
|
|
t.Fatalf("missing explanation for %s", path.String())
|
|
}
|
|
if strings.Contains(explanation.Effective.YAML, "CYCLIC_PRIVATE_CANARY") {
|
|
t.Fatalf("effective value leaked at %s", path.String())
|
|
}
|
|
for _, change := range explanation.History {
|
|
if strings.Contains(change.Before.YAML, "CYCLIC_PRIVATE_CANARY") || strings.Contains(change.After.YAML, "CYCLIC_PRIVATE_CANARY") {
|
|
t.Fatalf("history leaked at %s", path.String())
|
|
}
|
|
}
|
|
}
|
|
var output strings.Builder
|
|
if err := result.Inspection.WriteEffective(&output); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(output.String(), "CYCLIC_PRIVATE_CANARY") {
|
|
t.Fatal("effective rendering leaked")
|
|
}
|
|
}
|