// Copyright 2026 Alibaba Group // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. package app import ( "fmt" "net/http" "net/http/httptest" "os" "sync/atomic" "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/plugin" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/transport" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/mcptypes" ) func isolatePluginRuntime(t *testing.T) { t.Helper() dynamicMu.Lock() previousEndpoints := dynamicEndpoints previousProducts := dynamicProducts previousAliases := dynamicAliases previousToolEndpoints := dynamicToolEndpoints dynamicEndpoints = nil dynamicProducts = nil dynamicAliases = nil dynamicToolEndpoints = nil dynamicMu.Unlock() stdioMu.Lock() previousStdio := stdioClients stdioClients = make(map[string]*transport.StdioClient) stdioMu.Unlock() t.Cleanup(func() { StopAllStdioClients() dynamicMu.Lock() dynamicEndpoints = previousEndpoints dynamicProducts = previousProducts dynamicAliases = previousAliases dynamicToolEndpoints = previousToolEndpoints dynamicMu.Unlock() stdioMu.Lock() stdioClients = previousStdio stdioMu.Unlock() }) } func TestRegisterPluginHTTPServerDoesNotProbeEndpoint(t *testing.T) { isolatePluginRuntime(t) var calls atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { calls.Add(1) })) defer server.Close() registerPluginHTTPServer(mcptypes.ServerDescriptor{ Key: "offline-http", Endpoint: server.URL, CLI: mcptypes.CLIOverlay{ ID: "offline-http", Command: "offline-http", }, }) if got := calls.Load(); got != 0 { t.Fatalf("plugin endpoint calls during registration = %d, want 0", got) } if endpoint, ok := directRuntimeEndpoint("offline-http", ""); !ok || endpoint != server.URL { t.Fatalf("registered endpoint = (%q, %v), want (%q, true)", endpoint, ok, server.URL) } } func TestRegisterStdioServerFromManifestDoesNotStartProcess(t *testing.T) { isolatePluginRuntime(t) marker := t.TempDir() + "/started" client := transport.NewStdioClient("/bin/sh", []string{ "-c", fmt.Sprintf("printf started > %q", marker), }, nil) p := &plugin.Plugin{ Manifest: plugin.Manifest{Name: "lazy-stdio", Description: "lazy stdio test"}, Root: t.TempDir(), } descriptor := registerStdioServerFromManifest(p, plugin.StdioServerClient{Key: "local", Client: client}) if _, err := os.Stat(marker); !os.IsNotExist(err) { t.Fatalf("stdio process started during registration: stat error = %v", err) } if descriptor.Endpoint != StdioEndpoint("lazy-stdio", "local") { t.Fatalf("descriptor endpoint = %q", descriptor.Endpoint) } if _, ok := LookupStdioClient("lazy-stdio/local"); !ok { t.Fatal("stdio client was not registered for lazy execution") } }