Check item version ownership at effective paths
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Import Discovery and Scheduling Review Findings
|
# Import Discovery and Scheduling Review Findings
|
||||||
|
|
||||||
Status: in progress; issue 1 complete, five findings remain.
|
Status: in progress; issues 1–2 complete, four findings remain.
|
||||||
|
|
||||||
Reviewed implementation: `aa93aed..15943e2`, ending at
|
Reviewed implementation: `aa93aed..15943e2`, ending at
|
||||||
`15943e233ca3cd1286c03c0e7085871db344cd4b`, against the accepted
|
`15943e233ca3cd1286c03c0e7085871db344cd4b`, against the accepted
|
||||||
@@ -57,7 +57,7 @@ source contexts, repeated paths, and distinct alias admission. The regression
|
|||||||
failed before the fix. Focused discovery/resource tests and `./scripts/check.sh`
|
failed before the fix. Focused discovery/resource tests and `./scripts/check.sh`
|
||||||
passed with Go 1.26.9 and offline dependency selection.
|
passed with Go 1.26.9 and offline dependency selection.
|
||||||
|
|
||||||
## 2. [ ] P1 — Scope main-version protection to the actual effective path
|
## 2. [x] P1 — Scope main-version protection to the actual effective path
|
||||||
|
|
||||||
Owner: [composePendingItems](../../internal/engine/imports.go), around lines
|
Owner: [composePendingItems](../../internal/engine/imports.go), around lines
|
||||||
694–695, and its call to `importedVersionClaim`.
|
694–695, and its call to `importedVersionClaim`.
|
||||||
@@ -85,6 +85,13 @@ the effective paths really overlap. Cover imported/nested items, item helpers,
|
|||||||
main-item loading, underlays, and enabled source-version modes without adding
|
main-item loading, underlays, and enabled source-version modes without adding
|
||||||
blanket exemptions or banning ordinary domain version fields.
|
blanket exemptions or banning ordinary domain version fields.
|
||||||
|
|
||||||
|
Completed: removed the global-rule check against item-local field names; enclosing
|
||||||
|
composition checks retain effective-path protection, including the existing
|
||||||
|
main-item scoped check. New public regressions cover item bodies, helpers, nested
|
||||||
|
items, underlays, and selected main items, and failed before the change. The
|
||||||
|
full everyday check passed, including existing default/enabled source-version
|
||||||
|
and assigned-index ownership regressions.
|
||||||
|
|
||||||
## 3. [ ] P2 — Accept valid multi-component native filesystem roots
|
## 3. [ ] P2 — Accept valid multi-component native filesystem roots
|
||||||
|
|
||||||
Owner: [resolveNativeImportPath](../../internal/engine/filesystem_links.go),
|
Owner: [resolveNativeImportPath](../../internal/engine/filesystem_links.go),
|
||||||
|
|||||||
64
facade_item_version_test.go
Normal file
64
facade_item_version_test.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package configkit_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
|
||||||
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFacadeItemDomainVersionsKeepEffectiveScope(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name, item, child, helper string
|
||||||
|
underlay, mainItem bool
|
||||||
|
}{
|
||||||
|
{name: "item", item: "id: item\nversion: domain\n"},
|
||||||
|
{name: "helper", item: "id: item\nconfigkit: {imports: [{path: helper.yml}]}\n", helper: "version: domain\n"},
|
||||||
|
{name: "nested", item: "id: parent\nconfigkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n"},
|
||||||
|
{name: "underlay", item: "id: item\nversion: domain\n", underlay: true},
|
||||||
|
{name: "main-item", item: "id: parent\nconfigkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n", mainItem: true},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
files := fstest.MapFS{
|
||||||
|
"main.yml": {Data: []byte("version: 1\nconfigkit: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||||
|
"item.yml": {Data: []byte(test.item)},
|
||||||
|
"child.yml": {Data: []byte(test.child)},
|
||||||
|
"helper.yml": {Data: []byte(test.helper)},
|
||||||
|
"underlay.yml": {Data: []byte("configkit: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||||
|
}
|
||||||
|
filesystem := configkit.NewFilesystem("items", files)
|
||||||
|
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
|
||||||
|
Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil },
|
||||||
|
}}
|
||||||
|
var result *configkit.Result[map[string]any]
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
case test.underlay:
|
||||||
|
result, err = configkit.LoadLayered(configkit.Input{Source: configkit.Mapping("main", map[string]any{"version": "1"})}, configkit.LayeredOptions[map[string]any]{
|
||||||
|
Definition: definition, Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: filesystem.File("underlay.yml"), Imports: true}}}},
|
||||||
|
})
|
||||||
|
case test.mainItem:
|
||||||
|
result, err = configkit.Load(filesystem.File("item.yml"), configkit.Options[map[string]any]{
|
||||||
|
Imports: true, Destination: configkit.Keys("items"), PlacementMode: configkit.PlacementModeSequenceItem, Definition: definition,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
result, err = configkit.Load(filesystem.File("main.yml"), configkit.Options[map[string]any]{Imports: true, Definition: definition})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
item := result.Config["items"].([]any)[0].(map[string]any)
|
||||||
|
fieldPath := configkit.Keys("items").Index(0).Key("version")
|
||||||
|
if test.child != "" {
|
||||||
|
item = item["children"].([]any)[0].(map[string]any)
|
||||||
|
fieldPath = configkit.Keys("items").Index(0).Key("children").Index(0).Key("version")
|
||||||
|
}
|
||||||
|
if item["version"] != "domain" {
|
||||||
|
t.Fatalf("domain version = %#v", item)
|
||||||
|
}
|
||||||
|
if info, ok := result.Metadata().Lookup(fieldPath); !ok || !info.Explicit {
|
||||||
|
t.Fatalf("version metadata = %#v, %v", info, ok)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -691,9 +691,8 @@ func (s *importTraversal) composePendingItems(scope *acquisitionScope) []Diagnos
|
|||||||
if len(diagnostics) != 0 {
|
if len(diagnostics) != 0 {
|
||||||
return projectItemDiagnosticPaths(diagnostics, *scope, contribution.destination, index)
|
return projectItemDiagnosticPaths(diagnostics, *scope, contribution.destination, index)
|
||||||
}
|
}
|
||||||
if diagnostic := importedVersionClaim(documents, *itemScope, completed, s.rule); diagnostic != nil {
|
// Version ownership is checked by the enclosing composition once this
|
||||||
return []Diagnostic{*diagnostic}
|
// item has its effective coordinates, not against its local field names.
|
||||||
}
|
|
||||||
completed.acquisition = []acquisitionScope{cloneAcquisitionScope(*itemScope)}
|
completed.acquisition = []acquisitionScope{cloneAcquisitionScope(*itemScope)}
|
||||||
projectNestedEvidence(completed, *itemScope)
|
projectNestedEvidence(completed, *itemScope)
|
||||||
_, placed, err := placeDocumentWithMode(completed.root, contribution.destination, placementModeSequenceItem, contribution.payload.Location, maxDocumentDepth, maxDocumentNodes)
|
_, placed, err := placeDocumentWithMode(completed.root, contribution.destination, placementModeSequenceItem, contribution.payload.Location, maxDocumentDepth, maxDocumentNodes)
|
||||||
|
|||||||
Reference in New Issue
Block a user