Reuse parsed sensitivity paths
This commit is contained in:
@@ -468,6 +468,12 @@ separate design/evidence and do not block this sequence.
|
|||||||
|
|
||||||
### Final evidence and outcomes
|
### Final evidence and outcomes
|
||||||
|
|
||||||
|
- Post-review Stage 9 completion: sensitivity collection now reuses existing
|
||||||
|
valid parsed paths without reparsing; later projections still replace typed
|
||||||
|
history paths. Repeated parsed contributions and both numeric mapping/sequence
|
||||||
|
transitions are covered. Focused sensitivity/inheritance/rendering tests,
|
||||||
|
`./scripts/check.sh`, and `go test -race ./internal/engine` passed.
|
||||||
|
|
||||||
- Reviewed planning baseline `0c77deed05417edb32f42c7bf24df2fdc2581a25`,
|
- Reviewed planning baseline `0c77deed05417edb32f42c7bf24df2fdc2581a25`,
|
||||||
production baseline `48619de024d794cf41e90b171b7fcbe609dcd437`, and the
|
production baseline `48619de024d794cf41e90b171b7fcbe609dcd437`, and the
|
||||||
complete implementation diff through `799ebab5a6f15e8252474b8cb7ea164ef503f390`.
|
complete implementation diff through `799ebab5a6f15e8252474b8cb7ea164ef503f390`.
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ The older, completed ReleaseKit review record remains in Git history at
|
|||||||
|
|
||||||
## 1. Complete parsed-path reuse in sensitivity collection (P2)
|
## 1. Complete parsed-path reuse in sensitivity collection (P2)
|
||||||
|
|
||||||
Status: Open.
|
Status: Complete. Parsed accumulator paths are reused, while typed historical
|
||||||
|
paths still receive the later parsed replacement. Repeated root/escaped/numeric
|
||||||
|
contributions and both numeric mapping/sequence transitions are covered.
|
||||||
|
Focused sensitivity/inheritance/rendering tests, `./scripts/check.sh`, and
|
||||||
|
`go test -race ./internal/engine` passed.
|
||||||
|
|
||||||
Affected: [secret.go](../../internal/engine/secret.go), `addSecretPath`
|
Affected: [secret.go](../../internal/engine/secret.go), `addSecretPath`
|
||||||
(lines 85–89); implementation Stage 9, item 3.
|
(lines 85–89); implementation Stage 9, item 3.
|
||||||
|
|||||||
@@ -83,6 +83,20 @@ func classifySecretPaths(schema *schemaDescriptor, predicate func(Path) bool, me
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addSecretPath(paths map[string]Path, pointer string) {
|
func addSecretPath(paths map[string]Path, pointer string) {
|
||||||
|
if existing, found := paths[pointer]; found && existing.valid() {
|
||||||
|
// Accumulator keys already match their paths' canonical spelling. History
|
||||||
|
// can install typed segments, which still need the parsed replacement.
|
||||||
|
parsed := true
|
||||||
|
for _, segment := range existing.segments {
|
||||||
|
if segment.kind != pathSegmentParsed {
|
||||||
|
parsed = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parsed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if path, err := ParsePath(pointer); err == nil {
|
if path, err := ParsePath(pointer); err == nil {
|
||||||
paths[pointer] = path
|
paths[pointer] = path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,62 @@ package engine
|
|||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
func TestSecretPathCollectionKeepsParsedContributions(t *testing.T) {
|
||||||
|
for _, pointer := range []string{"", "/", "/records/0", "/records/a~1b~0/01"} {
|
||||||
|
t.Run(pointer, func(t *testing.T) {
|
||||||
|
paths := make(map[string]Path)
|
||||||
|
for repeat := 0; repeat < 3; repeat++ {
|
||||||
|
addSecretPath(paths, pointer)
|
||||||
|
path, found := paths[pointer]
|
||||||
|
if !found || !path.valid() || path.String() != pointer {
|
||||||
|
t.Fatalf("collected path = %#v, found %v; want %q", path, found, pointer)
|
||||||
|
}
|
||||||
|
for _, segment := range path.segments {
|
||||||
|
if segment.kind != pathSegmentParsed {
|
||||||
|
t.Fatalf("collected path %q retained a typed segment: %#v", pointer, segment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecretClassificationParsesTypedHistoryAfterNumericShapeChanges(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
earlier string
|
||||||
|
final string
|
||||||
|
historical Path
|
||||||
|
}{
|
||||||
|
{"mapping to sequence", "items: {'0': old}", "items: [new]", Keys("items", "0")},
|
||||||
|
{"sequence to mapping", "items: [old]", "items: {'0': new}", Keys("items").Index(0)},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
earlier := parseCompositionDocument(t, "earlier", test.earlier)
|
||||||
|
final := parseCompositionDocument(t, "final", test.final)
|
||||||
|
pointer := test.historical.String()
|
||||||
|
metadata := Metadata{history: map[string][]Change{pointer: {{Path: test.historical}}}}
|
||||||
|
seen := false
|
||||||
|
classified := classifySecretPaths(nil, func(path Path) bool {
|
||||||
|
if path.String() != pointer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
seen = true
|
||||||
|
for _, segment := range path.segments {
|
||||||
|
if segment.kind != pathSegmentParsed {
|
||||||
|
t.Fatalf("predicate received typed history after final projection: %#v", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, metadata, final, []historyApplication{{value: earlier}}, earlier)
|
||||||
|
if !seen || !classified[pointer] {
|
||||||
|
t.Fatalf("historical/final numeric path was not classified: %#v", classified)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSecretPredicateReceivesDetachedHistoricalPath(t *testing.T) {
|
func TestSecretPredicateReceivesDetachedHistoricalPath(t *testing.T) {
|
||||||
path := Keys("records").Key("1")
|
path := Keys("records").Key("1")
|
||||||
metadata := Metadata{history: map[string][]Change{
|
metadata := Metadata{history: map[string][]Change{
|
||||||
|
|||||||
Reference in New Issue
Block a user