blob: 00e10b362145b448dd830d47b1e16b87f6e54671 [file] [log] [blame]
Akron49ceeb42025-05-23 17:46:01 +02001package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "io"
7 "net/http"
8 "net/http/httptest"
Akron49ceeb42025-05-23 17:46:01 +02009 "testing"
10
Akrona00d4752025-05-26 17:34:36 +020011 tmconfig "github.com/KorAP/KoralPipe-TermMapper/config"
Akronfa55bb22025-05-26 15:10:42 +020012 "github.com/KorAP/KoralPipe-TermMapper/mapper"
Akron49ceeb42025-05-23 17:46:01 +020013 "github.com/gofiber/fiber/v2"
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16)
17
18func TestTransformEndpoint(t *testing.T) {
Akrona00d4752025-05-26 17:34:36 +020019 // Create test mapping list
20 mappingList := tmconfig.MappingList{
21 ID: "test-mapper",
22 FoundryA: "opennlp",
23 LayerA: "p",
24 FoundryB: "upos",
25 LayerB: "p",
26 Mappings: []tmconfig.MappingRule{
27 "[PIDAT] <> [opennlp/p=PIDAT & opennlp/p=AdjType:Pdt]",
28 "[DET] <> [opennlp/p=DET]",
29 },
30 }
Akron49ceeb42025-05-23 17:46:01 +020031
32 // Create mapper
Akrona00d4752025-05-26 17:34:36 +020033 m, err := mapper.NewMapper([]tmconfig.MappingList{mappingList})
Akron49ceeb42025-05-23 17:46:01 +020034 require.NoError(t, err)
35
Akron40aaa632025-06-03 17:57:52 +020036 // Create mock config for testing
37 mockConfig := &tmconfig.MappingLists{
38 Lists: []tmconfig.MappingList{mappingList},
39 }
40
Akron49ceeb42025-05-23 17:46:01 +020041 // Create fiber app
42 app := fiber.New()
Akron40aaa632025-06-03 17:57:52 +020043 setupRoutes(app, m, mockConfig)
Akron49ceeb42025-05-23 17:46:01 +020044
45 tests := []struct {
46 name string
47 mapID string
48 direction string
49 foundryA string
50 foundryB string
51 layerA string
52 layerB string
53 input string
54 expectedCode int
55 expectedBody string
56 expectedError string
57 }{
58 {
59 name: "Simple A to B mapping",
60 mapID: "test-mapper",
61 direction: "atob",
62 input: `{
63 "@type": "koral:token",
64 "wrap": {
65 "@type": "koral:term",
66 "foundry": "opennlp",
67 "key": "PIDAT",
68 "layer": "p",
69 "match": "match:eq"
70 }
71 }`,
72 expectedCode: http.StatusOK,
73 expectedBody: `{
74 "@type": "koral:token",
75 "wrap": {
76 "@type": "koral:termGroup",
77 "operands": [
78 {
79 "@type": "koral:term",
80 "foundry": "opennlp",
81 "key": "PIDAT",
82 "layer": "p",
83 "match": "match:eq"
84 },
85 {
86 "@type": "koral:term",
87 "foundry": "opennlp",
88 "key": "AdjType",
89 "layer": "p",
90 "match": "match:eq",
91 "value": "Pdt"
92 }
93 ],
94 "relation": "relation:and"
95 }
96 }`,
97 },
98 {
99 name: "B to A mapping",
100 mapID: "test-mapper",
101 direction: "btoa",
102 input: `{
103 "@type": "koral:token",
104 "wrap": {
105 "@type": "koral:termGroup",
106 "operands": [
107 {
108 "@type": "koral:term",
109 "foundry": "opennlp",
110 "key": "PIDAT",
111 "layer": "p",
112 "match": "match:eq"
113 },
114 {
115 "@type": "koral:term",
116 "foundry": "opennlp",
117 "key": "AdjType",
118 "layer": "p",
119 "match": "match:eq",
120 "value": "Pdt"
121 }
122 ],
123 "relation": "relation:and"
124 }
125 }`,
126 expectedCode: http.StatusOK,
127 expectedBody: `{
128 "@type": "koral:token",
129 "wrap": {
130 "@type": "koral:term",
131 "foundry": "opennlp",
132 "key": "PIDAT",
133 "layer": "p",
134 "match": "match:eq"
135 }
136 }`,
137 },
138 {
139 name: "Mapping with foundry override",
140 mapID: "test-mapper",
141 direction: "atob",
142 foundryB: "custom",
143 input: `{
144 "@type": "koral:token",
145 "wrap": {
146 "@type": "koral:term",
147 "foundry": "opennlp",
148 "key": "PIDAT",
149 "layer": "p",
150 "match": "match:eq"
151 }
152 }`,
153 expectedCode: http.StatusOK,
154 expectedBody: `{
155 "@type": "koral:token",
156 "wrap": {
157 "@type": "koral:termGroup",
158 "operands": [
159 {
160 "@type": "koral:term",
161 "foundry": "custom",
162 "key": "PIDAT",
163 "layer": "p",
164 "match": "match:eq"
165 },
166 {
167 "@type": "koral:term",
168 "foundry": "custom",
169 "key": "AdjType",
170 "layer": "p",
171 "match": "match:eq",
172 "value": "Pdt"
173 }
174 ],
175 "relation": "relation:and"
176 }
177 }`,
178 },
179 {
180 name: "Invalid mapping ID",
181 mapID: "nonexistent",
182 direction: "atob",
183 input: `{"@type": "koral:token"}`,
184 expectedCode: http.StatusInternalServerError,
185 expectedError: "mapping list with ID nonexistent not found",
186 },
187 {
188 name: "Invalid direction",
189 mapID: "test-mapper",
190 direction: "invalid",
191 input: `{"@type": "koral:token"}`,
192 expectedCode: http.StatusBadRequest,
193 expectedError: "invalid direction, must be 'atob' or 'btoa'",
194 },
195 {
196 name: "Invalid JSON",
197 mapID: "test-mapper",
198 direction: "atob",
199 input: `invalid json`,
200 expectedCode: http.StatusBadRequest,
201 expectedError: "invalid JSON in request body",
202 },
203 }
204
205 for _, tt := range tests {
206 t.Run(tt.name, func(t *testing.T) {
207 // Build URL with query parameters
208 url := "/" + tt.mapID + "/query"
209 if tt.direction != "" {
210 url += "?dir=" + tt.direction
211 }
212 if tt.foundryA != "" {
213 url += "&foundryA=" + tt.foundryA
214 }
215 if tt.foundryB != "" {
216 url += "&foundryB=" + tt.foundryB
217 }
218 if tt.layerA != "" {
219 url += "&layerA=" + tt.layerA
220 }
221 if tt.layerB != "" {
222 url += "&layerB=" + tt.layerB
223 }
224
225 // Make request
226 req := httptest.NewRequest(http.MethodPost, url, bytes.NewBufferString(tt.input))
227 req.Header.Set("Content-Type", "application/json")
228 resp, err := app.Test(req)
229 require.NoError(t, err)
230 defer resp.Body.Close()
231
232 // Check status code
233 assert.Equal(t, tt.expectedCode, resp.StatusCode)
234
235 // Read response body
236 body, err := io.ReadAll(resp.Body)
237 require.NoError(t, err)
238
239 if tt.expectedError != "" {
240 // Check error message
241 var errResp fiber.Map
242 err = json.Unmarshal(body, &errResp)
243 require.NoError(t, err)
244 assert.Equal(t, tt.expectedError, errResp["error"])
245 } else {
246 // Compare JSON responses
Akron121c66e2025-06-02 16:34:05 +0200247 var expected, actual any
Akron49ceeb42025-05-23 17:46:01 +0200248 err = json.Unmarshal([]byte(tt.expectedBody), &expected)
249 require.NoError(t, err)
250 err = json.Unmarshal(body, &actual)
251 require.NoError(t, err)
252 assert.Equal(t, expected, actual)
253 }
254 })
255 }
256}
257
258func TestHealthEndpoint(t *testing.T) {
Akrona00d4752025-05-26 17:34:36 +0200259 // Create test mapping list
260 mappingList := tmconfig.MappingList{
261 ID: "test-mapper",
262 Mappings: []tmconfig.MappingRule{
263 "[A] <> [B]",
264 },
265 }
Akron49ceeb42025-05-23 17:46:01 +0200266
Akrona00d4752025-05-26 17:34:36 +0200267 // Create mapper
268 m, err := mapper.NewMapper([]tmconfig.MappingList{mappingList})
Akron49ceeb42025-05-23 17:46:01 +0200269 require.NoError(t, err)
270
Akron40aaa632025-06-03 17:57:52 +0200271 // Create mock config for testing
272 mockConfig := &tmconfig.MappingLists{
273 Lists: []tmconfig.MappingList{mappingList},
274 }
275
Akron49ceeb42025-05-23 17:46:01 +0200276 // Create fiber app
277 app := fiber.New()
Akron40aaa632025-06-03 17:57:52 +0200278 setupRoutes(app, m, mockConfig)
Akron49ceeb42025-05-23 17:46:01 +0200279
280 // Test health endpoint
281 req := httptest.NewRequest(http.MethodGet, "/health", nil)
282 resp, err := app.Test(req)
283 require.NoError(t, err)
284 defer resp.Body.Close()
285
286 assert.Equal(t, http.StatusOK, resp.StatusCode)
287 body, err := io.ReadAll(resp.Body)
288 require.NoError(t, err)
289 assert.Equal(t, "OK", string(body))
Akron40aaa632025-06-03 17:57:52 +0200290
291 req = httptest.NewRequest(http.MethodGet, "/kalamarplugin", nil)
292 resp, err = app.Test(req)
293 require.NoError(t, err)
294 defer resp.Body.Close()
295
296 assert.Equal(t, http.StatusOK, resp.StatusCode)
297 body, err = io.ReadAll(resp.Body)
298 require.NoError(t, err)
Akronfc77b5e2025-06-04 11:44:43 +0200299 assert.Contains(t, string(body), "KoralPipe-TermMapper")
Akron40aaa632025-06-03 17:57:52 +0200300
Akron49ceeb42025-05-23 17:46:01 +0200301}