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 }