test_utils.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. //go:build integration
  2. // +build integration
  3. // Integration test helpers for imgproxy.
  4. // We use regular `go build` instead of Docker to make sure
  5. // tests run in the same environment as other tests,
  6. // including in CI, where everything runs in a custom Docker image
  7. // against the different libvips versions.
  8. package integration
  9. import (
  10. "context"
  11. "fmt"
  12. "io"
  13. "net"
  14. "net/http"
  15. "os"
  16. "os/exec"
  17. "path"
  18. "path/filepath"
  19. "testing"
  20. "time"
  21. "github.com/stretchr/testify/require"
  22. )
  23. const (
  24. buildContext = ".." // Source code folder
  25. binPath = "/tmp/imgproxy-test" // Path to the built imgproxy binary
  26. bindPort = 9090 // Port to bind imgproxy to
  27. bindHost = "127.0.0.1" // Host to bind imgproxy to
  28. )
  29. var (
  30. buildCmd = []string{"build", "-v", "-ldflags=-s -w", "-o", binPath} // imgproxy build command
  31. )
  32. // waitForPort tries to connect to host:port until successful or timeout
  33. func waitForPort(host string, port int, timeout time.Duration) error {
  34. var address string
  35. if net.ParseIP(host) != nil && net.ParseIP(host).To4() == nil {
  36. // IPv6 address, wrap in brackets
  37. address = fmt.Sprintf("[%s]:%d", host, port)
  38. } else {
  39. address = fmt.Sprintf("%s:%d", host, port)
  40. }
  41. deadline := time.Now().Add(timeout)
  42. for time.Now().Before(deadline) {
  43. conn, err := net.DialTimeout("tcp", address, 500*time.Millisecond)
  44. if err == nil {
  45. conn.Close()
  46. return nil // port is open
  47. }
  48. time.Sleep(200 * time.Millisecond)
  49. }
  50. return fmt.Errorf("timeout waiting for port %s", address)
  51. }
  52. func startImgproxy(t *testing.T, ctx context.Context, testImagesPath string) string {
  53. // Build the imgproxy binary
  54. buildCmd := exec.Command("go", buildCmd...)
  55. buildCmd.Dir = buildContext
  56. buildCmd.Env = os.Environ()
  57. buildOut, err := buildCmd.CombinedOutput()
  58. require.NoError(t, err, "failed to build imgproxy: %v\n%s", err, string(buildOut))
  59. // Start imgproxy in the background
  60. cmd := exec.CommandContext(ctx, binPath)
  61. // Set environment variables for imgproxy
  62. cmd.Env = append(os.Environ(), "IMGPROXY_BIND=:"+fmt.Sprintf("%d", bindPort))
  63. cmd.Env = append(cmd.Env, "IMGPROXY_LOCAL_FILESYSTEM_ROOT="+testImagesPath)
  64. cmd.Env = append(cmd.Env, "IMGPROXY_MAX_ANIMATION_FRAMES=999")
  65. cmd.Env = append(cmd.Env, "IMGPROXY_VIPS_LEAK_CHECK=true")
  66. cmd.Env = append(cmd.Env, "IMGPROXY_LOG_MEM_STATS=true")
  67. cmd.Env = append(cmd.Env, "IMGPROXY_DEVELOPMENT_ERRORS_MODE=true")
  68. // That one is for the build logs
  69. stdout, _ := os.CreateTemp("", "imgproxy-stdout-*")
  70. stderr, _ := os.CreateTemp("", "imgproxy-stderr-*")
  71. cmd.Stdout = stdout
  72. cmd.Stderr = stderr
  73. err = cmd.Start()
  74. require.NoError(t, err, "failed to start imgproxy: %v", err)
  75. // Wait for port 8090 to be available
  76. err = waitForPort(bindHost, bindPort, 5*time.Second)
  77. if err != nil {
  78. cmd.Process.Kill()
  79. require.NoError(t, err, "imgproxy did not start in time")
  80. }
  81. // Return a dummy container (nil) and connection string
  82. t.Cleanup(func() {
  83. cmd.Process.Kill()
  84. stdout.Close()
  85. stderr.Close()
  86. os.Remove(stdout.Name())
  87. os.Remove(stderr.Name())
  88. os.Remove(binPath)
  89. })
  90. return fmt.Sprintf("%s:%d", bindHost, bindPort)
  91. }
  92. // fetchImage fetches an image from the imgproxy server
  93. func fetchImage(t *testing.T, cs string, path string) []byte {
  94. url := fmt.Sprintf("http://%s/%s", cs, path)
  95. resp, err := http.Get(url)
  96. require.NoError(t, err, "Failed to fetch image from %s", url)
  97. defer resp.Body.Close()
  98. require.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code 200 OK, got %d, url: %s", resp.StatusCode, url)
  99. bytes, err := io.ReadAll(resp.Body)
  100. require.NoError(t, err, "Failed to read response body from %s", url)
  101. return bytes
  102. }
  103. // testImagesPath returns the absolute path to the test images directory
  104. func testImagesPath(t *testing.T) (string, error) {
  105. // Get current working directory
  106. dir, err := os.Getwd()
  107. require.NoError(t, err)
  108. // Convert to absolute path (if it's not already)
  109. absPath, err := filepath.Abs(dir)
  110. require.NoError(t, err)
  111. return path.Join(absPath, "../testdata/test-images"), nil
  112. }