blob: bc044346f23584682eabbbaa270e615f98a877cd [file] [log] [blame]
package mcp_test
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/korap/korap-mcp/config"
"github.com/korap/korap-mcp/logger"
"github.com/korap/korap-mcp/mcp"
"github.com/korap/korap-mcp/service"
"github.com/korap/korap-mcp/tools"
"github.com/korap/korap-mcp/validation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// MockKorAPServer provides mock KorAP responses for integration testing
type MockKorAPServer struct {
server *httptest.Server
requests []string
}
func NewMockKorAPServer() *MockKorAPServer {
mock := &MockKorAPServer{
requests: make([]string, 0),
}
mock.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Record the request
mock.requests = append(mock.requests, fmt.Sprintf("%s %s", r.Method, r.URL.Path))
// Handle different endpoints
switch {
case r.URL.Path == "/oauth2/token":
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"access_token": "mock_access_token",
"token_type": "Bearer",
"expires_in": 3600,
}
json.NewEncoder(w).Encode(response)
case r.URL.Path == "/search":
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"meta": map[string]interface{}{
"totalResults": 42,
"startIndex": 0,
"itemsPerPage": 25,
},
"matches": []map[string]interface{}{
{
"textSigle": "GOE/AGI/00000",
"corpusSigle": "GOE",
"title": "Test Document",
"snippet": "Test <mark>query</mark> result",
},
},
}
json.NewEncoder(w).Encode(response)
case r.URL.Path == "/corpus":
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"meta": map[string]interface{}{"totalResults": 2},
"@type": "koral:docGroup",
"operands": []map[string]interface{}{
{"key": "corpusSigle", "value": "GOE", "title": "Goethe-Korpus"},
{"key": "corpusSigle", "value": "WPD", "title": "Wikipedia-Korpus"},
},
}
json.NewEncoder(w).Encode(response)
case strings.HasPrefix(r.URL.Path, "/corpus/") && strings.HasSuffix(r.URL.Path, "/statistics"):
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"key": "corpusSigle",
"value": "test",
"tokens": 11823,
"sentences": 2156,
}
json.NewEncoder(w).Encode(response)
default:
http.NotFound(w, r)
}
}))
return mock
}
func (m *MockKorAPServer) URL() string {
return m.server.URL
}
func (m *MockKorAPServer) Close() {
m.server.Close()
}
func (m *MockKorAPServer) GetRequests() []string {
return m.requests
}
func (m *MockKorAPServer) Reset() {
m.requests = make([]string, 0)
}
func TestIntegration_ComponentSetup(t *testing.T) {
// Test that all main components can be created properly
logConfig := &config.LoggingConfig{
Level: "info",
Format: "json",
}
testLogger := logger.GetLogger(logConfig)
// Test registry
registry := tools.NewRegistry()
assert.NotNil(t, registry)
assert.Equal(t, 0, registry.Count())
// Test validator
validator := validation.New(testLogger)
assert.NotNil(t, validator)
// Test MCP server
mcpServer := mcp.NewServer("korap-mcp-server", "1.0.0")
assert.NotNil(t, mcpServer)
name, version := mcpServer.GetServerInfo()
assert.Equal(t, "korap-mcp-server", name)
assert.Equal(t, "1.0.0", version)
}
func TestIntegration_ServiceClientWithMockServer(t *testing.T) {
// Create mock server
mockServer := NewMockKorAPServer()
defer mockServer.Close()
// Create OAuth config
oauthConfig := &config.OAuthConfig{
Enabled: true,
ClientID: "test_client",
ClientSecret: "test_secret",
TokenURL: mockServer.URL() + "/oauth2/token",
Scopes: []string{"read"},
}
// Create service client
client, err := service.NewClient(service.ClientOptions{
BaseURL: mockServer.URL(),
Timeout: 10 * time.Second,
OAuthConfig: oauthConfig,
})
require.NoError(t, err)
assert.NotNil(t, client)
// Test authentication
err = client.AuthenticateWithClientCredentials(context.Background())
assert.NoError(t, err)
assert.True(t, client.IsAuthenticated())
// Verify OAuth request was made
requests := mockServer.GetRequests()
assert.Contains(t, requests, "POST /oauth2/token")
}
func TestIntegration_ToolsWithMockServer(t *testing.T) {
// Create mock server
mockServer := NewMockKorAPServer()
defer mockServer.Close()
// Create service client
oauthConfig := &config.OAuthConfig{
Enabled: true,
ClientID: "test_client",
ClientSecret: "test_secret",
TokenURL: mockServer.URL() + "/oauth2/token",
Scopes: []string{"read"},
}
client, err := service.NewClient(service.ClientOptions{
BaseURL: mockServer.URL(),
Timeout: 10 * time.Second,
OAuthConfig: oauthConfig,
})
require.NoError(t, err)
// Create registry and tools
registry := tools.NewRegistry()
searchTool := tools.NewSearchTool(client)
metadataTool := tools.NewMetadataTool(client)
err = registry.Register(searchTool)
assert.NoError(t, err)
err = registry.Register(metadataTool)
assert.NoError(t, err)
// Verify tools were registered
assert.Equal(t, 2, registry.Count())
toolNames := registry.Names()
assert.Contains(t, toolNames, "korap_search")
assert.Contains(t, toolNames, "korap_metadata")
// Test tool retrieval
tool, exists := registry.Get("korap_search")
assert.True(t, exists)
assert.NotNil(t, tool)
assert.Equal(t, "korap_search", tool.Name())
assert.Contains(t, tool.Description(), "KorAP")
assert.NotNil(t, tool.InputSchema())
// Test MCP tool listing
mcpTools := registry.List()
assert.Len(t, mcpTools, 2)
}
func TestIntegration_ConcurrentAccess(t *testing.T) {
// Create mock server
mockServer := NewMockKorAPServer()
defer mockServer.Close()
// Create service client
oauthConfig := &config.OAuthConfig{
Enabled: true,
ClientID: "test_client",
ClientSecret: "test_secret",
TokenURL: mockServer.URL() + "/oauth2/token",
Scopes: []string{"read"},
}
client, err := service.NewClient(service.ClientOptions{
BaseURL: mockServer.URL(),
Timeout: 10 * time.Second,
OAuthConfig: oauthConfig,
})
require.NoError(t, err)
// Create registry with tools
registry := tools.NewRegistry()
searchTool := tools.NewSearchTool(client)
err = registry.Register(searchTool)
require.NoError(t, err)
// Test concurrent access to registry
const numGoroutines = 10
done := make(chan bool, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(index int) {
defer func() { done <- true }()
// Test registry operations
assert.Equal(t, 1, registry.Count())
toolNames := registry.Names()
assert.Contains(t, toolNames, "korap_search")
tool, exists := registry.Get("korap_search")
assert.True(t, exists)
assert.NotNil(t, tool)
}(i)
}
// Wait for all goroutines to complete
for range numGoroutines {
<-done
}
}