mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-27 20:17:20 +02:00
Return reset func for testutil.LazyObj; Add testutil.LazySuite
This commit is contained in:
@@ -2,11 +2,9 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -32,8 +30,7 @@ type LoadTestSuite struct {
|
||||
testData *testutil.TestDataProvider
|
||||
testImagesPath string
|
||||
|
||||
addr net.Addr
|
||||
stopImgproxy context.CancelFunc
|
||||
server *TestServer
|
||||
}
|
||||
|
||||
// SetupSuite starts imgproxy instance server
|
||||
@@ -49,12 +46,12 @@ func (s *LoadTestSuite) SetupSuite() {
|
||||
config.DevelopmentErrorsMode = true
|
||||
|
||||
// In this test we start the single imgproxy server for all test cases
|
||||
s.addr, s.stopImgproxy = s.StartImgproxy(c)
|
||||
s.server = s.StartImgproxy(c)
|
||||
}
|
||||
|
||||
// TearDownSuite stops imgproxy instance server
|
||||
func (s *LoadTestSuite) TearDownSuite() {
|
||||
s.stopImgproxy()
|
||||
s.server.Shutdown()
|
||||
}
|
||||
|
||||
// testLoadFolder fetches images iterates over images in the specified folder,
|
||||
@@ -117,7 +114,7 @@ func (s *LoadTestSuite) testLoadFolder(folder string) {
|
||||
|
||||
// fetchImage fetches an image from the imgproxy server
|
||||
func (s *LoadTestSuite) fetchImage(path string) []byte {
|
||||
url := fmt.Sprintf("http://%s/%s", s.addr.String(), path)
|
||||
url := fmt.Sprintf("http://%s/%s", s.server.Addr, path)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
s.Require().NoError(err, "Failed to fetch image from %s", url)
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -36,6 +35,7 @@ type ProcessingHandlerTestSuite struct {
|
||||
// happen afterwards. It is done via lazy obj. When all config values will be moved
|
||||
// to imgproxy.Config struct, this can be removed.
|
||||
config testutil.LazyObj[*imgproxy.Config]
|
||||
server testutil.LazyObj[*TestServer]
|
||||
}
|
||||
|
||||
func (s *ProcessingHandlerTestSuite) SetupSuite() {
|
||||
@@ -44,15 +44,8 @@ func (s *ProcessingHandlerTestSuite) SetupSuite() {
|
||||
|
||||
// Initialize test data provider (local test files)
|
||||
s.testData = testutil.NewTestDataProvider(s.T())
|
||||
}
|
||||
|
||||
func (s *ProcessingHandlerTestSuite) TearDownSuite() {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
// setupObjs initializes lazy objects
|
||||
func (s *ProcessingHandlerTestSuite) setupObjs() {
|
||||
s.config = testutil.NewLazyObj(s.T(), func() (*imgproxy.Config, error) {
|
||||
s.config, _ = testutil.NewLazySuiteObj(s, func() (*imgproxy.Config, error) {
|
||||
c, err := imgproxy.LoadConfigFromEnv(nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
@@ -61,6 +54,21 @@ func (s *ProcessingHandlerTestSuite) setupObjs() {
|
||||
|
||||
return c, nil
|
||||
})
|
||||
|
||||
s.server, _ = testutil.NewLazySuiteObj(
|
||||
s,
|
||||
func() (*TestServer, error) {
|
||||
return s.StartImgproxy(s.config()), nil
|
||||
},
|
||||
func(s *TestServer) error {
|
||||
s.Shutdown()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *ProcessingHandlerTestSuite) TearDownSuite() {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
func (s *ProcessingHandlerTestSuite) SetupTest() {
|
||||
@@ -69,23 +77,17 @@ func (s *ProcessingHandlerTestSuite) SetupTest() {
|
||||
// NOTE: This must be moved to security config
|
||||
config.AllowLoopbackSourceAddresses = true
|
||||
// NOTE: end note
|
||||
|
||||
s.setupObjs()
|
||||
}
|
||||
|
||||
func (s *ProcessingHandlerTestSuite) SetupSubTest() {
|
||||
// We use t.Run() a lot, so we need to reset lazy objects at the beginning of each subtest
|
||||
s.setupObjs()
|
||||
s.ResetLazyObjects()
|
||||
}
|
||||
|
||||
// GET performs a GET request to the imageproxy real server
|
||||
// NOTE: Do not forget to move this to Suite in case of need in other future test suites
|
||||
func (s *ProcessingHandlerTestSuite) GET(path string, header ...http.Header) *http.Response {
|
||||
// In this test we start the imgproxy server instance per request
|
||||
addr, stopServer := s.StartImgproxy(s.config())
|
||||
defer stopServer()
|
||||
|
||||
url := fmt.Sprintf("http://%s%s", addr.String(), path)
|
||||
url := fmt.Sprintf("http://%s%s", s.server().Addr, path)
|
||||
|
||||
// Perform GET request to an url
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
@@ -99,13 +101,6 @@ func (s *ProcessingHandlerTestSuite) GET(path string, header ...http.Header) *ht
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Read the entire body into memory and replace the original body with memory reader
|
||||
// to avoid the defer
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
s.Require().NoError(err)
|
||||
resp.Body.Close()
|
||||
resp.Body = io.NopCloser(bytes.NewReader(bodyBytes))
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@@ -5,16 +5,21 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/imgproxy/imgproxy/v3"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/imgproxy/imgproxy/v3/testutil"
|
||||
)
|
||||
|
||||
type TestServer struct {
|
||||
Addr net.Addr
|
||||
Shutdown context.CancelFunc
|
||||
}
|
||||
|
||||
type Suite struct {
|
||||
suite.Suite
|
||||
testutil.LazySuite
|
||||
}
|
||||
|
||||
// StartImgproxy starts imgproxy instance for the tests
|
||||
// Returns instance, instance address and stop function
|
||||
func (s *Suite) StartImgproxy(c *imgproxy.Config) (net.Addr, context.CancelFunc) {
|
||||
func (s *Suite) StartImgproxy(c *imgproxy.Config) *TestServer {
|
||||
ctx, cancel := context.WithCancel(s.T().Context())
|
||||
|
||||
c.Server.Bind = ":0"
|
||||
@@ -32,5 +37,8 @@ func (s *Suite) StartImgproxy(c *imgproxy.Config) (net.Addr, context.CancelFunc)
|
||||
}
|
||||
}()
|
||||
|
||||
return <-addrCh, cancel
|
||||
return &TestServer{
|
||||
Addr: <-addrCh,
|
||||
Shutdown: cancel,
|
||||
}
|
||||
}
|
||||
|
59
testutil/lasy_suite.go
Normal file
59
testutil/lasy_suite.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// LazySuite is a test suite that automatically resets [LazyObj] instances.
|
||||
// It uses [LazySuite.AfterTest] to perform the reset after each test,
|
||||
// so if you also use this function in your test suite, don't forget to call
|
||||
// [LazySuite.AfterTest] or [LazySuite.ResetLazyObjects] explicitly.
|
||||
type LazySuite struct {
|
||||
suite.Suite
|
||||
|
||||
resets []context.CancelFunc
|
||||
}
|
||||
|
||||
// Lazy returns the LazySuite instance itself.
|
||||
// Needed to implement [LazySuiteFrom].
|
||||
func (s *LazySuite) Lazy() *LazySuite {
|
||||
return s
|
||||
}
|
||||
|
||||
// AfterTest is called by testify after each test.
|
||||
// If you also use this function in your test suite, don't forget to call
|
||||
// [LazySuite.AfterTest] or [LazySuite.ResetLazyObjects] explicitly.
|
||||
func (s *LazySuite) AfterTest(_, _ string) {
|
||||
// Reset lazy objects after each test
|
||||
s.ResetLazyObjects()
|
||||
}
|
||||
|
||||
// ResetLazyObjects resets all lazy objects created with [NewLazySuiteObj]
|
||||
func (s *LazySuite) ResetLazyObjects() {
|
||||
for _, reset := range s.resets {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
type LazySuiteFrom interface {
|
||||
Lazy() *LazySuite
|
||||
}
|
||||
|
||||
// NewLazySuiteObj creates a new [LazyObj] instance and registers its cleanup function
|
||||
// to a provided [LazySuite].
|
||||
func NewLazySuiteObj[T any](
|
||||
s LazySuiteFrom,
|
||||
newFn LazyObjNew[T],
|
||||
dropFn ...LazyObjDrop[T],
|
||||
) (LazyObj[T], context.CancelFunc) {
|
||||
// Get the [LazySuite] instance
|
||||
lazy := s.Lazy()
|
||||
// Create the [LazyObj] instance
|
||||
obj, cancel := NewLazyObj(lazy, newFn, dropFn...)
|
||||
// Add cleanup function to the resets list
|
||||
lazy.resets = append(lazy.resets, cancel)
|
||||
|
||||
return obj, cancel
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -9,24 +10,58 @@ import (
|
||||
// LazyObj is a function that returns an object of type T.
|
||||
type LazyObj[T any] func() T
|
||||
|
||||
// LazyObjInit is a function that initializes and returns an object of type T and an error if any.
|
||||
type LazyObjInit[T any] func() (T, error)
|
||||
// LazyObjT is an interface that provides access to the [testing.T] object.
|
||||
type LazyObjT interface {
|
||||
T() *testing.T
|
||||
}
|
||||
|
||||
// NewLazyObj creates a new LazyObj that initializes the object on the first call.
|
||||
func NewLazyObj[T any](t *testing.T, init LazyObjInit[T]) LazyObj[T] {
|
||||
t.Helper()
|
||||
// LazyObjNew is a function that creates and returns an object of type T and an error if any.
|
||||
type LazyObjNew[T any] func() (T, error)
|
||||
|
||||
// LazyObjDrop is a callback that is called when [LazyObj] is reset.
|
||||
// It receives a pointer to the object being dropped.
|
||||
// If the object was not yet initialized, the callback is not called.
|
||||
type LazyObjDrop[T any] func(T) error
|
||||
|
||||
// NewLazyObj creates a new [LazyObj] that initializes the object on the first call.
|
||||
// It returns a function that can be called to get the object and a cancel function
|
||||
// that can be called to reset the object.
|
||||
func NewLazyObj[T any](
|
||||
s LazyObjT,
|
||||
newFn LazyObjNew[T],
|
||||
dropFn ...LazyObjDrop[T],
|
||||
) (LazyObj[T], context.CancelFunc) {
|
||||
s.T().Helper()
|
||||
|
||||
var obj *T
|
||||
|
||||
return func() T {
|
||||
init := func() T {
|
||||
if obj != nil {
|
||||
return *obj
|
||||
}
|
||||
|
||||
o, err := init()
|
||||
require.NoError(t, err)
|
||||
o, err := newFn()
|
||||
require.NoError(s.T(), err, "Failed to initialize lazy object")
|
||||
|
||||
obj = &o
|
||||
return o
|
||||
}
|
||||
|
||||
cancel := func() {
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, fn := range dropFn {
|
||||
if fn == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(s.T(), fn(*obj), "Failed to reset lazy object")
|
||||
}
|
||||
|
||||
obj = nil
|
||||
}
|
||||
|
||||
return init, cancel
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/imgproxy/imgproxy/v3/ioutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -20,8 +21,8 @@ func ReadersEqual(t *testing.T, expected, actual io.Reader) bool {
|
||||
buf2 := make([]byte, bufSize)
|
||||
|
||||
for {
|
||||
n1, err1 := expected.Read(buf1)
|
||||
n2, err2 := actual.Read(buf2)
|
||||
n1, err1 := ioutil.TryReadFull(expected, buf1)
|
||||
n2, err2 := ioutil.TryReadFull(actual, buf2)
|
||||
|
||||
if n1 != n2 {
|
||||
return false
|
||||
|
Reference in New Issue
Block a user