ronantakizawa/codereview-bench huggingface dataset:
Repository: etcd-io/etcd
File: tests/integration/clientv3/metrics_test.go (Go)

Code before:
	)

	srv := &http.Server{Handler: promhttp.Handler()}
	srv.SetKeepAlivesEnabled(false)

	ln, err := transport.NewUnixListener(addr)
	if err != nil {
		t.Errorf("Error: %v occurred while listening on addr: %v", err, addr)
	}

	donec := make(chan struct{})
	defer func() {
		ln.Close()
		<-donec
	}()

	// listen for all Prometheus metrics
	go func() {
		defer close(donec)

		serr := srv.Serve(ln)
		if serr != nil && !transport.IsClosedConnError(serr) {
			t.Errorf("Err serving http requests: %v", serr)
		}
	}()

	url := "unix://" + addr + "/metrics"

	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
	defer clus.Terminate(t)

	clientMetrics := grpcprom.NewClientMetrics()
	prometheus.Register(clientMetrics)

	cfg := clientv3.Config{
		Endpoints: []string{clus.Members[0].GRPCURL},
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),
			grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
		},
	}
	cli, cerr := integration2.NewClient(t, cfg)
	if cerr != nil {
		t.Fatal(cerr)
	}
	defer cli.Close()

	// Perform some operations to generate metrics
	wc := cli.Watch(context.Background(), "foo")
	_, err = cli.Put(context.Background(), "foo", "bar")
	if err != nil {

Review comment (suggestion):
Thinking whether e2e test here would better, more accurate and would not require this custom code. But don't want to block on it.

Code after:

	srv := &http.Server{Handler: promhttp.Handler()}
	srv.SetKeepAlivesEnabled(false)

	ln, err := transport.NewUnixListener(addr)
	if err != nil {
		t.Errorf("Error: %v occurred while listening on addr: %v", err, addr)
	}

	donec := make(chan struct{})
	defer func() {
		ln.Close()
		<-donec
	}()

	// listen for all Prometheus metrics
	go func() {
		defer close(donec)

		serr := srv.Serve(ln)
		if serr != nil && !transport.IsClosedConnError(serr) {
			t.Errorf("Err serving http requests: %v", serr)
		}
	}()

	url := "unix://" + addr + "/metrics"

	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
	defer clus.Terminate(t)

	clientMetrics := grpcprom.NewClientMetrics()
	prometheus.Register(clientMetrics)

	cfg := clientv3.Config{
		Endpoints: []string{clus.Members[0].GRPCURL},
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),
			grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
		},
	}
	cli, cerr := integration2.NewClient(t, cfg)
	if cerr != nil {
		t.Fatal(cerr)
	}
	defer cli.Close()

	// Perform some operations to generate metrics
	wc := cli.Watch(context.Background(), "foo")
	_, err = cli.Put(context.Background(), "foo", "bar")
	if err != nil {
		t.Errorf("Error putting value in key store")
Repository: etcd-io/etcd
File: tests/e2e/cluster_downgrade_test.go (Go)

Code before:
	"fmt"
	"testing"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"go.etcd.io/etcd/api/v3/version"
	"go.etcd.io/etcd/client/pkg/v3/fileutil"
	"go.etcd.io/etcd/client/pkg/v3/types"
	clientv3 "go.etcd.io/etcd/client/v3"
	"go.etcd.io/etcd/server/v3/etcdserver"
	"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
	"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
	"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
	"go.etcd.io/etcd/server/v3/storage/datadir"
	"go.etcd.io/etcd/tests/v3/framework/config"
	"go.etcd.io/etcd/tests/v3/framework/e2e"
)

type CancellationState int

const (
	NoCancellation CancellationState = iota
	CancelRightBeforeEnable
	CancelRightAfterEnable
)

func TestDowngradeUpgradeClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, NoCancellation)
}

func TestDowngradeUpgradeClusterOf3(t *testing.T) {
	testDowngradeUpgrade(t, 3, false, NoCancellation)
}

func TestDowngradeUpgradeClusterOf1WithSnapshot(t *testing.T) {
	testDowngradeUpgrade(t, 1, true, NoCancellation)
}

