diff --git a/clientv3/watch.go b/clientv3/watch.go index d317010e229..98ec9a6af6b 100644 --- a/clientv3/watch.go +++ b/clientv3/watch.go @@ -1043,7 +1043,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/clientv3/watch_test.go b/clientv3/watch_test.go index a22858bc7ec..bb82764f27f 100644 --- a/clientv3/watch_test.go +++ b/clientv3/watch_test.go @@ -15,8 +15,11 @@ package clientv3 import ( + "context" "testing" + "google.golang.org/grpc/metadata" + "go.etcd.io/etcd/mvcc/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) + } + }) + } +}