github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/postgres/apecloudpostgres/manager_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package apecloudpostgres
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"testing"
    26  
    27  	"github.com/pashagolub/pgxmock/v2"
    28  	"github.com/stretchr/testify/assert"
    29  
    30  	"github.com/1aal/kubeblocks/pkg/constant"
    31  	"github.com/1aal/kubeblocks/pkg/lorry/dcs"
    32  	"github.com/1aal/kubeblocks/pkg/lorry/engines"
    33  	"github.com/1aal/kubeblocks/pkg/lorry/engines/models"
    34  	"github.com/1aal/kubeblocks/pkg/lorry/engines/postgres"
    35  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    36  )
    37  
    38  func MockDatabase(t *testing.T) (*Manager, pgxmock.PgxPoolIface, error) {
    39  	properties := map[string]string{
    40  		postgres.ConnectionURLKey: "user=test password=test host=localhost port=5432 dbname=postgres",
    41  	}
    42  	testConfig, err := postgres.NewConfig(properties)
    43  	assert.NotNil(t, testConfig)
    44  	assert.Nil(t, err)
    45  
    46  	viper.Set(constant.KBEnvPodName, "test-pod-0")
    47  	viper.Set(constant.KBEnvClusterCompName, "test")
    48  	viper.Set(constant.KBEnvNamespace, "default")
    49  	viper.Set(postgres.PGDATA, "test")
    50  	mock, err := pgxmock.NewPool(pgxmock.MonitorPingsOption(true))
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  
    55  	dbManager, err := NewManager(engines.Properties(properties))
    56  	if err != nil {
    57  		t.Fatal(err)
    58  	}
    59  
    60  	manager := dbManager.(*Manager)
    61  	manager.Pool = mock
    62  
    63  	return manager, mock, err
    64  }
    65  
    66  func TestIsConsensusReadyUp(t *testing.T) {
    67  	ctx := context.TODO()
    68  	manager, mock, _ := MockDatabase(t)
    69  	defer mock.Close()
    70  
    71  	t.Run("consensus has been ready up", func(t *testing.T) {
    72  		mock.ExpectQuery("SELECT extname FROM pg_extension").
    73  			WillReturnRows(pgxmock.NewRows([]string{"extname"}).AddRow("consensus_monitor"))
    74  
    75  		isReadyUp := manager.isConsensusReadyUp(ctx)
    76  		assert.True(t, isReadyUp)
    77  	})
    78  
    79  	t.Run("consensus has not been ready up", func(t *testing.T) {
    80  		mock.ExpectQuery("SELECT extname FROM pg_extension").
    81  			WillReturnRows(pgxmock.NewRows([]string{"extname"}))
    82  
    83  		isReadyUp := manager.isConsensusReadyUp(ctx)
    84  		assert.False(t, isReadyUp)
    85  	})
    86  
    87  	t.Run("query pg_extension error", func(t *testing.T) {
    88  		mock.ExpectQuery("SELECT extname FROM pg_extension").
    89  			WillReturnError(fmt.Errorf("some errors"))
    90  
    91  		isReadyUp := manager.isConsensusReadyUp(ctx)
    92  		assert.False(t, isReadyUp)
    93  	})
    94  
    95  	if err := mock.ExpectationsWereMet(); err != nil {
    96  		t.Errorf("there were unfulfilled expectations: %v", err)
    97  	}
    98  }
    99  
   100  func TestIsDBStartupReady(t *testing.T) {
   101  	manager, mock, _ := MockDatabase(t)
   102  	defer mock.Close()
   103  
   104  	t.Run("db start up has been set", func(t *testing.T) {
   105  		manager.DBStartupReady = true
   106  
   107  		isReady := manager.IsDBStartupReady()
   108  		assert.True(t, isReady)
   109  	})
   110  
   111  	t.Run("ping db failed", func(t *testing.T) {
   112  		manager.DBStartupReady = false
   113  		mock.ExpectPing().
   114  			WillReturnError(fmt.Errorf("some error"))
   115  
   116  		isReady := manager.IsDBStartupReady()
   117  		assert.False(t, isReady)
   118  	})
   119  
   120  	t.Run("ping db success but consensus not ready up", func(t *testing.T) {
   121  		manager.DBStartupReady = false
   122  		mock.ExpectPing()
   123  		mock.ExpectQuery("SELECT extname FROM pg_extension").
   124  			WillReturnRows(pgxmock.NewRows([]string{"extname"}))
   125  
   126  		isReady := manager.IsDBStartupReady()
   127  		assert.False(t, isReady)
   128  	})
   129  
   130  	t.Run("db is startup ready", func(t *testing.T) {
   131  		manager.DBStartupReady = false
   132  		mock.ExpectPing()
   133  		mock.ExpectQuery("SELECT extname FROM pg_extension").
   134  			WillReturnRows(pgxmock.NewRows([]string{"extname"}).AddRow("consensus_monitor"))
   135  
   136  		isReady := manager.IsDBStartupReady()
   137  		assert.True(t, isReady)
   138  	})
   139  
   140  	if err := mock.ExpectationsWereMet(); err != nil {
   141  		t.Errorf("there were unfulfilled expectations: %v", err)
   142  	}
   143  }
   144  
   145  func TestIsClusterInitialized(t *testing.T) {
   146  	ctx := context.TODO()
   147  	manager, mock, _ := MockDatabase(t)
   148  	defer mock.Close()
   149  
   150  	t.Run("is not first member", func(t *testing.T) {
   151  		manager.CurrentMemberName = "test-pod-1"
   152  
   153  		isClusterInitialized, err := manager.IsClusterInitialized(ctx, nil)
   154  		assert.True(t, isClusterInitialized)
   155  		assert.Nil(t, err)
   156  		manager.CurrentMemberName = "test-pod-0"
   157  	})
   158  
   159  	t.Run("db is not startup ready", func(t *testing.T) {
   160  		manager.DBStartupReady = false
   161  		mock.ExpectPing().
   162  			WillReturnError(fmt.Errorf("some error"))
   163  
   164  		isClusterInitialized, err := manager.IsClusterInitialized(ctx, nil)
   165  		assert.False(t, isClusterInitialized)
   166  		assert.Nil(t, err)
   167  	})
   168  
   169  	t.Run("query db user error", func(t *testing.T) {
   170  		manager.DBStartupReady = false
   171  		mock.ExpectPing()
   172  		mock.ExpectQuery("SELECT extname FROM pg_extension").
   173  			WillReturnRows(pgxmock.NewRows([]string{"extname"}).AddRow("consensus_monitor"))
   174  		mock.ExpectQuery("SELECT usename FROM pg_user").
   175  			WillReturnError(fmt.Errorf("some error"))
   176  
   177  		isClusterInitialized, err := manager.IsClusterInitialized(ctx, nil)
   178  		assert.False(t, isClusterInitialized)
   179  		assert.NotNil(t, err)
   180  	})
   181  
   182  	t.Run("parse query error", func(t *testing.T) {
   183  		manager.DBStartupReady = false
   184  		mock.ExpectPing()
   185  		mock.ExpectQuery("SELECT extname FROM pg_extension").
   186  			WillReturnRows(pgxmock.NewRows([]string{"extname"}).AddRow("consensus_monitor"))
   187  		mock.ExpectQuery("SELECT usename FROM pg_user").
   188  			WillReturnRows(pgxmock.NewRows([]string{"usename"}))
   189  
   190  		isClusterInitialized, err := manager.IsClusterInitialized(ctx, nil)
   191  		assert.False(t, isClusterInitialized)
   192  		assert.NotNil(t, err)
   193  	})
   194  
   195  	t.Run("cluster is initialized", func(t *testing.T) {
   196  		manager.DBStartupReady = false
   197  		mock.ExpectPing()
   198  		mock.ExpectQuery("SELECT extname FROM pg_extension").
   199  			WillReturnRows(pgxmock.NewRows([]string{"extname"}).AddRow("consensus_monitor"))
   200  		mock.ExpectQuery("SELECT usename FROM pg_user").
   201  			WillReturnRows(pgxmock.NewRows([]string{"usename"}).AddRow("replicator"))
   202  
   203  		isClusterInitialized, err := manager.IsClusterInitialized(ctx, nil)
   204  		assert.True(t, isClusterInitialized)
   205  		assert.Nil(t, err)
   206  	})
   207  
   208  	if err := mock.ExpectationsWereMet(); err != nil {
   209  		t.Errorf("there were unfulfilled expectations: %v", err)
   210  	}
   211  }
   212  
   213  func TestInitializeCluster(t *testing.T) {
   214  	ctx := context.TODO()
   215  	manager, mock, _ := MockDatabase(t)
   216  	defer mock.Close()
   217  
   218  	t.Run("exec create role and extension failed", func(t *testing.T) {
   219  		mock.ExpectExec("create role replicator").
   220  			WillReturnError(fmt.Errorf("some error"))
   221  
   222  		err := manager.InitializeCluster(ctx, nil)
   223  		assert.NotNil(t, err)
   224  	})
   225  
   226  	t.Run("exec create role and extension failed", func(t *testing.T) {
   227  		mock.ExpectExec("create role replicator").
   228  			WillReturnResult(pgxmock.NewResult("create", 1))
   229  
   230  		err := manager.InitializeCluster(ctx, nil)
   231  		assert.Nil(t, err)
   232  	})
   233  
   234  	if err := mock.ExpectationsWereMet(); err != nil {
   235  		t.Errorf("there were unfulfilled expectations: %v", err)
   236  	}
   237  }
   238  
   239  func TestGetMemberRoleWithHost(t *testing.T) {
   240  	ctx := context.TODO()
   241  	manager, mock, _ := MockDatabase(t)
   242  	defer mock.Close()
   243  	roles := []string{models.FOLLOWER, models.CANDIDATE, models.LEADER, models.LEARNER, ""}
   244  
   245  	t.Run("query paxos role failed", func(t *testing.T) {
   246  		mock.ExpectQuery("select paxos_role from consensus_member_status;").
   247  			WillReturnError(fmt.Errorf("some error"))
   248  
   249  		role, err := manager.GetMemberRoleWithHost(ctx, "")
   250  		assert.Equal(t, "", role)
   251  		assert.NotNil(t, err)
   252  	})
   253  
   254  	t.Run("parse query failed", func(t *testing.T) {
   255  		mock.ExpectQuery("select paxos_role from consensus_member_status;").
   256  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}))
   257  
   258  		role, err := manager.GetMemberRoleWithHost(ctx, "")
   259  		assert.Equal(t, "", role)
   260  		assert.NotNil(t, err)
   261  	})
   262  
   263  	t.Run("get member role with host success", func(t *testing.T) {
   264  		for i, r := range roles {
   265  			mock.ExpectQuery("select paxos_role from consensus_member_status;").
   266  				WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(i))
   267  
   268  			role, err := manager.GetMemberRoleWithHost(ctx, "")
   269  			assert.Equal(t, r, role)
   270  			assert.Nil(t, err)
   271  		}
   272  	})
   273  
   274  	if err := mock.ExpectationsWereMet(); err != nil {
   275  		t.Errorf("there were unfulfilled expectations: %v", err)
   276  	}
   277  }
   278  
   279  func TestIsLeaderWithHost(t *testing.T) {
   280  	ctx := context.TODO()
   281  	manager, mock, _ := MockDatabase(t)
   282  	defer mock.Close()
   283  
   284  	t.Run("get member role with host failed", func(t *testing.T) {
   285  		mock.ExpectQuery("select paxos_role from consensus_member_status;").
   286  			WillReturnError(fmt.Errorf("some error"))
   287  
   288  		isLeader, err := manager.IsLeaderWithHost(ctx, "")
   289  		assert.False(t, isLeader)
   290  		assert.NotNil(t, err)
   291  	})
   292  
   293  	t.Run("check is leader success", func(t *testing.T) {
   294  		mock.ExpectQuery("select paxos_role from consensus_member_status;").
   295  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(2))
   296  
   297  		isLeader, err := manager.IsLeaderWithHost(ctx, "")
   298  		assert.True(t, isLeader)
   299  		assert.Nil(t, err)
   300  	})
   301  
   302  	if err := mock.ExpectationsWereMet(); err != nil {
   303  		t.Errorf("there were unfulfilled expectations: %v", err)
   304  	}
   305  }
   306  
   307  func TestIsLeader(t *testing.T) {
   308  	ctx := context.TODO()
   309  	manager, mock, _ := MockDatabase(t)
   310  	defer mock.Close()
   311  
   312  	t.Run("is leader has been set", func(t *testing.T) {
   313  		manager.SetIsLeader(true)
   314  
   315  		isLeader, err := manager.IsLeader(ctx, nil)
   316  		assert.True(t, isLeader)
   317  		assert.Nil(t, err)
   318  	})
   319  
   320  	t.Run("is leader has not been set", func(t *testing.T) {
   321  		manager.UnsetIsLeader()
   322  		mock.ExpectQuery("select paxos_role from consensus_member_status;").
   323  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(2))
   324  
   325  		isLeader, err := manager.IsLeader(ctx, nil)
   326  		assert.True(t, isLeader)
   327  		assert.Nil(t, err)
   328  	})
   329  
   330  	if err := mock.ExpectationsWereMet(); err != nil {
   331  		t.Errorf("there were unfulfilled expectations: %v", err)
   332  	}
   333  }
   334  
   335  func TestGetMemberAddrs(t *testing.T) {
   336  	ctx := context.TODO()
   337  	manager, mock, _ := MockDatabase(t)
   338  	defer mock.Close()
   339  	cluster := &dcs.Cluster{
   340  		Leader: &dcs.Leader{
   341  			Name: manager.CurrentMemberName,
   342  		},
   343  	}
   344  	cluster.Members = append(cluster.Members, dcs.Member{
   345  		Name: manager.CurrentMemberName,
   346  	})
   347  
   348  	t.Run("query ip port failed", func(t *testing.T) {
   349  		mock.ExpectQuery("select ip_port from consensus_cluster_status;").
   350  			WillReturnError(fmt.Errorf("some errors"))
   351  
   352  		addrs := manager.GetMemberAddrs(ctx, cluster)
   353  		assert.Nil(t, addrs)
   354  	})
   355  
   356  	t.Run("parse query failed", func(t *testing.T) {
   357  		mock.ExpectQuery("select ip_port from consensus_cluster_status;").
   358  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}))
   359  
   360  		addrs := manager.GetMemberAddrs(ctx, cluster)
   361  		assert.Nil(t, addrs)
   362  	})
   363  
   364  	t.Run("get member addrs success", func(t *testing.T) {
   365  		mock.ExpectQuery("select ip_port from consensus_cluster_status;").
   366  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRows([][]any{{"a"}, {"b"}, {"c"}}...))
   367  
   368  		addrs := manager.GetMemberAddrs(ctx, cluster)
   369  		assert.Equal(t, []string{"a", "b", "c"}, addrs)
   370  	})
   371  
   372  	t.Run("has set addrs", func(t *testing.T) {
   373  		manager.DBState = &dcs.DBState{}
   374  		manager.memberAddrs = []string{"a", "b", "c"}
   375  
   376  		addrs := manager.GetMemberAddrs(ctx, cluster)
   377  		assert.Equal(t, []string{"a", "b", "c"}, addrs)
   378  	})
   379  
   380  	if err := mock.ExpectationsWereMet(); err != nil {
   381  		t.Errorf("there were unfulfilled expectations: %v", err)
   382  	}
   383  }
   384  
   385  func TestIsCurrentMemberInCluster(t *testing.T) {
   386  	ctx := context.TODO()
   387  	manager, mock, _ := MockDatabase(t)
   388  	defer mock.Close()
   389  	manager.DBState = &dcs.DBState{}
   390  	cluster := &dcs.Cluster{
   391  		Namespace:       manager.Namespace,
   392  		ClusterCompName: manager.ClusterCompName,
   393  	}
   394  
   395  	t.Run("currentMember is in cluster", func(t *testing.T) {
   396  		manager.memberAddrs = []string{cluster.GetMemberAddrWithName(manager.CurrentMemberName)}
   397  
   398  		inCluster := manager.IsCurrentMemberInCluster(ctx, cluster)
   399  		assert.True(t, inCluster)
   400  	})
   401  
   402  	t.Run("currentMember is in cluster", func(t *testing.T) {
   403  		manager.memberAddrs[0] = cluster.GetMemberAddrWithName("test-pod-1")
   404  
   405  		inCluster := manager.IsCurrentMemberInCluster(ctx, cluster)
   406  		assert.False(t, inCluster)
   407  	})
   408  }
   409  
   410  func TestIsCurrentMemberHealthy(t *testing.T) {
   411  	ctx := context.TODO()
   412  	manager, mock, _ := MockDatabase(t)
   413  	defer mock.Close()
   414  	cluster := &dcs.Cluster{}
   415  	cluster.Members = append(cluster.Members, dcs.Member{
   416  		Name: manager.CurrentMemberName,
   417  	})
   418  
   419  	t.Run("cluster has no leader", func(t *testing.T) {
   420  		isHealthy := manager.IsCurrentMemberHealthy(ctx, cluster)
   421  		assert.True(t, isHealthy)
   422  	})
   423  
   424  	cluster.Leader = &dcs.Leader{
   425  		Name: manager.CurrentMemberName,
   426  	}
   427  
   428  	t.Run("get member health status failed", func(t *testing.T) {
   429  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   430  			WillReturnError(fmt.Errorf("some error"))
   431  
   432  		isHealthy := manager.IsCurrentMemberHealthy(ctx, cluster)
   433  		assert.False(t, isHealthy)
   434  	})
   435  
   436  	t.Run("member is healthy", func(t *testing.T) {
   437  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   438  			WillReturnRows(pgxmock.NewRows([]string{"connected", "log_delay_num"}).AddRow(true, 0))
   439  
   440  		isHealthy := manager.IsCurrentMemberHealthy(ctx, cluster)
   441  		assert.True(t, isHealthy)
   442  	})
   443  
   444  	if err := mock.ExpectationsWereMet(); err != nil {
   445  		t.Errorf("there were unfulfilled expectations: %v", err)
   446  	}
   447  }
   448  
   449  func TestGetMemberHealthyStatus(t *testing.T) {
   450  	ctx := context.TODO()
   451  	manager, mock, _ := MockDatabase(t)
   452  	defer mock.Close()
   453  	cluster := &dcs.Cluster{}
   454  	cluster.Members = append(cluster.Members, dcs.Member{
   455  		Name: manager.CurrentMemberName,
   456  	})
   457  	cluster.Leader = &dcs.Leader{
   458  		Name: manager.CurrentMemberName,
   459  	}
   460  
   461  	t.Run("query failed", func(t *testing.T) {
   462  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   463  			WillReturnError(fmt.Errorf("some error"))
   464  
   465  		healthStatus, err := manager.getMemberHealthStatus(ctx, cluster, cluster.GetMemberWithName(manager.CurrentMemberName))
   466  		assert.NotNil(t, err)
   467  		assert.Nil(t, healthStatus)
   468  	})
   469  
   470  	t.Run("parse query failed", func(t *testing.T) {
   471  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   472  			WillReturnRows(pgxmock.NewRows([]string{"connected, log_delay_num"}))
   473  
   474  		healthStatus, err := manager.getMemberHealthStatus(ctx, cluster, cluster.GetMemberWithName(manager.CurrentMemberName))
   475  		assert.NotNil(t, err)
   476  		assert.Nil(t, healthStatus)
   477  	})
   478  
   479  	t.Run("get member health status success", func(t *testing.T) {
   480  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   481  			WillReturnRows(pgxmock.NewRows([]string{"connected", "log_delay_num"}).AddRow(true, 0))
   482  
   483  		healthStatus, err := manager.getMemberHealthStatus(ctx, cluster, cluster.GetMemberWithName(manager.CurrentMemberName))
   484  		assert.Nil(t, err)
   485  		assert.True(t, healthStatus.Connected)
   486  		assert.Equal(t, int64(0), healthStatus.LogDelayNum)
   487  	})
   488  
   489  	t.Run("health status has been set", func(t *testing.T) {
   490  		manager.healthStatus = &postgres.ConsensusMemberHealthStatus{
   491  			Connected:   false,
   492  			LogDelayNum: 200,
   493  		}
   494  		manager.DBState = &dcs.DBState{}
   495  
   496  		healthStatus, err := manager.getMemberHealthStatus(ctx, cluster, cluster.GetMemberWithName(manager.CurrentMemberName))
   497  		assert.Nil(t, err)
   498  		assert.False(t, healthStatus.Connected)
   499  		assert.Equal(t, int64(200), healthStatus.LogDelayNum)
   500  	})
   501  
   502  	if err := mock.ExpectationsWereMet(); err != nil {
   503  		t.Errorf("there were unfulfilled expectations: %v", err)
   504  	}
   505  }
   506  
   507  func TestIsMemberLagging(t *testing.T) {
   508  	ctx := context.TODO()
   509  	manager, mock, _ := MockDatabase(t)
   510  	defer mock.Close()
   511  	cluster := &dcs.Cluster{
   512  		HaConfig: &dcs.HaConfig{},
   513  	}
   514  	cluster.Members = append(cluster.Members, dcs.Member{
   515  		Name: manager.CurrentMemberName,
   516  	})
   517  	currentMember := cluster.GetMemberWithName(manager.CurrentMemberName)
   518  
   519  	t.Run("cluster has no leader", func(t *testing.T) {
   520  		isLagging, lag := manager.IsMemberLagging(ctx, cluster, currentMember)
   521  		assert.False(t, isLagging)
   522  		assert.Equal(t, int64(0), lag)
   523  	})
   524  
   525  	cluster.Leader = &dcs.Leader{
   526  		Name: manager.CurrentMemberName,
   527  	}
   528  
   529  	t.Run("get member health status failed", func(t *testing.T) {
   530  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   531  			WillReturnError(fmt.Errorf("some error"))
   532  
   533  		isLagging, lag := manager.IsMemberLagging(ctx, cluster, currentMember)
   534  		assert.True(t, isLagging)
   535  		assert.Equal(t, int64(1), lag)
   536  	})
   537  
   538  	t.Run("member is not lagging", func(t *testing.T) {
   539  		mock.ExpectQuery("select connected, log_delay_num from consensus_cluster_health").
   540  			WillReturnRows(pgxmock.NewRows([]string{"connected", "log_delay_num"}).AddRow(true, 0))
   541  
   542  		isLagging, lag := manager.IsMemberLagging(ctx, cluster, currentMember)
   543  		assert.False(t, isLagging)
   544  		assert.Equal(t, int64(0), lag)
   545  	})
   546  
   547  	cluster.Leader = &dcs.Leader{
   548  		Name: manager.CurrentMemberName,
   549  	}
   550  }
   551  
   552  func TestJoinCurrentMemberToCluster(t *testing.T) {
   553  	ctx := context.TODO()
   554  	manager, mock, _ := MockDatabase(t)
   555  	defer mock.Close()
   556  	cluster := &dcs.Cluster{}
   557  	cluster.Leader = &dcs.Leader{
   558  		Name: manager.CurrentMemberName,
   559  	}
   560  	cluster.Members = append(cluster.Members, dcs.Member{
   561  		Name: manager.CurrentMemberName,
   562  	})
   563  
   564  	t.Run("exec alter system failed", func(t *testing.T) {
   565  		mock.ExpectExec("alter system").
   566  			WillReturnError(fmt.Errorf("some error"))
   567  
   568  		err := manager.JoinCurrentMemberToCluster(ctx, cluster)
   569  		assert.NotNil(t, err)
   570  	})
   571  
   572  	t.Run("exec alter system success", func(t *testing.T) {
   573  		mock.ExpectExec("alter system").
   574  			WillReturnResult(pgxmock.NewResult("alter system", 1))
   575  
   576  		err := manager.JoinCurrentMemberToCluster(ctx, cluster)
   577  		assert.Nil(t, err)
   578  	})
   579  
   580  	if err := mock.ExpectationsWereMet(); err != nil {
   581  		t.Errorf("there were unfulfilled expectations: %v", err)
   582  	}
   583  }
   584  
   585  func TestLeaveMemberFromCluster(t *testing.T) {
   586  	ctx := context.TODO()
   587  	manager, mock, _ := MockDatabase(t)
   588  	defer mock.Close()
   589  	cluster := &dcs.Cluster{}
   590  
   591  	t.Run("exec alter system failed", func(t *testing.T) {
   592  		mock.ExpectExec("alter system").
   593  			WillReturnError(fmt.Errorf("some error"))
   594  
   595  		err := manager.LeaveMemberFromCluster(ctx, cluster, "")
   596  		assert.NotNil(t, err)
   597  	})
   598  
   599  	t.Run("exec alter system success", func(t *testing.T) {
   600  		mock.ExpectExec("alter system").
   601  			WillReturnResult(pgxmock.NewResult("alter system", 1))
   602  
   603  		err := manager.LeaveMemberFromCluster(ctx, cluster, "")
   604  		assert.Nil(t, err)
   605  	})
   606  
   607  	if err := mock.ExpectationsWereMet(); err != nil {
   608  		t.Errorf("there were unfulfilled expectations: %v", err)
   609  	}
   610  }
   611  
   612  func TestIsClusterHealthy(t *testing.T) {
   613  	ctx := context.TODO()
   614  	manager, mock, _ := MockDatabase(t)
   615  	defer mock.Close()
   616  	cluster := &dcs.Cluster{}
   617  	cluster.Members = append(cluster.Members, dcs.Member{
   618  		Name: manager.CurrentMemberName,
   619  	})
   620  
   621  	t.Run("cluster has no leader", func(t *testing.T) {
   622  		isClusterHealthy := manager.IsClusterHealthy(ctx, cluster)
   623  		assert.True(t, isClusterHealthy)
   624  	})
   625  
   626  	cluster.Leader = &dcs.Leader{}
   627  
   628  	t.Run("current member is leader", func(t *testing.T) {
   629  		cluster.Leader.Name = manager.CurrentMemberName
   630  		isClusterHealthy := manager.IsClusterHealthy(ctx, cluster)
   631  		assert.True(t, isClusterHealthy)
   632  	})
   633  
   634  	t.Run("cluster is healthy", func(t *testing.T) {
   635  		cluster.Leader.Name = "test"
   636  		cluster.Members[0].Name = "test"
   637  		manager.DBState = &dcs.DBState{}
   638  		manager.healthStatus = &postgres.ConsensusMemberHealthStatus{
   639  			Connected: true,
   640  		}
   641  
   642  		isClusterHealthy := manager.IsClusterHealthy(ctx, cluster)
   643  		assert.True(t, isClusterHealthy)
   644  	})
   645  }
   646  
   647  func TestPromote(t *testing.T) {
   648  	ctx := context.TODO()
   649  	manager, mock, _ := MockDatabase(t)
   650  	defer mock.Close()
   651  	cluster := &dcs.Cluster{
   652  		Namespace:       manager.Namespace,
   653  		ClusterCompName: manager.ClusterCompName,
   654  	}
   655  
   656  	t.Run("query leader ip port failed", func(t *testing.T) {
   657  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   658  			WillReturnError(fmt.Errorf("some error"))
   659  
   660  		err := manager.Promote(ctx, cluster)
   661  		assert.NotNil(t, err)
   662  	})
   663  
   664  	t.Run("parse query failed", func(t *testing.T) {
   665  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   666  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}))
   667  
   668  		err := manager.Promote(ctx, cluster)
   669  		assert.NotNil(t, err)
   670  	})
   671  
   672  	t.Run("exec promote failed", func(t *testing.T) {
   673  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   674  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRow(":"))
   675  		mock.ExpectExec("alter system").
   676  			WillReturnError(fmt.Errorf("some error"))
   677  
   678  		err := manager.Promote(ctx, cluster)
   679  		assert.NotNil(t, err)
   680  	})
   681  
   682  	t.Run("exec promote success", func(t *testing.T) {
   683  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   684  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRow(":"))
   685  		mock.ExpectExec("alter system").
   686  			WillReturnResult(pgxmock.NewResult("alter system", 1))
   687  
   688  		err := manager.Promote(ctx, cluster)
   689  		assert.Nil(t, err)
   690  	})
   691  
   692  	t.Run("current member is already the leader", func(t *testing.T) {
   693  		manager.SetIsLeader(true)
   694  
   695  		err := manager.Promote(ctx, cluster)
   696  		assert.Nil(t, err)
   697  	})
   698  
   699  	if err := mock.ExpectationsWereMet(); err != nil {
   700  		t.Errorf("there were unfulfilled expectations: %v", err)
   701  	}
   702  }
   703  
   704  func TestIsPromoted(t *testing.T) {
   705  	ctx := context.TODO()
   706  	manager, mock, _ := MockDatabase(t)
   707  	defer mock.Close()
   708  
   709  	t.Run("is promoted", func(t *testing.T) {
   710  		manager.SetIsLeader(true)
   711  		isPromoted := manager.IsPromoted(ctx)
   712  
   713  		assert.True(t, isPromoted)
   714  	})
   715  }
   716  
   717  func TestHasOtherHealthyLeader(t *testing.T) {
   718  	ctx := context.TODO()
   719  	manager, mock, _ := MockDatabase(t)
   720  	defer mock.Close()
   721  	cluster := &dcs.Cluster{}
   722  
   723  	t.Run("query failed", func(t *testing.T) {
   724  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   725  			WillReturnError(fmt.Errorf("some error"))
   726  
   727  		member := manager.HasOtherHealthyLeader(ctx, cluster)
   728  		assert.Nil(t, member)
   729  	})
   730  
   731  	t.Run("parse query failed", func(t *testing.T) {
   732  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   733  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}))
   734  
   735  		member := manager.HasOtherHealthyLeader(ctx, cluster)
   736  		assert.Nil(t, member)
   737  	})
   738  
   739  	t.Run("has other healthy leader", func(t *testing.T) {
   740  		cluster.Members = append(cluster.Members, dcs.Member{
   741  			Name: "test",
   742  		})
   743  		mock.ExpectQuery("select ip_port from consensus_cluster_status").
   744  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRow("test:5432"))
   745  
   746  		member := manager.HasOtherHealthyLeader(ctx, cluster)
   747  		assert.NotNil(t, member)
   748  	})
   749  
   750  	t.Run("current member is leader", func(t *testing.T) {
   751  		manager.SetIsLeader(true)
   752  
   753  		member := manager.HasOtherHealthyLeader(ctx, cluster)
   754  		assert.Nil(t, member)
   755  	})
   756  
   757  	if err := mock.ExpectationsWereMet(); err != nil {
   758  		t.Errorf("there were unfulfilled expectations: %v", err)
   759  	}
   760  }
   761  
   762  func TestHasOtherHealthyMembers(t *testing.T) {
   763  	ctx := context.TODO()
   764  	manager, mock, _ := MockDatabase(t)
   765  	defer mock.Close()
   766  	cluster := &dcs.Cluster{}
   767  	cluster.Members = append(cluster.Members, dcs.Member{
   768  		Name: manager.CurrentMemberName,
   769  	})
   770  
   771  	t.Run("", func(t *testing.T) {
   772  		members := manager.HasOtherHealthyMembers(ctx, cluster, manager.CurrentMemberName)
   773  		assert.Equal(t, 0, len(members))
   774  	})
   775  
   776  	if err := mock.ExpectationsWereMet(); err != nil {
   777  		t.Errorf("there were unfulfilled expectations: %v", err)
   778  	}
   779  }
   780  
   781  func TestGetDBState(t *testing.T) {
   782  	ctx := context.TODO()
   783  	manager, mock, _ := MockDatabase(t)
   784  	defer mock.Close()
   785  	cluster := &dcs.Cluster{}
   786  	cluster.Members = append(cluster.Members, dcs.Member{
   787  		Name: manager.CurrentMemberName,
   788  	})
   789  	cluster.Leader = &dcs.Leader{
   790  		Name: manager.CurrentMemberName,
   791  	}
   792  
   793  	t.Run("check is leader failed", func(t *testing.T) {
   794  		mock.ExpectQuery("select paxos_role").
   795  			WillReturnError(fmt.Errorf("some error"))
   796  
   797  		dbState := manager.GetDBState(ctx, cluster)
   798  		assert.Nil(t, dbState)
   799  	})
   800  
   801  	t.Run("get member addrs failed", func(t *testing.T) {
   802  		mock.ExpectQuery("select paxos_role").
   803  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(2))
   804  		mock.ExpectQuery("select ip_port").
   805  			WillReturnError(fmt.Errorf("some error"))
   806  
   807  		dbState := manager.GetDBState(ctx, cluster)
   808  		assert.Nil(t, dbState)
   809  	})
   810  
   811  	t.Run("get member health status failed", func(t *testing.T) {
   812  		mock.ExpectQuery("select paxos_role").
   813  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(2))
   814  		mock.ExpectQuery("select ip_port").
   815  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRows([][]any{{"a"}, {"b"}, {"c"}}...))
   816  		mock.ExpectQuery("select connected, log_delay_num").
   817  			WillReturnError(fmt.Errorf("some error"))
   818  
   819  		dbState := manager.GetDBState(ctx, cluster)
   820  		assert.Nil(t, dbState)
   821  	})
   822  
   823  	t.Run("get db state success", func(t *testing.T) {
   824  		mock.ExpectQuery("select paxos_role").
   825  			WillReturnRows(pgxmock.NewRows([]string{"paxos_role"}).AddRow(2))
   826  		mock.ExpectQuery("select ip_port").
   827  			WillReturnRows(pgxmock.NewRows([]string{"ip_port"}).AddRows([][]any{{"a"}, {"b"}, {"c"}}...))
   828  		mock.ExpectQuery("select connected, log_delay_num").
   829  			WillReturnRows(pgxmock.NewRows([]string{"connected", "log_delay_num"}).AddRow(true, 20))
   830  
   831  		dbState := manager.GetDBState(ctx, cluster)
   832  		assert.NotNil(t, dbState)
   833  		assert.Equal(t, []string{"a", "b", "c"}, manager.memberAddrs)
   834  		assert.Equal(t, int64(20), manager.healthStatus.LogDelayNum)
   835  		assert.True(t, manager.healthStatus.Connected)
   836  	})
   837  
   838  	if err := mock.ExpectationsWereMet(); err != nil {
   839  		t.Errorf("there were unfulfilled expectations: %v", err)
   840  	}
   841  }