blob: bc044346f23584682eabbbaa270e615f98a877cd [file] [log] [blame]
Akron7d373c42025-06-17 15:03:15 +02001package mcp_test
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11 "time"
12
13 "github.com/korap/korap-mcp/config"
14 "github.com/korap/korap-mcp/logger"
15 "github.com/korap/korap-mcp/mcp"
16 "github.com/korap/korap-mcp/service"
17 "github.com/korap/korap-mcp/tools"
18 "github.com/korap/korap-mcp/validation"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
21)
22
23// MockKorAPServer provides mock KorAP responses for integration testing
24type MockKorAPServer struct {
25 server *httptest.Server
26 requests []string
27}
28
29func NewMockKorAPServer() *MockKorAPServer {
30 mock := &MockKorAPServer{
31 requests: make([]string, 0),
32 }
33
34 mock.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
35 // Record the request
36 mock.requests = append(mock.requests, fmt.Sprintf("%s %s", r.Method, r.URL.Path))
37
38 // Handle different endpoints
39 switch {
40 case r.URL.Path == "/oauth2/token":
41 w.Header().Set("Content-Type", "application/json")
42 response := map[string]interface{}{
43 "access_token": "mock_access_token",
44 "token_type": "Bearer",
45 "expires_in": 3600,
46 }
47 json.NewEncoder(w).Encode(response)
48
49 case r.URL.Path == "/search":
50 w.Header().Set("Content-Type", "application/json")
51 response := map[string]interface{}{
52 "meta": map[string]interface{}{
53 "totalResults": 42,
54 "startIndex": 0,
55 "itemsPerPage": 25,
56 },
57 "matches": []map[string]interface{}{
58 {
59 "textSigle": "GOE/AGI/00000",
60 "corpusSigle": "GOE",
61 "title": "Test Document",
62 "snippet": "Test <mark>query</mark> result",
63 },
64 },
65 }
66 json.NewEncoder(w).Encode(response)
67
68 case r.URL.Path == "/corpus":
69 w.Header().Set("Content-Type", "application/json")
70 response := map[string]interface{}{
71 "meta": map[string]interface{}{"totalResults": 2},
72 "@type": "koral:docGroup",
73 "operands": []map[string]interface{}{
74 {"key": "corpusSigle", "value": "GOE", "title": "Goethe-Korpus"},
75 {"key": "corpusSigle", "value": "WPD", "title": "Wikipedia-Korpus"},
76 },
77 }
78 json.NewEncoder(w).Encode(response)
79
80 case strings.HasPrefix(r.URL.Path, "/corpus/") && strings.HasSuffix(r.URL.Path, "/statistics"):
81 w.Header().Set("Content-Type", "application/json")
82 response := map[string]interface{}{
83 "key": "corpusSigle",
84 "value": "test",
85 "tokens": 11823,
86 "sentences": 2156,
87 }
88 json.NewEncoder(w).Encode(response)
89
90 default:
91 http.NotFound(w, r)
92 }
93 }))
94
95 return mock
96}
97
98func (m *MockKorAPServer) URL() string {
99 return m.server.URL
100}
101
102func (m *MockKorAPServer) Close() {
103 m.server.Close()
104}
105
106func (m *MockKorAPServer) GetRequests() []string {
107 return m.requests
108}
109
110func (m *MockKorAPServer) Reset() {
111 m.requests = make([]string, 0)
112}
113
114func TestIntegration_ComponentSetup(t *testing.T) {
115 // Test that all main components can be created properly
116 logConfig := &config.LoggingConfig{
117 Level: "info",
118 Format: "json",
119 }
120 testLogger := logger.GetLogger(logConfig)
121
122 // Test registry
123 registry := tools.NewRegistry()
124 assert.NotNil(t, registry)
125 assert.Equal(t, 0, registry.Count())
126
127 // Test validator
128 validator := validation.New(testLogger)
129 assert.NotNil(t, validator)
130
131 // Test MCP server
132 mcpServer := mcp.NewServer("korap-mcp-server", "1.0.0")
133 assert.NotNil(t, mcpServer)
134
135 name, version := mcpServer.GetServerInfo()
136 assert.Equal(t, "korap-mcp-server", name)
137 assert.Equal(t, "1.0.0", version)
138}
139
140func TestIntegration_ServiceClientWithMockServer(t *testing.T) {
141 // Create mock server
142 mockServer := NewMockKorAPServer()
143 defer mockServer.Close()
144
145 // Create OAuth config
146 oauthConfig := &config.OAuthConfig{
147 Enabled: true,
148 ClientID: "test_client",
149 ClientSecret: "test_secret",
150 TokenURL: mockServer.URL() + "/oauth2/token",
151 Scopes: []string{"read"},
152 }
153
154 // Create service client
155 client, err := service.NewClient(service.ClientOptions{
156 BaseURL: mockServer.URL(),
157 Timeout: 10 * time.Second,
158 OAuthConfig: oauthConfig,
159 })
160 require.NoError(t, err)
161 assert.NotNil(t, client)
162
163 // Test authentication
164 err = client.AuthenticateWithClientCredentials(context.Background())
165 assert.NoError(t, err)
166 assert.True(t, client.IsAuthenticated())
167
168 // Verify OAuth request was made
169 requests := mockServer.GetRequests()
170 assert.Contains(t, requests, "POST /oauth2/token")
171}
172
173func TestIntegration_ToolsWithMockServer(t *testing.T) {
174 // Create mock server
175 mockServer := NewMockKorAPServer()
176 defer mockServer.Close()
177
178 // Create service client
179 oauthConfig := &config.OAuthConfig{
180 Enabled: true,
181 ClientID: "test_client",
182 ClientSecret: "test_secret",
183 TokenURL: mockServer.URL() + "/oauth2/token",
184 Scopes: []string{"read"},
185 }
186
187 client, err := service.NewClient(service.ClientOptions{
188 BaseURL: mockServer.URL(),
189 Timeout: 10 * time.Second,
190 OAuthConfig: oauthConfig,
191 })
192 require.NoError(t, err)
193
194 // Create registry and tools
195 registry := tools.NewRegistry()
196 searchTool := tools.NewSearchTool(client)
197 metadataTool := tools.NewMetadataTool(client)
198
199 err = registry.Register(searchTool)
200 assert.NoError(t, err)
201
202 err = registry.Register(metadataTool)
203 assert.NoError(t, err)
204
205 // Verify tools were registered
206 assert.Equal(t, 2, registry.Count())
207 toolNames := registry.Names()
208 assert.Contains(t, toolNames, "korap_search")
209 assert.Contains(t, toolNames, "korap_metadata")
210
211 // Test tool retrieval
212 tool, exists := registry.Get("korap_search")
213 assert.True(t, exists)
214 assert.NotNil(t, tool)
215 assert.Equal(t, "korap_search", tool.Name())
216 assert.Contains(t, tool.Description(), "KorAP")
217 assert.NotNil(t, tool.InputSchema())
218
219 // Test MCP tool listing
220 mcpTools := registry.List()
221 assert.Len(t, mcpTools, 2)
222}
223
224func TestIntegration_ConcurrentAccess(t *testing.T) {
225 // Create mock server
226 mockServer := NewMockKorAPServer()
227 defer mockServer.Close()
228
229 // Create service client
230 oauthConfig := &config.OAuthConfig{
231 Enabled: true,
232 ClientID: "test_client",
233 ClientSecret: "test_secret",
234 TokenURL: mockServer.URL() + "/oauth2/token",
235 Scopes: []string{"read"},
236 }
237
238 client, err := service.NewClient(service.ClientOptions{
239 BaseURL: mockServer.URL(),
240 Timeout: 10 * time.Second,
241 OAuthConfig: oauthConfig,
242 })
243 require.NoError(t, err)
244
245 // Create registry with tools
246 registry := tools.NewRegistry()
247 searchTool := tools.NewSearchTool(client)
248
249 err = registry.Register(searchTool)
250 require.NoError(t, err)
251
252 // Test concurrent access to registry
253 const numGoroutines = 10
254 done := make(chan bool, numGoroutines)
255
256 for i := 0; i < numGoroutines; i++ {
257 go func(index int) {
258 defer func() { done <- true }()
259
260 // Test registry operations
261 assert.Equal(t, 1, registry.Count())
262
263 toolNames := registry.Names()
264 assert.Contains(t, toolNames, "korap_search")
265
266 tool, exists := registry.Get("korap_search")
267 assert.True(t, exists)
268 assert.NotNil(t, tool)
269 }(i)
270 }
271
272 // Wait for all goroutines to complete
273 for range numGoroutines {
274 <-done
275 }
276}