func TestDowngradeUpgradeClusterOf3WithSnapshot(t *testing.T) {
	testDowngradeUpgrade(t, 3, true, NoCancellation)
}

func TestDowngradeCancellationWithoutEnablingClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, CancelRightBeforeEnable)
}

func TestDowngradeCancellationRightAfterEnablingClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, CancelRightAfterEnable)

Review comment (suggestion):
is `CancelRightBeforeEnable` necessary? cancellation does not do anything before the downgrade process starts

Code after:
	"fmt"
	"testing"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"go.etcd.io/etcd/api/v3/version"
	"go.etcd.io/etcd/client/pkg/v3/fileutil"
	"go.etcd.io/etcd/client/pkg/v3/types"
	clientv3 "go.etcd.io/etcd/client/v3"
	"go.etcd.io/etcd/server/v3/etcdserver"
	"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
	"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
	"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
	"go.etcd.io/etcd/server/v3/storage/datadir"
	"go.etcd.io/etcd/tests/v3/framework/config"
	"go.etcd.io/etcd/tests/v3/framework/e2e"
)

type CancellationState int

const (
	noCancellation CancellationState = iota
	cancelRightBeforeEnable
	cancelRightAfterEnable
)

func TestDowngradeUpgradeClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, noCancellation)
}

func TestDowngradeUpgradeClusterOf3(t *testing.T) {
	testDowngradeUpgrade(t, 3, false, noCancellation)
}

func TestDowngradeUpgradeClusterOf1WithSnapshot(t *testing.T) {
	testDowngradeUpgrade(t, 1, true, noCancellation)
}

func TestDowngradeUpgradeClusterOf3WithSnapshot(t *testing.T) {
	testDowngradeUpgrade(t, 3, true, noCancellation)
}

func TestDowngradeCancellationWithoutEnablingClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, cancelRightBeforeEnable)
}

