diff --git a/client/v3/watch.go b/client/v3/watch.go index 41a6ec97633..963f7e65c39 100644 --- a/client/v3/watch.go +++ b/client/v3/watch.go @@ -1036,7 +1036,7 @@ func (pr *progressRequest) toPB() *pb.WatchRequest { func streamKeyFromCtx(ctx context.Context) string { if md, ok := metadata.FromOutgoingContext(ctx); ok { - return fmt.Sprintf("%+v", md) + return fmt.Sprintf("%+v", map[string][]string(md)) } return "" } diff --git a/client/v3/watch_test.go b/client/v3/watch_test.go index 2a56ca4a938..721fc4a8a23 100644 --- a/client/v3/watch_test.go +++ b/client/v3/watch_test.go @@ -15,8 +15,11 @@ package clientv3 import ( + "context" "testing" + "google.golang.org/grpc/metadata" + "go.etcd.io/etcd/api/v3/mvccpb" ) @@ -53,3 +56,53 @@ func TestEvent(t *testing.T) { } } } + +// TestStreamKeyFromCtx tests the streamKeyFromCtx function to ensure it correctly +// formats metadata as a map[string][]string when extracting metadata from the context. +// +// The fmt package in Go guarantees that maps are printed in a consistent order, +// sorted by the keys. This test verifies that the streamKeyFromCtx function +// produces the expected formatted string representation of metadata maps when called with +// various context scenarios. +func TestStreamKeyFromCtx(t *testing.T) { + tests := []struct { + name string + ctx context.Context + expected string + }{ + { + name: "multiple keys", + ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{ + "key1": []string{"value1"}, + "key2": []string{"value2a", "value2b"}, + }), + expected: "map[key1:[value1] key2:[value2a value2b]]", + }, + { + name: "no keys", + ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{}), + expected: "map[]", + }, + { + name: "only one key", + ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{ + "key1": []string{"value1", "value1a"}, + }), + expected: "map[key1:[value1 value1a]]", + }, + { + name: "no metadata", + ctx: context.Background(), + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := streamKeyFromCtx(tt.ctx) + if actual != tt.expected { + t.Errorf("streamKeyFromCtx() = %v, expected %v", actual, tt.expected) + } + }) + } +}