Make service endpoint more robust

Change-Id: I7e4048a95f8e9c3837d4831213a1f454c9c20cd4
diff --git a/.gitignore b/.gitignore
index 6752854..3f6d077 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
 .*
 !.gitignore
 !.dockerignore
-testdata
 testdata/sandbox
 examples/
 todo.txt
diff --git a/cmd/termmapper/main.go b/cmd/termmapper/main.go
index 644fa06..4c868b5 100644
--- a/cmd/termmapper/main.go
+++ b/cmd/termmapper/main.go
@@ -2,8 +2,10 @@
 
 import (
 	"fmt"
+	"net/url"
 	"os"
 	"os/signal"
+	"path"
 	"path/filepath"
 	"strings"
 	"syscall"
@@ -350,12 +352,21 @@
     </dl>`
 
 	if data.MapID != "" {
+
+		serviceURL, err := url.Parse(data.ServiceURL)
+		if err != nil {
+			log.Warn().Err(err).Msg("Failed to join URL path")
+		}
+
+		// Use path.Join to normalize the path part
+		serviceURL.Path = path.Join(serviceURL.Path, data.MapID+"/query")
+
 		html += `   <script>
   		<!-- activates/deactivates Mapper. -->
   		  
        let data = {
          'action'  : 'pipe',
-         'service' : '` + data.ServiceURL + `/` + data.MapID + `/query'
+         'service' : '` + serviceURL.String() + `'
        };
 
        function pluginit (p) {
diff --git a/cmd/termmapper/main_test.go b/cmd/termmapper/main_test.go
index 06a8c7e..2d77155 100644
--- a/cmd/termmapper/main_test.go
+++ b/cmd/termmapper/main_test.go
@@ -1105,3 +1105,63 @@
 	expectedJSURL := "https://korap.ids-mannheim.de/plugin/termmapper/main-config-mapper/query"
 	assert.Contains(t, htmlContent, "'service' : '"+expectedJSURL+"'")
 }
+
+func TestGenerateKalamarPluginHTMLWithURLJoining(t *testing.T) {
+	tests := []struct {
+		name       string
+		serviceURL string
+		mapID      string
+		expected   string
+	}{
+		{
+			name:       "Service URL without trailing slash",
+			serviceURL: "https://example.com/plugin/termmapper",
+			mapID:      "test-mapper",
+			expected:   "'service' : 'https://example.com/plugin/termmapper/test-mapper/query'",
+		},
+		{
+			name:       "Service URL with trailing slash",
+			serviceURL: "https://example.com/plugin/termmapper/",
+			mapID:      "test-mapper",
+			expected:   "'service' : 'https://example.com/plugin/termmapper/test-mapper/query'",
+		},
+		{
+			name:       "Map ID with leading slash",
+			serviceURL: "https://example.com/plugin/termmapper",
+			mapID:      "/test-mapper",
+			expected:   "'service' : 'https://example.com/plugin/termmapper/test-mapper/query'",
+		},
+		{
+			name:       "Both with slashes",
+			serviceURL: "https://example.com/plugin/termmapper/",
+			mapID:      "/test-mapper",
+			expected:   "'service' : 'https://example.com/plugin/termmapper/test-mapper/query'",
+		},
+		{
+			name:       "Complex map ID",
+			serviceURL: "https://example.com/api/v1/",
+			mapID:      "complex-mapper-name_123",
+			expected:   "'service' : 'https://example.com/api/v1/complex-mapper-name_123/query'",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			data := TemplateData{
+				Title:       "Test Mapper",
+				Version:     "1.0.0",
+				Hash:        "abcd1234",
+				Date:        "2024-01-01",
+				Description: "Test description",
+				Server:      "https://example.com/",
+				SDK:         "https://example.com/js/sdk.js",
+				ServiceURL:  tt.serviceURL,
+				MapID:       tt.mapID,
+				Mappings:    []TemplateMapping{},
+			}
+
+			html := generateKalamarPluginHTML(data)
+			assert.Contains(t, html, tt.expected)
+		})
+	}
+}