blob: a83586d34f2dfcf383a3b61cb9d7b3df0b9b3e82 [file] [log] [blame]
Akron32d53de2025-05-22 13:45:32 +02001package mapper
2
3import (
4 "encoding/json"
Akron32d53de2025-05-22 13:45:32 +02005 "testing"
6
Akronfa55bb22025-05-26 15:10:42 +02007 "github.com/KorAP/KoralPipe-TermMapper/ast"
Akrona00d4752025-05-26 17:34:36 +02008 "github.com/KorAP/KoralPipe-TermMapper/config"
Akronfa55bb22025-05-26 15:10:42 +02009 "github.com/KorAP/KoralPipe-TermMapper/matcher"
Akron32d53de2025-05-22 13:45:32 +020010 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12)
13
14func TestMapper(t *testing.T) {
Akrona00d4752025-05-26 17:34:36 +020015 // Create test mapping list
16 mappingList := config.MappingList{
17 ID: "test-mapper",
18 FoundryA: "opennlp",
19 LayerA: "p",
20 FoundryB: "upos",
21 LayerB: "p",
22 Mappings: []config.MappingRule{
23 "[PIDAT] <> [opennlp/p=PIDAT & opennlp/p=AdjType:Pdt]",
24 "[DET] <> [opennlp/p=DET]",
25 },
26 }
Akron32d53de2025-05-22 13:45:32 +020027
28 // Create a new mapper
Akrona00d4752025-05-26 17:34:36 +020029 m, err := NewMapper([]config.MappingList{mappingList})
Akron32d53de2025-05-22 13:45:32 +020030 require.NoError(t, err)
31
32 tests := []struct {
33 name string
34 mappingID string
35 opts MappingOptions
36 input string
37 expected string
38 expectError bool
39 }{
40 {
41 name: "Simple A to B mapping",
42 mappingID: "test-mapper",
43 opts: MappingOptions{
44 Direction: AtoB,
45 },
46 input: `{
47 "@type": "koral:token",
48 "wrap": {
49 "@type": "koral:term",
50 "foundry": "opennlp",
51 "key": "PIDAT",
52 "layer": "p",
53 "match": "match:eq"
54 }
55 }`,
56 expected: `{
57 "@type": "koral:token",
58 "wrap": {
59 "@type": "koral:termGroup",
60 "operands": [
61 {
62 "@type": "koral:term",
63 "foundry": "opennlp",
64 "key": "PIDAT",
65 "layer": "p",
66 "match": "match:eq"
67 },
68 {
69 "@type": "koral:term",
70 "foundry": "opennlp",
71 "key": "AdjType",
72 "layer": "p",
73 "match": "match:eq",
74 "value": "Pdt"
75 }
76 ],
77 "relation": "relation:and"
78 }
79 }`,
80 },
81 {
82 name: "B to A mapping",
83 mappingID: "test-mapper",
84 opts: MappingOptions{
85 Direction: BtoA,
86 },
87 input: `{
88 "@type": "koral:token",
89 "wrap": {
90 "@type": "koral:termGroup",
91 "operands": [
92 {
93 "@type": "koral:term",
94 "foundry": "opennlp",
95 "key": "PIDAT",
96 "layer": "p",
97 "match": "match:eq"
98 },
99 {
100 "@type": "koral:term",
101 "foundry": "opennlp",
102 "key": "AdjType",
103 "layer": "p",
104 "match": "match:eq",
105 "value": "Pdt"
106 }
107 ],
108 "relation": "relation:and"
109 }
110 }`,
111 expected: `{
112 "@type": "koral:token",
113 "wrap": {
114 "@type": "koral:term",
115 "foundry": "opennlp",
116 "key": "PIDAT",
117 "layer": "p",
118 "match": "match:eq"
119 }
120 }`,
121 },
122 {
123 name: "Mapping with foundry override",
124 mappingID: "test-mapper",
125 opts: MappingOptions{
126 Direction: AtoB,
127 FoundryB: "custom",
128 },
129 input: `{
130 "@type": "koral:token",
131 "wrap": {
132 "@type": "koral:term",
133 "foundry": "opennlp",
134 "key": "PIDAT",
135 "layer": "p",
136 "match": "match:eq"
137 }
138 }`,
139 expected: `{
140 "@type": "koral:token",
141 "wrap": {
142 "@type": "koral:termGroup",
143 "operands": [
144 {
145 "@type": "koral:term",
146 "foundry": "custom",
147 "key": "PIDAT",
148 "layer": "p",
149 "match": "match:eq"
150 },
151 {
152 "@type": "koral:term",
153 "foundry": "custom",
154 "key": "AdjType",
155 "layer": "p",
156 "match": "match:eq",
157 "value": "Pdt"
158 }
159 ],
160 "relation": "relation:and"
161 }
162 }`,
163 },
164 {
165 name: "Invalid mapping ID",
166 mappingID: "nonexistent",
167 opts: MappingOptions{
168 Direction: AtoB,
169 },
170 input: `{
171 "@type": "koral:token",
172 "wrap": {
173 "@type": "koral:term",
174 "foundry": "opennlp",
175 "key": "PIDAT",
176 "layer": "p",
177 "match": "match:eq"
178 }
179 }`,
180 expectError: true,
181 },
182 {
183 name: "Invalid direction",
184 mappingID: "test-mapper",
185 opts: MappingOptions{
186 Direction: "invalid",
187 },
188 input: `{
189 "@type": "koral:token",
190 "wrap": {
191 "@type": "koral:term",
192 "foundry": "opennlp",
193 "key": "PIDAT",
194 "layer": "p",
195 "match": "match:eq"
196 }
197 }`,
198 expectError: true,
199 },
200 }
201
202 for _, tt := range tests {
203 t.Run(tt.name, func(t *testing.T) {
204 // Parse input JSON
205 var inputData interface{}
206 err := json.Unmarshal([]byte(tt.input), &inputData)
207 require.NoError(t, err)
208
209 // Apply mappings
210 result, err := m.ApplyMappings(tt.mappingID, tt.opts, inputData)
211 if tt.expectError {
212 assert.Error(t, err)
213 return
214 }
215 require.NoError(t, err)
216
217 // Parse expected JSON
218 var expectedData interface{}
219 err = json.Unmarshal([]byte(tt.expected), &expectedData)
220 require.NoError(t, err)
221
222 // Compare results
223 assert.Equal(t, expectedData, result)
224 })
225 }
226}
Akrond5850f82025-05-23 16:44:44 +0200227
228func TestMatchComplexPatterns(t *testing.T) {
229 tests := []struct {
230 name string
231 pattern ast.Pattern
232 replacement ast.Replacement
233 input ast.Node
234 expected ast.Node
235 }{
236 {
237 name: "Deep nested pattern with mixed operators",
238 pattern: ast.Pattern{
239 Root: &ast.TermGroup{
240 Operands: []ast.Node{
241 &ast.Term{
242 Key: "A",
243 Match: ast.MatchEqual,
244 },
245 &ast.TermGroup{
246 Operands: []ast.Node{
247 &ast.Term{
248 Key: "B",
249 Match: ast.MatchEqual,
250 },
251 &ast.TermGroup{
252 Operands: []ast.Node{
253 &ast.Term{
254 Key: "C",
255 Match: ast.MatchEqual,
256 },
257 &ast.Term{
258 Key: "D",
259 Match: ast.MatchEqual,
260 },
261 },
262 Relation: ast.AndRelation,
263 },
264 },
265 Relation: ast.OrRelation,
266 },
267 },
268 Relation: ast.AndRelation,
269 },
270 },
271 replacement: ast.Replacement{
272 Root: &ast.Term{
273 Key: "RESULT",
274 Match: ast.MatchEqual,
275 },
276 },
277 input: &ast.TermGroup{
278 Operands: []ast.Node{
279 &ast.Term{
280 Key: "A",
281 Match: ast.MatchEqual,
282 },
283 &ast.TermGroup{
284 Operands: []ast.Node{
285 &ast.Term{
286 Key: "C",
287 Match: ast.MatchEqual,
288 },
289 &ast.Term{
290 Key: "D",
291 Match: ast.MatchEqual,
292 },
293 },
294 Relation: ast.AndRelation,
295 },
296 },
297 Relation: ast.AndRelation,
298 },
299 expected: &ast.Term{
300 Key: "RESULT",
301 Match: ast.MatchEqual,
302 },
303 },
304 }
305
306 for _, tt := range tests {
307 t.Run(tt.name, func(t *testing.T) {
308 m, err := matcher.NewMatcher(tt.pattern, tt.replacement)
309 require.NoError(t, err)
310 result := m.Replace(tt.input)
311 assert.Equal(t, tt.expected, result)
312 })
313 }
314}
315
316func TestInvalidPatternReplacement(t *testing.T) {
Akrona00d4752025-05-26 17:34:36 +0200317 // Create test mapping list
318 mappingList := config.MappingList{
319 ID: "test-mapper",
320 FoundryA: "opennlp",
321 LayerA: "p",
322 FoundryB: "upos",
323 LayerB: "p",
324 Mappings: []config.MappingRule{
325 "[PIDAT] <> [opennlp/p=PIDAT & opennlp/p=AdjType:Pdt]",
326 },
327 }
Akrond5850f82025-05-23 16:44:44 +0200328
329 // Create a new mapper
Akrona00d4752025-05-26 17:34:36 +0200330 m, err := NewMapper([]config.MappingList{mappingList})
Akrond5850f82025-05-23 16:44:44 +0200331 require.NoError(t, err)
332
333 tests := []struct {
334 name string
335 input string
336 expectError bool
337 errorMsg string
338 }{
339 {
340 name: "Invalid input - empty term group",
341 input: `{
342 "@type": "koral:token",
343 "wrap": {
344 "@type": "koral:termGroup",
345 "operands": [],
346 "relation": "relation:and"
347 }
348 }`,
349 expectError: true,
350 errorMsg: "failed to parse JSON into AST: error parsing wrapped node: term group must have at least one operand",
351 },
352 }
353
354 for _, tt := range tests {
355 t.Run(tt.name, func(t *testing.T) {
356 var inputData any
357 err := json.Unmarshal([]byte(tt.input), &inputData)
358 require.NoError(t, err)
359
360 result, err := m.ApplyMappings("test-mapper", MappingOptions{Direction: AtoB}, inputData)
361 if tt.expectError {
362 assert.Error(t, err)
363 assert.Equal(t, tt.errorMsg, err.Error())
364 assert.Nil(t, result)
365 } else {
366 assert.NoError(t, err)
367 assert.NotNil(t, result)
368 }
369 })
370 }
371}