func TestDowngradeCancellationRightAfterEnablingClusterOf1(t *testing.T) {
	testDowngradeUpgrade(t, 1, false, cancelRightAfterEnable)
Repository: etcd-io/etcd
File: server/mock/mockstorage/storage_recorder.go (Go)

Code before:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mockstorage

import (
	"github.com/coreos/go-semver/semver"

	"go.etcd.io/etcd/client/pkg/v3/testutil"
	"go.etcd.io/etcd/server/v3/storage"
	"go.etcd.io/raft/v3"
	"go.etcd.io/raft/v3/raftpb"
)

type StorageRecorder interface {
	storage.Storage
	testutil.Recorder
}

type storageRecorder struct {
	testutil.Recorder
	dbPath string // must have '/' suffix if set
}

func NewStorageRecorder(db string) StorageRecorder {
	return &storageRecorder{&testutil.RecorderBuffered{}, db}
}

func NewStorageRecorderStream(db string) StorageRecorder {
	return &storageRecorder{testutil.NewRecorderStream(), db}
}

func (p *storageRecorder) Save(st raftpb.HardState, ents []raftpb.Entry) error {
	p.Record(testutil.Action{Name: "Save"})
	return nil
}

func (p *storageRecorder) SaveSnap(st raftpb.Snapshot) error {
	if !raft.IsEmptySnap(st) {
		p.Record(testutil.Action{Name: "SaveSnap"})
	}
	return nil
}

func (p *storageRecorder) Release(st raftpb.Snapshot) error {

Review comment (suggestion):
An alternative & simpler solution is to rename `storageRecorder` to `StorageRecorder`.

Code after:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mockstorage

import (
	"github.com/coreos/go-semver/semver"

	"go.etcd.io/etcd/client/pkg/v3/testutil"
	"go.etcd.io/raft/v3"
	"go.etcd.io/raft/v3/raftpb"
)

type StorageRecorder struct {
	testutil.Recorder
	dbPath string // must have '/' suffix if set
}

func NewStorageRecorder(db string) *StorageRecorder {
	return &StorageRecorder{&testutil.RecorderBuffered{}, db}
}

func NewStorageRecorderStream(db string) *StorageRecorder {
	return &StorageRecorder{testutil.NewRecorderStream(), db}
}

func (p *StorageRecorder) Save(st raftpb.HardState, ents []raftpb.Entry) error {
	p.Record(testutil.Action{Name: "Save"})
	return nil
}

func (p *StorageRecorder) SaveSnap(st raftpb.Snapshot) error {
	if !raft.IsEmptySnap(st) {
		p.Record(testutil.Action{Name: "SaveSnap"})
	}
	return nil
}

func (p *StorageRecorder) Release(st raftpb.Snapshot) error {
	if !raft.IsEmptySnap(st) {
		p.Record(testutil.Action{Name: "Release"})
	}
	return nil
}
Repository: etcd-io/etcd
File: server/storage/schema/alarm.go (Go)

Code before:
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package schema

import (
	"go.uber.org/zap"

	"go.etcd.io/etcd/api/v3/etcdserverpb"
	"go.etcd.io/etcd/server/v3/storage/backend"
)

type AlarmBackend interface {
	CreateAlarmBucket()
	MustPutAlarm(alarm *etcdserverpb.AlarmMember)
	MustDeleteAlarm(alarm *etcdserverpb.AlarmMember)
	GetAllAlarms() ([]*etcdserverpb.AlarmMember, error)
	ForceCommit()
}

type alarmBackend struct {
	lg *zap.Logger
	be backend.Backend
}

func NewAlarmBackend(lg *zap.Logger, be backend.Backend) AlarmBackend {
	return &alarmBackend{
		lg: lg,
		be: be,
	}
}

func (s *alarmBackend) CreateAlarmBucket() {
	tx := s.be.BatchTx()
	tx.LockOutsideApply()
	defer tx.Unlock()
	tx.UnsafeCreateBucket(Alarm)
}

func (s *alarmBackend) MustPutAlarm(alarm *etcdserverpb.AlarmMember) {
	tx := s.be.BatchTx()
	tx.LockInsideApply()
	defer tx.Unlock()
	s.mustUnsafePutAlarm(tx, alarm)

Review comment (refactor):
It's duplicated to 

https://github.com/etcd-io/etcd/blob/f1d09c0e6badd3b8ce5c7aef685d23f706457be9/server/etcdserver/api/v3alarm/alarms.go#L32-L38

The simplest solution is to move the interface into `storage/alarm`. cc @serathius 

Code after:
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package schema

import (
	"go.uber.org/zap"

	"go.etcd.io/etcd/api/v3/etcdserverpb"
	"go.etcd.io/etcd/server/v3/storage/backend"
)

type AlarmBackend interface {
	CreateAlarmBucket()
	MustPutAlarm(member *etcdserverpb.AlarmMember)
	MustDeleteAlarm(alarm *etcdserverpb.AlarmMember)
	GetAllAlarms() ([]*etcdserverpb.AlarmMember, error)
	ForceCommit()
}

type alarmBackend struct {
	lg *zap.Logger
	be backend.Backend
}

func NewAlarmBackend(lg *zap.Logger, be backend.Backend) AlarmBackend {
	return &alarmBackend{
		lg: lg,
		be: be,
	}
}

func (s *alarmBackend) CreateAlarmBucket() {
	tx := s.be.BatchTx()
	tx.LockOutsideApply()
	defer tx.Unlock()
	tx.UnsafeCreateBucket(Alarm)
}

func (s *alarmBackend) MustPutAlarm(alarm *etcdserverpb.AlarmMember) {
	tx := s.be.BatchTx()
	tx.LockInsideApply()
	defer tx.Unlock()
	s.mustUnsafePutAlarm(tx, alarm)
Repository: etcd-io/etcd
File: etcdutl/snapshot/v3_snapshot.go (Go)

Code before:
				_, err = h.Write(v)
				if err != nil {
					return fmt.Errorf("cannot hash bucket key: %q value: %q err: %w", k, v, err)
				}
				if iskeyb {
					var rev mvcc.Revision
					rev, err = bytesToRev(k)
					if err != nil {
						return fmt.Errorf("cannot parse revision key: %q err: %w", k, err)
					}
					ds.Revision = rev.Main

					var kv mvccpb.KeyValue
					err = kv.Unmarshal(v)
					if err != nil {
						return fmt.Errorf("cannot unmarshal value, key: %q value: %q err: %w", k, v, err)
					}
					key := string(kv.Key)
					// Version == 0 indicates logical deletion.
					// refer to https://etcd.io/docs/v3.5/learning/data_model/
					isActive := kv.Version > 0
					if curr, exists := keyStates[key]; !exists || rev.Main > curr.rev {
						keyStates[key] = struct {
							rev    int64
							active bool
						}{rev.Main, isActive}
					}
				}
				return nil
			}); err != nil {
				return fmt.Errorf("error during bucket key iteration, name: %q err: %w", string(next), err)
			}
		}
		return nil
	}); err != nil {
		return ds, err
	}

	for _, state := range keyStates {
		if state.active {
			ds.TotalKey++
		}
	}
	ds.Hash = h.Sum32()
	return ds, nil
}

