package adminapi import ( "context" "strings" "sync" "testing" "gitea.mynodes.xyz/ilker/llmgw/internal/mcpclient/mcptest" "gitea.mynodes.xyz/ilker/llmgw/internal/secrets" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) type memMCP struct { mu sync.Mutex servers map[string]store.MCPServer secrets map[string]string } func (m *memMCP) MCPServers(context.Context) ([]store.MCPServer, error) { m.mu.Lock() defer m.mu.Unlock() var out []store.MCPServer for _, s := range m.servers { out = append(out, s) } return out, nil } func (m *memMCP) MCPServer(_ context.Context, name string) (store.MCPServer, error) { m.mu.Lock() defer m.mu.Unlock() s, ok := m.servers[name] if !ok { return s, store.ErrNotFound } return s, nil } func (m *memMCP) PutMCPServer(_ context.Context, s store.MCPServer) error { m.mu.Lock() defer m.mu.Unlock() m.servers[s.Name] = s return nil } func (m *memMCP) DeleteMCPServer(_ context.Context, name string) error { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.servers[name]; !ok { return store.ErrNotFound } delete(m.servers, name) return nil } func (m *memMCP) SetClusterSecret(_ context.Context, name, value string) error { m.mu.Lock() defer m.mu.Unlock() m.secrets[name] = value return nil } func TestAnMCPServerIsRegisteredWithItsTokenKeptAsASecretAndTried(t *testing.T) { srv := mcptest.New() defer srv.Close() a, _ := setup(t, testModels()) st := &memMCP{servers: map[string]store.MCPServer{}, secrets: map[string]string{}} a.SetMCP(st) secrets.SetStore(func(_ context.Context, name string) (string, error) { st.mu.Lock() defer st.mu.Unlock() return st.secrets[name], nil }) t.Cleanup(func() { secrets.SetStore(nil) }) var v MCPServerView body := `{"url":"` + srv.URL + `","description":"tickets","headers":{"X-Org":"acme"},"secrets":{"Authorization":"Bearer t0ken"}}` if code := call(t, a, "PUT", "/api/v1/mcp/servers/jira", body, &v); code != 200 { t.Fatalf("add: %d", code) } // The token is in the store, named by reference; the row has none. if st.secrets["mcp/jira/Authorization"] != "Bearer t0ken" || v.HeaderRefs["Authorization"] != "store:mcp/jira/Authorization" || strings.Contains(v.Headers["Authorization"], "t0ken") || !v.Enabled { t.Errorf("view %+v, secrets %v", v, st.secrets) } var tv MCPTestView if code := call(t, a, "POST", "/api/v1/mcp/servers/jira/test", "", &tv); code != 200 || !tv.OK || len(tv.Tools) != 3 || tv.Tools[1] != "jira.echo" { t.Errorf("test: %d %+v", code, tv) } if code := call(t, a, "PUT", "/api/v1/mcp/servers/Bad.Name", body, nil); code != 400 { t.Errorf("a name with a dot: %d", code) } if code := call(t, a, "PUT", "/api/v1/mcp/servers/x", `{"url":"ftp://x"}`, nil); code != 400 { t.Errorf("not an http address: %d", code) } // Switched off, kept; then removed. if code := call(t, a, "PUT", "/api/v1/mcp/servers/jira", `{"enabled":false}`, &v); code != 200 || v.Enabled || v.URL != srv.URL { t.Errorf("off: %d %+v", code, v) } if code := call(t, a, "DELETE", "/api/v1/mcp/servers/jira", "", nil); code != 200 { t.Errorf("remove: %d", code) } if code := call(t, a, "DELETE", "/api/v1/mcp/servers/jira", "", nil); code != 404 { t.Errorf("remove again: %d", code) } }