1
0
mirror of https://github.com/redis/go-redis.git synced 2025-12-02 06:22:31 +03:00

Merge remote-tracking branch 'origin/master' into ndyakov/state-machine-conn

This commit is contained in:
Nedyalko Dyakov
2025-10-28 12:24:30 +02:00
8 changed files with 71 additions and 43 deletions

View File

@@ -8,7 +8,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Check Spelling - name: Check Spelling
uses: rojopolis/spellcheck-github-actions@0.52.0 uses: rojopolis/spellcheck-github-actions@0.53.0
with: with:
config_path: .github/spellcheck-settings.yml config_path: .github/spellcheck-settings.yml
task_name: Markdown task_name: Markdown

View File

@@ -141,7 +141,9 @@ func (c cmdable) BitPos(ctx context.Context, key string, bit int64, pos ...int64
args[3] = pos[0] args[3] = pos[0]
args[4] = pos[1] args[4] = pos[1]
default: default:
panic("too many arguments") cmd := NewIntCmd(ctx)
cmd.SetErr(errors.New("too many arguments"))
return cmd
} }
cmd := NewIntCmd(ctx, args...) cmd := NewIntCmd(ctx, args...)
_ = c(ctx, cmd) _ = c(ctx, cmd)
@@ -182,7 +184,9 @@ func (c cmdable) BitFieldRO(ctx context.Context, key string, values ...interface
args[0] = "BITFIELD_RO" args[0] = "BITFIELD_RO"
args[1] = key args[1] = key
if len(values)%2 != 0 { if len(values)%2 != 0 {
panic("BitFieldRO: invalid number of arguments, must be even") c := NewIntSliceCmd(ctx)
c.SetErr(errors.New("BitFieldRO: invalid number of arguments, must be even"))
return c
} }
for i := 0; i < len(values); i += 2 { for i := 0; i < len(values); i += 2 {
args = append(args, "GET", values[i], values[i+1]) args = append(args, "GET", values[i], values[i+1])

View File

@@ -693,7 +693,9 @@ func (c cmdable) MemoryUsage(ctx context.Context, key string, samples ...int) *I
args := []interface{}{"memory", "usage", key} args := []interface{}{"memory", "usage", key}
if len(samples) > 0 { if len(samples) > 0 {
if len(samples) != 1 { if len(samples) != 1 {
panic("MemoryUsage expects single sample count") cmd := NewIntCmd(ctx)
cmd.SetErr(errors.New("MemoryUsage expects single sample count"))
return cmd
} }
args = append(args, "SAMPLES", samples[0]) args = append(args, "SAMPLES", samples[0])
} }

View File

@@ -735,6 +735,9 @@ var _ = Describe("Commands", func() {
n, err = client.MemoryUsage(ctx, "foo", 0).Result() n, err = client.MemoryUsage(ctx, "foo", 0).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(n).NotTo(BeZero()) Expect(n).NotTo(BeZero())
_, err = client.MemoryUsage(ctx, "foo", 0, 1).Result()
Expect(err).Should(MatchError("MemoryUsage expects single sample count"))
}) })
}) })
@@ -1598,6 +1601,9 @@ var _ = Describe("Commands", func() {
pos, err = client.BitPos(ctx, "mykey", 0, 0, 0).Result() pos, err = client.BitPos(ctx, "mykey", 0, 0, 0).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(pos).To(Equal(int64(-1))) Expect(pos).To(Equal(int64(-1)))
_, err = client.BitPos(ctx, "mykey", 0, 0, 0, 0, 0).Result()
Expect(err).Should(MatchError("too many arguments"))
}) })
It("should BitPosSpan", func() { It("should BitPosSpan", func() {
@@ -1635,6 +1641,9 @@ var _ = Describe("Commands", func() {
nn, err = client.BitFieldRO(ctx, "mykey", "u8", 0, "u4", 8, "u4", 12, "u4", 13).Result() nn, err = client.BitFieldRO(ctx, "mykey", "u8", 0, "u4", 8, "u4", 12, "u4", 13).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(nn).To(Equal([]int64{0, 15, 15, 14})) Expect(nn).To(Equal([]int64{0, 15, 15, 14}))
_, err = client.BitFieldRO(ctx, "mykey", "u8", 0, "u4", 8, "u4", 12, "u4").Result()
Expect(err).Should(MatchError("BitFieldRO: invalid number of arguments, must be even"))
}) })
It("should Decr", func() { It("should Decr", func() {
@@ -5254,6 +5263,9 @@ var _ = Describe("Commands", func() {
Score: 1, Score: 1,
Member: "one", Member: "one",
}})) }}))
_, err = client.ZPopMax(ctx, "zset", 10, 11).Result()
Expect(err).Should(MatchError("too many arguments"))
}) })
It("should ZPopMin", func() { It("should ZPopMin", func() {
@@ -5321,6 +5333,9 @@ var _ = Describe("Commands", func() {
Score: 3, Score: 3,
Member: "three", Member: "three",
}})) }}))
_, err = client.ZPopMin(ctx, "zset", 10, 11).Result()
Expect(err).Should(MatchError("too many arguments"))
}) })
It("should ZRange", func() { It("should ZRange", func() {

View File

@@ -95,7 +95,7 @@ func TestPeekPushNotificationName(t *testing.T) {
t.Run("NotPushNotification", func(t *testing.T) { t.Run("NotPushNotification", func(t *testing.T) {
// Test with regular array instead of push notification // Test with regular array instead of push notification
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString("*2\r\n$6\r\nMOVING\r\n$4\r\ndata\r\n") fmt.Fprint(buf, "*2\r\n$6\r\nMOVING\r\n$4\r\ndata\r\n")
reader := NewReader(buf) reader := NewReader(buf)
_, err := reader.PeekPushNotificationName() _, err := reader.PeekPushNotificationName()
@@ -112,7 +112,7 @@ func TestPeekPushNotificationName(t *testing.T) {
t.Run("InsufficientData", func(t *testing.T) { t.Run("InsufficientData", func(t *testing.T) {
// Test with buffer smaller than peek size - this might panic due to bounds checking // Test with buffer smaller than peek size - this might panic due to bounds checking
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">") fmt.Fprint(buf, ">")
reader := NewReader(buf) reader := NewReader(buf)
func() { func() {
@@ -146,7 +146,7 @@ func TestPeekPushNotificationName(t *testing.T) {
t.Run(fmt.Sprintf("Type_%c", respType), func(t *testing.T) { t.Run(fmt.Sprintf("Type_%c", respType), func(t *testing.T) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteByte(respType) buf.WriteByte(respType)
buf.WriteString("test data that fills the buffer completely") fmt.Fprint(buf, "test data that fills the buffer completely")
reader := NewReader(buf) reader := NewReader(buf)
_, err := reader.PeekPushNotificationName() _, err := reader.PeekPushNotificationName()
@@ -167,7 +167,7 @@ func TestPeekPushNotificationName(t *testing.T) {
t.Run("ZeroLengthArray", func(t *testing.T) { t.Run("ZeroLengthArray", func(t *testing.T) {
// Create push notification with zero elements: >0\r\n // Create push notification with zero elements: >0\r\n
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">0\r\npadding_data_to_fill_buffer_completely") fmt.Fprint(buf, ">0\r\npadding_data_to_fill_buffer_completely")
reader := NewReader(buf) reader := NewReader(buf)
_, err := reader.PeekPushNotificationName() _, err := reader.PeekPushNotificationName()
@@ -209,7 +209,7 @@ func TestPeekPushNotificationName(t *testing.T) {
for _, tc := range corruptedCases { for _, tc := range corruptedCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(tc.data) fmt.Fprint(buf, tc.data)
reader := NewReader(buf) reader := NewReader(buf)
// Some corrupted data might not error but return unexpected results // Some corrupted data might not error but return unexpected results
@@ -230,7 +230,7 @@ func TestPeekPushNotificationName(t *testing.T) {
// Create buffer that is exactly 36 bytes (the peek window size) // Create buffer that is exactly 36 bytes (the peek window size)
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
// ">1\r\n$4\r\nTEST\r\n" = 14 bytes, need 22 more // ">1\r\n$4\r\nTEST\r\n" = 14 bytes, need 22 more
buf.WriteString(">1\r\n$4\r\nTEST\r\n1234567890123456789012") fmt.Fprint(buf, ">1\r\n$4\r\nTEST\r\n1234567890123456789012")
if buf.Len() != 36 { if buf.Len() != 36 {
t.Errorf("Expected buffer length 36, got %d", buf.Len()) t.Errorf("Expected buffer length 36, got %d", buf.Len())
} }
@@ -295,25 +295,26 @@ func createValidPushNotification(notificationName, data string) *bytes.Buffer {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
simpleOrString := rand.Intn(2) == 0 simpleOrString := rand.Intn(2) == 0
defMsg := fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)
if data == "" { if data == "" {
// Single element notification // Single element notification
buf.WriteString(">1\r\n") fmt.Fprint(buf, ">1\r\n")
if simpleOrString { if simpleOrString {
buf.WriteString(fmt.Sprintf("+%s\r\n", notificationName)) fmt.Fprintf(buf, "+%s\r\n", notificationName)
} else { } else {
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)) fmt.Fprint(buf, defMsg)
} }
} else { } else {
// Two element notification // Two element notification
buf.WriteString(">2\r\n") fmt.Fprint(buf, ">2\r\n")
if simpleOrString { if simpleOrString {
buf.WriteString(fmt.Sprintf("+%s\r\n", notificationName)) fmt.Fprintf(buf, "+%s\r\n", notificationName)
buf.WriteString(fmt.Sprintf("+%s\r\n", data)) fmt.Fprintf(buf, "+%s\r\n", data)
} else { } else {
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)) fmt.Fprint(buf, defMsg)
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)) fmt.Fprint(buf, defMsg)
} }
} }
@@ -333,14 +334,14 @@ func createPushNotificationWithArgs(notificationName string, args ...string) *by
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
totalElements := 1 + len(args) totalElements := 1 + len(args)
buf.WriteString(fmt.Sprintf(">%d\r\n", totalElements)) fmt.Fprintf(buf, ">%d\r\n", totalElements)
// Write notification name // Write notification name
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(notificationName), notificationName)
// Write arguments // Write arguments
for _, arg := range args { for _, arg := range args {
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(arg), arg)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(arg), arg)
} }
return buf return buf
@@ -349,8 +350,8 @@ func createPushNotificationWithArgs(notificationName string, args ...string) *by
// createSingleElementPushNotification creates a push notification with single element // createSingleElementPushNotification creates a push notification with single element
func createSingleElementPushNotification(notificationName string) *bytes.Buffer { func createSingleElementPushNotification(notificationName string) *bytes.Buffer {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">1\r\n") fmt.Fprint(buf, ">1\r\n")
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationName), notificationName)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(notificationName), notificationName)
return buf return buf
} }

View File

@@ -2,6 +2,7 @@ package proto_test
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"testing" "testing"
@@ -86,7 +87,7 @@ func TestReader_ReadLine(t *testing.T) {
func benchmarkParseReply(b *testing.B, reply string, wanterr bool) { func benchmarkParseReply(b *testing.B, reply string, wanterr bool) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
buf.WriteString(reply) fmt.Fprint(buf, reply)
} }
p := proto.NewReader(buf) p := proto.NewReader(buf)
b.ResetTimer() b.ResetTimer()

View File

@@ -837,14 +837,14 @@ func createFakeRESP3PushNotification(notificationType string, args ...string) *b
// RESP3 Push notification format: ><len>\r\n<elements>\r\n // RESP3 Push notification format: ><len>\r\n<elements>\r\n
totalElements := 1 + len(args) // notification type + arguments totalElements := 1 + len(args) // notification type + arguments
buf.WriteString(fmt.Sprintf(">%d\r\n", totalElements)) fmt.Fprintf(buf, ">%d\r\n", totalElements)
// Write notification type as bulk string // Write notification type as bulk string
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(notificationType), notificationType)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(notificationType), notificationType)
// Write arguments as bulk strings // Write arguments as bulk strings
for _, arg := range args { for _, arg := range args {
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(arg), arg)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(arg), arg)
} }
return buf return buf
@@ -868,11 +868,11 @@ func createFakeRESP3Array(elements ...string) *bytes.Buffer {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
// RESP3 Array format: *<len>\r\n<elements>\r\n // RESP3 Array format: *<len>\r\n<elements>\r\n
buf.WriteString(fmt.Sprintf("*%d\r\n", len(elements))) fmt.Fprintf(buf, "*%d\r\n", len(elements))
// Write elements as bulk strings // Write elements as bulk strings
for _, element := range elements { for _, element := range elements {
buf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(element), element)) fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(element), element)
} }
return buf return buf
@@ -881,7 +881,7 @@ func createFakeRESP3Array(elements ...string) *bytes.Buffer {
// createFakeRESP3Error creates a fake RESP3 error // createFakeRESP3Error creates a fake RESP3 error
func createFakeRESP3Error(message string) *bytes.Buffer { func createFakeRESP3Error(message string) *bytes.Buffer {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(fmt.Sprintf("-%s\r\n", message)) fmt.Fprintf(buf, "-%s\r\n", message)
return buf return buf
} }
@@ -1117,7 +1117,7 @@ func TestProcessorWithFakeBuffer(t *testing.T) {
// Create fake RESP3 push notification with no elements // Create fake RESP3 push notification with no elements
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">0\r\n") // Empty push notification fmt.Fprint(buf, ">0\r\n") // Empty push notification
reader := createReaderWithPrimedBuffer(buf) reader := createReaderWithPrimedBuffer(buf)
ctx := context.Background() ctx := context.Background()
@@ -1154,9 +1154,9 @@ func TestProcessorWithFakeBuffer(t *testing.T) {
// Create fake RESP3 push notification with integer as first element // Create fake RESP3 push notification with integer as first element
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">2\r\n") // 2 elements fmt.Fprint(buf, ">2\r\n") // 2 elements
buf.WriteString(":123\r\n") // Integer instead of string fmt.Fprint(buf, ":123\r\n") // Integer instead of string
buf.WriteString("$4\r\ndata\r\n") // String data fmt.Fprint(buf, "$4\r\ndata\r\n") // String data
reader := proto.NewReader(buf) reader := proto.NewReader(buf)
ctx := context.Background() ctx := context.Background()
@@ -1273,8 +1273,8 @@ func TestVoidProcessorWithFakeBuffer(t *testing.T) {
// Create invalid RESP3 data // Create invalid RESP3 data
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">1\r\n") // Push notification with 1 element fmt.Fprint(buf, ">1\r\n") // Push notification with 1 element
buf.WriteString("invalid\r\n") // Invalid format (should be $<len>\r\n<data>\r\n) fmt.Fprint(buf, "invalid\r\n") // Invalid format (should be $<len>\r\n<data>\r\n)
reader := proto.NewReader(buf) reader := proto.NewReader(buf)
ctx := context.Background() ctx := context.Background()
@@ -1332,9 +1332,9 @@ func TestProcessorErrorHandling(t *testing.T) {
// Create buffer with corrupted RESP3 data // Create buffer with corrupted RESP3 data
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">2\r\n") // Says 2 elements fmt.Fprint(buf, ">2\r\n") // Says 2 elements
buf.WriteString("$6\r\nMOVING\r\n") // First element OK fmt.Fprint(buf, "$6\r\nMOVING\r\n") // First element OK
buf.WriteString("corrupted") // Second element corrupted (no proper format) fmt.Fprint(buf, "corrupted") // Second element corrupted (no proper format)
reader := proto.NewReader(buf) reader := proto.NewReader(buf)
ctx := context.Background() ctx := context.Background()
@@ -1360,8 +1360,8 @@ func TestProcessorErrorHandling(t *testing.T) {
// Create buffer with partial RESP3 data // Create buffer with partial RESP3 data
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString(">2\r\n") // Says 2 elements fmt.Fprint(buf, ">2\r\n") // Says 2 elements
buf.WriteString("$6\r\nMOVING\r\n") // First element OK fmt.Fprint(buf, "$6\r\nMOVING\r\n") // First element OK
// Missing second element // Missing second element
reader := proto.NewReader(buf) reader := proto.NewReader(buf)

View File

@@ -2,6 +2,7 @@ package redis
import ( import (
"context" "context"
"errors"
"strings" "strings"
"time" "time"
@@ -313,7 +314,9 @@ func (c cmdable) ZPopMax(ctx context.Context, key string, count ...int64) *ZSlic
case 1: case 1:
args = append(args, count[0]) args = append(args, count[0])
default: default:
panic("too many arguments") cmd := NewZSliceCmd(ctx)
cmd.SetErr(errors.New("too many arguments"))
return cmd
} }
cmd := NewZSliceCmd(ctx, args...) cmd := NewZSliceCmd(ctx, args...)
@@ -333,7 +336,9 @@ func (c cmdable) ZPopMin(ctx context.Context, key string, count ...int64) *ZSlic
case 1: case 1:
args = append(args, count[0]) args = append(args, count[0])
default: default:
panic("too many arguments") cmd := NewZSliceCmd(ctx)
cmd.SetErr(errors.New("too many arguments"))
return cmd
} }
cmd := NewZSliceCmd(ctx, args...) cmd := NewZSliceCmd(ctx, args...)