mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-21 14:11:22 +02:00
IMG-33: adds simple integration tests for image.Load() (#1462)
* Load() integration test * Added test-images as submodule
This commit is contained in:
@@ -31,7 +31,7 @@ ENV GOPATH=$HOME/go
|
||||
ENV PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
||||
|
||||
# gnupg is requiered for clang-format
|
||||
RUN apt-get install -y gnupg lsb-release ssh unzip
|
||||
RUN apt-get install -y gnupg lsb-release ssh
|
||||
|
||||
# Install air
|
||||
RUN go install github.com/air-verse/air@latest
|
||||
|
@@ -28,6 +28,5 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"initializeCommand": "mkdir -p ${localWorkspaceFolder}/.devcontainer/images/ && curl -O -L https://github.com/imgproxy/test-images/archive/refs/heads/main.zip && unzip main.zip -d ${localWorkspaceFolder}/.devcontainer/images/ && rm main.zip && mv ${localWorkspaceFolder}/.devcontainer/images/test-images-main/* ${localWorkspaceFolder}/.devcontainer/images/ && rmdir ${localWorkspaceFolder}/.devcontainer/images/test-images-main",
|
||||
"postCreateCommand": "lefthook install"
|
||||
}
|
||||
|
66
.github/ci-docker/Dockerfile
vendored
66
.github/ci-docker/Dockerfile
vendored
@@ -1,66 +0,0 @@
|
||||
FROM public.ecr.aws/ubuntu/ubuntu:noble
|
||||
|
||||
ARG VIPS_VERSIONS="8.14 8.15 8.16"
|
||||
|
||||
RUN apt-get -qq update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
curl \
|
||||
git \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
gobject-introspection \
|
||||
libgirepository1.0-dev \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
libssl-dev \
|
||||
libglib2.0-dev \
|
||||
libxml2-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev \
|
||||
libwebp-dev \
|
||||
librsvg2-dev \
|
||||
libexif-dev \
|
||||
liblcms2-dev \
|
||||
&& python3 -m venv /root/.python \
|
||||
&& /root/.python/bin/pip install meson ninja \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \
|
||||
&& export PATH="/root/.cargo/bin:$PATH" \
|
||||
&& cargo install cargo-c \
|
||||
&& cd /root \
|
||||
&& git clone --depth 1 https://github.com/DarthSim/quantizr.git \
|
||||
&& cd quantizr \
|
||||
&& cargo cinstall --release --library-type=cdylib --prefix=/usr/local --libdir=/usr/local/lib \
|
||||
&& rm -rf /root/.rustup /root/.cargo
|
||||
|
||||
ENV PATH="/root/.python/bin:$PATH"
|
||||
ENV LD_LIBRARY_PATH="/usr/local/lib"
|
||||
|
||||
RUN \
|
||||
mkdir /root/vips \
|
||||
&& cd /root/vips \
|
||||
&& curl -s -S -L -o vips_releases.json "https://api.github.com/repos/libvips/libvips/releases" \
|
||||
&& for VIPS_VERSION in $VIPS_VERSIONS; do \
|
||||
mkdir $VIPS_VERSION \
|
||||
&& export VIPS_RELEASE=$(grep -m 1 "\"tag_name\": \"v$VIPS_VERSION." vips_releases.json | sed -E 's/.*"v([^"]+)".*/\1/') \
|
||||
&& echo "Building Vips $VIPS_RELEASE as $VIPS_VERSION" \
|
||||
&& curl -s -S -L -o libvips-$VIPS_RELEASE.tar.gz https://github.com/libvips/libvips/archive/refs/tags/v$VIPS_RELEASE.tar.gz \
|
||||
&& tar -xzf libvips-$VIPS_RELEASE.tar.gz \
|
||||
&& cd libvips-$VIPS_RELEASE \
|
||||
&& meson setup _build \
|
||||
--buildtype=release \
|
||||
--strip \
|
||||
--prefix=/root/vips/$VIPS_VERSION \
|
||||
--libdir=lib \
|
||||
-Dgtk_doc=false \
|
||||
&& ninja -C _build \
|
||||
&& ninja -C _build install \
|
||||
&& cd .. \
|
||||
&& rm -rf libvips-$VIPS_RELEASE.tar.gz libvips-$VIPS_RELEASE; \
|
||||
done
|
||||
|
||||
WORKDIR /go/src
|
||||
|
||||
ENTRYPOINT [ "/bin/bash" ]
|
4
.github/ci-docker/hooks/push
vendored
4
.github/ci-docker/hooks/push
vendored
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
DATETAG=$(date +%Y%m%d%H%M)
|
||||
docker tag $IMAGE_NAME $DOCKER_REPO:$DATETAG
|
||||
docker push $DOCKER_REPO:$DATETAG
|
44
.github/workflows/build-ci-docker.yml
vendored
44
.github/workflows/build-ci-docker.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: Build CI Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
vips_versions:
|
||||
description: 'Whitespace separated list of libvips versions to build'
|
||||
required: true
|
||||
default: "8.14 8.15 8.16"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate Docker tag
|
||||
id: tag
|
||||
run: echo "tag=ghcr.io/imgproxy/imgproxy-ci:$(date +%Y%m%d%H%M)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./.github/ci-docker/Dockerfile
|
||||
tags: ${{ steps.tag.outputs.tag }}
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
"VIPS_VERSIONS=${{ github.event.inputs.vips_versions }}"
|
||||
push: true
|
57
.github/workflows/ci.yml
vendored
57
.github/workflows/ci.yml
vendored
@@ -5,44 +5,54 @@ on:
|
||||
|
||||
env:
|
||||
CGO_LDFLAGS_ALLOW: "-s|-w"
|
||||
PKG_CONFIG_LIBDIR: /opt/imgproxy/lib/pkgconfig
|
||||
LD_LIBRARY_PATH: /opt/imgproxy/lib
|
||||
GOFLAGS: -buildvcs=false
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/imgproxy/imgproxy-ci:202410292002
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.23.x", "1.22.x", "1.21.x"]
|
||||
vips-version: ["8.16", "8.15", "8.14"]
|
||||
image: ghcr.io/imgproxy/imgproxy-base:latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
submodules: true
|
||||
- name: Setup cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Download mods
|
||||
run: go mod download
|
||||
- name: Mark git workspace as safe
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
env:
|
||||
LD_LIBRARY_PATH: "/usr/local/lib:/root/vips/${{ matrix.vips-version }}/lib"
|
||||
PKG_CONFIG_PATH: "/usr/local/lib/pkgconfig:/root/vips/${{ matrix.vips-version }}/lib/pkgconfig"
|
||||
run: go test -tags integration ./...
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/imgproxy/imgproxy-ci:202410292002
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.23.x"]
|
||||
vips-version: ["8.16"]
|
||||
image: ghcr.io/imgproxy/imgproxy-base:latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
submodules: true
|
||||
- name: Setup cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Download mods
|
||||
run: go mod download
|
||||
- name: Lint
|
||||
@@ -51,20 +61,25 @@ jobs:
|
||||
version: v2.1.6
|
||||
args: --timeout 10m0s
|
||||
env:
|
||||
LD_LIBRARY_PATH: "/usr/local/lib:/root/vips/${{ matrix.vips-version }}/lib"
|
||||
PKG_CONFIG_PATH: "/usr/local/lib/pkgconfig:/root/vips/${{ matrix.vips-version }}/lib/pkgconfig"
|
||||
PKG_CONFIG_LIBDIR: /opt/imgproxy/lib/pkgconfig
|
||||
GOFLAGS: -buildvcs=false
|
||||
|
||||
c-lint:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: cpp-linter/cpp-linter-action@v2
|
||||
id: linter
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
style: file
|
||||
version: 18 # Ubuntu 24.04 provides clang-format-18
|
||||
tidy-checks: '-*' # disable clang-tidy
|
||||
tidy-checks: "-*" # disable clang-tidy
|
||||
|
||||
- name: Fail fast
|
||||
continue-on-error: true # TODO: remove this line in the future
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,5 +8,4 @@ tmp/
|
||||
docker-base
|
||||
docs/sitemap.txt
|
||||
.env
|
||||
.devcontainer/images/*
|
||||
k6/*.json
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "testdata/test-images"]
|
||||
path = testdata/test-images
|
||||
url = git@github.com:imgproxy/test-images.git
|
@@ -14,4 +14,4 @@ fi
|
||||
export CGO_LDFLAGS_ALLOW="-s|-w"
|
||||
export CGO_CFLAGS_ALLOW="-I|-Xpreprocessor"
|
||||
|
||||
golangci-lint run
|
||||
golangci-lint --build-tags integration run
|
||||
|
2
go.mod
2
go.mod
@@ -25,6 +25,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.60.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0
|
||||
github.com/bugsnag/bugsnag-go/v2 v2.5.1
|
||||
github.com/corona10/goimagehash v1.1.0
|
||||
github.com/felixge/httpsnoop v1.0.4
|
||||
github.com/fsouza/fake-gcs-server v1.42.2
|
||||
github.com/getsentry/sentry-go v0.34.1
|
||||
@@ -160,6 +161,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
|
4
go.sum
4
go.sum
@@ -170,6 +170,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI=
|
||||
github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -342,6 +344,8 @@ github.com/newrelic/go-agent/v3 v3.39.0 h1:VVhsJR422oOxU/sJ1HZrop/OC7G1GTClIviVJ
|
||||
github.com/newrelic/go-agent/v3 v3.39.0/go.mod h1:4QXvru0vVy/iu7mfkNHT7T2+9TC9zPGO8aUEdKqY138=
|
||||
github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1 h1:6OX5VXMuj2salqNBc41eXKz6K+nV6OB/hhlGnAKCbwU=
|
||||
github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1/go.mod h1:2kY6OeOxrJ+RIQlVjWDc/pZlT3MIf30prs6drzMfJ6E=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
|
141
integration/load_test.go
Normal file
141
integration/load_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/corona10/goimagehash"
|
||||
"github.com/imgproxy/imgproxy/v3/imagetype"
|
||||
"github.com/imgproxy/imgproxy/v3/vips"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
similarityThreshold = 5 // Distance between images to be considered similar
|
||||
)
|
||||
|
||||
// testLoadFolder fetches images iterates over images in the specified folder,
|
||||
// runs imgproxy on each image, and compares the result with the reference image
|
||||
// which is expected to be in the `integration` folder with the same name
|
||||
// but with `.png` extension.
|
||||
func testLoadFolder(t *testing.T, cs, sourcePath, folder string) {
|
||||
t.Logf("Testing folder: %s", folder)
|
||||
|
||||
walkPath := path.Join(sourcePath, folder)
|
||||
|
||||
// Iterate over the files in the source folder
|
||||
err := filepath.Walk(walkPath, func(path string, info os.FileInfo, err error) error {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Skip directories
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get the base name of the file (8-bpp.png)
|
||||
basePath := filepath.Base(path)
|
||||
|
||||
// Replace the extension with .png
|
||||
referencePath := strings.TrimSuffix(basePath, filepath.Ext(basePath)) + ".png"
|
||||
|
||||
// Construct the full path to the reference image (integration/ folder)
|
||||
referencePath = filepath.Join(sourcePath, "integration", folder, referencePath)
|
||||
|
||||
// Construct the source URL for imgproxy (no processing)
|
||||
sourceUrl := fmt.Sprintf("insecure/plain/local:///%s/%s@png", folder, basePath)
|
||||
|
||||
imgproxyImageBytes := fetchImage(t, cs, sourceUrl)
|
||||
imgproxyImage, err := png.Decode(bytes.NewReader(imgproxyImageBytes))
|
||||
require.NoError(t, err, "Failed to decode PNG image from imgproxy for %s", basePath)
|
||||
|
||||
referenceFile, err := os.Open(referencePath)
|
||||
require.NoError(t, err)
|
||||
defer referenceFile.Close()
|
||||
|
||||
referenceImage, err := png.Decode(referenceFile)
|
||||
require.NoError(t, err, "Failed to decode PNG reference image for %s", referencePath)
|
||||
|
||||
hash1, err := goimagehash.DifferenceHash(imgproxyImage)
|
||||
require.NoError(t, err)
|
||||
|
||||
hash2, err := goimagehash.DifferenceHash(referenceImage)
|
||||
require.NoError(t, err)
|
||||
|
||||
distance, err := hash1.Distance(hash2)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.LessOrEqual(t, distance, similarityThreshold,
|
||||
"Image %s differs from reference image %s by %d, which is greater than the allowed threshold of %d",
|
||||
basePath, referencePath, distance, similarityThreshold)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestLoadSaveToPng ensures that our load pipeline works,
|
||||
// including standard and custom loaders. For each source image
|
||||
// in the folder, it does the passthrough request through imgproxy:
|
||||
// no processing, just convert format of the source file to png.
|
||||
// Then, it compares the result with the reference image.
|
||||
func TestLoadSaveToPng(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// TODO: Will be moved to test suite (like in processing_test.go)
|
||||
// Since we use SupportsLoad, we need to initialize vips
|
||||
defer vips.Shutdown() // either way it needs to be deinitialized
|
||||
err := vips.Init()
|
||||
require.NoError(t, err, "Failed to initialize vips")
|
||||
|
||||
path, err := testImagesPath(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
cs := startImgproxy(t, ctx, path)
|
||||
|
||||
if vips.SupportsLoad(imagetype.GIF) {
|
||||
testLoadFolder(t, cs, path, "gif")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.JPEG) {
|
||||
testLoadFolder(t, cs, path, "jpg")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.HEIC) {
|
||||
testLoadFolder(t, cs, path, "heif")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.JXL) {
|
||||
testLoadFolder(t, cs, path, "jxl")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.SVG) {
|
||||
testLoadFolder(t, cs, path, "svg")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.TIFF) {
|
||||
testLoadFolder(t, cs, path, "tiff")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.WEBP) {
|
||||
testLoadFolder(t, cs, path, "webp")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.BMP) {
|
||||
testLoadFolder(t, cs, path, "bmp")
|
||||
}
|
||||
|
||||
if vips.SupportsLoad(imagetype.ICO) {
|
||||
testLoadFolder(t, cs, path, "ico")
|
||||
}
|
||||
}
|
137
integration/test_utils.go
Normal file
137
integration/test_utils.go
Normal file
@@ -0,0 +1,137 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
// Integration test helpers for imgproxy.
|
||||
// We use regular `go build` instead of Docker to make sure
|
||||
// tests run in the same environment as other tests,
|
||||
// including in CI, where everything runs in a custom Docker image
|
||||
// against the different libvips versions.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
buildContext = ".." // Source code folder
|
||||
binPath = "/tmp/imgproxy-test" // Path to the built imgproxy binary
|
||||
bindPort = 9090 // Port to bind imgproxy to
|
||||
bindHost = "127.0.0.1" // Host to bind imgproxy to
|
||||
)
|
||||
|
||||
var (
|
||||
buildCmd = []string{"build", "-v", "-ldflags=-s -w", "-o", binPath} // imgproxy build command
|
||||
)
|
||||
|
||||
// waitForPort tries to connect to host:port until successful or timeout
|
||||
func waitForPort(host string, port int, timeout time.Duration) error {
|
||||
var address string
|
||||
if net.ParseIP(host) != nil && net.ParseIP(host).To4() == nil {
|
||||
// IPv6 address, wrap in brackets
|
||||
address = fmt.Sprintf("[%s]:%d", host, port)
|
||||
} else {
|
||||
address = fmt.Sprintf("%s:%d", host, port)
|
||||
}
|
||||
deadline := time.Now().Add(timeout)
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
conn, err := net.DialTimeout("tcp", address, 500*time.Millisecond)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
return nil // port is open
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
return fmt.Errorf("timeout waiting for port %s", address)
|
||||
}
|
||||
|
||||
func startImgproxy(t *testing.T, ctx context.Context, testImagesPath string) string {
|
||||
// Build the imgproxy binary
|
||||
buildCmd := exec.Command("go", buildCmd...)
|
||||
buildCmd.Dir = buildContext
|
||||
buildCmd.Env = os.Environ()
|
||||
buildOut, err := buildCmd.CombinedOutput()
|
||||
require.NoError(t, err, "failed to build imgproxy: %v\n%s", err, string(buildOut))
|
||||
|
||||
// Start imgproxy in the background
|
||||
cmd := exec.CommandContext(ctx, binPath)
|
||||
|
||||
// Set environment variables for imgproxy
|
||||
cmd.Env = append(os.Environ(), "IMGPROXY_BIND=:"+fmt.Sprintf("%d", bindPort))
|
||||
cmd.Env = append(cmd.Env, "IMGPROXY_LOCAL_FILESYSTEM_ROOT="+testImagesPath)
|
||||
cmd.Env = append(cmd.Env, "IMGPROXY_MAX_ANIMATION_FRAMES=999")
|
||||
cmd.Env = append(cmd.Env, "IMGPROXY_VIPS_LEAK_CHECK=true")
|
||||
cmd.Env = append(cmd.Env, "IMGPROXY_LOG_MEM_STATS=true")
|
||||
cmd.Env = append(cmd.Env, "IMGPROXY_DEVELOPMENT_ERRORS_MODE=true")
|
||||
|
||||
// That one is for the build logs
|
||||
stdout, _ := os.CreateTemp("", "imgproxy-stdout-*")
|
||||
stderr, _ := os.CreateTemp("", "imgproxy-stderr-*")
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
err = cmd.Start()
|
||||
require.NoError(t, err, "failed to start imgproxy: %v", err)
|
||||
|
||||
// Wait for port 8090 to be available
|
||||
err = waitForPort(bindHost, bindPort, 5*time.Second)
|
||||
if err != nil {
|
||||
cmd.Process.Kill()
|
||||
require.NoError(t, err, "imgproxy did not start in time")
|
||||
}
|
||||
|
||||
// Return a dummy container (nil) and connection string
|
||||
t.Cleanup(func() {
|
||||
cmd.Process.Kill()
|
||||
stdout.Close()
|
||||
stderr.Close()
|
||||
os.Remove(stdout.Name())
|
||||
os.Remove(stderr.Name())
|
||||
os.Remove(binPath)
|
||||
})
|
||||
|
||||
return fmt.Sprintf("%s:%d", bindHost, bindPort)
|
||||
}
|
||||
|
||||
// fetchImage fetches an image from the imgproxy server
|
||||
func fetchImage(t *testing.T, cs string, path string) []byte {
|
||||
url := fmt.Sprintf("http://%s/%s", cs, path)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
require.NoError(t, err, "Failed to fetch image from %s", url)
|
||||
defer resp.Body.Close()
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code 200 OK, got %d, url: %s", resp.StatusCode, url)
|
||||
|
||||
bytes, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err, "Failed to read response body from %s", url)
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
// testImagesPath returns the absolute path to the test images directory
|
||||
func testImagesPath(t *testing.T) (string, error) {
|
||||
// Get current working directory
|
||||
dir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Convert to absolute path (if it's not already)
|
||||
absPath, err := filepath.Abs(dir)
|
||||
require.NoError(t, err)
|
||||
|
||||
return path.Join(absPath, "../testdata/test-images"), nil
|
||||
}
|
6
main.go
6
main.go
@@ -84,7 +84,7 @@ func shutdown() {
|
||||
errorreport.Close()
|
||||
}
|
||||
|
||||
func run() error {
|
||||
func run(ctx context.Context) error {
|
||||
if err := initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,7 +103,7 @@ func run() error {
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
if err := prometheus.StartServer(cancel); err != nil {
|
||||
return err
|
||||
@@ -137,7 +137,7 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if err := run(); err != nil {
|
||||
if err := run(context.Background()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
1
testdata/test-images
vendored
Submodule
1
testdata/test-images
vendored
Submodule
Submodule testdata/test-images added at 9bee50dcc1
Reference in New Issue
Block a user