55 "encoding/json"
66 "errors"
77 "fmt"
8+ "net/http"
89 "os"
910 "os/exec"
1011 "strconv"
@@ -23,10 +24,18 @@ import (
2324// Note: make sure you ran `task install` before running this test
2425// ------------------------------------------------------------------------------------------------
2526
27+ const (
28+ MCPServerListen = "localhost:50081"
29+ MCPServerDebug = true
30+ ArgoCDMockListen = "localhost:50084"
31+ ArgoCDMockToken = "secure-token"
32+ ArgoCDMockDebug = true
33+ )
34+
2635func TestServer (t * testing.T ) {
2736
2837 // start the argocd mock server
29- cmd := exec .CommandContext (context .Background (), "argocd-mock" )
38+ cmd := exec .CommandContext (context .Background (), "argocd-mock" , "--listen" , ArgoCDMockListen , "--token" , ArgoCDMockToken , "--debug" , strconv . FormatBool ( ArgoCDMockDebug )) //nolint:gosec // (it's ok to use `strconv.FormatBool` )
3039 cmd .Stdout = os .Stdout
3140 cmd .Stderr = os .Stderr
3241 go func () {
@@ -42,23 +51,17 @@ func TestServer(t *testing.T) {
4251 t .Logf ("killed the Argo CD mock server: %v" , cmd .String ())
4352 }()
4453
45- os .Setenv ("MCP_SERVER_LISTEN" , "localhost:50081" )
46- os .Setenv ("MCP_SERVER_DEBUG" , "true" )
47- os .Setenv ("ARGOCD_SERVER_LISTEN" , "localhost:50084" )
48- os .Setenv ("ARGOCD_SERVER_TOKEN" , "secure-token" )
49- os .Setenv ("ARGOCD_SERVER_DEBUG" , "true" )
50-
5154 testdata := []struct {
5255 name string
5356 init func (* testing.T ) (* mcp.ClientSession , KillMCPServerFunc )
5457 }{
5558 {
5659 name : "stdio" ,
57- init : newStdioSession ("localhost:50081" , true , "http://localhost:50084" , "secure-token" ),
60+ init : newStdioSession (MCPServerListen , MCPServerDebug , "http://" + ArgoCDMockListen , ArgoCDMockToken ),
5861 },
5962 {
6063 name : "http" ,
61- init : newHTTPSession ("localhost:50081" , true , "http://localhost:50084" , "secure-token" ),
64+ init : newHTTPSession (MCPServerListen , MCPServerDebug , "http://" + ArgoCDMockListen , ArgoCDMockToken ),
6265 },
6366 }
6467
@@ -182,11 +185,11 @@ func TestServer(t *testing.T) {
182185 }{
183186 {
184187 name : "stdio" ,
185- init : newStdioSession ("localhost:50081" , true , "http://localhost:50085" , "another-token" ), // invalid URL and token for the Argo CD server
188+ init : newStdioSession (MCPServerListen , MCPServerDebug , "http://localhost:50085" , "another-token" ), // invalid URL and token for the Argo CD server
186189 },
187190 {
188191 name : "http" ,
189- init : newHTTPSession ("localhost:50081" , true , "http://localhost:50085" , "another-token" ), // invalid URL and token for the Argo CD server
192+ init : newHTTPSession (MCPServerListen , MCPServerDebug , "http://localhost:50085" , "another-token" ), // invalid URL and token for the Argo CD server
190193 },
191194 }
192195
@@ -214,10 +217,10 @@ func TestServer(t *testing.T) {
214217
215218type KillMCPServerFunc func ()
216219
217- func newStdioSession (mcpServerListen string , mcpServerDebug bool , argocdURL string , argocdToken string ) func (* testing.T ) (* mcp.ClientSession , KillMCPServerFunc ) {
220+ func newStdioSession (mcpServerListenPort string , mcpServerDebug bool , argocdURL string , argocdToken string ) func (* testing.T ) (* mcp.ClientSession , KillMCPServerFunc ) {
218221 return func (t * testing.T ) (* mcp.ClientSession , KillMCPServerFunc ) {
219222 ctx := context .Background ()
220- cmd := newServerCmd (ctx , "stdio" , mcpServerListen , strconv .FormatBool (mcpServerDebug ), argocdURL , argocdToken )
223+ cmd := newServerCmd (ctx , "stdio" , mcpServerListenPort , strconv .FormatBool (mcpServerDebug ), argocdURL , argocdToken )
221224 cl := mcp .NewClient (& mcp.Implementation {Name : "e2e-test-client" , Version : "v1.0.0" }, nil )
222225 session , err := cl .Connect (ctx , & mcp.CommandTransport {Command : cmd }, nil )
223226 require .NoError (t , err )
@@ -231,6 +234,7 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
231234 return func (t * testing.T ) (* mcp.ClientSession , KillMCPServerFunc ) {
232235 ctx := context .Background ()
233236 cmd := newServerCmd (ctx , "http" , mcpServerListen , strconv .FormatBool (mcpServerDebug ), argocdURL , argocdToken )
237+ cmd .Stderr = os .Stdout
234238 go func () {
235239 t .Logf ("starting the MCP server: %v" , cmd .String ())
236240 if err := cmd .Run (); err != nil {
@@ -241,13 +245,16 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
241245 }
242246 }
243247 }()
244- time .Sleep (5 * time .Second )
248+ t .Logf ("waiting for the MCP server to start" )
249+ err := waitForMCPServer (mcpServerListen )
250+ require .NoError (t , err , "failed to wait for the MCP server to start" )
251+
245252 cl := mcp .NewClient (& mcp.Implementation {Name : "e2e-test-client" , Version : "v1.0.0" }, nil )
246253 session , err := cl .Connect (ctx , & mcp.StreamableClientTransport {
247254 MaxRetries : 5 ,
248- Endpoint : fmt .Sprintf ("http://%s/mcp" , os . Getenv ( "MCP_SERVER_LISTEN" ) ),
255+ Endpoint : fmt .Sprintf ("http://%s/mcp" , mcpServerListen ),
249256 }, nil )
250- require .NoError (t , err )
257+ require .NoError (t , err , "failed to connect to the MCP server" )
251258 return session , func () {
252259 t .Logf ("killing the MCP server" )
253260 if err := cmd .Process .Kill (); err != nil {
@@ -258,6 +265,34 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
258265 }
259266}
260267
268+ func waitForMCPServer (mcpServerListen string ) error {
269+ // wait until the MCP server is ready to accept connections with a timeout of 30 seconds
270+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
271+ defer cancel ()
272+ for {
273+ select {
274+ case <- ctx .Done ():
275+ return fmt .Errorf ("timeout waiting for MCP server to start" )
276+ default :
277+ }
278+ req , err := http .NewRequestWithContext (ctx , "GET" , fmt .Sprintf ("http://%s/health" , mcpServerListen ), nil )
279+ if err != nil {
280+ return fmt .Errorf ("failed to create request: %w" , err )
281+ }
282+ resp , err := http .DefaultClient .Do (req )
283+ if err != nil {
284+ time .Sleep (100 * time .Millisecond )
285+ continue
286+ }
287+ resp .Body .Close ()
288+ if resp .StatusCode == http .StatusOK {
289+ break
290+ }
291+ time .Sleep (100 * time .Millisecond )
292+ }
293+ return nil
294+ }
295+
261296func newServerCmd (ctx context.Context , transport string , mcpServerListen string , mcpServerDebug string , argocdURL string , argocdToken string ) * exec.Cmd {
262297 return exec .CommandContext (ctx ,
263298 "argocd-mcp-server" ,
0 commit comments