From 1f42b00573a40989f03025e46c624054c7a140c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 10:49:35 +0200 Subject: [PATCH 01/22] fix(benchmarks): re-add redis install for ruedis and valkey --- .github/workflows/benchmark.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index eb467c2d..eea33cc6 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -84,6 +84,19 @@ jobs: docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.5 sleep 30 + - name: Setup Redis + if: ${{ matrix.package == 'rueidis' || matrix.package == 'valkey' }} + uses: shogo82148/actions-setup-redis@v1 + with: + redis-version: '7.x' + auto-start: 'false' + + - name: Run Redis + if: ${{ matrix.package == 'rueidis' || matrix.package == 'valkey' }} + run: | + redis-server --port 6379 & + sleep 15 + - name: Install etcd if: ${{ matrix.package == 'etcd' }} run: | From b90664baad67503a39ecc607e9cdf142cdf3d949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 12:37:39 +0200 Subject: [PATCH 02/22] feat: add a test module for testing redis and friends --- testhelpers/redis/go.mod | 65 +++++++++++++ testhelpers/redis/go.sum | 194 +++++++++++++++++++++++++++++++++++++ testhelpers/redis/redis.go | 153 +++++++++++++++++++++++++++++ 3 files changed, 412 insertions(+) create mode 100644 testhelpers/redis/go.mod create mode 100644 testhelpers/redis/go.sum create mode 100644 testhelpers/redis/redis.go diff --git a/testhelpers/redis/go.mod b/testhelpers/redis/go.mod new file mode 100644 index 00000000..94a7df5c --- /dev/null +++ b/testhelpers/redis/go.mod @@ -0,0 +1,65 @@ +module github.com/gofiber/storage/testhelpers/redis + +go 1.23.0 + +require ( + github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.37.0 + github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 +) + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.0.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mdelapenya/tlscert v0.2.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v4 v4.25.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/testhelpers/redis/go.sum b/testhelpers/redis/go.sum new file mode 100644 index 00000000..b7f31c70 --- /dev/null +++ b/testhelpers/redis/go.sum @@ -0,0 +1,194 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= +github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= +github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= +github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= +github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 h1:9HIY28I9ME/Zmb+zey1p/I1mto5+5ch0wLX+nJdOsQ4= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0/go.mod h1:Abu9g/25Qv+FkYVx3U4Voaynou1c+7D0HIhaQJXvk6E= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go new file mode 100644 index 00000000..2c6dd04f --- /dev/null +++ b/testhelpers/redis/redis.go @@ -0,0 +1,153 @@ +package testredis + +import ( + "context" + "crypto/tls" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/modules/redis" +) + +const ( + // Image is the default image used for running Redis in tests. + Image = "docker.io/redis:7" + ImageEnvVar = "TEST_REDIS_IMAGE" + Port = "6379/tcp" +) + +// Config represents the configuration for a Redis container +type Config struct { + // TLS settings + UseTLS bool + SecureURL bool + MTLSDisabled bool + UseAddress bool + UseHostPort bool + UseURL bool +} + +// Option is a function that configures a Config +type Option func(*Config) + +// WithTLS configures the container to use TLS +func WithTLS(secureURL bool, mtlsDisabled bool) Option { + return func(c *Config) { + c.UseTLS = true + c.SecureURL = secureURL + c.MTLSDisabled = mtlsDisabled + } +} + +// WithAddress sets the container to use address-based connection +func WithAddress() Option { + return func(c *Config) { + c.UseAddress = true + } +} + +// WithHostPort sets the container to use host and port based connection +func WithHostPort() Option { + return func(c *Config) { + c.UseHostPort = true + } +} + +// WithURL sets the container to use a URL +func WithURL(useContainerURI bool) Option { + return func(c *Config) { + c.UseURL = useContainerURI + } +} + +// Container represents a running Redis container +type Container struct { + URL string + Addrs []string + Host string + Port int + TLSConfig *tls.Config +} + +// Start starts a Redis container for testing and returns its configuration +func Start(t testing.TB, opts ...Option) *Container { + t.Helper() + + config := &Config{ + UseURL: true, // by default, use the URL provided by the testcontainer + } + for _, o := range opts { + o(config) + } + + img := Image + if imgFromEnv := os.Getenv(ImageEnvVar); imgFromEnv != "" { + img = imgFromEnv + } + + ctx := context.Background() + tcOpts := []testcontainers.ContainerCustomizer{} + + if config.UseTLS { + tcOpts = append(tcOpts, redis.WithTLS()) + + cmds := []string{ + "--port", "0", + "--tls-port", "6379", + "--tls-cert-file", "/tls/server.crt", + "--tls-key-file", "/tls/server.key", + "--tls-ca-cert-file", "/tls/ca.crt", + } + + cmds = append(cmds, "--tls-auth-clients", func() string { + if config.MTLSDisabled { + return "no" + } + return "yes" + }()) + + tcOpts = append(tcOpts, testcontainers.WithCmd(cmds...)) + } + + c, err := redis.Run(ctx, img, tcOpts...) + testcontainers.CleanupContainer(t, c) + require.NoError(t, err) + + ctr := &Container{ + TLSConfig: c.TLSConfig(), + } + + uri, err := c.ConnectionString(ctx) + require.NoError(t, err) + + if config.UseHostPort { + host, err := c.Host(ctx) + require.NoError(t, err) + + port, err := c.MappedPort(ctx, Port) + require.NoError(t, err) + + ctr.Host = host + ctr.Port = port.Int() + } + + if config.UseAddress { + // trim the schemes from the URI + addr := strings.TrimPrefix(uri, "redis://") + addr = strings.TrimPrefix(addr, "rediss://") + ctr.Addrs = []string{addr} + } + + if config.UseURL { + ctr.URL = uri + if !config.SecureURL { + // Replace the scheme with the insecure one + ctr.URL = strings.Replace(ctr.URL, "rediss://", "redis://", 1) + } + } + + return ctr +} From 76a7b0a65770d31c9e4e5709af94903238f6d481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:18:19 +0200 Subject: [PATCH 03/22] chore: support for overriding the redis image --- testhelpers/redis/redis.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go index 2c6dd04f..d12fbc06 100644 --- a/testhelpers/redis/redis.go +++ b/testhelpers/redis/redis.go @@ -28,6 +28,10 @@ type Config struct { UseAddress bool UseHostPort bool UseURL bool + // Image is the image to use for the Redis container + // + // Optional. Default is "docker.io/redis:7", but could be set to Valkey. + Image string } // Option is a function that configures a Config @@ -63,6 +67,13 @@ func WithURL(useContainerURI bool) Option { } } +// WithImage sets the image to use for the Redis container +func WithImage(image string) Option { + return func(c *Config) { + c.Image = image + } +} + // Container represents a running Redis container type Container struct { URL string @@ -88,6 +99,10 @@ func Start(t testing.TB, opts ...Option) *Container { img = imgFromEnv } + if config.Image != "" { + img = config.Image + } + ctx := context.Background() tcOpts := []testcontainers.ContainerCustomizer{} From 05b5be3f18a68ca4227054db3b9ae1186c9e10df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 12:37:58 +0200 Subject: [PATCH 04/22] chore: consume the testredis module in redis --- redis/go.mod | 10 ++- redis/go.sum | 16 ++--- redis/redis_test.go | 167 +++++--------------------------------------- 3 files changed, 33 insertions(+), 160 deletions(-) diff --git a/redis/go.mod b/redis/go.mod index 0ab3209e..d169b1d5 100644 --- a/redis/go.mod +++ b/redis/go.mod @@ -3,18 +3,19 @@ module github.com/gofiber/storage/redis/v3 go 1.23.0 require ( + github.com/gofiber/storage/testhelpers/redis v0.0.0-00010101000000-000000000000 github.com/redis/go-redis/v9 v9.7.3 github.com/stretchr/testify v1.10.0 - github.com/testcontainers/testcontainers-go v0.37.0 - github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 ) +replace github.com/gofiber/storage/testhelpers/redis => ../testhelpers/redis + require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect @@ -49,6 +50,8 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/shirou/gopsutil/v4 v4.25.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/testcontainers/testcontainers-go v0.37.0 // indirect + github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -62,6 +65,7 @@ require ( go.opentelemetry.io/proto/otlp v1.5.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/sys v0.32.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/redis/go.sum b/redis/go.sum index e870d7b3..7acf264a 100644 --- a/redis/go.sum +++ b/redis/go.sum @@ -12,8 +12,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -53,8 +53,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -180,12 +180,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA= -google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= -google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/redis/redis_test.go b/redis/redis_test.go index 36e9f204..f32adde2 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -1,161 +1,30 @@ package redis import ( - "context" - "os" - "strings" "testing" "time" "github.com/stretchr/testify/require" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/modules/redis" + testredis "github.com/gofiber/storage/testhelpers/redis" ) -const ( - // redisImage is the default image used for running Redis in tests. - redisImage = "docker.io/redis:7" - redisImageEnvVar = "TEST_REDIS_IMAGE" - redisPort = "6379/tcp" -) - -type testStoreSettings struct { - address bool - hostPort bool - url bool - - // TLS settings - secureURL bool - mtlsDisabled bool - tls bool -} - -type testStoreOption func(*testStoreSettings) - -// withAddress sets the test store to use address-based connection. -func withAddress() testStoreOption { - return func(o *testStoreSettings) { - o.address = true - } -} - -// withHostPort sets the test store to use host and port based connection. -func withHostPort() testStoreOption { - return func(o *testStoreSettings) { - o.hostPort = true - } -} - -// withTLS configures the test store to use TLS. -// Parameters: -// - secureURL: when true, uses "rediss://" scheme, otherwise uses "redis://" -// - mtlsDisabled: when true, disables mutual TLS authentication (--tls-auth-clients no) -func withTLS(secureURL bool, mtlsDisabled bool) testStoreOption { - return func(o *testStoreSettings) { - o.tls = true - o.secureURL = secureURL - o.mtlsDisabled = mtlsDisabled - } -} - -// withURL sets the test store to use a URL. -// Use it when you want to explicitly combine multiple addresses in the same test -// to verify which one is being used. -// - true: the URL will receive the URI provided by the testcontainer -// - false: the URL will be set to an empty string -func withURL(useContainerURI bool) testStoreOption { - return func(o *testStoreSettings) { - o.url = useContainerURI - } -} - -// newConfigFromContainer creates a Redis configuration using a test container. +// newConfigFromContainer creates a Redis configuration using Testcontainers. // It configures the container based on the provided options and returns a Config // that can be used to connect to the container. // The container is cleaned up when the test completes. -func newConfigFromContainer(t testing.TB, opts ...testStoreOption) Config { +func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { t.Helper() - settings := &testStoreSettings{ - url: true, // by default, the URL will be set to the URI provided by the testcontainer - address: false, - hostPort: false, - } - for _, o := range opts { - o(settings) - } - - img := redisImage - if imgFromEnv := os.Getenv(redisImageEnvVar); imgFromEnv != "" { - img = imgFromEnv - } + redisCtr := testredis.Start(t, opts...) cfg := Config{ - Reset: true, - } - - ctx := context.Background() - - tcOpts := []testcontainers.ContainerCustomizer{} - - if settings.tls { - tcOpts = append(tcOpts, redis.WithTLS()) - - // Use Redis module's TLS options, but for the MTLS case, disable the auth-clients flag - cmds := []string{ - "--port", "0", - "--tls-port", "6379", - "--tls-cert-file", "/tls/server.crt", - "--tls-key-file", "/tls/server.key", - "--tls-ca-cert-file", "/tls/ca.crt", - } - - cmds = append(cmds, "--tls-auth-clients", func() string { - if settings.mtlsDisabled { - return "no" - } - return "yes" - }()) - - // completely override the default CMD, as the Redis module is opinionated about the CMD - tcOpts = append(tcOpts, testcontainers.WithCmd(cmds...)) - } - - c, err := redis.Run(ctx, img, tcOpts...) - testcontainers.CleanupContainer(t, c) - require.NoError(t, err) - - cfg.TLSConfig = c.TLSConfig() - - uri, err := c.ConnectionString(ctx) - require.NoError(t, err) - - if settings.hostPort { - host, err := c.Host(ctx) - require.NoError(t, err) - - port, err := c.MappedPort(ctx, redisPort) - require.NoError(t, err) - - cfg.Host = host - cfg.Port = port.Int() - } - - if settings.address { - // trim the schemes from the URI - addr := strings.TrimPrefix(uri, "redis://") - addr = strings.TrimPrefix(addr, "rediss://") - cfg.Addrs = []string{addr} - } - - if settings.url { - cfg.URL = uri - } - - if !settings.secureURL { - // Replace the scheme with the insecure one - cfg.URL = strings.Replace(cfg.URL, "rediss://", "redis://", 1) + Reset: true, + TLSConfig: redisCtr.TLSConfig, + Host: redisCtr.Host, + Port: redisCtr.Port, + Addrs: redisCtr.Addrs, + URL: redisCtr.URL, } return cfg @@ -165,7 +34,7 @@ func newConfigFromContainer(t testing.TB, opts ...testStoreOption) Config { // It configures the container based on the provided options and returns a Storage // instance connected to the container. The caller is responsible for calling // Close() on the returned Storage when done. -func newTestStore(t testing.TB, opts ...testStoreOption) *Storage { +func newTestStore(t testing.TB, opts ...testredis.Option) *Storage { return New(newConfigFromContainer(t, opts...)) } @@ -330,7 +199,7 @@ func Test_Redis_Initialize_WithHostPort(t *testing.T) { val = []byte("kent") ) - testStore := newTestStore(t, withHostPort()) + testStore := newTestStore(t, testredis.WithHostPort()) defer testStore.Close() err := testStore.Set(key, val, 0) @@ -348,7 +217,7 @@ func Test_Redis_Initialize_WithURL_TLS_Verify(t *testing.T) { testFn := func(t *testing.T, secureURL bool, mtlsDisabled bool) { t.Helper() - testStore := newTestStore(t, withTLS(secureURL, mtlsDisabled)) + testStore := newTestStore(t, testredis.WithTLS(secureURL, mtlsDisabled)) defer testStore.Close() var ( @@ -390,7 +259,7 @@ func Test_Redis_Initialize_WithURL_TLS_Verify(t *testing.T) { func Test_Redis_Universal_Addrs(t *testing.T) { // This should failover and create a Single Node connection. - testStoreUniversal := newTestStore(t, withAddress()) + testStoreUniversal := newTestStore(t, testredis.WithAddress()) defer testStoreUniversal.Close() var ( key = "bruce" @@ -416,7 +285,7 @@ func Test_Redis_Universal_With_URL_Undefined(t *testing.T) { // This should failover to creating a regular *redis.Client // The URL should get ignored since it's empty // the withURL option goes last to include it in the config - testStoreUniversal := newTestStore(t, withAddress(), withURL(false)) + testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithURL(false)) defer testStoreUniversal.Close() var ( key = "bruce" @@ -441,7 +310,7 @@ func Test_Redis_Universal_With_URL_Undefined(t *testing.T) { func Test_Redis_Universal_With_URL_Defined(t *testing.T) { // This should failover to creating a regular *redis.Client // The Addrs field should get ignored since URL is defined - testStoreUniversal := newTestStore(t, withAddress(), withURL(true)) + testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithURL(true)) defer testStoreUniversal.Close() var ( @@ -467,7 +336,7 @@ func Test_Redis_Universal_With_URL_Defined(t *testing.T) { func Test_Redis_Universal_With_HostPort(t *testing.T) { // This should failover to creating a regular *redis.Client // The Host and Port should get ignored since Addrs is defined - testStoreUniversal := newTestStore(t, withAddress(), withHostPort(), withURL(false)) + testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithHostPort(), testredis.WithURL(false)) defer testStoreUniversal.Close() var ( key = "bruce" @@ -492,7 +361,7 @@ func Test_Redis_Universal_With_HostPort(t *testing.T) { func Test_Redis_Universal_With_HostPort_And_URL(t *testing.T) { // This should failover to creating a regular *redis.Client // The Host and Port should get ignored since Addrs is defined - testStoreUniversal := newTestStore(t, withAddress(), withHostPort(), withURL(true)) + testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithHostPort(), testredis.WithURL(true)) defer testStoreUniversal.Close() var ( From 5b7af066bbb6d59a9fe219b0830a5fffd8f5d7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 12:58:15 +0200 Subject: [PATCH 05/22] chore: consume the testredis module in rueidis --- .github/workflows/benchmark.yml | 4 +- .github/workflows/test-rueidis.yml | 28 +---- rueidis/go.mod | 53 +++++++- rueidis/go.sum | 192 +++++++++++++++++++++++++++-- rueidis/rueidis_test.go | 179 +++++++++++++++++++-------- 5 files changed, 373 insertions(+), 83 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index eea33cc6..efe01955 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -85,14 +85,14 @@ jobs: sleep 30 - name: Setup Redis - if: ${{ matrix.package == 'rueidis' || matrix.package == 'valkey' }} + if: ${{ matrix.package == 'valkey' }} uses: shogo82148/actions-setup-redis@v1 with: redis-version: '7.x' auto-start: 'false' - name: Run Redis - if: ${{ matrix.package == 'rueidis' || matrix.package == 'valkey' }} + if: ${{ matrix.package == 'valkey' }} run: | redis-server --port 6379 & sleep 15 diff --git a/.github/workflows/test-rueidis.yml b/.github/workflows/test-rueidis.yml index 933aedf9..d2487eff 100644 --- a/.github/workflows/test-rueidis.yml +++ b/.github/workflows/test-rueidis.yml @@ -15,33 +15,15 @@ jobs: strategy: matrix: go-version: - - 1.20.x - - 1.21.x + - 1.23.x + - 1.24.x redis: - - '6.x' - - '7.x' + - '6' + - '7' steps: - name: Fetch Repository uses: actions/checkout@v4 - - name: Generate TLS certs - run: ./.github/scripts/gen-test-certs.sh - - - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1 - with: - redis-version: ${{ matrix.redis }} - auto-start: 'false' - redis-port: '6379' - redis-tls-port: '6380' - - - name: Run Redis - run: | - redis-server --tls-port 6380 --port 6379 \ - --tls-cert-file /home/runner/work/storage/storage/tls/redis.crt \ - --tls-key-file /home/runner/work/storage/storage/tls/redis.key \ - --tls-ca-cert-file /home/runner/work/storage/storage/tls/ca.crt & - - name: Setup Redis Cluster uses: vishnudxb/redis-cluster@1.0.9 with: @@ -59,4 +41,6 @@ jobs: go-version: '${{ matrix.go-version }}' - name: Run Test + env: + TEST_REDIS_IMAGE: "docker.io/redis:${{ matrix.redis }}" run: cd ./rueidis && go test ./... -v -race diff --git a/rueidis/go.mod b/rueidis/go.mod index d3cbac8b..77e42a08 100644 --- a/rueidis/go.mod +++ b/rueidis/go.mod @@ -1,15 +1,64 @@ module github.com/gofiber/storage/rueidis -go 1.20 +go 1.23.0 require ( + github.com/gofiber/storage/testhelpers/redis v0.0.0-00010101000000-000000000000 github.com/redis/rueidis v1.0.44 github.com/stretchr/testify v1.10.0 ) +replace github.com/gofiber/storage/testhelpers/redis => ../testhelpers/redis + require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.0.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mdelapenya/tlscert v0.2.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.24.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v4 v4.25.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/testcontainers/testcontainers-go v0.37.0 // indirect + github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/rueidis/go.sum b/rueidis/go.sum index 9945233e..9c321b8e 100644 --- a/rueidis/go.sum +++ b/rueidis/go.sum @@ -1,20 +1,198 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= +github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= +github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= +github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/redis/rueidis v1.0.44 h1:QfhfuovwEabcywfEXofRjPZuT29pjtpIWDJlCGHZfg8= github.com/redis/rueidis v1.0.44/go.mod h1:bnbkk4+CkXZgDPEbUtSos/o55i4RhFYYesJ4DS2zmq0= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= +github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= +github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 h1:9HIY28I9ME/Zmb+zey1p/I1mto5+5ch0wLX+nJdOsQ4= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0/go.mod h1:Abu9g/25Qv+FkYVx3U4Voaynou1c+7D0HIhaQJXvk6E= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/rueidis/rueidis_test.go b/rueidis/rueidis_test.go index 265fb5a7..cca815e6 100644 --- a/rueidis/rueidis_test.go +++ b/rueidis/rueidis_test.go @@ -1,17 +1,40 @@ package rueidis import ( - "crypto/tls" - "log" "testing" "time" "github.com/stretchr/testify/require" + + testredis "github.com/gofiber/storage/testhelpers/redis" ) -var testStore = New(Config{ - Reset: true, -}) +// newConfigFromContainer creates a Rueidis configuration using Testcontainers. +// It configures the container based on the provided options and returns a Config +// that can be used to connect to the container. +// The container is cleaned up when the test completes. +func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { + t.Helper() + + redisCtr := testredis.Start(t, opts...) + + cfg := Config{ + Reset: true, + TLSConfig: redisCtr.TLSConfig, + InitAddress: redisCtr.Addrs, + URL: redisCtr.URL, + } + + return cfg +} + +// newTestStore creates a new Rueidis storage instance backed by Testcontainers. +// It configures the container based on the provided options and returns a Storage +// instance connected to the container. The caller is responsible for calling +// Close() on the returned Storage when done. +func newTestStore(t testing.TB, opts ...testredis.Option) *Storage { + return New(newConfigFromContainer(t, opts...)) +} func Test_Rueidis_Set(t *testing.T) { var ( @@ -19,6 +42,9 @@ func Test_Rueidis_Set(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) } @@ -29,6 +55,9 @@ func Test_Rueidis_Set_Override(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -42,6 +71,9 @@ func Test_Rueidis_Get(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -57,6 +89,9 @@ func Test_Rueidis_Set_Expiration(t *testing.T) { exp = 1 * time.Second ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, exp) require.NoError(t, err) @@ -66,12 +101,18 @@ func Test_Rueidis_Set_Expiration(t *testing.T) { func Test_Rueidis_Get_Expired(t *testing.T) { key := "john" + testStore := newTestStore(t) + defer testStore.Close() + result, err := testStore.Get(key) require.NoError(t, err) require.Zero(t, len(result)) } func Test_Rueidis_Get_NotExist(t *testing.T) { + testStore := newTestStore(t) + defer testStore.Close() + result, err := testStore.Get("notexist") require.NoError(t, err) require.Zero(t, len(result)) @@ -83,6 +124,9 @@ func Test_Rueidis_Delete(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -97,6 +141,9 @@ func Test_Rueidis_Delete(t *testing.T) { func Test_Rueidis_Reset(t *testing.T) { val := []byte("doe") + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set("john1", val, 0) require.NoError(t, err) @@ -116,99 +163,122 @@ func Test_Rueidis_Reset(t *testing.T) { } func Test_Rueidis_Close(t *testing.T) { + testStore := newTestStore(t) require.Nil(t, testStore.Close()) } func Test_Rueidis_Conn(t *testing.T) { + testStore := newTestStore(t) + defer testStore.Close() + require.True(t, testStore.Conn() != nil) } func Test_Rueidis_WithTLS(t *testing.T) { - cer, err := tls.LoadX509KeyPair("/home/runner/work/storage/storage/tls/client.crt", "/home/runner/work/storage/storage/tls/client.key") - if err != nil { - log.Println(err) + testFn := func(t *testing.T, secureURL bool, mtlsDisabled bool) { + t.Helper() + + testStore := newTestStore(t, testredis.WithTLS(secureURL, mtlsDisabled)) + defer testStore.Close() + + var ( + key = "clark" + val = []byte("kent") + ) + + err := testStore.Set(key, val, 0) + require.NoError(t, err) + + result, err := testStore.Get(key) + require.NoError(t, err) + require.Equal(t, val, result) + + err = testStore.Delete(key) require.NoError(t, err) } - tlsCfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - InsecureSkipVerify: true, - Certificates: []tls.Certificate{cer}, - CipherSuites: []uint16{ - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - }, - } - storeTLS := New(Config{ - InitAddress: []string{"localhost:6380"}, - TLSConfig: tlsCfg, + t.Run("insecure-url/mtls-disabled", func(t *testing.T) { + testFn(t, false, true) }) - var ( - key = "clark" - val = []byte("kent") - ) + t.Run("insecure-url/mtls-enabled", func(t *testing.T) { + testFn(t, false, false) + }) - err = storeTLS.Set(key, val, 0) - require.NoError(t, err) + t.Run("secure-url/mtls-disabled", func(t *testing.T) { + testFn(t, true, true) + }) - result, err := storeTLS.Get(key) - require.NoError(t, err) - require.Equal(t, val, result) - - err = storeTLS.Delete(key) - require.NoError(t, err) - require.Nil(t, storeTLS.Close()) + t.Run("secure-url/mtls-enabled", func(t *testing.T) { + testFn(t, true, false) + }) } func Test_Rueidis_With_HostPort(t *testing.T) { - store := New(Config{ - InitAddress: []string{"localhost:6379"}, - }) + testStore := newTestStore(t, testredis.WithHostPort()) + defer testStore.Close() var ( key = "bruce" val = []byte("wayne") ) - err := store.Set(key, val, 0) + err := testStore.Set(key, val, 0) require.NoError(t, err) - result, err := store.Get(key) + result, err := testStore.Get(key) require.NoError(t, err) require.Equal(t, val, result) - err = store.Delete(key) + err = testStore.Delete(key) require.NoError(t, err) - require.Nil(t, store.Close()) + require.Nil(t, testStore.Close()) } func Test_Rueidis_With_URL(t *testing.T) { - store := New(Config{ - URL: "redis://localhost:6379", - }) + testStore := newTestStore(t, testredis.WithAddress(), testredis.WithURL(false)) + defer testStore.Close() var ( key = "bruce" val = []byte("wayne") ) - err := store.Set(key, val, 0) + err := testStore.Set(key, val, 0) require.NoError(t, err) - result, err := store.Get(key) + result, err := testStore.Get(key) require.NoError(t, err) require.Equal(t, val, result) - err = store.Delete(key) + err = testStore.Delete(key) require.NoError(t, err) - require.Nil(t, store.Close()) + require.Nil(t, testStore.Close()) +} + +func Test_Rueidis_With_TLS_URL(t *testing.T) { + testStore := newTestStore(t, testredis.WithTLS(true, false), testredis.WithAddress(), testredis.WithURL(true)) + defer testStore.Close() + + var ( + key = "bruce" + val = []byte("wayne") + ) + + err := testStore.Set(key, val, 0) + require.NoError(t, err) + + result, err := testStore.Get(key) + require.NoError(t, err) + require.Equal(t, val, result) + + err = testStore.Delete(key) + require.NoError(t, err) + require.Nil(t, testStore.Close()) } func Test_Rueidis_Cluster(t *testing.T) { + // TODO: Replace with containerized cluster when testcontainers-go Rueidis module supports clustering store := New(Config{ InitAddress: []string{ "localhost:7000", @@ -238,6 +308,9 @@ func Test_Rueidis_Cluster(t *testing.T) { } func Benchmark_Rueidis_Set(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + b.ReportAllocs() b.ResetTimer() @@ -250,6 +323,9 @@ func Benchmark_Rueidis_Set(b *testing.B) { } func Benchmark_Rueidis_Get(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + err := testStore.Set("john", []byte("doe"), 0) require.NoError(b, err) @@ -264,6 +340,9 @@ func Benchmark_Rueidis_Get(b *testing.B) { } func Benchmark_Rueidis_SetAndDelete(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + b.ReportAllocs() b.ResetTimer() From 892e6fec63d0c792a27d95bd55fab1a9ef6d755c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:21:16 +0200 Subject: [PATCH 06/22] chore: consume the testredis module in valkey --- .github/workflows/benchmark.yml | 14 +-- .github/workflows/test-valkey.yml | 26 +--- valkey/go.mod | 49 ++++++++ valkey/go.sum | 183 +++++++++++++++++++++++++++- valkey/valkey_test.go | 195 ++++++++++++++++++++++-------- 5 files changed, 377 insertions(+), 90 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index efe01955..158c4923 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -84,19 +84,6 @@ jobs: docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.5 sleep 30 - - name: Setup Redis - if: ${{ matrix.package == 'valkey' }} - uses: shogo82148/actions-setup-redis@v1 - with: - redis-version: '7.x' - auto-start: 'false' - - - name: Run Redis - if: ${{ matrix.package == 'valkey' }} - run: | - redis-server --port 6379 & - sleep 15 - - name: Install etcd if: ${{ matrix.package == 'etcd' }} run: | @@ -132,6 +119,7 @@ jobs: TEST_POSTGRES_IMAGE: "docker.io/postgres:16-alpine" TEST_REDIS_IMAGE: "docker.io/redis:7" TEST_SCYLLADB_IMAGE: "scylladb/scylla:6.2" + TEST_VALKEY_IMAGE: "valkey/valkey:8" - name: Get Previous Benchmark Results uses: actions/cache@v4 diff --git a/.github/workflows/test-valkey.yml b/.github/workflows/test-valkey.yml index 9e66e3f7..1040116c 100644 --- a/.github/workflows/test-valkey.yml +++ b/.github/workflows/test-valkey.yml @@ -17,32 +17,14 @@ jobs: matrix: go-version: - 1.23.x + - 1.24.x valkey: - - '7.x' - - '8.x' + - '7' + - '8' steps: - name: Fetch Repository uses: actions/checkout@v4 - - name: Generate TLS certs - run: ./.github/scripts/gen-test-certs.sh - - - name: Setup Valkey - uses: shogo82148/actions-setup-redis@v1 - with: - distribution: 'valkey' - redis-version: ${{ matrix.valkey }} - auto-start: 'false' - redis-port: '6379' - redis-tls-port: '6380' - - - name: Run Valkey - run: | - valkey-server --tls-port 6380 --port 6379 \ - --tls-cert-file /home/runner/work/storage/storage/tls/valkey.crt \ - --tls-key-file /home/runner/work/storage/storage/tls/valkey.key \ - --tls-ca-cert-file /home/runner/work/storage/storage/tls/ca.crt & - - name: Setup Valkey Cluster uses: vishnudxb/redis-cluster@1.0.9 with: @@ -60,4 +42,6 @@ jobs: go-version: '${{ matrix.go-version }}' - name: Run Test + env: + TEST_VALKEY_IMAGE: "valkey/valkey:${{ matrix.valkey }}" run: cd ./valkey && go test ./... -v -race diff --git a/valkey/go.mod b/valkey/go.mod index c30c95d8..f465ca59 100644 --- a/valkey/go.mod +++ b/valkey/go.mod @@ -3,13 +3,62 @@ module github.com/gofiber/storage/valkey go 1.23.0 require ( + github.com/gofiber/storage/testhelpers/redis v0.0.0-00010101000000-000000000000 github.com/stretchr/testify v1.10.0 github.com/valkey-io/valkey-go v1.0.59 ) +replace github.com/gofiber/storage/testhelpers/redis => ../testhelpers/redis + require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.0.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mdelapenya/tlscert v0.2.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v4 v4.25.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/testcontainers/testcontainers-go v0.37.0 // indirect + github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/crypto v0.37.0 // indirect golang.org/x/sys v0.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/valkey/go.sum b/valkey/go.sum index 6ee70fdf..cca10b90 100644 --- a/valkey/go.sum +++ b/valkey/go.sum @@ -1,27 +1,198 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= +github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= +github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= +github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= +github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 h1:9HIY28I9ME/Zmb+zey1p/I1mto5+5ch0wLX+nJdOsQ4= +github.com/testcontainers/testcontainers-go/modules/redis v0.37.0/go.mod h1:Abu9g/25Qv+FkYVx3U4Voaynou1c+7D0HIhaQJXvk6E= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/valkey-io/valkey-go v1.0.59 h1:W67Z0UY+Qqk3k8NKkFCFlM3X4yQUniixl7dSJAch2Qo= github.com/valkey-io/valkey-go v1.0.59/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/valkey/valkey_test.go b/valkey/valkey_test.go index cf59cf75..80dd3520 100644 --- a/valkey/valkey_test.go +++ b/valkey/valkey_test.go @@ -1,17 +1,55 @@ package valkey import ( - "crypto/tls" - "log" + "os" "testing" "time" "github.com/stretchr/testify/require" + + testredis "github.com/gofiber/storage/testhelpers/redis" ) -var testStore = New(Config{ - Reset: true, -}) +const ( + // valkeyImage is the default image used for running Valkey in tests. + valkeyImage = "valkey/valkey:8" + valkeyImageEnvVar = "TEST_VALKEY_IMAGE" +) + +// newConfigFromContainer creates a Redis configuration using Testcontainers. +// It configures the container based on the provided options and returns a Config +// that can be used to connect to the container. +// The container is cleaned up when the test completes. +func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { + t.Helper() + + img := valkeyImage + if imgFromEnv := os.Getenv(valkeyImageEnvVar); imgFromEnv != "" { + img = imgFromEnv + } + + // Force Valkey image when running outside of GitHub Actions + opts = append(opts, testredis.WithImage(img)) + + redisCtr := testredis.Start(t, opts...) + + cfg := Config{ + Reset: true, + TLSConfig: redisCtr.TLSConfig, + InitAddress: redisCtr.Addrs, + URL: redisCtr.URL, + } + + return cfg +} + +// newTestStore creates a new Redis storage instance backed by Testcontainers. +// It configures the container based on the provided options and returns a Storage +// instance connected to the container. The caller is responsible for calling +// Close() on the returned Storage when done. +func newTestStore(t testing.TB, opts ...testredis.Option) *Storage { + return New(newConfigFromContainer(t, opts...)) +} func Test_Valkey_Set(t *testing.T) { var ( @@ -19,6 +57,9 @@ func Test_Valkey_Set(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) } @@ -29,6 +70,9 @@ func Test_Valkey_Set_Override(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -42,6 +86,9 @@ func Test_Valkey_Get(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -57,6 +104,9 @@ func Test_Valkey_Set_Expiration(t *testing.T) { exp = 1 * time.Second ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, exp) require.NoError(t, err) @@ -66,12 +116,18 @@ func Test_Valkey_Set_Expiration(t *testing.T) { func Test_Valkey_Get_Expired(t *testing.T) { key := "john" + testStore := newTestStore(t) + defer testStore.Close() + result, err := testStore.Get(key) require.NoError(t, err) require.Zero(t, len(result)) } func Test_Valkey_Get_NotExist(t *testing.T) { + testStore := newTestStore(t) + defer testStore.Close() + result, err := testStore.Get("notexist") require.NoError(t, err) require.Zero(t, len(result)) @@ -83,6 +139,9 @@ func Test_Valkey_Delete(t *testing.T) { val = []byte("doe") ) + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set(key, val, 0) require.NoError(t, err) @@ -97,6 +156,9 @@ func Test_Valkey_Delete(t *testing.T) { func Test_Valkey_Reset(t *testing.T) { val := []byte("doe") + testStore := newTestStore(t) + defer testStore.Close() + err := testStore.Set("john1", val, 0) require.NoError(t, err) @@ -116,99 +178,123 @@ func Test_Valkey_Reset(t *testing.T) { } func Test_Valkey_Close(t *testing.T) { + testStore := newTestStore(t) require.Nil(t, testStore.Close()) } func Test_Valkey_Conn(t *testing.T) { + testStore := newTestStore(t) + defer testStore.Close() + require.True(t, testStore.Conn() != nil) } func Test_Valkey_WithTLS(t *testing.T) { - cer, err := tls.LoadX509KeyPair("/home/runner/work/storage/storage/tls/client.crt", "/home/runner/work/storage/storage/tls/client.key") - if err != nil { - log.Println(err) + testFn := func(t *testing.T, secureURL bool, mtlsDisabled bool) { + t.Helper() + + testStore := newTestStore(t, testredis.WithTLS(secureURL, mtlsDisabled)) + defer testStore.Close() + + var ( + key = "clark" + val = []byte("kent") + ) + + err := testStore.Set(key, val, 0) + require.NoError(t, err) + + result, err := testStore.Get(key) + require.NoError(t, err) + require.Equal(t, val, result) + + err = testStore.Delete(key) require.NoError(t, err) } - tlsCfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - InsecureSkipVerify: true, - Certificates: []tls.Certificate{cer}, - CipherSuites: []uint16{ - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - }, - } - storeTLS := New(Config{ - InitAddress: []string{"localhost:6380"}, - TLSConfig: tlsCfg, + t.Run("insecure-url/mtls-disabled", func(t *testing.T) { + testFn(t, false, true) }) - var ( - key = "clark" - val = []byte("kent") - ) + t.Run("insecure-url/mtls-enabled", func(t *testing.T) { + testFn(t, false, false) + }) - err = storeTLS.Set(key, val, 0) - require.NoError(t, err) + t.Run("secure-url/mtls-disabled", func(t *testing.T) { + testFn(t, true, true) + }) - result, err := storeTLS.Get(key) - require.NoError(t, err) - require.Equal(t, val, result) - - err = storeTLS.Delete(key) - require.NoError(t, err) - require.Nil(t, storeTLS.Close()) + t.Run("secure-url/mtls-enabled", func(t *testing.T) { + testFn(t, true, false) + }) } func Test_Valkey_With_HostPort(t *testing.T) { - store := New(Config{ - InitAddress: []string{"localhost:6379"}, - }) + testStore := newTestStore(t, testredis.WithHostPort()) + defer testStore.Close() var ( key = "bruce" val = []byte("wayne") ) - err := store.Set(key, val, 0) + err := testStore.Set(key, val, 0) require.NoError(t, err) - result, err := store.Get(key) + result, err := testStore.Get(key) require.NoError(t, err) require.Equal(t, val, result) - err = store.Delete(key) + err = testStore.Delete(key) require.NoError(t, err) - require.Nil(t, store.Close()) + require.Nil(t, testStore.Close()) } func Test_Valkey_With_URL(t *testing.T) { - store := New(Config{ - URL: "redis://localhost:6379", - }) + testStore := newTestStore(t, testredis.WithAddress(), testredis.WithURL(false)) + defer testStore.Close() var ( key = "bruce" val = []byte("wayne") ) - err := store.Set(key, val, 0) + err := testStore.Set(key, val, 0) require.NoError(t, err) - result, err := store.Get(key) + result, err := testStore.Get(key) require.NoError(t, err) require.Equal(t, val, result) - err = store.Delete(key) + err = testStore.Delete(key) require.NoError(t, err) - require.Nil(t, store.Close()) + require.Nil(t, testStore.Close()) +} + +func Test_Valkey_With_TLS_URL(t *testing.T) { + testStore := newTestStore(t, testredis.WithTLS(true, false), testredis.WithAddress(), testredis.WithURL(true)) + defer testStore.Close() + + var ( + key = "bruce" + val = []byte("wayne") + ) + + err := testStore.Set(key, val, 0) + require.NoError(t, err) + + result, err := testStore.Get(key) + require.NoError(t, err) + require.Equal(t, val, result) + + err = testStore.Delete(key) + require.NoError(t, err) + require.Nil(t, testStore.Close()) } func Test_Valkey_Cluster(t *testing.T) { + t.Skip("TODO: Replace with containerized cluster when testcontainers-go Valkey module supports clustering") + // TODO: Replace with containerized cluster when testcontainers-go Valkey module supports clustering store := New(Config{ InitAddress: []string{ "localhost:7000", @@ -238,6 +324,9 @@ func Test_Valkey_Cluster(t *testing.T) { } func Benchmark_Valkey_Set(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + b.ReportAllocs() b.ResetTimer() @@ -250,6 +339,9 @@ func Benchmark_Valkey_Set(b *testing.B) { } func Benchmark_Valkey_Get(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + err := testStore.Set("john", []byte("doe"), 0) require.NoError(b, err) @@ -264,6 +356,9 @@ func Benchmark_Valkey_Get(b *testing.B) { } func Benchmark_Valkey_SetAndDelete(b *testing.B) { + testStore := newTestStore(b) + defer testStore.Close() + b.ReportAllocs() b.ResetTimer() From 7866e27c7598e458ced1fce4bfa401bec71d7ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:22:50 +0200 Subject: [PATCH 07/22] chore: remove shell script for cert generation --- .github/scripts/gen-test-certs.sh | 63 ------------------------------- 1 file changed, 63 deletions(-) delete mode 100755 .github/scripts/gen-test-certs.sh diff --git a/.github/scripts/gen-test-certs.sh b/.github/scripts/gen-test-certs.sh deleted file mode 100755 index 2cef602b..00000000 --- a/.github/scripts/gen-test-certs.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -# Generate some test certificates which are used by the regression test suite: -# -# ./tls/ca.{crt,key} Self signed CA certificate. -# ./tls/redis.{crt,key} A certificate with no key usage/policy restrictions. -# ./tls/client.{crt,key} A certificate restricted for SSL client usage. -# ./tls/server.{crt,key} A certificate restricted for SSL server usage. - -set -e - -generate_cert() { - local name=$1 - local cn="$2" - local opts="$3" - - local keyfile=./tls/${name}.key - local certfile=./tls/${name}.crt - - [ -f $keyfile ] || openssl genrsa -out $keyfile 2048 - openssl req \ - -new -sha256 \ - -subj "/O=Redis Test/CN=$cn" \ - -key $keyfile | \ - openssl x509 \ - -req -sha256 \ - -CA ./tls/ca.crt \ - -CAkey ./tls/ca.key \ - -CAserial ./tls/ca.txt \ - -CAcreateserial \ - -days 365 \ - $opts \ - -out $certfile -} - -mkdir -p ./tls -[ -f ./tls/ca.key ] || openssl genrsa -out ./tls/ca.key 4096 -openssl req \ - -x509 -new -nodes -sha256 \ - -key ./tls/ca.key \ - -days 3650 \ - -subj '/O=Redis Test/CN=Certificate Authority' \ - -out ./tls/ca.crt - -cat > ./tls/openssl.cnf <<_END_ -[ server_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = server -subjectAltName = DNS:localhost" - -[ client_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = client -_END_ - -generate_cert server "Server-only" "-extfile ./tls/openssl.cnf -extensions server_cert" -generate_cert client "Client-only" "-extfile ./tls/openssl.cnf -extensions client_cert" -generate_cert redis "localhost" "-extfile ./tls/openssl.cnf -extensions server_cert" -generate_cert valkey "localhost" "-extfile ./tls/openssl.cnf -extensions server_cert" - -# List generated certs -ls -la ./tls -echo "$PWD" From dd7391a11a7fb6fc6122cbfc429d789adfb43bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:38:12 +0200 Subject: [PATCH 08/22] chore(ci): exclude testhelpers root dir from the filters --- .github/workflows/benchmark.yml | 5 ++++- .github/workflows/golangci-lint.yml | 5 ++++- .github/workflows/release-drafter.yml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 158c4923..5f92fcd1 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -28,7 +28,10 @@ jobs: - name: Generate filters id: filter-setup run: | - filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') + filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') + # Add all testhelpers subdirectories to filters + testhelpers_filters=$(find ./testhelpers -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | awk '{printf "testhelpers/%s: \"testhelpers/%s/**\"\n", $1, $1}') + filters="$filters\n$testhelpers_filters" echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index bb4abea6..ebd07f09 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -31,7 +31,10 @@ jobs: - name: Generate filters id: filter-setup run: | - filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') + filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') + # Add all testhelpers subdirectories to filters + testhelpers_filters=$(find ./testhelpers -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | awk '{printf "testhelpers/%s: \"testhelpers/%s/**\"\n", $1, $1}') + filters="$filters\n$testhelpers_filters" echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index e42ccc8e..c90f9adc 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,7 +18,7 @@ jobs: - name: Generate filters id: filter-setup run: | - filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') + filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT From ba60ab9a1f14e03deaf42b5f15af351177cf5100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:41:37 +0200 Subject: [PATCH 09/22] fix: indentation --- .github/workflows/benchmark.yml | 2 +- .github/workflows/golangci-lint.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 5f92fcd1..f593e5ca 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -31,9 +31,9 @@ jobs: filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') # Add all testhelpers subdirectories to filters testhelpers_filters=$(find ./testhelpers -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | awk '{printf "testhelpers/%s: \"testhelpers/%s/**\"\n", $1, $1}') - filters="$filters\n$testhelpers_filters" echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT + echo "$testhelpers_filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT shell: bash diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index ebd07f09..0895f9a6 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -34,9 +34,9 @@ jobs: filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') # Add all testhelpers subdirectories to filters testhelpers_filters=$(find ./testhelpers -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | awk '{printf "testhelpers/%s: \"testhelpers/%s/**\"\n", $1, $1}') - filters="$filters\n$testhelpers_filters" echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT + echo "$testhelpers_filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT shell: bash From b7893d7d4ac76fbce3b0e0e5e52defb9ebbdf508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 13:44:50 +0200 Subject: [PATCH 10/22] fix: do not run benchmarks for the helpers --- .github/workflows/benchmark.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index f593e5ca..65fe54c2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -29,11 +29,8 @@ jobs: id: filter-setup run: | filters=$(find . -maxdepth 1 -type d ! -path ./.git ! -path . ! -path ./testhelpers -exec basename {} \; | grep -v '^\.' | awk '{printf "%s: \"%s/**\"\n", $1, $1}') - # Add all testhelpers subdirectories to filters - testhelpers_filters=$(find ./testhelpers -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | awk '{printf "testhelpers/%s: \"testhelpers/%s/**\"\n", $1, $1}') echo "filters<> $GITHUB_OUTPUT echo "$filters" >> $GITHUB_OUTPUT - echo "$testhelpers_filters" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT shell: bash From a77e60ff9fbe61f86b94c62fcf6d9bda51a91c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 30 Apr 2025 14:40:41 +0200 Subject: [PATCH 11/22] chore: add tests for the helper module --- .github/workflows/test-teshelpers-redis.yml | 36 ++++ testhelpers/redis/redis_test.go | 192 ++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 .github/workflows/test-teshelpers-redis.yml create mode 100644 testhelpers/redis/redis_test.go diff --git a/.github/workflows/test-teshelpers-redis.yml b/.github/workflows/test-teshelpers-redis.yml new file mode 100644 index 00000000..d8a5e0c6 --- /dev/null +++ b/.github/workflows/test-teshelpers-redis.yml @@ -0,0 +1,36 @@ +on: + push: + branches: + - master + - main + paths: + - 'testhelpers/redis/**' + pull_request: + paths: + - 'testhelpers/redis/**' +name: "Tests TestHelper Redis" +jobs: + Tests: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: + - 1.23.x + - 1.24.x + redis: + - '6' + - '7' + steps: + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: '${{ matrix.go-version }}' + + - name: Run Test + env: + TEST_REDIS_IMAGE: "docker.io/redis:${{ matrix.redis }}" + working-directory: testhelpers/redis + run: go test ./... -v -race diff --git a/testhelpers/redis/redis_test.go b/testhelpers/redis/redis_test.go new file mode 100644 index 00000000..7e04fdf6 --- /dev/null +++ b/testhelpers/redis/redis_test.go @@ -0,0 +1,192 @@ +package testredis + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStart(t *testing.T) { + t.Run("default-configuration", func(t *testing.T) { + ctr := Start(t) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.Nil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("with-custom-image", func(t *testing.T) { + customImage := "docker.io/redis:6" + ctr := Start(t, WithImage(customImage)) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.Nil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("with-image-from-env", func(t *testing.T) { + envImage := "docker.io/redis:7" + t.Setenv(ImageEnvVar, envImage) + + ctr := Start(t) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.Nil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("with-tls", func(t *testing.T) { + t.Run("secure-url", func(t *testing.T) { + t.Run("mtls-disabled", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithTLS(true, false)) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) + require.NotNil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("mtls-enabled", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithTLS(true, true)) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) + require.NotNil(t, ctr.TLSConfig) + }) + }) + + t.Run("insecure-url", func(t *testing.T) { + t.Run("mtls-disabled", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithTLS(false, true)) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.NotNil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("mtls-enabled", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithTLS(false, false)) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.NotNil(t, ctr.TLSConfig) + require.Empty(t, ctr.Addrs) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + }) + }) + + t.Run("with-host-and-port", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithHostPort()) + require.NotEmpty(t, ctr.Host) + require.NotZero(t, ctr.Port) + require.NotEmpty(t, ctr.URL) + require.Empty(t, ctr.Addrs) + }) + + t.Run("with-address", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithAddress()) + require.NotEmpty(t, ctr.Addrs) + require.Len(t, ctr.Addrs, 1) + require.NotEmpty(t, ctr.URL) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("with-url", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, WithURL(true)) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + }) + + t.Run("with-multiple-options", func(t *testing.T) { + t.Run("address/url", func(t *testing.T) { + t.Run("no-tls", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, + WithAddress(), + WithURL(true), + ) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "redis://")) + require.NotEmpty(t, ctr.Addrs) + require.Len(t, ctr.Addrs, 1) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + + t.Run("tls", func(t *testing.T) { + t.Parallel() + + ctr := Start(t, + WithTLS(true, false), + WithAddress(), + WithURL(true), + ) + require.NotEmpty(t, ctr.URL) + require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) + require.NotEmpty(t, ctr.Addrs) + require.Len(t, ctr.Addrs, 1) + require.Empty(t, ctr.Host) + require.Zero(t, ctr.Port) + }) + }) + }) +} + +func TestConfig(t *testing.T) { + t.Run("with-tls", func(t *testing.T) { + config := &Config{} + WithTLS(true, false)(config) + require.True(t, config.UseTLS) + require.True(t, config.SecureURL) + require.False(t, config.MTLSDisabled) + }) + + t.Run("with-address", func(t *testing.T) { + config := &Config{} + WithAddress()(config) + require.True(t, config.UseAddress) + }) + + t.Run("with-host-port", func(t *testing.T) { + config := &Config{} + WithHostPort()(config) + require.True(t, config.UseHostPort) + }) + + t.Run("with-url", func(t *testing.T) { + config := &Config{} + WithURL(true)(config) + require.True(t, config.UseURL) + }) + + t.Run("with-image", func(t *testing.T) { + customImage := "docker.io/redis:6" + config := &Config{} + WithImage(customImage)(config) + require.Equal(t, customImage, config.Image) + }) +} From 30b20739d0aa7a88f9cada8a72cf156fdd64cc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 09:48:40 +0200 Subject: [PATCH 12/22] chore: verify mtls properly in tests --- testhelpers/redis/redis.go | 7 +++++-- testhelpers/redis/redis_test.go | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go index d12fbc06..9b6f49a4 100644 --- a/testhelpers/redis/redis.go +++ b/testhelpers/redis/redis.go @@ -77,6 +77,7 @@ func WithImage(image string) Option { // Container represents a running Redis container type Container struct { URL string + cmds []string Addrs []string Host string Port int @@ -106,16 +107,17 @@ func Start(t testing.TB, opts ...Option) *Container { ctx := context.Background() tcOpts := []testcontainers.ContainerCustomizer{} + var cmds []string if config.UseTLS { tcOpts = append(tcOpts, redis.WithTLS()) - cmds := []string{ + cmds = append(cmds, "--port", "0", "--tls-port", "6379", "--tls-cert-file", "/tls/server.crt", "--tls-key-file", "/tls/server.key", "--tls-ca-cert-file", "/tls/ca.crt", - } + ) cmds = append(cmds, "--tls-auth-clients", func() string { if config.MTLSDisabled { @@ -133,6 +135,7 @@ func Start(t testing.TB, opts ...Option) *Container { ctr := &Container{ TLSConfig: c.TLSConfig(), + cmds: cmds, } uri, err := c.ConnectionString(ctx) diff --git a/testhelpers/redis/redis_test.go b/testhelpers/redis/redis_test.go index 7e04fdf6..68085b1c 100644 --- a/testhelpers/redis/redis_test.go +++ b/testhelpers/redis/redis_test.go @@ -42,7 +42,7 @@ func TestStart(t *testing.T) { t.Run("with-tls", func(t *testing.T) { t.Run("secure-url", func(t *testing.T) { - t.Run("mtls-disabled", func(t *testing.T) { + t.Run("mtls-enabled", func(t *testing.T) { t.Parallel() ctr := Start(t, WithTLS(true, false)) @@ -52,15 +52,17 @@ func TestStart(t *testing.T) { require.Empty(t, ctr.Addrs) require.Empty(t, ctr.Host) require.Zero(t, ctr.Port) + require.Contains(t, ctr.cmds, "--tls-auth-clients", "yes") }) - t.Run("mtls-enabled", func(t *testing.T) { + t.Run("mtls-disabled", func(t *testing.T) { t.Parallel() ctr := Start(t, WithTLS(true, true)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) require.NotNil(t, ctr.TLSConfig) + require.Contains(t, ctr.cmds, "--tls-auth-clients", "no") }) }) From 941ef46eaa8201e570a508a2dc3376fa172fd120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 09:57:19 +0200 Subject: [PATCH 13/22] chore: add integration tests to the redis helper module --- testhelpers/redis/go.mod | 2 ++ testhelpers/redis/go.sum | 8 ++++++-- testhelpers/redis/redis_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/testhelpers/redis/go.mod b/testhelpers/redis/go.mod index 94a7df5c..4f50761a 100644 --- a/testhelpers/redis/go.mod +++ b/testhelpers/redis/go.mod @@ -3,6 +3,7 @@ module github.com/gofiber/storage/testhelpers/redis go 1.23.0 require ( + github.com/redis/go-redis/v9 v9.8.0 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.37.0 github.com/testcontainers/testcontainers-go/modules/redis v0.37.0 @@ -18,6 +19,7 @@ require ( github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.0.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect diff --git a/testhelpers/redis/go.sum b/testhelpers/redis/go.sum index b7f31c70..f4e21362 100644 --- a/testhelpers/redis/go.sum +++ b/testhelpers/redis/go.sum @@ -6,6 +6,10 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -89,8 +93,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= diff --git a/testhelpers/redis/redis_test.go b/testhelpers/redis/redis_test.go index 68085b1c..2c44a9da 100644 --- a/testhelpers/redis/redis_test.go +++ b/testhelpers/redis/redis_test.go @@ -1,9 +1,12 @@ package testredis import ( + "context" "strings" "testing" + "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/require" ) @@ -156,6 +159,28 @@ func TestStart(t *testing.T) { }) }) }) + + t.Run("can-connect", func(t *testing.T) { + ctr := Start(t) + + options, err := redis.ParseURL(ctr.URL) + require.NoError(t, err) + + // Use go-redis client to verify connectivity + client := redis.NewUniversalClient(&redis.UniversalOptions{ + Addrs: []string{options.Addr}, + TLSConfig: ctr.TLSConfig, + Username: options.Username, + Password: options.Password, + DB: options.DB, + }) + defer client.Close() + + // Ping should succeed and return "PONG" + resp := client.Ping(context.Background()) + require.NoError(t, resp.Err()) + require.Equal(t, "PONG", resp.Val()) + }) } func TestConfig(t *testing.T) { From 5794b5068f57e5412ba2ab3a46922c568e0d71cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 10:00:56 +0200 Subject: [PATCH 14/22] chore: proper parallel layout --- testhelpers/redis/redis_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/testhelpers/redis/redis_test.go b/testhelpers/redis/redis_test.go index 2c44a9da..9d62f5c4 100644 --- a/testhelpers/redis/redis_test.go +++ b/testhelpers/redis/redis_test.go @@ -44,7 +44,11 @@ func TestStart(t *testing.T) { }) t.Run("with-tls", func(t *testing.T) { + t.Parallel() + t.Run("secure-url", func(t *testing.T) { + t.Parallel() + t.Run("mtls-enabled", func(t *testing.T) { t.Parallel() @@ -70,6 +74,8 @@ func TestStart(t *testing.T) { }) t.Run("insecure-url", func(t *testing.T) { + t.Parallel() + t.Run("mtls-disabled", func(t *testing.T) { t.Parallel() @@ -126,7 +132,11 @@ func TestStart(t *testing.T) { }) t.Run("with-multiple-options", func(t *testing.T) { + t.Parallel() + t.Run("address/url", func(t *testing.T) { + t.Parallel() + t.Run("no-tls", func(t *testing.T) { t.Parallel() @@ -161,6 +171,8 @@ func TestStart(t *testing.T) { }) t.Run("can-connect", func(t *testing.T) { + t.Parallel() + ctr := Start(t) options, err := redis.ParseURL(ctr.URL) @@ -184,7 +196,11 @@ func TestStart(t *testing.T) { } func TestConfig(t *testing.T) { + t.Parallel() + t.Run("with-tls", func(t *testing.T) { + t.Parallel() + config := &Config{} WithTLS(true, false)(config) require.True(t, config.UseTLS) @@ -193,24 +209,32 @@ func TestConfig(t *testing.T) { }) t.Run("with-address", func(t *testing.T) { + t.Parallel() + config := &Config{} WithAddress()(config) require.True(t, config.UseAddress) }) t.Run("with-host-port", func(t *testing.T) { + t.Parallel() + config := &Config{} WithHostPort()(config) require.True(t, config.UseHostPort) }) t.Run("with-url", func(t *testing.T) { + t.Parallel() + config := &Config{} WithURL(true)(config) require.True(t, config.UseURL) }) t.Run("with-image", func(t *testing.T) { + t.Parallel() + customImage := "docker.io/redis:6" config := &Config{} WithImage(customImage)(config) From 1e6e787ec53f940ed18dfc19d7f42c5da2a9d8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 10:20:28 +0200 Subject: [PATCH 15/22] fix: missing eval of redis image from env var --- redis/redis_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/redis/redis_test.go b/redis/redis_test.go index f32adde2..656637d5 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -1,6 +1,7 @@ package redis import ( + "os" "testing" "time" @@ -9,6 +10,12 @@ import ( testredis "github.com/gofiber/storage/testhelpers/redis" ) +const ( + // redisImage is the default image used for running Redis in tests. + redisImage = "docker.io/redis:7" + redisImageEnvVar = "TEST_REDIS_IMAGE" +) + // newConfigFromContainer creates a Redis configuration using Testcontainers. // It configures the container based on the provided options and returns a Config // that can be used to connect to the container. @@ -16,6 +23,13 @@ import ( func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { t.Helper() + img := redisImage + if imgFromEnv := os.Getenv(redisImageEnvVar); imgFromEnv != "" { + img = imgFromEnv + } + + opts = append(opts, testredis.WithImage(img)) + redisCtr := testredis.Start(t, opts...) cfg := Config{ From 4d516c348a7775d1dc99afef2df8d5b962dc6f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 10:38:30 +0200 Subject: [PATCH 16/22] chore: pass the redis-like image explicitly in tests --- redis/redis_test.go | 4 +-- testhelpers/redis/redis.go | 28 +++-------------- testhelpers/redis/redis_test.go | 54 +++++++++++++-------------------- valkey/valkey_test.go | 5 +-- 4 files changed, 27 insertions(+), 64 deletions(-) diff --git a/redis/redis_test.go b/redis/redis_test.go index 656637d5..b8be18dd 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -28,9 +28,7 @@ func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { img = imgFromEnv } - opts = append(opts, testredis.WithImage(img)) - - redisCtr := testredis.Start(t, opts...) + redisCtr := testredis.Start(t, img, opts...) cfg := Config{ Reset: true, diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go index 9b6f49a4..23b7b9fc 100644 --- a/testhelpers/redis/redis.go +++ b/testhelpers/redis/redis.go @@ -3,7 +3,6 @@ package testredis import ( "context" "crypto/tls" - "os" "strings" "testing" @@ -13,10 +12,7 @@ import ( ) const ( - // Image is the default image used for running Redis in tests. - Image = "docker.io/redis:7" - ImageEnvVar = "TEST_REDIS_IMAGE" - Port = "6379/tcp" + Port = "6379/tcp" ) // Config represents the configuration for a Redis container @@ -28,10 +24,6 @@ type Config struct { UseAddress bool UseHostPort bool UseURL bool - // Image is the image to use for the Redis container - // - // Optional. Default is "docker.io/redis:7", but could be set to Valkey. - Image string } // Option is a function that configures a Config @@ -67,13 +59,6 @@ func WithURL(useContainerURI bool) Option { } } -// WithImage sets the image to use for the Redis container -func WithImage(image string) Option { - return func(c *Config) { - c.Image = image - } -} - // Container represents a running Redis container type Container struct { URL string @@ -85,7 +70,7 @@ type Container struct { } // Start starts a Redis container for testing and returns its configuration -func Start(t testing.TB, opts ...Option) *Container { +func Start(t testing.TB, img string, opts ...Option) *Container { t.Helper() config := &Config{ @@ -95,13 +80,8 @@ func Start(t testing.TB, opts ...Option) *Container { o(config) } - img := Image - if imgFromEnv := os.Getenv(ImageEnvVar); imgFromEnv != "" { - img = imgFromEnv - } - - if config.Image != "" { - img = config.Image + if img == "" { + panic("Redis image is not set: callers must provide an image using WithImage()") } ctx := context.Background() diff --git a/testhelpers/redis/redis_test.go b/testhelpers/redis/redis_test.go index 9d62f5c4..588b3f56 100644 --- a/testhelpers/redis/redis_test.go +++ b/testhelpers/redis/redis_test.go @@ -10,9 +10,18 @@ import ( "github.com/stretchr/testify/require" ) +// Image is the default image used for running Redis in tests. +const testImage = "docker.io/redis:7" + func TestStart(t *testing.T) { + t.Run("panics-if-image-is-not-set", func(t *testing.T) { + require.Panics(t, func() { + Start(t, "") + }) + }) + t.Run("default-configuration", func(t *testing.T) { - ctr := Start(t) + ctr := Start(t, testImage) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "redis://")) require.Nil(t, ctr.TLSConfig) @@ -23,19 +32,7 @@ func TestStart(t *testing.T) { t.Run("with-custom-image", func(t *testing.T) { customImage := "docker.io/redis:6" - ctr := Start(t, WithImage(customImage)) - require.True(t, strings.HasPrefix(ctr.URL, "redis://")) - require.Nil(t, ctr.TLSConfig) - require.Empty(t, ctr.Addrs) - require.Empty(t, ctr.Host) - require.Zero(t, ctr.Port) - }) - - t.Run("with-image-from-env", func(t *testing.T) { - envImage := "docker.io/redis:7" - t.Setenv(ImageEnvVar, envImage) - - ctr := Start(t) + ctr := Start(t, customImage) require.True(t, strings.HasPrefix(ctr.URL, "redis://")) require.Nil(t, ctr.TLSConfig) require.Empty(t, ctr.Addrs) @@ -52,7 +49,7 @@ func TestStart(t *testing.T) { t.Run("mtls-enabled", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithTLS(true, false)) + ctr := Start(t, testImage, WithTLS(true, false)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) require.NotNil(t, ctr.TLSConfig) @@ -65,7 +62,7 @@ func TestStart(t *testing.T) { t.Run("mtls-disabled", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithTLS(true, true)) + ctr := Start(t, testImage, WithTLS(true, true)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "rediss://")) require.NotNil(t, ctr.TLSConfig) @@ -79,7 +76,7 @@ func TestStart(t *testing.T) { t.Run("mtls-disabled", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithTLS(false, true)) + ctr := Start(t, testImage, WithTLS(false, true)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "redis://")) require.NotNil(t, ctr.TLSConfig) @@ -91,7 +88,7 @@ func TestStart(t *testing.T) { t.Run("mtls-enabled", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithTLS(false, false)) + ctr := Start(t, testImage, WithTLS(false, false)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "redis://")) require.NotNil(t, ctr.TLSConfig) @@ -105,7 +102,7 @@ func TestStart(t *testing.T) { t.Run("with-host-and-port", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithHostPort()) + ctr := Start(t, testImage, WithHostPort()) require.NotEmpty(t, ctr.Host) require.NotZero(t, ctr.Port) require.NotEmpty(t, ctr.URL) @@ -115,7 +112,7 @@ func TestStart(t *testing.T) { t.Run("with-address", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithAddress()) + ctr := Start(t, testImage, WithAddress()) require.NotEmpty(t, ctr.Addrs) require.Len(t, ctr.Addrs, 1) require.NotEmpty(t, ctr.URL) @@ -126,7 +123,7 @@ func TestStart(t *testing.T) { t.Run("with-url", func(t *testing.T) { t.Parallel() - ctr := Start(t, WithURL(true)) + ctr := Start(t, testImage, WithURL(true)) require.NotEmpty(t, ctr.URL) require.True(t, strings.HasPrefix(ctr.URL, "redis://")) }) @@ -140,7 +137,7 @@ func TestStart(t *testing.T) { t.Run("no-tls", func(t *testing.T) { t.Parallel() - ctr := Start(t, + ctr := Start(t, testImage, WithAddress(), WithURL(true), ) @@ -155,7 +152,7 @@ func TestStart(t *testing.T) { t.Run("tls", func(t *testing.T) { t.Parallel() - ctr := Start(t, + ctr := Start(t, testImage, WithTLS(true, false), WithAddress(), WithURL(true), @@ -173,7 +170,7 @@ func TestStart(t *testing.T) { t.Run("can-connect", func(t *testing.T) { t.Parallel() - ctr := Start(t) + ctr := Start(t, testImage) options, err := redis.ParseURL(ctr.URL) require.NoError(t, err) @@ -231,13 +228,4 @@ func TestConfig(t *testing.T) { WithURL(true)(config) require.True(t, config.UseURL) }) - - t.Run("with-image", func(t *testing.T) { - t.Parallel() - - customImage := "docker.io/redis:6" - config := &Config{} - WithImage(customImage)(config) - require.Equal(t, customImage, config.Image) - }) } diff --git a/valkey/valkey_test.go b/valkey/valkey_test.go index 80dd3520..0a35fa44 100644 --- a/valkey/valkey_test.go +++ b/valkey/valkey_test.go @@ -28,10 +28,7 @@ func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { img = imgFromEnv } - // Force Valkey image when running outside of GitHub Actions - opts = append(opts, testredis.WithImage(img)) - - redisCtr := testredis.Start(t, opts...) + redisCtr := testredis.Start(t, img, opts...) cfg := Config{ Reset: true, From 331a84b6c54942d529b036aac4f5a830481205bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 11:56:55 +0200 Subject: [PATCH 17/22] fix: reuse valkey container in benchmarks --- testhelpers/redis/redis.go | 19 ++++++++++++++++++- valkey/valkey_test.go | 37 ++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go index 23b7b9fc..9e14df7d 100644 --- a/testhelpers/redis/redis.go +++ b/testhelpers/redis/redis.go @@ -24,6 +24,7 @@ type Config struct { UseAddress bool UseHostPort bool UseURL bool + name string } // Option is a function that configures a Config @@ -59,6 +60,15 @@ func WithURL(useContainerURI bool) Option { } } +// WithReuse sets the container to be reused, +// providing a name to identify the container. +// This container is not cleaned up when the test completes. +func WithReuse(name string) Option { + return func(c *Config) { + c.name = name + } +} + // Container represents a running Redis container type Container struct { URL string @@ -109,8 +119,15 @@ func Start(t testing.TB, img string, opts ...Option) *Container { tcOpts = append(tcOpts, testcontainers.WithCmd(cmds...)) } + if config.name != "" { + tcOpts = append(tcOpts, testcontainers.WithReuseByName(config.name)) + } + c, err := redis.Run(ctx, img, tcOpts...) - testcontainers.CleanupContainer(t, c) + if config.name == "" { + // only cleanup the container if it's not being reused + testcontainers.CleanupContainer(t, c) + } require.NoError(t, err) ctr := &Container{ diff --git a/valkey/valkey_test.go b/valkey/valkey_test.go index 0a35fa44..ab4bd95f 100644 --- a/valkey/valkey_test.go +++ b/valkey/valkey_test.go @@ -2,6 +2,7 @@ package valkey import ( "os" + "sync" "testing" "time" @@ -16,6 +17,23 @@ const ( valkeyImageEnvVar = "TEST_VALKEY_IMAGE" ) +var ( + // benchmarkStore is a singleton store used for all benchmarks + benchmarkStore *Storage + // benchmarkStoreOnce ensures the store is initialized only once + benchmarkStoreOnce sync.Once +) + +// initBenchmarkStore initializes the singleton store for benchmarks. +// There is no need to call Close() on the returned store, it will be reused +// for all benchmarks. Testcontainers will reuse the container if it already exists +// and will terminate it at the end of a test session. +func initBenchmarkStore(b *testing.B) { + benchmarkStoreOnce.Do(func() { + benchmarkStore = newTestStore(b, testredis.WithReuse("valkey-benchmark")) + }) +} + // newConfigFromContainer creates a Redis configuration using Testcontainers. // It configures the container based on the provided options and returns a Config // that can be used to connect to the container. @@ -321,48 +339,45 @@ func Test_Valkey_Cluster(t *testing.T) { } func Benchmark_Valkey_Set(b *testing.B) { - testStore := newTestStore(b) - defer testStore.Close() + initBenchmarkStore(b) b.ReportAllocs() b.ResetTimer() var err error for i := 0; i < b.N; i++ { - err = testStore.Set("john", []byte("doe"), 0) + err = benchmarkStore.Set("john", []byte("doe"), 0) } require.NoError(b, err) } func Benchmark_Valkey_Get(b *testing.B) { - testStore := newTestStore(b) - defer testStore.Close() + initBenchmarkStore(b) - err := testStore.Set("john", []byte("doe"), 0) + err := benchmarkStore.Set("john", []byte("doe"), 0) require.NoError(b, err) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err = testStore.Get("john") + _, err = benchmarkStore.Get("john") } require.NoError(b, err) } func Benchmark_Valkey_SetAndDelete(b *testing.B) { - testStore := newTestStore(b) - defer testStore.Close() + initBenchmarkStore(b) b.ReportAllocs() b.ResetTimer() var err error for i := 0; i < b.N; i++ { - _ = testStore.Set("john", []byte("doe"), 0) - err = testStore.Delete("john") + _ = benchmarkStore.Set("john", []byte("doe"), 0) + err = benchmarkStore.Delete("john") } require.NoError(b, err) From 168125aba4e4263fdd6ebe1ce3417f4289e94e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 12:23:18 +0200 Subject: [PATCH 18/22] chore: refine message --- testhelpers/redis/redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testhelpers/redis/redis.go b/testhelpers/redis/redis.go index 9e14df7d..ec89c8ee 100644 --- a/testhelpers/redis/redis.go +++ b/testhelpers/redis/redis.go @@ -91,7 +91,7 @@ func Start(t testing.TB, img string, opts ...Option) *Container { } if img == "" { - panic("Redis image is not set: callers must provide an image using WithImage()") + panic("Redis image is not set: callers must provide a non-empty image parameter") } ctx := context.Background() From ba248c9967ed07da01bb92e04e454663cfe047b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 12:25:20 +0200 Subject: [PATCH 19/22] chore: align valkey expiration test with redis --- valkey/valkey_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/valkey/valkey_test.go b/valkey/valkey_test.go index ab4bd95f..997462e9 100644 --- a/valkey/valkey_test.go +++ b/valkey/valkey_test.go @@ -112,7 +112,7 @@ func Test_Valkey_Get(t *testing.T) { require.Equal(t, val, result) } -func Test_Valkey_Set_Expiration(t *testing.T) { +func Test_Valkey_Expiration(t *testing.T) { var ( key = "john" val = []byte("doe") @@ -126,13 +126,6 @@ func Test_Valkey_Set_Expiration(t *testing.T) { require.NoError(t, err) time.Sleep(1100 * time.Millisecond) -} - -func Test_Valkey_Get_Expired(t *testing.T) { - key := "john" - - testStore := newTestStore(t) - defer testStore.Close() result, err := testStore.Get(key) require.NoError(t, err) From 4c19cbad8975ec52f00aef4ddb0d73e7986426a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 12:30:26 +0200 Subject: [PATCH 20/22] chore: skip cluster tests --- redis/redis_test.go | 3 ++- rueidis/rueidis_test.go | 3 ++- valkey/valkey_test.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/redis/redis_test.go b/redis/redis_test.go index b8be18dd..001130f8 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -397,7 +397,8 @@ func Test_Redis_Universal_With_HostPort_And_URL(t *testing.T) { } func Test_Redis_Cluster(t *testing.T) { - // TODO: Replace with containerized cluster when testcontainers-go Redis module supports clustering + t.Skip("TODO: Replace with containerized cluster when testcontainers-go Redis module supports clustering") + testStoreUniversal := New(Config{ Addrs: []string{ "localhost:7000", diff --git a/rueidis/rueidis_test.go b/rueidis/rueidis_test.go index cca815e6..2d51ec9d 100644 --- a/rueidis/rueidis_test.go +++ b/rueidis/rueidis_test.go @@ -278,7 +278,8 @@ func Test_Rueidis_With_TLS_URL(t *testing.T) { } func Test_Rueidis_Cluster(t *testing.T) { - // TODO: Replace with containerized cluster when testcontainers-go Rueidis module supports clustering + t.Skip("TODO: Replace with containerized cluster when testcontainers-go Redis module supports clustering") + store := New(Config{ InitAddress: []string{ "localhost:7000", diff --git a/valkey/valkey_test.go b/valkey/valkey_test.go index 997462e9..de9e9929 100644 --- a/valkey/valkey_test.go +++ b/valkey/valkey_test.go @@ -302,7 +302,7 @@ func Test_Valkey_With_TLS_URL(t *testing.T) { func Test_Valkey_Cluster(t *testing.T) { t.Skip("TODO: Replace with containerized cluster when testcontainers-go Valkey module supports clustering") - // TODO: Replace with containerized cluster when testcontainers-go Valkey module supports clustering + store := New(Config{ InitAddress: []string{ "localhost:7000", From 288cedb4a4b7c936877a685b88e79995ab31f35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 12:30:40 +0200 Subject: [PATCH 21/22] chore: align ruedis expiration test with redis --- rueidis/rueidis_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/rueidis/rueidis_test.go b/rueidis/rueidis_test.go index 2d51ec9d..74486ece 100644 --- a/rueidis/rueidis_test.go +++ b/rueidis/rueidis_test.go @@ -82,7 +82,7 @@ func Test_Rueidis_Get(t *testing.T) { require.Equal(t, val, result) } -func Test_Rueidis_Set_Expiration(t *testing.T) { +func Test_Rueidis_Expiration(t *testing.T) { var ( key = "john" val = []byte("doe") @@ -96,13 +96,6 @@ func Test_Rueidis_Set_Expiration(t *testing.T) { require.NoError(t, err) time.Sleep(1100 * time.Millisecond) -} - -func Test_Rueidis_Get_Expired(t *testing.T) { - key := "john" - - testStore := newTestStore(t) - defer testStore.Close() result, err := testStore.Get(key) require.NoError(t, err) From bf7c3091006b457bbd38a76bd039a9ff2535db2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 May 2025 12:31:03 +0200 Subject: [PATCH 22/22] chore: read redis image for ruedis --- rueidis/rueidis_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rueidis/rueidis_test.go b/rueidis/rueidis_test.go index 74486ece..86111613 100644 --- a/rueidis/rueidis_test.go +++ b/rueidis/rueidis_test.go @@ -1,6 +1,7 @@ package rueidis import ( + "os" "testing" "time" @@ -9,6 +10,12 @@ import ( testredis "github.com/gofiber/storage/testhelpers/redis" ) +const ( + // redisImage is the default image used for running Redis in tests. + redisImage = "docker.io/redis:7" + redisImageEnvVar = "TEST_REDIS_IMAGE" +) + // newConfigFromContainer creates a Rueidis configuration using Testcontainers. // It configures the container based on the provided options and returns a Config // that can be used to connect to the container. @@ -16,7 +23,12 @@ import ( func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config { t.Helper() - redisCtr := testredis.Start(t, opts...) + img := redisImage + if imgFromEnv := os.Getenv(redisImageEnvVar); imgFromEnv != "" { + img = imgFromEnv + } + + redisCtr := testredis.Start(t, img, opts...) cfg := Config{ Reset: true,