func bytesToRev(b []byte) (rev mvcc.Revision, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%s", r)

Review comment (suggestion):
- Pls use `isTombstone` to check whether it's a tombstone. We can expose the function. https://github.com/etcd-io/etcd/blob/c6b9e9e9277121b347f63eb82d28c2c79611557e/server/storage/mvcc/revision.go#L120
- No need to compare revision. They are guaranteed to be increasing.

Code after:
				}
				if iskeyb {
					var rev mvcc.Revision
					rev, err = bytesToRev(k)
					if err != nil {
						return fmt.Errorf("cannot parse revision key: %q err: %w", k, err)
					}
					ds.Revision = rev.Main

					var kv mvccpb.KeyValue
					err = kv.Unmarshal(v)
					if err != nil {
						return fmt.Errorf("cannot unmarshal value, key: %q value: %q err: %w", k, v, err)
					}
					key := string(kv.Key)
					// refer to https://etcd.io/docs/v3.5/learning/data_model/
					if !mvcc.IsTombstone(k) {
						seenKeys[key] = struct{}{}
					} else {
						delete(seenKeys, key)
					}
				}
				return nil
			}); err != nil {
				return fmt.Errorf("error during bucket key iteration, name: %q err: %w", string(next), err)
			}
		}
		return nil
	}); err != nil {
		return ds, err
	}

	ds.TotalKey = len(seenKeys)
	ds.Hash = h.Sum32()
	return ds, nil
}

func bytesToRev(b []byte) (rev mvcc.Revision, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%s", r)
		}
	}()
	return mvcc.BytesToRev(b), err
}

// RestoreConfig configures snapshot restore operation.
type RestoreConfig struct {
	// SnapshotPath is the path of snapshot file to restore from.
	SnapshotPath string
Repository: etcd-io/etcd
File: tests/robustness/validate/patch_history.go (Go)

Code before:
					case model.RangeOperation:
					case model.PutOperation:
						kv := keyValue{Key: event.Key, Value: event.Value}
						if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
							earliestReturnTime[kv] = resp.Time.Nanoseconds()
						}
					case model.DeleteOperation:
					default:
						panic(fmt.Sprintf("unknown event type %q", event.Type))
					}
				}
			}
		}
	}

	for i := len(persistedRequests) - 1; i >= 0; i-- {
		request := persistedRequests[i]
		switch request.Type {
		case model.Txn:
			lastReturnTime--
			for _, op := range request.Txn.OperationsOnSuccess {
				if op.Type != model.PutOperation {
					continue
				}
				kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
				if count := persistedPutCount[kv]; count > 1 {
					continue
				}
				returnTime, ok := earliestReturnTime[kv]
				if ok {
					lastReturnTime = min(returnTime, lastReturnTime)
					earliestReturnTime[kv] = lastReturnTime
				}
			}
		case model.LeaseGrant:
		case model.LeaseRevoke:
		case model.Compact:
		default:
			panic(fmt.Sprintf("Unknown request type: %q", request.Type))
		}
	}
	return earliestReturnTime
}

