blob: 3965f1d22a17931132710c191237e0300fa459aa [file] [log] [blame]
Akronb7e1f352025-05-16 15:45:23 +02001package ast
2
3import (
Akron8b3942b2025-05-23 13:36:37 +02004 "encoding/json"
Akronb7e1f352025-05-16 15:45:23 +02005 "testing"
6
7 "github.com/stretchr/testify/assert"
8)
9
10func TestNodeTypes(t *testing.T) {
11 tests := []struct {
12 name string
13 node Node
14 expected NodeType
15 }{
16 {
17 name: "Token node returns correct type",
18 node: &Token{Wrap: &Term{}},
19 expected: TokenNode,
20 },
21 {
22 name: "TermGroup node returns correct type",
23 node: &TermGroup{
24 Operands: []Node{&Term{}},
25 Relation: AndRelation,
26 },
27 expected: TermGroupNode,
28 },
29 {
30 name: "Term node returns correct type",
31 node: &Term{
32 Foundry: "opennlp",
33 Key: "DET",
34 Layer: "p",
35 Match: MatchEqual,
36 },
37 expected: TermNode,
38 },
Akron1a5fccd2025-05-27 09:54:09 +020039 {
40 name: "Rewrite node returns correct type",
41 node: &Rewrite{
42 Editor: "Kustvakt",
43 Operation: "operation:injection",
44 Scope: "foundry",
45 Src: "Kustvakt",
46 },
47 expected: RewriteNode,
48 },
Akronb7e1f352025-05-16 15:45:23 +020049 }
50
51 for _, tt := range tests {
52 t.Run(tt.name, func(t *testing.T) {
53 assert.Equal(t, tt.expected, tt.node.Type())
54 })
55 }
56}
57
58func TestTermGroupConstruction(t *testing.T) {
59 term1 := &Term{
60 Foundry: "opennlp",
61 Key: "DET",
62 Layer: "p",
63 Match: MatchEqual,
64 }
65
66 term2 := &Term{
67 Foundry: "opennlp",
68 Key: "AdjType",
69 Layer: "m",
70 Match: MatchEqual,
71 Value: "Pdt",
72 }
73
Akron1a5fccd2025-05-27 09:54:09 +020074 rewrites := []Rewrite{
75 {
76 Editor: "Kustvakt",
77 Operation: "operation:injection",
78 Scope: "foundry",
79 Src: "Kustvakt",
80 Comment: "Default foundry has been added.",
81 },
82 }
83
Akronb7e1f352025-05-16 15:45:23 +020084 group := &TermGroup{
85 Operands: []Node{term1, term2},
86 Relation: AndRelation,
Akron1a5fccd2025-05-27 09:54:09 +020087 Rewrites: rewrites,
Akronb7e1f352025-05-16 15:45:23 +020088 }
89
90 assert.Len(t, group.Operands, 2)
91 assert.Equal(t, AndRelation, group.Relation)
92 assert.Equal(t, TermGroupNode, group.Type())
Akron1a5fccd2025-05-27 09:54:09 +020093 assert.Equal(t, rewrites, group.Rewrites)
Akronb7e1f352025-05-16 15:45:23 +020094
95 // Test operands are correctly set
96 assert.Equal(t, term1, group.Operands[0])
97 assert.Equal(t, term2, group.Operands[1])
98}
99
100func TestTokenConstruction(t *testing.T) {
101 term := &Term{
102 Foundry: "opennlp",
103 Key: "DET",
104 Layer: "p",
105 Match: MatchEqual,
106 }
107
Akron1a5fccd2025-05-27 09:54:09 +0200108 rewrites := []Rewrite{
109 {
110 Editor: "Kustvakt",
111 Operation: "operation:injection",
112 Scope: "foundry",
113 Src: "Kustvakt",
114 Comment: "Default foundry has been added.",
115 },
116 }
117
118 token := &Token{
119 Wrap: term,
120 Rewrites: rewrites,
121 }
Akronb7e1f352025-05-16 15:45:23 +0200122
123 assert.Equal(t, TokenNode, token.Type())
124 assert.Equal(t, term, token.Wrap)
Akron1a5fccd2025-05-27 09:54:09 +0200125 assert.Equal(t, rewrites, token.Rewrites)
Akronb7e1f352025-05-16 15:45:23 +0200126}
127
128func TestTermConstruction(t *testing.T) {
129 tests := []struct {
130 name string
131 term *Term
132 foundry string
133 key string
134 layer string
135 match MatchType
136 hasValue bool
137 value string
Akron1a5fccd2025-05-27 09:54:09 +0200138 rewrites []Rewrite
Akronb7e1f352025-05-16 15:45:23 +0200139 }{
140 {
141 name: "Term without value",
142 term: &Term{
143 Foundry: "opennlp",
144 Key: "DET",
145 Layer: "p",
146 Match: MatchEqual,
147 },
148 foundry: "opennlp",
149 key: "DET",
150 layer: "p",
151 match: MatchEqual,
152 hasValue: false,
153 },
154 {
155 name: "Term with value",
156 term: &Term{
157 Foundry: "opennlp",
158 Key: "AdjType",
159 Layer: "m",
160 Match: MatchEqual,
161 Value: "Pdt",
162 },
163 foundry: "opennlp",
164 key: "AdjType",
165 layer: "m",
166 match: MatchEqual,
167 hasValue: true,
168 value: "Pdt",
169 },
170 {
171 name: "Term with not equal match",
172 term: &Term{
173 Foundry: "opennlp",
174 Key: "DET",
175 Layer: "p",
176 Match: MatchNotEqual,
177 },
178 foundry: "opennlp",
179 key: "DET",
180 layer: "p",
181 match: MatchNotEqual,
182 hasValue: false,
183 },
Akron1a5fccd2025-05-27 09:54:09 +0200184 {
185 name: "Term with rewrites",
186 term: &Term{
187 Foundry: "opennlp",
188 Key: "DET",
189 Layer: "p",
190 Match: MatchEqual,
191 Rewrites: []Rewrite{
192 {
193 Editor: "Kustvakt",
194 Operation: "operation:injection",
195 Scope: "foundry",
196 Src: "Kustvakt",
197 Comment: "Default foundry has been added.",
198 },
199 },
200 },
201 foundry: "opennlp",
202 key: "DET",
203 layer: "p",
204 match: MatchEqual,
205 hasValue: false,
206 rewrites: []Rewrite{
207 {
208 Editor: "Kustvakt",
209 Operation: "operation:injection",
210 Scope: "foundry",
211 Src: "Kustvakt",
212 Comment: "Default foundry has been added.",
213 },
214 },
215 },
Akronb7e1f352025-05-16 15:45:23 +0200216 }
217
218 for _, tt := range tests {
219 t.Run(tt.name, func(t *testing.T) {
220 assert.Equal(t, TermNode, tt.term.Type())
221 assert.Equal(t, tt.foundry, tt.term.Foundry)
222 assert.Equal(t, tt.key, tt.term.Key)
223 assert.Equal(t, tt.layer, tt.term.Layer)
224 assert.Equal(t, tt.match, tt.term.Match)
225 if tt.hasValue {
226 assert.Equal(t, tt.value, tt.term.Value)
227 } else {
228 assert.Empty(t, tt.term.Value)
229 }
Akron1a5fccd2025-05-27 09:54:09 +0200230 if tt.rewrites != nil {
231 assert.Equal(t, tt.rewrites, tt.term.Rewrites)
232 } else {
233 assert.Empty(t, tt.term.Rewrites)
234 }
Akronb7e1f352025-05-16 15:45:23 +0200235 })
236 }
237}
238
239func TestPatternAndReplacement(t *testing.T) {
240 // Create a simple pattern
241 patternTerm := &Term{
242 Foundry: "opennlp",
243 Key: "DET",
244 Layer: "p",
245 Match: MatchEqual,
246 }
247 pattern := Pattern{Root: patternTerm}
248
249 // Create a simple replacement
250 replacementTerm := &Term{
251 Foundry: "opennlp",
252 Key: "COMBINED_DET",
253 Layer: "p",
254 Match: MatchEqual,
255 }
256 replacement := Replacement{Root: replacementTerm}
257
258 // Test pattern
259 assert.NotNil(t, pattern.Root)
260 assert.Equal(t, patternTerm, pattern.Root)
261
262 // Test replacement
263 assert.NotNil(t, replacement.Root)
264 assert.Equal(t, replacementTerm, replacement.Root)
265}
Akron8b3942b2025-05-23 13:36:37 +0200266
267func TestCatchallNode(t *testing.T) {
268 tests := []struct {
269 name string
270 nodeType string
271 content string
272 wrap Node
273 operands []Node
274 expectType NodeType
275 }{
276 {
277 name: "CatchallNode with custom type",
278 nodeType: "customType",
279 content: `{"key": "value"}`,
280 expectType: NodeType("customType"),
281 },
282 {
283 name: "CatchallNode with wrapped term",
284 nodeType: "wrapper",
285 content: `{"key": "value"}`,
286 wrap: &Term{
287 Foundry: "test",
288 Key: "TEST",
289 Layer: "x",
290 Match: MatchEqual,
291 },
292 expectType: NodeType("wrapper"),
293 },
294 {
295 name: "CatchallNode with operands",
296 nodeType: "custom_group",
297 content: `{"key": "value"}`,
298 operands: []Node{
299 &Term{Foundry: "test1", Key: "TEST1", Layer: "x", Match: MatchEqual},
300 &Term{Foundry: "test2", Key: "TEST2", Layer: "y", Match: MatchEqual},
301 },
302 expectType: NodeType("custom_group"),
303 },
304 }
305
306 for _, tt := range tests {
307 t.Run(tt.name, func(t *testing.T) {
308 rawContent := json.RawMessage(tt.content)
309 node := &CatchallNode{
310 NodeType: tt.nodeType,
311 RawContent: rawContent,
312 Wrap: tt.wrap,
313 Operands: tt.operands,
314 }
315
316 assert.Equal(t, tt.expectType, node.Type())
317 if tt.wrap != nil {
318 assert.Equal(t, tt.wrap, node.Wrap)
319 }
320 if tt.operands != nil {
321 assert.Equal(t, tt.operands, node.Operands)
322 }
323 assert.Equal(t, rawContent, node.RawContent)
324 })
325 }
326}
327
Akron1a5fccd2025-05-27 09:54:09 +0200328func TestRewriteConstruction(t *testing.T) {
329 rewrite := &Rewrite{
330 Editor: "Kustvakt",
331 Operation: "operation:injection",
332 Scope: "foundry",
333 Src: "Kustvakt",
334 Comment: "Default foundry has been added.",
335 }
336
337 assert.Equal(t, RewriteNode, rewrite.Type())
338 assert.Equal(t, "Kustvakt", rewrite.Editor)
339 assert.Equal(t, "operation:injection", rewrite.Operation)
340 assert.Equal(t, "foundry", rewrite.Scope)
341 assert.Equal(t, "Kustvakt", rewrite.Src)
342 assert.Equal(t, "Default foundry has been added.", rewrite.Comment)
343}
344
Akron8b3942b2025-05-23 13:36:37 +0200345func TestComplexNestedStructures(t *testing.T) {
Akron441bd122025-05-30 14:19:50 +0200346 // Test nested tokens and term groups
347 termGroup := &TermGroup{
Akron8b3942b2025-05-23 13:36:37 +0200348 Operands: []Node{
Akron441bd122025-05-30 14:19:50 +0200349 &Term{
350 Foundry: "opennlp",
351 Key: "DET",
352 Layer: "p",
353 Match: MatchEqual,
354 },
355 &Term{
356 Foundry: "opennlp",
357 Key: "AdjType",
358 Layer: "m",
359 Match: MatchEqual,
360 Value: "Pdt",
361 },
Akron8b3942b2025-05-23 13:36:37 +0200362 },
363 Relation: AndRelation,
364 }
365
Akron441bd122025-05-30 14:19:50 +0200366 token := &Token{
367 Wrap: termGroup,
Akron8b3942b2025-05-23 13:36:37 +0200368 }
369
Akron441bd122025-05-30 14:19:50 +0200370 assert.Equal(t, TokenNode, token.Type())
Akron8b3942b2025-05-23 13:36:37 +0200371 assert.NotNil(t, token.Wrap)
Akron441bd122025-05-30 14:19:50 +0200372 assert.Equal(t, TermGroupNode, token.Wrap.Type())
373
374 // Test that the nested structure is correct
375 if tg, ok := token.Wrap.(*TermGroup); ok {
376 assert.Equal(t, 2, len(tg.Operands))
377 assert.Equal(t, AndRelation, tg.Relation)
378 }
Akron8b3942b2025-05-23 13:36:37 +0200379}
380
381func TestEdgeCases(t *testing.T) {
382 tests := []struct {
383 name string
384 test func(t *testing.T)
385 }{
386 {
387 name: "Empty TermGroup",
388 test: func(t *testing.T) {
389 group := &TermGroup{
390 Operands: []Node{},
391 Relation: AndRelation,
392 }
393 assert.Empty(t, group.Operands)
394 assert.Equal(t, AndRelation, group.Relation)
395 },
396 },
397 {
398 name: "Token with nil wrap",
399 test: func(t *testing.T) {
400 token := &Token{Wrap: nil}
401 assert.Equal(t, TokenNode, token.Type())
402 assert.Nil(t, token.Wrap)
403 },
404 },
405 {
406 name: "Term with empty strings",
407 test: func(t *testing.T) {
408 term := &Term{
409 Foundry: "",
410 Key: "",
411 Layer: "",
412 Match: MatchEqual,
413 Value: "",
414 }
415 assert.Equal(t, TermNode, term.Type())
416 assert.Empty(t, term.Foundry)
417 assert.Empty(t, term.Key)
418 assert.Empty(t, term.Layer)
419 assert.Empty(t, term.Value)
420 },
421 },
422 {
423 name: "Complex Pattern and Replacement",
424 test: func(t *testing.T) {
425 // Create a complex pattern
426 patternGroup := &TermGroup{
427 Operands: []Node{
428 &Term{Foundry: "f1", Key: "k1", Layer: "l1", Match: MatchEqual},
429 &Token{Wrap: &Term{Foundry: "f2", Key: "k2", Layer: "l2", Match: MatchNotEqual}},
430 },
431 Relation: OrRelation,
432 }
433 pattern := Pattern{Root: patternGroup}
434
435 // Create a complex replacement
436 replacementGroup := &TermGroup{
437 Operands: []Node{
438 &Term{Foundry: "f3", Key: "k3", Layer: "l3", Match: MatchEqual},
439 &Term{Foundry: "f4", Key: "k4", Layer: "l4", Match: MatchEqual},
440 },
441 Relation: AndRelation,
442 }
443 replacement := Replacement{Root: replacementGroup}
444
445 assert.Equal(t, TermGroupNode, pattern.Root.Type())
446 assert.Equal(t, TermGroupNode, replacement.Root.Type())
447 },
448 },
449 }
450
451 for _, tt := range tests {
452 t.Run(tt.name, tt.test)
453 }
454}
Akron441bd122025-05-30 14:19:50 +0200455
456func TestCloneMethod(t *testing.T) {
457 tests := []struct {
458 name string
459 node Node
460 }{
461 {
462 name: "Clone Term",
463 node: &Term{
464 Foundry: "opennlp",
465 Key: "DET",
466 Layer: "p",
467 Match: MatchEqual,
468 Value: "test",
469 Rewrites: []Rewrite{
470 {
471 Editor: "test",
472 Scope: "foundry",
473 },
474 },
475 },
476 },
477 {
478 name: "Clone Token",
479 node: &Token{
480 Wrap: &Term{
481 Foundry: "opennlp",
482 Key: "DET",
483 Layer: "p",
484 Match: MatchEqual,
485 },
486 Rewrites: []Rewrite{
487 {
488 Editor: "test",
489 Scope: "layer",
490 },
491 },
492 },
493 },
494 {
495 name: "Clone TermGroup",
496 node: &TermGroup{
497 Operands: []Node{
498 &Term{
499 Foundry: "opennlp",
500 Key: "DET",
501 Layer: "p",
502 Match: MatchEqual,
503 },
504 &Term{
505 Foundry: "opennlp",
506 Key: "AdjType",
507 Layer: "m",
508 Match: MatchEqual,
509 Value: "Pdt",
510 },
511 },
512 Relation: AndRelation,
513 Rewrites: []Rewrite{
514 {
515 Editor: "test",
516 Scope: "foundry",
517 },
518 },
519 },
520 },
521 {
522 name: "Clone CatchallNode",
523 node: &CatchallNode{
524 NodeType: "koral:unknown",
525 RawContent: []byte(`{"@type":"koral:unknown","test":"value"}`),
526 Wrap: &Term{
527 Foundry: "opennlp",
528 Key: "DET",
529 Layer: "p",
530 Match: MatchEqual,
531 },
532 Operands: []Node{
533 &Term{
534 Foundry: "opennlp",
535 Key: "AdjType",
536 Layer: "m",
537 Match: MatchEqual,
538 Value: "Pdt",
539 },
540 },
541 },
542 },
543 {
544 name: "Clone Rewrite",
545 node: &Rewrite{
546 Editor: "termMapper",
547 Operation: "injection",
548 Scope: "foundry",
549 Src: "test",
550 Comment: "test comment",
551 Original: "original_value",
552 },
553 },
554 }
555
556 for _, tt := range tests {
557 t.Run(tt.name, func(t *testing.T) {
558 cloned := tt.node.Clone()
559
560 // Check that the clone is not the same instance
561 assert.NotSame(t, tt.node, cloned)
562
563 // Check that the clone has the same type
564 assert.Equal(t, tt.node.Type(), cloned.Type())
565
566 // Check that nodes are equal (deep comparison)
567 assert.True(t, NodesEqual(tt.node, cloned))
568
569 // Test that modifying the clone doesn't affect the original
570 switch original := tt.node.(type) {
571 case *Term:
572 clonedTerm := cloned.(*Term)
573 clonedTerm.Foundry = "modified"
574 assert.NotEqual(t, original.Foundry, clonedTerm.Foundry)
575
576 case *Token:
577 clonedToken := cloned.(*Token)
578 if clonedToken.Wrap != nil {
579 if termWrap, ok := clonedToken.Wrap.(*Term); ok {
580 termWrap.Foundry = "modified"
581 if originalWrap, ok := original.Wrap.(*Term); ok {
582 assert.NotEqual(t, originalWrap.Foundry, termWrap.Foundry)
583 }
584 }
585 }
586
587 case *TermGroup:
588 clonedGroup := cloned.(*TermGroup)
589 clonedGroup.Relation = OrRelation
590 assert.NotEqual(t, original.Relation, clonedGroup.Relation)
591
592 case *CatchallNode:
593 clonedCatchall := cloned.(*CatchallNode)
594 clonedCatchall.NodeType = "modified"
595 assert.NotEqual(t, original.NodeType, clonedCatchall.NodeType)
596
597 case *Rewrite:
598 clonedRewrite := cloned.(*Rewrite)
599 clonedRewrite.Editor = "modified"
600 assert.NotEqual(t, original.Editor, clonedRewrite.Editor)
601 }
602 })
603 }
604}
605
606func TestCloneNilNodes(t *testing.T) {
607 // Test cloning nodes with nil fields
608 tests := []struct {
609 name string
610 node Node
611 }{
612 {
613 name: "Token with nil wrap",
614 node: &Token{Wrap: nil},
615 },
616 {
617 name: "TermGroup with empty operands",
618 node: &TermGroup{
619 Operands: []Node{},
620 Relation: AndRelation,
621 },
622 },
623 {
624 name: "CatchallNode with nil wrap and operands",
625 node: &CatchallNode{
626 NodeType: "koral:unknown",
627 RawContent: nil,
628 Wrap: nil,
629 Operands: nil,
630 },
631 },
632 }
633
634 for _, tt := range tests {
635 t.Run(tt.name, func(t *testing.T) {
636 cloned := tt.node.Clone()
637 assert.NotSame(t, tt.node, cloned)
638 assert.Equal(t, tt.node.Type(), cloned.Type())
639 assert.True(t, NodesEqual(tt.node, cloned))
640 })
641 }
642}
643
644func TestApplyFoundryAndLayerOverrides(t *testing.T) {
645 tests := []struct {
646 name string
647 node Node
648 foundry string
649 layer string
650 expectedChanges func(t *testing.T, node Node)
651 }{
652 {
653 name: "Apply foundry and layer to Term",
654 node: &Term{
655 Foundry: "original",
656 Key: "DET",
657 Layer: "original",
658 Match: MatchEqual,
659 },
660 foundry: "new_foundry",
661 layer: "new_layer",
662 expectedChanges: func(t *testing.T, node Node) {
663 term := node.(*Term)
664 assert.Equal(t, "new_foundry", term.Foundry)
665 assert.Equal(t, "new_layer", term.Layer)
666 },
667 },
668 {
669 name: "Apply only foundry to Term",
670 node: &Term{
671 Foundry: "original",
672 Key: "DET",
673 Layer: "original",
674 Match: MatchEqual,
675 },
676 foundry: "new_foundry",
677 layer: "",
678 expectedChanges: func(t *testing.T, node Node) {
679 term := node.(*Term)
680 assert.Equal(t, "new_foundry", term.Foundry)
681 assert.Equal(t, "original", term.Layer) // Should remain unchanged
682 },
683 },
684 {
685 name: "Apply to TermGroup",
686 node: &TermGroup{
687 Operands: []Node{
688 &Term{
689 Foundry: "original1",
690 Key: "DET",
691 Layer: "original1",
692 Match: MatchEqual,
693 },
694 &Term{
695 Foundry: "original2",
696 Key: "AdjType",
697 Layer: "original2",
698 Match: MatchEqual,
699 Value: "Pdt",
700 },
701 },
702 Relation: AndRelation,
703 },
704 foundry: "new_foundry",
705 layer: "new_layer",
706 expectedChanges: func(t *testing.T, node Node) {
707 termGroup := node.(*TermGroup)
708 for _, operand := range termGroup.Operands {
709 if term, ok := operand.(*Term); ok {
710 assert.Equal(t, "new_foundry", term.Foundry)
711 assert.Equal(t, "new_layer", term.Layer)
712 }
713 }
714 },
715 },
716 {
717 name: "Apply to Token with wrapped Term",
718 node: &Token{
719 Wrap: &Term{
720 Foundry: "original",
721 Key: "DET",
722 Layer: "original",
723 Match: MatchEqual,
724 },
725 },
726 foundry: "new_foundry",
727 layer: "new_layer",
728 expectedChanges: func(t *testing.T, node Node) {
729 token := node.(*Token)
730 if term, ok := token.Wrap.(*Term); ok {
731 assert.Equal(t, "new_foundry", term.Foundry)
732 assert.Equal(t, "new_layer", term.Layer)
733 }
734 },
735 },
736 {
737 name: "Apply to CatchallNode",
738 node: &CatchallNode{
739 NodeType: "koral:unknown",
740 Wrap: &Term{
741 Foundry: "original",
742 Key: "DET",
743 Layer: "original",
744 Match: MatchEqual,
745 },
746 Operands: []Node{
747 &Term{
748 Foundry: "original2",
749 Key: "AdjType",
750 Layer: "original2",
751 Match: MatchEqual,
752 Value: "Pdt",
753 },
754 },
755 },
756 foundry: "new_foundry",
757 layer: "new_layer",
758 expectedChanges: func(t *testing.T, node Node) {
759 catchall := node.(*CatchallNode)
760 if term, ok := catchall.Wrap.(*Term); ok {
761 assert.Equal(t, "new_foundry", term.Foundry)
762 assert.Equal(t, "new_layer", term.Layer)
763 }
764 for _, operand := range catchall.Operands {
765 if term, ok := operand.(*Term); ok {
766 assert.Equal(t, "new_foundry", term.Foundry)
767 assert.Equal(t, "new_layer", term.Layer)
768 }
769 }
770 },
771 },
772 {
773 name: "Apply to nested structure",
774 node: &Token{
775 Wrap: &TermGroup{
776 Operands: []Node{
777 &Term{
778 Foundry: "original1",
779 Key: "DET",
780 Layer: "original1",
781 Match: MatchEqual,
782 },
783 &Token{
784 Wrap: &Term{
785 Foundry: "original2",
786 Key: "AdjType",
787 Layer: "original2",
788 Match: MatchEqual,
789 Value: "Pdt",
790 },
791 },
792 },
793 Relation: AndRelation,
794 },
795 },
796 foundry: "new_foundry",
797 layer: "new_layer",
798 expectedChanges: func(t *testing.T, node Node) {
799 token := node.(*Token)
800 if termGroup, ok := token.Wrap.(*TermGroup); ok {
801 for _, operand := range termGroup.Operands {
802 switch op := operand.(type) {
803 case *Term:
804 assert.Equal(t, "new_foundry", op.Foundry)
805 assert.Equal(t, "new_layer", op.Layer)
806 case *Token:
807 if innerTerm, ok := op.Wrap.(*Term); ok {
808 assert.Equal(t, "new_foundry", innerTerm.Foundry)
809 assert.Equal(t, "new_layer", innerTerm.Layer)
810 }
811 }
812 }
813 }
814 },
815 },
816 }
817
818 for _, tt := range tests {
819 t.Run(tt.name, func(t *testing.T) {
820 // Clone the node to avoid modifying the original test data
821 cloned := tt.node.Clone()
822
823 // Apply the overrides
824 ApplyFoundryAndLayerOverrides(cloned, tt.foundry, tt.layer)
825
826 // Check the expected changes
827 tt.expectedChanges(t, cloned)
828 })
829 }
830}
831
832func TestApplyFoundryAndLayerOverridesNilNode(t *testing.T) {
833 // Test that applying overrides to a nil node doesn't panic
834 assert.NotPanics(t, func() {
835 ApplyFoundryAndLayerOverrides(nil, "foundry", "layer")
836 })
837}
838
839func TestApplyFoundryAndLayerOverridesEmptyValues(t *testing.T) {
840 // Test applying empty foundry and layer values
841 term := &Term{
842 Foundry: "original_foundry",
843 Key: "DET",
844 Layer: "original_layer",
845 Match: MatchEqual,
846 }
847
848 ApplyFoundryAndLayerOverrides(term, "", "")
849
850 // Values should remain unchanged
851 assert.Equal(t, "original_foundry", term.Foundry)
852 assert.Equal(t, "original_layer", term.Layer)
853}
Akron7c91cde2025-06-24 17:11:22 +0200854
855func TestRestrictToObligatory(t *testing.T) {
856 tests := []struct {
857 name string
858 node Node
859 foundry string
860 layer string
861 expected Node
862 }{
863 {
864 name: "Simple term - kept as is",
865 node: &Term{
866 Foundry: "old_foundry",
867 Key: "DET",
868 Layer: "old_layer",
869 Match: MatchEqual,
870 },
871 foundry: "new_foundry",
872 layer: "new_layer",
873 expected: &Term{
874 Foundry: "new_foundry",
875 Key: "DET",
876 Layer: "new_layer",
877 Match: MatchEqual,
878 },
879 },
880 {
881 name: "AND group - kept as is",
882 node: &TermGroup{
883 Operands: []Node{
884 &Term{
885 Foundry: "old_foundry",
886 Key: "A",
887 Layer: "old_layer",
888 Match: MatchEqual,
889 },
890 &Term{
891 Foundry: "old_foundry",
892 Key: "B",
893 Layer: "old_layer",
894 Match: MatchEqual,
895 },
896 &Term{
897 Foundry: "old_foundry",
898 Key: "C",
899 Layer: "old_layer",
900 Match: MatchEqual,
901 },
902 },
903 Relation: AndRelation,
904 },
905 foundry: "new_foundry",
906 layer: "new_layer",
907 expected: &TermGroup{
908 Operands: []Node{
909 &Term{
910 Foundry: "new_foundry",
911 Key: "A",
912 Layer: "new_layer",
913 Match: MatchEqual,
914 },
915 &Term{
916 Foundry: "new_foundry",
917 Key: "B",
918 Layer: "new_layer",
919 Match: MatchEqual,
920 },
921 &Term{
922 Foundry: "new_foundry",
923 Key: "C",
924 Layer: "new_layer",
925 Match: MatchEqual,
926 },
927 },
928 Relation: AndRelation,
929 },
930 },
931 {
932 name: "OR group - becomes nil",
933 node: &TermGroup{
934 Operands: []Node{
935 &Term{
936 Foundry: "old_foundry",
937 Key: "A",
938 Layer: "old_layer",
939 Match: MatchEqual,
940 },
941 &Term{
942 Foundry: "old_foundry",
943 Key: "B",
944 Layer: "old_layer",
945 Match: MatchEqual,
946 },
947 },
948 Relation: OrRelation,
949 },
950 foundry: "new_foundry",
951 layer: "new_layer",
952 expected: nil,
953 },
954 {
955 name: "Mixed AND with nested OR - OR removed",
956 node: &TermGroup{
957 Operands: []Node{
958 &Term{
959 Foundry: "old_foundry",
960 Key: "A",
961 Layer: "old_layer",
962 Match: MatchEqual,
963 },
964 &Term{
965 Foundry: "old_foundry",
966 Key: "B",
967 Layer: "old_layer",
968 Match: MatchEqual,
969 },
970 &TermGroup{
971 Operands: []Node{
972 &Term{
973 Foundry: "old_foundry",
974 Key: "C",
975 Layer: "old_layer",
976 Match: MatchEqual,
977 },
978 &Term{
979 Foundry: "old_foundry",
980 Key: "D",
981 Layer: "old_layer",
982 Match: MatchEqual,
983 },
984 },
985 Relation: OrRelation,
986 },
987 &Term{
988 Foundry: "old_foundry",
989 Key: "E",
990 Layer: "old_layer",
991 Match: MatchEqual,
992 },
993 },
994 Relation: AndRelation,
995 },
996 foundry: "new_foundry",
997 layer: "new_layer",
998 expected: &TermGroup{
999 Operands: []Node{
1000 &Term{
1001 Foundry: "new_foundry",
1002 Key: "A",
1003 Layer: "new_layer",
1004 Match: MatchEqual,
1005 },
1006 &Term{
1007 Foundry: "new_foundry",
1008 Key: "B",
1009 Layer: "new_layer",
1010 Match: MatchEqual,
1011 },
1012 &Term{
1013 Foundry: "new_foundry",
1014 Key: "E",
1015 Layer: "new_layer",
1016 Match: MatchEqual,
1017 },
1018 },
1019 Relation: AndRelation,
1020 },
1021 },
1022 {
1023 name: "AND group with all OR operands - becomes nil",
1024 node: &TermGroup{
1025 Operands: []Node{
1026 &TermGroup{
1027 Operands: []Node{
1028 &Term{Key: "A", Match: MatchEqual},
1029 &Term{Key: "B", Match: MatchEqual},
1030 },
1031 Relation: OrRelation,
1032 },
1033 &TermGroup{
1034 Operands: []Node{
1035 &Term{Key: "C", Match: MatchEqual},
1036 &Term{Key: "D", Match: MatchEqual},
1037 },
1038 Relation: OrRelation,
1039 },
1040 },
1041 Relation: AndRelation,
1042 },
1043 foundry: "",
1044 layer: "",
1045 expected: nil,
1046 },
1047 {
1048 name: "AND group with one operand after restriction - returns single operand",
1049 node: &TermGroup{
1050 Operands: []Node{
1051 &Term{
1052 Foundry: "old_foundry",
1053 Key: "A",
1054 Layer: "old_layer",
1055 Match: MatchEqual,
1056 },
1057 &TermGroup{
1058 Operands: []Node{
1059 &Term{Key: "B", Match: MatchEqual},
1060 &Term{Key: "C", Match: MatchEqual},
1061 },
1062 Relation: OrRelation,
1063 },
1064 },
1065 Relation: AndRelation,
1066 },
1067 foundry: "new_foundry",
1068 layer: "new_layer",
1069 expected: &Term{
1070 Foundry: "new_foundry",
1071 Key: "A",
1072 Layer: "new_layer",
1073 Match: MatchEqual,
1074 },
1075 },
1076 {
1077 name: "Token with wrapped term",
1078 node: &Token{
1079 Wrap: &Term{
1080 Foundry: "old_foundry",
1081 Key: "DET",
1082 Layer: "old_layer",
1083 Match: MatchEqual,
1084 },
1085 Rewrites: []Rewrite{
1086 {Editor: "test"},
1087 },
1088 },
1089 foundry: "new_foundry",
1090 layer: "new_layer",
1091 expected: &Token{
1092 Wrap: &Term{
1093 Foundry: "new_foundry",
1094 Key: "DET",
1095 Layer: "new_layer",
1096 Match: MatchEqual,
1097 },
1098 Rewrites: []Rewrite{
1099 {Editor: "test"},
1100 },
1101 },
1102 },
1103 {
1104 name: "Token with wrapped OR group - becomes nil",
1105 node: &Token{
1106 Wrap: &TermGroup{
1107 Operands: []Node{
1108 &Term{Key: "A", Match: MatchEqual},
1109 &Term{Key: "B", Match: MatchEqual},
1110 },
1111 Relation: OrRelation,
1112 },
1113 },
1114 foundry: "",
1115 layer: "",
1116 expected: nil,
1117 },
1118 {
1119 name: "Nil node",
1120 node: nil,
1121 foundry: "foundry",
1122 layer: "layer",
1123 expected: nil,
1124 },
1125 }
1126
1127 for _, tt := range tests {
1128 t.Run(tt.name, func(t *testing.T) {
1129 result := RestrictToObligatory(tt.node, tt.foundry, tt.layer)
1130 assert.True(t, NodesEqual(tt.expected, result),
1131 "Expected: %+v\nGot: %+v", tt.expected, result)
1132 })
1133 }
1134}
1135
1136func TestRestrictToObligatoryDoesNotModifyOriginal(t *testing.T) {
1137 // Test that the original node is not modified
1138 original := &TermGroup{
1139 Operands: []Node{
1140 &Term{
1141 Foundry: "original",
1142 Key: "A",
1143 Layer: "original",
1144 Match: MatchEqual,
1145 },
1146 &TermGroup{
1147 Operands: []Node{
1148 &Term{Key: "B", Match: MatchEqual},
1149 &Term{Key: "C", Match: MatchEqual},
1150 },
1151 Relation: OrRelation,
1152 },
1153 },
1154 Relation: AndRelation,
1155 }
1156
1157 // Clone to check that original remains unchanged
1158 originalClone := original.Clone()
1159
1160 // Apply restriction
1161 result := RestrictToObligatory(original, "new_foundry", "new_layer")
1162
1163 // Original should be unchanged
1164 assert.True(t, NodesEqual(originalClone, original))
1165
1166 // Result should be different
1167 expected := &Term{
1168 Foundry: "new_foundry",
1169 Key: "A",
1170 Layer: "new_layer",
1171 Match: MatchEqual,
1172 }
1173 assert.True(t, NodesEqual(expected, result))
1174}