// Package testutil provides isolated infrastructure for integration tests. package testutil import ( "context" "net" "os" "os/exec" "testing" "time" _ "github.com/gogf/gf/contrib/nosql/redis/v2" "github.com/gogf/gf/v2/database/gredis" ) // Redis starts a disposable loopback-only server with persistence disabled. // Tests never use the application's Redis configuration or an existing server. func Redis(t *testing.T) *gredis.Redis { t.Helper() binary := os.Getenv("REDIS_SERVER_BINARY") if binary == "" { var err error binary, err = exec.LookPath("redis-server") if err != nil { t.Skip("real Redis tests require redis-server on PATH or REDIS_SERVER_BINARY") } } listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } address := listener.Addr().String() _, port, err := net.SplitHostPort(address) if err != nil { t.Fatal(err) } _ = listener.Close() command := exec.Command(binary, "--bind", "127.0.0.1", "--port", port, "--save", "", "--appendonly", "no") command.Dir = t.TempDir() if err = command.Start(); err != nil { t.Fatal(err) } t.Cleanup(func() { _ = command.Process.Kill() _ = command.Wait() }) client, err := gredis.New(&gredis.Config{Address: address, DialTimeout: 100 * time.Millisecond, ReadTimeout: time.Second, WriteTimeout: time.Second}) if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = client.Close(context.Background()) }) for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); { var connection net.Conn connection, err = net.DialTimeout("tcp", address, 50*time.Millisecond) if err == nil { _ = connection.Close() if _, err = client.Do(context.Background(), "PING"); err == nil { return client } } time.Sleep(20 * time.Millisecond) } t.Fatalf("isolated Redis did not start: %v", err) return nil }