From 7b94717a3f43b5710baf968b47e3fcd0d161f1e0 Mon Sep 17 00:00:00 2001
From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com>
Date: Thu, 12 May 2022 10:35:30 +0300
Subject: [PATCH] fix: pytest wait_ready implementation

Waiting for a valid `leader_id` on a node isn't enough. It may already
have one, but still be a Learner. Instead, the fixture should wait until
the node is promoted to voter.
---
 src/main.rs             |  2 ++
 src/traft/node.rs       |  7 +++++++
 test/int/conftest.py    |  6 ++----
 test/int/test_basics.py | 12 +++++-------
 4 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 7e1acbb0fd..614d22b8f0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -589,6 +589,8 @@ fn postjoin(args: &args::Run) {
             }
         };
     }
+
+    node.mark_as_ready();
 }
 
 fn main_tarantool(args: args::Tarantool) -> ! {
diff --git a/src/traft/node.rs b/src/traft/node.rs
index fa1d4efecb..548c77208f 100644
--- a/src/traft/node.rs
+++ b/src/traft/node.rs
@@ -53,6 +53,7 @@ pub struct Status {
     pub id: u64,
     pub leader_id: u64,
     pub raft_state: String,
+    pub is_ready: bool,
 }
 
 /// The heart of `traft` module - the Node.
@@ -103,6 +104,7 @@ impl Node {
             id: cfg.id,
             leader_id: 0,
             raft_state: "Follower".into(),
+            is_ready: false,
         }));
 
         let main_loop_fn = {
@@ -136,6 +138,11 @@ impl Node {
         self.status.borrow().clone()
     }
 
+    pub fn mark_as_ready(&self) {
+        self.status.borrow_mut().is_ready = true;
+        self.status_cond.broadcast();
+    }
+
     pub fn wait_status(&self) {
         self.status_cond.wait();
     }
diff --git a/test/int/conftest.py b/test/int/conftest.py
index b2fa826ba8..236fddcd99 100644
--- a/test/int/conftest.py
+++ b/test/int/conftest.py
@@ -118,6 +118,7 @@ class RaftStatus:
     id: int
     raft_state: str
     leader_id: int
+    is_ready: bool
 
     def __eq__(self, other):
         match other:
@@ -139,9 +140,6 @@ class RaftStatus:
 
         return False
 
-    def is_ready(self):
-        return self.leader_id > INVALID_ID
-
 
 @dataclass
 class Instance:
@@ -280,7 +278,7 @@ class Instance:
     @funcy.retry(tries=20, timeout=0.1)
     def wait_ready(self):
         status = self.__raft_status()
-        assert status.is_ready()
+        assert status.is_ready
         self.raft_id = status.id
 
     @funcy.retry(tries=4, timeout=0.1, errors=AssertionError)
diff --git a/test/int/test_basics.py b/test/int/test_basics.py
index 045501f364..3fdc16984f 100644
--- a/test/int/test_basics.py
+++ b/test/int/test_basics.py
@@ -36,6 +36,7 @@ def test_raft_status():
         id=1,
         raft_state="SomeState",
         leader_id=1,
+        is_ready=False,
     )
 
     assert (s == "SomeState") is True
@@ -48,13 +49,10 @@ def test_raft_status():
     assert (s == ("OtherState", 1)) is False
 
     assert (s == s) is True
-    assert (s == RaftStatus(s.id, s.raft_state, s.leader_id)) is True
-    assert (s == RaftStatus(-1, s.raft_state, s.leader_id)) is False
-    assert (s == RaftStatus(s.id, "OtherState", s.leader_id)) is False
-    assert (s == RaftStatus(s.id, s.raft_state, -1)) is False
-
-    assert RaftStatus(1, "Follower", 0).is_ready() is False
-    assert RaftStatus(1, "Follower", 1).is_ready() is True
+    assert (s == RaftStatus(s.id, s.raft_state, s.leader_id, True)) is True
+    assert (s == RaftStatus(-1, s.raft_state, s.leader_id, True)) is False
+    assert (s == RaftStatus(s.id, "OtherState", s.leader_id, True)) is False
+    assert (s == RaftStatus(s.id, s.raft_state, -1, True)) is False
 
 
 def test_call_normalization(instance: Instance):
-- 
GitLab