Identify conflicting sequence registration positions
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package engine
|
||||
|
||||
import "fmt"
|
||||
|
||||
type sequenceSelectorSegmentKind uint8
|
||||
|
||||
const (
|
||||
@@ -87,9 +89,13 @@ func DefinitionWithSequenceIdentityRegistrations[T any](definition Definition[T]
|
||||
|
||||
func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration) (sequencePolicy, []Diagnostic) {
|
||||
policy := sequencePolicy{}
|
||||
compiled := make([]sequenceIdentityRegistration, 0, len(registrations))
|
||||
type positionedRegistration struct {
|
||||
registration sequenceIdentityRegistration
|
||||
position int
|
||||
}
|
||||
compiled := make([]positionedRegistration, 0, len(registrations))
|
||||
var diagnostics []Diagnostic
|
||||
for _, registration := range registrations {
|
||||
for position, registration := range registrations {
|
||||
selector := cloneSequenceIdentitySelector(registration.selector)
|
||||
if registration.invalidPath != nil {
|
||||
diagnostics = append(diagnostics, Diagnostic{
|
||||
@@ -113,18 +119,21 @@ func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration)
|
||||
})
|
||||
continue
|
||||
}
|
||||
compiled = append(compiled, sequenceIdentityRegistration{selector: selector, identityFields: fields})
|
||||
compiled = append(compiled, positionedRegistration{
|
||||
registration: sequenceIdentityRegistration{selector: selector, identityFields: fields},
|
||||
position: position + 1,
|
||||
})
|
||||
}
|
||||
|
||||
for leftIndex := 0; leftIndex < len(compiled); leftIndex++ {
|
||||
for rightIndex := leftIndex + 1; rightIndex < len(compiled); rightIndex++ {
|
||||
left, right := compiled[leftIndex], compiled[rightIndex]
|
||||
left, right := compiled[leftIndex].registration, compiled[rightIndex].registration
|
||||
if !sequenceIdentitySelectorsOverlap(left.selector, right.selector) || equalSequenceIdentityFields(left.identityFields, right.identityFields) {
|
||||
continue
|
||||
}
|
||||
diagnostics = append(diagnostics, Diagnostic{
|
||||
Code: CodeOptionsInvalid, Severity: SeverityError, Path: selectorDiagnosticPath(left.selector),
|
||||
Message: "overlapping sequence registrations must use the same identity fields",
|
||||
Message: fmt.Sprintf("sequence registrations %d and %d overlap with conflicting identity fields", compiled[leftIndex].position, compiled[rightIndex].position),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -133,7 +142,8 @@ func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration)
|
||||
}
|
||||
|
||||
unique := make([]sequenceIdentityRegistration, 0, len(compiled))
|
||||
for _, registration := range compiled {
|
||||
for _, positioned := range compiled {
|
||||
registration := positioned.registration
|
||||
duplicate := false
|
||||
for _, existing := range unique {
|
||||
if sequenceIdentitySelectorsEqual(existing.selector, registration.selector) && equalSequenceIdentityFields(existing.identityFields, registration.identityFields) {
|
||||
|
||||
@@ -178,6 +178,34 @@ func TestCompileSequenceIdentityPolicyRejectsConflictingOverlapsWithoutInput(t *
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceOverlapDiagnosticsIdentifyOriginalRegistrationPositions(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{
|
||||
{selector: sequenceIdentitySelector{literalSequenceSelector("invalid")}},
|
||||
{selector: sequenceIdentitySelector{literalSequenceSelector("unrelated")}, identityFields: []string{"id"}},
|
||||
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), anyMappingSequenceSelector(), literalSequenceSelector("items")}, identityFields: []string{"id"}},
|
||||
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), literalSequenceSelector("foo"), literalSequenceSelector("items")}, identityFields: []string{"name"}},
|
||||
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), literalSequenceSelector("bar"), literalSequenceSelector("items")}, identityFields: []string{"code"}},
|
||||
})
|
||||
if len(diagnostics) != 3 {
|
||||
t.Fatalf("diagnostics = %#v, want the invalid registration and two conflicts", diagnostics)
|
||||
}
|
||||
for _, pair := range []string{"3 and 4", "3 and 5"} {
|
||||
found := false
|
||||
for _, diagnostic := range diagnostics {
|
||||
if strings.Contains(diagnostic.Message, pair) {
|
||||
found = true
|
||||
if diagnostic.Code != CodeOptionsInvalid || diagnostic.Path.String() != "" || len(diagnostic.Related) != 0 {
|
||||
t.Fatalf("definition overlap invented input coordinates: %#v", diagnostic)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("conflicting registration positions %s not identified: %#v", pair, diagnostics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileSequenceIdentityPolicyAllowsDisjointSelectorsAndRoot(t *testing.T) {
|
||||
policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{
|
||||
{selector: sequenceIdentitySelector{}, identityFields: []string{"id"}},
|
||||
|
||||
Reference in New Issue
Block a user