1
0
mirror of https://github.com/redis/go-redis.git synced 2026-10-22 19:49:00 +03:00
Files
go-redis/geo_commands_unit_test.go
Yingzi Zhang 46ddf76cc9 fix(cmd): don't modify the caller's geo queries (#4173)
* fix(cmd): don't modify the caller's geo queries

geoLocationArgs incremented withLen on the GeoRadiusQuery that the
caller passed, and GeoLocationCmd read it back to parse the reply. A
second GeoRadius call with the same query parsed the reply with a
doubled withLen and failed with "got 2 elements in the array, wanted
3", and sharing one query between goroutines was a data race. Count
the WITH flags when the reply is read, as GeoSearchLocationCmd does,
and drop the withLen field.

geoSearchArgs also wrote the default "km" unit back to the
GeoSearchQuery. Keep the default in a local variable instead.

* fix(cmd): copy the geo query into the command

GeoLocationCmd and GeoSearchLocationCmd kept the caller's query and read
its WITH flags when the reply arrived. With a pipeline or autopipelining,
a change to the query between queuing and reading parsed the reply with
flags that weren't sent. Keep a copy of the query in the command, so the
reply is parsed with the flags of the command, and make Clone a plain
copy.
2026-10-21 14:47:28 +03:00

49 lines
1.6 KiB
Go

package redis
import (
"context"
"strings"
"testing"
"github.com/redis/go-redis/v9/internal/proto"
)
// A pipelined or autopipelined command is built when it is queued and its
// reply is read later. Changing the caller's query in between must not change
// how the reply is parsed.
func TestGeoCmdsParseReplyWithQueuedFlags(t *testing.T) {
// Two entries, each [name, dist]: the reply to a WITHDIST query.
const withDistReply = "*2\r\n" +
"*2\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n" +
"*2\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n"
t.Run("GeoLocationCmd", func(t *testing.T) {
q := &GeoRadiusQuery{Radius: 200, Unit: "km", WithDist: true}
cmd := NewGeoLocationCmd(context.Background(), q, "georadius", "Sicily", 15, 37)
q.WithDist, q.WithCoord = false, true
if err := cmd.readReply(proto.NewReader(strings.NewReader(withDistReply))); err != nil {
t.Fatalf("readReply: %v", err)
}
if got := cmd.Val(); len(got) != 2 || got[0].Dist != 56.4413 || got[1].Dist != 190.4424 {
t.Fatalf("got %+v", got)
}
})
t.Run("GeoSearchLocationCmd", func(t *testing.T) {
q := &GeoSearchLocationQuery{
GeoSearchQuery: GeoSearchQuery{Member: "Catania", Radius: 200},
WithDist: true,
}
cmd := NewGeoSearchLocationCmd(context.Background(), q, "geosearch", "Sicily")
q.WithDist, q.WithCoord = false, true
if err := cmd.readReply(proto.NewReader(strings.NewReader(withDistReply))); err != nil {
t.Fatalf("readReply: %v", err)
}
if got := cmd.Val(); len(got) != 2 || got[0].Dist != 56.4413 || got[1].Dist != 190.4424 {
t.Fatalf("got %+v", got)
}
})
}