Disallow non-supported nodes in pattern and replacement
diff --git a/pkg/matcher/matcher_test.go b/pkg/matcher/matcher_test.go
index adc8784..47af1f9 100644
--- a/pkg/matcher/matcher_test.go
+++ b/pkg/matcher/matcher_test.go
@@ -11,6 +11,130 @@
"github.com/stretchr/testify/assert"
)
+func TestNewMatcherValidation(t *testing.T) {
+ tests := []struct {
+ name string
+ pattern ast.Pattern
+ replacement ast.Replacement
+ expectedError string
+ }{
+ {
+ name: "Valid pattern and replacement",
+ pattern: ast.Pattern{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ replacement: ast.Replacement{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "COMBINED_DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expectedError: "",
+ },
+ {
+ name: "Invalid pattern - CatchallNode",
+ pattern: ast.Pattern{
+ Root: &ast.CatchallNode{
+ NodeType: "custom",
+ },
+ },
+ replacement: ast.Replacement{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expectedError: "invalid pattern: catchall nodes are not allowed in pattern/replacement ASTs",
+ },
+ {
+ name: "Invalid replacement - CatchallNode",
+ pattern: ast.Pattern{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ replacement: ast.Replacement{
+ Root: &ast.CatchallNode{
+ NodeType: "custom",
+ },
+ },
+ expectedError: "invalid replacement: catchall nodes are not allowed in pattern/replacement ASTs",
+ },
+ {
+ name: "Invalid pattern - Empty TermGroup",
+ pattern: ast.Pattern{
+ Root: &ast.TermGroup{
+ Operands: []ast.Node{},
+ Relation: ast.AndRelation,
+ },
+ },
+ replacement: ast.Replacement{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expectedError: "invalid pattern: empty term group",
+ },
+ {
+ name: "Invalid pattern - Nested CatchallNode",
+ pattern: ast.Pattern{
+ Root: &ast.TermGroup{
+ Operands: []ast.Node{
+ &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ &ast.CatchallNode{
+ NodeType: "custom",
+ },
+ },
+ Relation: ast.AndRelation,
+ },
+ },
+ replacement: ast.Replacement{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expectedError: "invalid pattern: invalid operand: catchall nodes are not allowed in pattern/replacement ASTs",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ matcher, err := NewMatcher(tt.pattern, tt.replacement)
+ if tt.expectedError != "" {
+ assert.Error(t, err)
+ assert.Equal(t, tt.expectedError, err.Error())
+ assert.Nil(t, matcher)
+ } else {
+ assert.NoError(t, err)
+ assert.NotNil(t, matcher)
+ }
+ })
+ }
+}
+
func TestMatchSimplePattern(t *testing.T) {
// Create a simple pattern: match a term with DET
pattern := ast.Pattern{
@@ -32,7 +156,9 @@
},
}
- m := NewMatcher(pattern, replacement)
+ m, err := NewMatcher(pattern, replacement)
+ assert.NoError(t, err)
+ assert.NotNil(t, m)
tests := []struct {
name string
@@ -149,7 +275,9 @@
},
}
- m := NewMatcher(pattern, replacement)
+ m, err := NewMatcher(pattern, replacement)
+ assert.NoError(t, err)
+ assert.NotNil(t, m)
tests := []struct {
name string
@@ -263,25 +391,12 @@
}
func TestReplace(t *testing.T) {
- // Create pattern and replacement
pattern := ast.Pattern{
- Root: &ast.TermGroup{
- Operands: []ast.Node{
- &ast.Term{
- Foundry: "opennlp",
- Key: "DET",
- Layer: "p",
- Match: ast.MatchEqual,
- },
- &ast.Term{
- Foundry: "opennlp",
- Key: "AdjType",
- Layer: "m",
- Match: ast.MatchEqual,
- Value: "Pdt",
- },
- },
- Relation: ast.AndRelation,
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
},
}
@@ -294,7 +409,9 @@
},
}
- m := NewMatcher(pattern, replacement)
+ m, err := NewMatcher(pattern, replacement)
+ assert.NoError(t, err)
+ assert.NotNil(t, m)
tests := []struct {
name string
@@ -321,11 +438,23 @@
},
Relation: ast.AndRelation,
},
- expected: &ast.Term{
- Foundry: "opennlp",
- Key: "COMBINED_DET",
- Layer: "p",
- Match: ast.MatchEqual,
+ expected: &ast.TermGroup{
+ Operands: []ast.Node{
+ &ast.Term{
+ Foundry: "opennlp",
+ Key: "COMBINED_DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ &ast.Term{
+ Foundry: "opennlp",
+ Key: "AdjType",
+ Layer: "m",
+ Match: ast.MatchEqual,
+ Value: "Pdt",
+ },
+ },
+ Relation: ast.AndRelation,
},
},
{
@@ -431,7 +560,6 @@
}
func TestMatchNodeOrder(t *testing.T) {
- // Test that operands can match in any order
pattern := ast.Pattern{
Root: &ast.TermGroup{
Operands: []ast.Node{
@@ -462,7 +590,9 @@
},
}
- m := NewMatcher(pattern, replacement)
+ m, err := NewMatcher(pattern, replacement)
+ assert.NoError(t, err)
+ assert.NotNil(t, m)
// Test with operands in different orders
input1 := &ast.TermGroup{
@@ -508,7 +638,6 @@
}
func TestMatchWithUnknownNodes(t *testing.T) {
- // Create a pattern that looks for a term with DET inside any structure
pattern := ast.Pattern{
Root: &ast.Term{
Foundry: "opennlp",
@@ -527,7 +656,9 @@
},
}
- m := NewMatcher(pattern, replacement)
+ m, err := NewMatcher(pattern, replacement)
+ assert.NoError(t, err)
+ assert.NotNil(t, m)
tests := []struct {
name string