func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
	counter := map[keyValue]int64{}
	for _, client := range reports {
		for _, op := range client.KeyValue {
			request := op.Input.(model.EtcdRequest)
			countPuts(counter, request)
		}

Review comment (suggestion):
I think we should rather test uniqueness of `clientPutCount` instead of `persistedPutCount`, it should be more obvious for reader, while there is not much difference in effect.

Code after:
			for _, resp := range watch.Responses {
				for _, event := range resp.Events {
					switch event.Type {
					case model.RangeOperation:
					case model.PutOperation:
						kv := keyValue{Key: event.Key, Value: event.Value}
						if count := clientPutCount[kv]; count > 1 {
							continue
						}
						if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
							earliestReturnTime[kv] = resp.Time.Nanoseconds()
						}
					case model.DeleteOperation:
					default:
						panic(fmt.Sprintf("unknown event type %q", event.Type))
					}
				}
			}
		}
	}

	for i := len(persistedRequests) - 1; i >= 0; i-- {
		request := persistedRequests[i]
		switch request.Type {
		case model.Txn:
			lastReturnTime--
			for _, op := range request.Txn.OperationsOnSuccess {
				if op.Type != model.PutOperation {
					continue
				}
				kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
				if count := clientPutCount[kv]; count > 1 {
					continue
				}
				returnTime, ok := earliestReturnTime[kv]
				if ok {
					lastReturnTime = min(returnTime, lastReturnTime)
					earliestReturnTime[kv] = lastReturnTime
				}
			}
		case model.LeaseGrant:
		case model.LeaseRevoke:
		case model.Compact:
		default:
			panic(fmt.Sprintf("Unknown request type: %q", request.Type))
		}
	}
	return earliestReturnTime
}

func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
Repository: etcd-io/etcd
File: tests/robustness/validate/patch_history.go (Go)

Code before:
						panic(fmt.Sprintf("unknown event type %q", event.Type))
					}
				}
			}
		}
	}

	for i := len(persistedRequests) - 1; i >= 0; i-- {
		request := persistedRequests[i]
		switch request.Type {
		case model.Txn:
			lastReturnTime--
			for _, op := range request.Txn.OperationsOnSuccess {
				if op.Type != model.PutOperation {
					continue
				}
				kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
				if count := persistedPutCount[kv]; count > 1 {
					continue
				}
				returnTime, ok := earliestReturnTime[kv]
				if ok {
					lastReturnTime = min(returnTime, lastReturnTime)
					earliestReturnTime[kv] = lastReturnTime
				}
			}
		case model.LeaseGrant:
		case model.LeaseRevoke:
		case model.Compact:
		default:
			panic(fmt.Sprintf("Unknown request type: %q", request.Type))
		}
	}
	return earliestReturnTime
}

func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
	counter := map[keyValue]int64{}
	for _, client := range reports {
		for _, op := range client.KeyValue {
			request := op.Input.(model.EtcdRequest)
			countPuts(counter, request)
		}
	}
	return counter
}

func countPersistedPuts(requests []model.EtcdRequest) map[keyValue]int64 {
	counter := map[keyValue]int64{}
	for _, request := range requests {
		countPuts(counter, request)

Review comment (suggestion):
This is the only loop that cannot accept non-unique operations, however having it checked just in this place might seem odd. I think it would be clearer if we make it consistent and just not calculate returnTime for non-unique operations. 

Could you add this if block to all loops in `putReturnTime`? Maybe also rename `putReturnTime` to `uniquePutReturnTime`.


Code after:
						}
						if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
							earliestReturnTime[kv] = resp.Time.Nanoseconds()
						}
					case model.DeleteOperation:
					default:
						panic(fmt.Sprintf("unknown event type %q", event.Type))
					}
				}
			}
		}
	}

	for i := len(persistedRequests) - 1; i >= 0; i-- {
		request := persistedRequests[i]
		switch request.Type {
		case model.Txn:
			lastReturnTime--
			for _, op := range request.Txn.OperationsOnSuccess {
				if op.Type != model.PutOperation {
					continue
				}
				kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
				if count := clientPutCount[kv]; count > 1 {
					continue
				}
				returnTime, ok := earliestReturnTime[kv]
				if ok {
					lastReturnTime = min(returnTime, lastReturnTime)
					earliestReturnTime[kv] = lastReturnTime
				}
			}
		case model.LeaseGrant:
		case model.LeaseRevoke:
		case model.Compact:
		default:
			panic(fmt.Sprintf("Unknown request type: %q", request.Type))
		}
	}
	return earliestReturnTime
}

