From 41b41cc599757b2334b27479e78d149c435a1d8d Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Tue, 29 Sep 2026 10:40:47 +0000 Subject: [PATCH] ably: fix broken log format when presence history data fails to decode RESTPresence history logged the decode failure with %w, which the logger's Printf-style formatting renders as %!w(...). Use %v, as the message decoders in rest_channel.go do. --- ably/rest_presence.go | 2 +- ably/rest_presence_test.go | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 ably/rest_presence_test.go diff --git a/ably/rest_presence.go b/ably/rest_presence.go index 9fb4728d..de9604d0 100644 --- a/ably/rest_presence.go +++ b/ably/rest_presence.go @@ -248,7 +248,7 @@ func (t *fullPresenceDecoder) decodeMessagesData() { m.Message, err = m.Message.withDecodedData(cipher) if err != nil { // RSL6b - t.c.log().Errorf("Couldn't fully decode presence message data from channel %q: %w", t.c.Name, err) + t.c.log().Errorf("Couldn't fully decode presence message data from channel %q: %v", t.c.Name, err) } } } diff --git a/ably/rest_presence_test.go b/ably/rest_presence_test.go new file mode 100644 index 00000000..d3a3fc30 --- /dev/null +++ b/ably/rest_presence_test.go @@ -0,0 +1,78 @@ +//go:build !integration +// +build !integration + +package ably_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "strings" + "sync" + "testing" + + "github.com/ably/ably-go/ably" + + "github.com/stretchr/testify/assert" +) + +type recordingLogger struct { + mtx sync.Mutex + logs []string +} + +func (l *recordingLogger) Printf(level ably.LogLevel, format string, v ...interface{}) { + l.mtx.Lock() + defer l.mtx.Unlock() + l.logs = append(l.logs, fmt.Sprintf(format, v...)) +} + +func (l *recordingLogger) lines() []string { + l.mtx.Lock() + defer l.mtx.Unlock() + return append([]string(nil), l.logs...) +} + +func TestRESTPresence_HistoryLogsDecodeFailure(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("Content-Type", "application/json") + rw.Write([]byte(`[{"action":2,"clientId":"a","data":"x","encoding":"nonsense"}]`)) + })) + defer server.Close() + + serverURL, err := url.Parse(server.URL) + assert.NoError(t, err) + port, err := strconv.Atoi(serverURL.Port()) + assert.NoError(t, err) + + logger := &recordingLogger{} + client, err := ably.NewREST( + ably.WithTLS(false), + ably.WithToken("token"), + ably.WithUseBinaryProtocol(false), + ably.WithEndpoint(serverURL.Hostname()), + ably.WithPort(port), + ably.WithLogLevel(ably.LogError), + ably.WithLogHandler(logger), + ) + assert.NoError(t, err) + + pages, err := client.Channels.Get("test").Presence.History().Pages(context.Background()) + assert.NoError(t, err) + assert.True(t, pages.Next(context.Background())) + assert.Len(t, pages.Items(), 1) + + var found string + for _, line := range logger.lines() { + if strings.Contains(line, "Couldn't fully decode presence message data") { + found = line + } + } + if assert.NotEmpty(t, found, "expected a decode failure to be logged") { + assert.NotContains(t, found, "%!", "log line has a formatting error: %s", found) + assert.Contains(t, found, "unknown encoding nonsense") + } +}