Add integration tests
Change-Id: If46e6ddd7713fe53d5e83bb8a3c4ed01eb3b13fb
diff --git a/mcp/integration_test.go b/mcp/integration_test.go
new file mode 100644
index 0000000..bc04434
--- /dev/null
+++ b/mcp/integration_test.go
@@ -0,0 +1,276 @@
+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
+ }
+}
diff --git a/validation/validator_test.go b/validation/validator_test.go
index 88d01a2..147865c 100644
--- a/validation/validator_test.go
+++ b/validation/validator_test.go
@@ -895,8 +895,3 @@
})
}
}
-
-// Helper function to create bool pointers
-func boolPtr(b bool) *bool {
- return &b
-}