func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
	counter := map[keyValue]int64{}
	for _, client := range reports {
		for _, op := range client.KeyValue {
			request := op.Input.(model.EtcdRequest)
			countPuts(counter, request)
		}
	}
	return counter
Repository: etcd-io/etcd
File: server/embed/config_test.go (Go)

Code before:
		},
		{
			"IPv6 address and Hostname, ExperimentalSetMemberLocalAddr=true",
			[]string{"https://[2001:db8:85a3::8a2e:370:7334]:2380", "https://123-host-3.corp.internal:2380"},
			"SetMemberLocalAddr=true",
			"2001:db8:85a3::8a2e:370:7334",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := NewConfig()
			cfg.AdvertisePeerUrls = types.MustNewURLs(tt.advertisePeerURLs)
			cfg.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(tt.serverFeatureGates)

			require.NoError(t, cfg.Validate())
			require.Equal(t, tt.expectedLocalAddr, cfg.InferLocalAddr())
		})
	}
}

func TestSetMemberLocalAddrValidate(t *testing.T) {
	tcs := []struct {
		name               string
		serverFeatureGates string
		expectError        bool
	}{
		{
			name: "Default config should pass",
		},
		{
			name:               "Enabling SetMemberLocalAddr should pass",
			serverFeatureGates: "SetMemberLocalAddr=true",
		},
	}
	for _, tc := range tcs {
		t.Run(tc.name, func(t *testing.T) {
			cfg := *NewConfig()
			cfg.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(tc.serverFeatureGates)
			err := cfg.Validate()
			if (err != nil) != tc.expectError {
				t.Errorf("config.Validate() = %q, expected error: %v", err, tc.expectError)
			}
		})
	}
}

func (s *securityConfig) equals(t *transport.TLSInfo) bool {
	return s.CertFile == t.CertFile &&
		s.CertAuth == t.ClientCertAuth &&
		s.TrustedCAFile == t.TrustedCAFile &&

Review comment (suggestion):
You add this field, but it isn't set at all, which means it's aways `false`. Suggest to just remove it.

Code after:
		},
		{
			"IPv6 address and Hostname, ExperimentalSetMemberLocalAddr=true",
			[]string{"https://[2001:db8:85a3::8a2e:370:7334]:2380", "https://123-host-3.corp.internal:2380"},
			"SetMemberLocalAddr=true",
			"2001:db8:85a3::8a2e:370:7334",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := NewConfig()
			cfg.AdvertisePeerUrls = types.MustNewURLs(tt.advertisePeerURLs)
			cfg.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(tt.serverFeatureGates)

			require.NoError(t, cfg.Validate())
			require.Equal(t, tt.expectedLocalAddr, cfg.InferLocalAddr())
		})
	}
}

func TestSetMemberLocalAddrValidate(t *testing.T) {
	tcs := []struct {
		name               string
		serverFeatureGates string
	}{
		{
			name: "Default config should pass",
		},
		{
			name:               "Enabling SetMemberLocalAddr should pass",
			serverFeatureGates: "SetMemberLocalAddr=true",
		},
	}
	for _, tc := range tcs {
		t.Run(tc.name, func(t *testing.T) {
			cfg := *NewConfig()
			cfg.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(tc.serverFeatureGates)
			err := cfg.Validate()
			require.NoError(t, err)
		})
	}
}

func (s *securityConfig) equals(t *transport.TLSInfo) bool {
	return s.CertFile == t.CertFile &&
		s.CertAuth == t.ClientCertAuth &&
		s.TrustedCAFile == t.TrustedCAFile &&
		s.ClientCertFile == t.ClientCertFile &&
		s.ClientKeyFile == t.ClientKeyFile &&
		s.KeyFile == t.KeyFile &&