costmodel_test.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package costmodel
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "testing"
  7. "time"
  8. "github.com/opencost/opencost/pkg/costmodel"
  9. "github.com/opencost/opencost/pkg/env"
  10. )
  11. func TestMCPServerGracefulShutdown(t *testing.T) {
  12. // Test that MCP server responds to context cancellation and shuts down gracefully
  13. ctx, cancel := context.WithCancel(context.Background())
  14. defer cancel()
  15. accesses := &costmodel.Accesses{}
  16. port := env.GetMCPHTTPPort()
  17. // Channel to signal when server is ready
  18. serverReady := make(chan error, 1)
  19. // Start MCP server
  20. go func() {
  21. err := StartMCPServer(ctx, accesses, nil)
  22. serverReady <- err
  23. }()
  24. // Wait for server to be ready by attempting to connect
  25. serverUp := false
  26. for i := 0; i < 10; i++ {
  27. time.Sleep(100 * time.Millisecond)
  28. client := &http.Client{Timeout: 1 * time.Second}
  29. resp, err := client.Get(fmt.Sprintf("http://localhost:%d/", port))
  30. if err == nil {
  31. resp.Body.Close()
  32. serverUp = true
  33. break
  34. }
  35. }
  36. if !serverUp {
  37. t.Skip("MCP server did not start (may be expected in test environment)")
  38. }
  39. // Trigger shutdown by cancelling context
  40. shutdownStart := time.Now()
  41. cancel()
  42. // Wait for shutdown to complete (with reasonable timeout)
  43. shutdownDone := make(chan bool, 1)
  44. go func() {
  45. time.Sleep(15 * time.Second)
  46. shutdownDone <- false
  47. }()
  48. // Give shutdown goroutine time to execute
  49. time.Sleep(1 * time.Second)
  50. // Verify server is no longer accepting connections
  51. client := &http.Client{Timeout: 1 * time.Second}
  52. _, err := client.Get(fmt.Sprintf("http://localhost:%d/", port))
  53. if err == nil {
  54. t.Error("Server still accepting connections after shutdown")
  55. }
  56. shutdownDone <- true
  57. <-shutdownDone
  58. shutdownDuration := time.Since(shutdownStart)
  59. t.Logf("Graceful shutdown completed in %v", shutdownDuration)
  60. // Verify shutdown completed in reasonable time (should be much less than 12s)
  61. if shutdownDuration > 12*time.Second {
  62. t.Errorf("Shutdown took too long: %v (expected < 12s)", shutdownDuration)
  63. }
  64. }