472 lines
20 KiB
Go
472 lines
20 KiB
Go
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
|
)
|
|
|
|
func parseItemPolicyDocument(t *testing.T, sourceID, input string) *document.Node {
|
|
t.Helper()
|
|
root, issues := yamladapter.Parse(strings.NewReader(input), sourceID, yamladapter.Limits{
|
|
Depth: maxDocumentDepth, Nodes: maxDocumentNodes,
|
|
})
|
|
if len(issues) != 0 {
|
|
t.Fatalf("parse document: %#v", issues)
|
|
}
|
|
return root
|
|
}
|
|
|
|
func TestPendingItemCompositionKeepsLocalDeclarationsAndHelperIdentity(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
declaration := sequenceDeclarationSpec{
|
|
destination: Keys("prompts"), identityFields: []string{"id"}, identityPresent: true,
|
|
}
|
|
itemScope := acquisitionScope{contributions: []acquiredContribution{
|
|
{
|
|
document: parseItemPolicyDocument(t, "item", "name: authored\nprompts:\n - id: authored\n"),
|
|
sequenceDeclarations: []sequenceDeclarationSpec{declaration},
|
|
},
|
|
{
|
|
document: parseItemPolicyDocument(t, "helper", "id: supplied\nprompts:\n - id: helper\n"),
|
|
},
|
|
}}
|
|
outer := acquisitionScope{contributions: []acquiredContribution{{
|
|
source: SourceInfo{ID: "item"},
|
|
payload: parseItemPolicyDocument(t, "outer", "{}\n"),
|
|
destination: Keys("items"), pendingItem: &itemScope,
|
|
}}}
|
|
|
|
composed, diagnostics := composeAcquiredScopeWithPolicy(
|
|
&outer, collectionPolicyScope{}, sequencePolicy{}, newImportInclusionGraph(), nil, nil, nil, &importAccounting{},
|
|
)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("compose pending item scope: %#v", diagnostics)
|
|
}
|
|
|
|
item := outer.contributions[0].payload
|
|
if got, ok := documentAtPath(item, Keys("id")); !ok || got == nil || got.Text != "supplied" {
|
|
t.Fatalf("helper-supplied record identity = %#v", got)
|
|
}
|
|
if got, ok := documentAtPath(item, Keys("prompts")); !ok || got == nil || got.Kind != document.Sequence || len(got.Sequence) != 2 {
|
|
t.Fatalf("composed prompts = %#v, want two identified entries", got)
|
|
}
|
|
itemPolicy, ok := composed.collectionPolicies.at(Keys("items"))
|
|
if !ok {
|
|
t.Fatalf("projected item collection policy missing")
|
|
}
|
|
owned, ok := itemPolicy.recordPolicyAt(0)
|
|
if !ok {
|
|
t.Fatalf("projected item owner missing: %#v", itemPolicy)
|
|
}
|
|
if policy, ok := owned.scope.at(Keys("prompts")); !ok || !policy.identityPresent || len(policy.identityFields) != 1 || policy.identityFields[0] != "id" {
|
|
t.Fatalf("item-owned prompt policy = %#v, %v", policy, ok)
|
|
}
|
|
if _, ok := composed.collectionPolicies.at(Keys("prompts")); ok {
|
|
t.Fatal("item-local declaration escaped its owning record")
|
|
}
|
|
}
|
|
|
|
func TestPendingItemCompositionProjectsNestedIdentityDiagnostics(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
childScope := acquisitionScope{contributions: []acquiredContribution{
|
|
{document: parseItemPolicyDocument(t, "child", "prompts:\n - id: same\n"), sequenceDeclarations: []sequenceDeclarationSpec{{
|
|
destination: Keys("prompts"), identityFields: []string{"id"}, identityPresent: true,
|
|
}}},
|
|
{document: parseItemPolicyDocument(t, "child-helper", "prompts:\n - id: same\n")},
|
|
}}
|
|
itemScope := acquisitionScope{contributions: []acquiredContribution{{
|
|
document: parseItemPolicyDocument(t, "item", "name: parent\n"),
|
|
}, {
|
|
source: SourceInfo{ID: "child"}, payload: parseItemPolicyDocument(t, "child", "{}\n"),
|
|
destination: Keys("children"), pendingItem: &childScope,
|
|
}}}
|
|
outer := acquisitionScope{contributions: []acquiredContribution{{
|
|
source: SourceInfo{ID: "item"}, payload: parseItemPolicyDocument(t, "outer", "{}\n"),
|
|
destination: Keys("items"), pendingItem: &itemScope,
|
|
}}}
|
|
|
|
traversal := importTraversal{accounting: &importAccounting{}, limits: defaultImportLimits()}
|
|
diagnostics := traversal.composePendingItems(&outer)
|
|
if len(diagnostics) != 1 {
|
|
t.Fatalf("diagnostics = %#v, want one conflict", diagnostics)
|
|
}
|
|
if diagnostics[0].Code != CodeComposeConflict || diagnostics[0].Path.String() != "/items/0/children/0/prompts" {
|
|
t.Fatalf("diagnostic = %#v, want projected collection path", diagnostics[0])
|
|
}
|
|
if strings.Contains(fmt.Sprint(diagnostics[0]), "same") {
|
|
t.Fatalf("diagnostic disclosed identity value: %v", diagnostics[0])
|
|
}
|
|
}
|
|
|
|
func TestPendingItemCompositionAppliesOuterApplicationIdentityConstraints(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
itemScope := acquisitionScope{contributions: []acquiredContribution{{
|
|
document: parseItemPolicyDocument(t, "item", "prompts:\n - text: missing-id\n"),
|
|
}}}
|
|
outer := acquisitionScope{contributions: []acquiredContribution{{
|
|
source: SourceInfo{ID: "item"}, payload: parseItemPolicyDocument(t, "outer", "{}\n"),
|
|
destination: Keys("items"), pendingItem: &itemScope,
|
|
}}}
|
|
policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{{
|
|
selector: sequenceIdentitySelector{literalSequenceSelector("items")}, identityFields: []string{"id"},
|
|
}})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("compile application identity: %#v", diagnostics)
|
|
}
|
|
|
|
_, diagnostics = composeAcquiredScopeWithPolicy(&outer, collectionPolicyScope{}, policy,
|
|
newImportInclusionGraph(), nil, nil, nil, &importAccounting{})
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeComposeInvalid || diagnostics[0].Path.String() != "/items/0/id" {
|
|
t.Fatalf("application identity diagnostics = %#v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestReplacingOuterCollectionRetiresDiscardedItemIdentity(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
outerPath := Keys("items")
|
|
nestedPath := Keys("items").Index(0).Key("prompts")
|
|
lower := collectionPolicyScope{}
|
|
outerPolicy := collectionPolicy{}
|
|
outerPolicy.setRecordPolicy(newCollectionRecordPolicy())
|
|
lower.set(outerPath, outerPolicy)
|
|
lower.set(nestedPath, collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
|
|
|
|
declarations := []sequenceDeclarationSpec{{
|
|
destination: outerPath, mode: sequenceModeReplace, modePresent: true,
|
|
}}
|
|
input := parseItemPolicyDocument(t, "replacement", "items:\n - prompts:\n - text: new\n")
|
|
composed, diagnostics := composeDocumentsWithCollectionPolicy(sequencePolicy{}, lower, declarations, input)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("compose replacement: %#v", diagnostics)
|
|
}
|
|
policy, ok := composed.collectionPolicies.at(nestedPath)
|
|
if !ok || policy.identityPresent || len(policy.recordOrder) != 0 {
|
|
t.Fatalf("discarded nested owner policy = %#v, %v", policy, ok)
|
|
}
|
|
}
|
|
|
|
func TestNestedItemPolicyProjectionUsesStableOutputIndexes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
itemsPath := Keys("items")
|
|
outerPolicy := collectionPolicy{}
|
|
retainedScope := collectionPolicyScope{}
|
|
retainedScope.set(Keys("retained"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
|
|
outerPolicy.setRecordPolicy(collectionRecordPolicy{owner: newCollectionRecordPolicy().owner, scope: retainedScope})
|
|
composed := &composedDocument{collectionPolicies: collectionPolicyScope{}}
|
|
composed.collectionPolicies.set(itemsPath, outerPolicy)
|
|
|
|
firstScope := collectionPolicyScope{}
|
|
firstScope.set(Keys("prompts"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
|
|
secondScope := collectionPolicyScope{}
|
|
secondScope.set(Keys("prompts"), collectionPolicy{identityFields: []string{"slug"}, identityPresent: true})
|
|
scope := acquisitionScope{contributions: []acquiredContribution{
|
|
{document: parseItemPolicyDocument(t, "first", "items:\n - id: first\n"), destination: itemsPath, nested: &composedDocument{collectionPolicies: firstScope}},
|
|
{document: parseItemPolicyDocument(t, "second", "items:\n - id: second\n"), destination: itemsPath, nested: &composedDocument{collectionPolicies: secondScope}},
|
|
}}
|
|
|
|
projectNestedCollectionPolicies(composed, scope, nil)
|
|
policy, ok := composed.collectionPolicies.at(itemsPath)
|
|
if !ok || len(policy.recordOrder) != 3 {
|
|
t.Fatalf("record-owned policies = %#v, %v; want retained plus two item scopes", policy, ok)
|
|
}
|
|
for index, wantField := range []string{"id", "id", "slug"} {
|
|
record, ok := policy.recordPolicyAt(index)
|
|
if !ok {
|
|
t.Fatalf("record policy %d missing", index)
|
|
}
|
|
path := Keys("retained")
|
|
if index != 0 {
|
|
path = Keys("prompts")
|
|
}
|
|
nested, ok := record.scope.at(path)
|
|
if !ok || !nested.identityPresent || len(nested.identityFields) != 1 || nested.identityFields[0] != wantField {
|
|
t.Errorf("record policy %d = %#v, %v; want %q", index, nested, ok, wantField)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestItemPolicyProjectionCountsContributionsWithoutLocalPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
items := Keys("items")
|
|
for _, assignments := range []map[int]int{nil, {1: 2, 3: 4}, {1: 2}, {3: 4}} {
|
|
t.Run(fmt.Sprint(assignments), func(t *testing.T) {
|
|
local := collectionPolicyScope{}
|
|
local.set(Keys("steps"), collectionPolicy{identityFields: []string{"name"}, identityPresent: true})
|
|
scope := acquisitionScope{contributions: []acquiredContribution{
|
|
{document: parseItemPolicyDocument(t, "inline", "items: [{id: inline-a}, {id: inline-b}]\n")},
|
|
{document: parseItemPolicyDocument(t, "first", "items: [{id: first}]\n"), destination: items, nested: &composedDocument{collectionPolicies: local}},
|
|
{document: parseItemPolicyDocument(t, "gap", "items: [{id: gap}]\n"), destination: items},
|
|
{document: parseItemPolicyDocument(t, "second", "items: [{id: second}]\n"), destination: items, nested: &composedDocument{collectionPolicies: local}},
|
|
}}
|
|
ranges := make(map[sequenceRangeKey]sequenceRange)
|
|
for sourceIndex, output := range assignments {
|
|
ranges[sequenceRangeKey{sourceIndex: sourceIndex, path: items.String()}] = sequenceRange{start: output, end: output + 1}
|
|
}
|
|
composed := &composedDocument{}
|
|
projectNestedCollectionPolicies(composed, scope, ranges)
|
|
policy, _ := composed.collectionPolicies.at(items)
|
|
if len(policy.recordOrder) != 5 {
|
|
t.Fatalf("record policies = %d, want 5 positions", len(policy.recordOrder))
|
|
}
|
|
for index := range 5 {
|
|
record, _ := policy.recordPolicyAt(index)
|
|
_, found := record.scope.at(Keys("steps"))
|
|
if found != (index == 2 || index == 4) {
|
|
t.Errorf("local policy at item %d = %t", index, found)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestItemPolicyProjectionDetachesRepeatedScopes(t *testing.T) {
|
|
t.Parallel()
|
|
local := collectionPolicyScope{}
|
|
local.set(Keys("steps"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
|
|
scope := acquisitionScope{}
|
|
ranges := make(map[sequenceRangeKey]sequenceRange)
|
|
for index, destination := range []string{"items", "other", "items"} {
|
|
path := Keys(destination)
|
|
scope.contributions = append(scope.contributions, acquiredContribution{
|
|
document: parseItemPolicyDocument(t, "item", destination+": [{}]\n"), destination: path,
|
|
nested: &composedDocument{collectionPolicies: local},
|
|
})
|
|
output := 0
|
|
if index == 2 {
|
|
output = 1
|
|
}
|
|
ranges[sequenceRangeKey{sourceIndex: index, path: path.String()}] = sequenceRange{start: output, end: output + 1}
|
|
}
|
|
first, second := &composedDocument{}, &composedDocument{}
|
|
projectNestedCollectionPolicies(first, scope, ranges)
|
|
projectNestedCollectionPolicies(second, scope, ranges)
|
|
policy := first.collectionPolicies.collections[Keys("items").String()].policy
|
|
if len(policy.recordOrder) != 2 || policy.recordOrder[0] == policy.recordOrder[1] {
|
|
t.Fatal("separately placed records did not retain distinct owners")
|
|
}
|
|
other := second.collectionPolicies.collections[Keys("items").String()].policy
|
|
if policy.recordOrder[0] == other.recordOrder[0] {
|
|
t.Fatal("independent projections reused a record owner")
|
|
}
|
|
// Mutate stored state directly: a detached query result would conceal an
|
|
// accidental alias between incrementally constructed record scopes.
|
|
steps := Keys("steps").String()
|
|
firstRecord := policy.recordPolicies[policy.recordOrder[0]]
|
|
firstRecord.collections[steps].policy.identityFields[0] = "changed"
|
|
for _, check := range []struct {
|
|
composed *composedDocument
|
|
path Path
|
|
index int
|
|
}{
|
|
{first, Keys("items"), 1}, {first, Keys("other"), 0},
|
|
{second, Keys("items"), 0}, {second, Keys("items"), 1}, {second, Keys("other"), 0},
|
|
} {
|
|
outer, _ := check.composed.collectionPolicies.at(check.path)
|
|
record, found := outer.recordPolicyAt(check.index)
|
|
identity, present := record.scope.at(Keys("steps"))
|
|
if !found || !present || !reflect.DeepEqual(identity.identityFields, []string{"id"}) {
|
|
t.Fatalf("mutation leaked into %s item %d: %#v", check.path, check.index, identity)
|
|
}
|
|
}
|
|
if local.collections[steps].policy.identityFields[0] != "id" {
|
|
t.Fatal("projected state mutated the incoming scope")
|
|
}
|
|
local.collections[steps].policy.identityFields[0] = "source-changed"
|
|
if other.recordPolicies[other.recordOrder[0]].collections[steps].policy.identityFields[0] != "id" {
|
|
t.Fatal("incoming scope remained aliased to projected state")
|
|
}
|
|
}
|
|
|
|
func TestOverlaySequenceTransfersIncomingItemPolicyOwnership(t *testing.T) {
|
|
path := Keys("records")
|
|
registered := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{{selector: sequenceIdentitySelector{literalSequenceSelector("records")}, identityFields: []string{"id"}}})
|
|
makePolicies := func(childKey string) collectionPolicyScope {
|
|
local := collectionPolicyScope{}
|
|
local.set(Keys("steps"), collectionPolicy{
|
|
identityFields: []string{childKey}, identityPresent: true,
|
|
explicitMode: sequenceModeAppend, explicitModePresent: true,
|
|
})
|
|
record := newCollectionRecordPolicy()
|
|
record.scope = local
|
|
outer := collectionPolicy{identityFields: []string{"id"}, identityPresent: true}
|
|
outer.setRecordPolicy(record)
|
|
scope := collectionPolicyScope{}
|
|
scope.set(path, outer)
|
|
return scope
|
|
}
|
|
checkPolicy := func(t *testing.T, scope collectionPolicyScope, index int, key string) {
|
|
t.Helper()
|
|
outer, ok := scope.at(path)
|
|
if !ok {
|
|
t.Fatal("outer collection policy missing")
|
|
}
|
|
record, ok := outer.recordPolicyAt(index)
|
|
if !ok {
|
|
t.Fatalf("record policy %d missing", index)
|
|
}
|
|
local, ok := record.scope.at(Keys("steps"))
|
|
if !ok || !local.identityPresent || !reflect.DeepEqual(local.identityFields, []string{key}) || !local.explicitModePresent || local.explicitMode != sequenceModeAppend {
|
|
t.Fatalf("record %d nested policy = %#v, %v; want key %q and append", index, local, ok, key)
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
lower string
|
|
higher string
|
|
declarations []sequenceDeclarationSpec
|
|
wantRecords int
|
|
}{
|
|
{
|
|
name: "upserting the same outer identity replaces its local policy",
|
|
lower: "records: [{id: same, steps: [{id: old}]}]\n",
|
|
higher: "records: [{id: same, steps: [{slug: new}]}]\n",
|
|
wantRecords: 1,
|
|
},
|
|
{
|
|
name: "retained and appended outer records keep independent policies",
|
|
lower: "records: [{id: lower, steps: [{id: old}]}]\n",
|
|
higher: "records: [{id: higher, steps: [{slug: new}]}]\n",
|
|
wantRecords: 2,
|
|
},
|
|
{
|
|
name: "whole sequence replacement transfers new local policy",
|
|
lower: "records: [{id: same, steps: [{id: old}]}]\n",
|
|
higher: "records: [{id: same, steps: [{slug: new}]}]\n",
|
|
declarations: []sequenceDeclarationSpec{{
|
|
destination: path, mode: sequenceModeReplace, modePresent: true,
|
|
}},
|
|
wantRecords: 1,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
lowerRoot := parseItemPolicyDocument(t, "lower", test.lower)
|
|
incomingRoot := parseItemPolicyDocument(t, "higher", test.higher)
|
|
assembly := newAssemblyAccumulator(&composedDocument{
|
|
root: lowerRoot, collectionPolicies: makePolicies("id"), origins: make(map[string]Origin),
|
|
})
|
|
if diagnostics := assembly.applyOverlayWithCollectionPolicy(
|
|
incomingRoot, nil, makePolicies("slug"), nil, LayerBase, registered, test.declarations,
|
|
); len(diagnostics) != 0 {
|
|
t.Fatalf("overlay diagnostics = %#v", diagnostics)
|
|
}
|
|
outer, ok := assembly.current.collectionPolicies.at(path)
|
|
if !ok || len(outer.recordOrder) != test.wantRecords {
|
|
t.Fatalf("outer record policies = %#v, %v", outer, ok)
|
|
}
|
|
if test.wantRecords == 2 {
|
|
checkPolicy(t, assembly.current.collectionPolicies, 0, "id")
|
|
checkPolicy(t, assembly.current.collectionPolicies, 1, "slug")
|
|
} else {
|
|
checkPolicy(t, assembly.current.collectionPolicies, 0, "slug")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAncestorReplacementRequiresIdentityWhenRetainedModeReturns(t *testing.T) {
|
|
path := Keys("catalog", "prompts")
|
|
newAssembly := func(t *testing.T) *assemblyAccumulator {
|
|
t.Helper()
|
|
policy := collectionPolicy{
|
|
identityFields: []string{"id"}, identityPresent: true,
|
|
explicitMode: sequenceModeAppend, explicitModePresent: true,
|
|
}
|
|
scope := collectionPolicyScope{}
|
|
scope.set(path, policy)
|
|
return newAssemblyAccumulator(&composedDocument{
|
|
root: parseItemPolicyDocument(t, "base", "catalog:\n prompts: [{id: old}]\n"),
|
|
collectionPolicies: scope, origins: make(map[string]Origin),
|
|
})
|
|
}
|
|
removeAncestor := func(t *testing.T, assembly *assemblyAccumulator) {
|
|
t.Helper()
|
|
if diagnostics := assembly.applyOverlayWithCollectionPolicy(
|
|
parseItemPolicyDocument(t, "remove", "catalog: null\n"), nil, collectionPolicyScope{}, nil,
|
|
LayerBase, sequencePolicy{}, nil,
|
|
); len(diagnostics) != 0 {
|
|
t.Fatalf("ancestor removal diagnostics = %#v", diagnostics)
|
|
}
|
|
}
|
|
|
|
t.Run("retained append mode requires a renewed identity", func(t *testing.T) {
|
|
assembly := newAssembly(t)
|
|
removeAncestor(t, assembly)
|
|
policy, ok := assembly.current.collectionPolicies.at(path)
|
|
if !ok || policy.identityPresent || !policy.explicitModePresent || policy.explicitMode != sequenceModeAppend {
|
|
t.Fatalf("retired collection policy = %#v, %v", policy, ok)
|
|
}
|
|
diagnostics := assembly.applyOverlayWithCollectionPolicy(
|
|
parseItemPolicyDocument(t, "reintroduction", "catalog:\n prompts: [{id: restored, name: restored}]\n"), nil,
|
|
collectionPolicyScope{}, nil, LayerBase, sequencePolicy{}, nil,
|
|
)
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceInvalid {
|
|
t.Fatalf("append without renewed identity diagnostics = %#v", diagnostics)
|
|
}
|
|
})
|
|
|
|
t.Run("a renewed key restores the retained append mode", func(t *testing.T) {
|
|
assembly := newAssembly(t)
|
|
removeAncestor(t, assembly)
|
|
declaration := sequenceDeclarationSpec{
|
|
destination: path, identityFields: []string{"name"}, identityPresent: true,
|
|
}
|
|
diagnostics := assembly.applyOverlayWithCollectionPolicy(
|
|
parseItemPolicyDocument(t, "keyed", "catalog:\n prompts: [{name: next}]\n"), nil,
|
|
collectionPolicyScope{}, nil, LayerBase, sequencePolicy{}, []sequenceDeclarationSpec{declaration},
|
|
)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("renewed identity diagnostics = %#v", diagnostics)
|
|
}
|
|
policy, ok := assembly.current.collectionPolicies.at(path)
|
|
if !ok || !policy.identityPresent || !reflect.DeepEqual(policy.identityFields, []string{"name"}) || !policy.explicitModePresent || policy.explicitMode != sequenceModeAppend {
|
|
t.Fatalf("renewed collection policy = %#v, %v", policy, ok)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestItemSourceVersionProcessingConsumesDeclarationsBeforeComposition(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := parseItemPolicyDocument(t, "item", `version: v1
|
|
configkit:
|
|
sequences:
|
|
- destination: [prompts]
|
|
key: id
|
|
prompts:
|
|
- id: example
|
|
`)
|
|
rule := &VersionRule{Path: Keys("version"), Check: func(version Version, _ *Validation) error {
|
|
if !version.Present || version.Text != "v1" {
|
|
t.Errorf("version = %#v, want v1", version)
|
|
}
|
|
return nil
|
|
}}
|
|
processed := processSourceVersionWithDirectiveContext(
|
|
root, rule, sourceVersionContribution, Keys("items").Index(0), sourceVersionModeRequired, rootDirectiveContextSource,
|
|
)
|
|
if len(processed.diagnostics) != 0 {
|
|
t.Fatalf("process item source version: %#v", processed.diagnostics)
|
|
}
|
|
if len(processed.sequences) != 1 || !processed.sequences[0].identityPresent {
|
|
t.Fatalf("extracted item declarations = %#v", processed.sequences)
|
|
}
|
|
if _, ok := documentAtPath(processed.payload, Keys("version")); ok {
|
|
t.Fatalf("consumed version header remains in item payload: %#v", processed.payload)
|
|
}
|
|
if _, ok := documentAtPath(processed.payload, Keys("configkit")); ok {
|
|
t.Fatalf("consumed controls remain in item payload: %#v", processed.payload)
|
|
}
|
|
}
|