Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Adding a unit test for the integrated add_peer functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
rnistuk committed Sep 4, 2018
1 parent 09b690d commit 5c3b0fb
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 7 deletions.
3 changes: 1 addition & 2 deletions raft/raft.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ bool
raft::validate_new_peer(std::shared_ptr<bzn::session_base> session, const bzn::message &peer)
{
// uuid's are signed as text files, so we must append the end of line character
const auto uuid{this->get_uuid() + "\x0a"};
const auto uuid{peer["uuid"].asString() + "\x0a"};
if (!peer.isMember("signature") || peer["signature"].asString().empty())
{
this->send_session_error_message(session, ERROR_INVALID_SIGNATURE);
Expand All @@ -448,7 +448,6 @@ raft::validate_new_peer(std::shared_ptr<bzn::session_base> session, const bzn::m
return false;
}
return true;

}


Expand Down
1 change: 1 addition & 0 deletions raft/raft.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ namespace bzn
FRIEND_TEST(raft_test, test_that_joint_quorum_is_converted_to_single_quorum_and_committed);
FRIEND_TEST(raft_test, test_that_bad_add_or_remove_peer_requests_fail);
FRIEND_TEST(raft_test, test_that_a_four_node_swarm_cannot_reach_consensus_with_two_nodes);
FRIEND_TEST(raft_test, test_that_add_node_works_securely);


void setup_peer_tracking(const bzn::peers_list_t& peers);
Expand Down
138 changes: 133 additions & 5 deletions raft/test/raft_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,28 @@ namespace

const std::string TEST_STATE_DIR = "./.raft_test_state/";

// note the \x0a appended to the end of the uuid's. Since the OpenSSL signing
// is done on the command line with text files, the \x0a is the new line
// marker and may need to be appended for the signing to work.
const std::string valid_uuid {"9dc2f619-2e77-49f7-9b20-5b55fd87ea44"};
const std::string invalid_uuid {"8eb1e708-2e77-49f7-9b20-5b55fd87ea44"};

const std::string signature {
"DprtbrBfC6pXRxiUCBTpvfIr/gDbnjhUIrQysnqXNL3HkGqN07avQ9eilz4T058F"
"HariK5L5V2xoUn2c9K5sKch4qGrCYe2Rs7+alsiDBsqHZvrwQQZbSRnz6iKNijcl"
"8rjquBeqsRthLTRYxLFveV7bNsaYfF4jOsfyQwKeQ7hJX3pHhYkQyh1k8WrzAWhr"
"0/PdqSOT9fBgroNo0TTg3RDSJ6I78xR7gSBYE5C7PNH63dq8qw2WPi+VElHBtJD+"
"Esu3ZMiHnCmGPEx1p40vLSiVz5z3JFJhIb9FfY1RGa3RmNBdPiE8PvYvFGrmLsKD"
"HClL3CzUwC4W/RhFcoML6/+SNawtaqmjJ4AASyWkstf22t/qpfAYkXCSoh1QD46J"
"waOLHE3L2g7woUT3EFa2ziahkLJSADdlmR1ZKRFA09l7+tkEv2rez0NMpeUXXo+A"
"/JG5jMKuJVtJrEEHKubSpxpAb9KSbp6nrKHnAeJKJWyY+aE1LvKBqCN2PcakL6Mn"
"TMdaVgAShwSPmtQE5Z+m+WRJcsHDxVPaSK4BQOx4q4U2fHKqE5dc05bODu2kc/Qt"
"/In3dL6pHqCTGyUWmgZxjUMvAJ3qNxuMjJu0F1d8KxMSSRQ9OC+OpOIPgsiqLP8F"
"DEYcqSC0p2wvbMrtbL1Vz+xGJLi0hryGD6aLA3Es4vk="
};



void
fill_entries_with_test_data(const size_t sz, std::vector<bzn::log_entry>& entries)
{
Expand Down Expand Up @@ -132,6 +154,16 @@ namespace
}


bzn::message
make_secure_add_peer_request(const std::string& uuid)
{
bzn::message msg{make_add_peer_request()};
msg["data"]["peer"]["signature"] = signature;
msg["data"]["peer"]["uuid"] = uuid;
return msg;
}


bzn::message
make_duplicate_add_peer_request()
{
Expand Down Expand Up @@ -344,6 +376,7 @@ namespace bzn
std::shared_ptr<bzn::Mocksession_base> mock_session;
};


TEST(raft, test_that_default_raft_state_is_follower)
{
clean_state_folder();
Expand Down Expand Up @@ -1730,7 +1763,6 @@ namespace bzn

TEST_F(raft_test, test_that_joint_quorum_is_converted_to_single_quorum_and_committed)
{
// TODO make sure comit i=indexes are correct.
auto mock_steady_timer = std::make_unique<NiceMock<bzn::asio::Mocksteady_timer_base>>();

// intercept the timeout callback...
Expand Down Expand Up @@ -1783,8 +1815,8 @@ namespace bzn
EXPECT_EQ(raft->get_state(), bzn::raft_state::candidate);

// now send in each vote...
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid1", 1, true), mock_session);
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid2", 1, true), mock_session);
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid1", 1, true), this->mock_session);
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid2", 1, true), this->mock_session);

EXPECT_EQ(raft->get_state(), bzn::raft_state::leader);

Expand Down Expand Up @@ -1873,12 +1905,108 @@ namespace bzn
}



TEST_F(raft_test, test_that_add_node_works_securely)
{
auto mock_steady_timer = std::make_unique<NiceMock<bzn::asio::Mocksteady_timer_base>>();

// intercept the timeout callback...
bzn::asio::wait_handler wh;
EXPECT_CALL(*mock_steady_timer, async_wait(_)).WillRepeatedly(Invoke(
[&](auto handler)
{ wh = handler; }));

}
EXPECT_CALL(*this->mock_io_context, make_unique_steady_timer()).WillOnce(Invoke(
[&]()
{ return std::move(mock_steady_timer); }));

bzn::message_handler mh;
EXPECT_CALL(*this->mock_node, register_for_message("raft", _)).WillOnce(Invoke(
[&](const auto&, auto handler)
{
mh = handler;
return true;
}));

// create raft...
auto raft = std::make_shared<bzn::raft>(this->mock_io_context, this->mock_node, TEST_PEER_LIST, TEST_NODE_UUID, TEST_STATE_DIR, bzn::DEFAULT_MAX_STORAGE_SIZE, true);

bool commit_handler_called = false;
int commit_handler_times_called = 0;
raft->register_commit_handler(
[&](const bzn::message& msg)
{
LOG(info) << "commit:\n" << msg.toStyledString().substr(0, MAX_MESSAGE_SIZE) << "...";
commit_handler_called = true;
++commit_handler_times_called;
return true;
});

// and away we go...
raft->start();

EXPECT_CALL(*this->mock_session, send_message(An<std::shared_ptr<bzn::message>>(),_))
.WillRepeatedly(Invoke(
[&](const auto& msg, auto)
{
auto root = *msg.get();
if(root.isMember("response"))
{
EXPECT_EQ(root["response"]["msg"].asString(), SUCCESS_PEER_ADDED_TO_SWARM);
}
else if(root.isMember("error"))
{
EXPECT_EQ(root["error"].asString(), ERROR_UNABLE_TO_VALIDATE_UUID);
}
}));

wh(boost::system::error_code());

// send the votes
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid1", 1, true), this->mock_session);
raft->handle_request_vote_response(bzn::create_request_vote_response("uuid2", 1, true), this->mock_session);

EXPECT_EQ(raft->get_state(), bzn::raft_state::leader);

EXPECT_CALL(*this->mock_node, send_message(_, _)).WillRepeatedly(Invoke(
[](const auto& /*session*/, const auto& msg)
{
std::cout << msg->toStyledString();
}));


// attempting to add an invalid peer must fail
{
EXPECT_CALL(*this->mock_node, send_message(_, _)).WillRepeatedly(Invoke(
[](const auto& /*session*/, const auto& /*msg*/)
{
//std::cout << msg->toStyledString();
}));
bzn::message bad_add_peer{make_secure_add_peer_request(invalid_uuid)};
mh(bad_add_peer, this->mock_session);
}

// Adding a valid peer that has a valid signature must be added.
{
bzn::message msg = make_secure_add_peer_request(valid_uuid);
mh(msg,this->mock_session);

// do the concensus and commit the joint quorum
// expire timer...
wh(boost::system::error_code());

raft->handle_request_append_entries_response(bzn::create_append_entries_response("uuid1", 1, true, 1), mock_session);
raft->handle_request_append_entries_response(bzn::create_append_entries_response("uuid2", 1, true, 1), mock_session);
raft->handle_request_append_entries_response(bzn::create_append_entries_response(TEST_NODE_UUID, 1, true, 1), mock_session);
wh(boost::system::error_code());

auto entry = raft->raft_log->last_quorum_entry();
EXPECT_EQ(entry.entry_type, bzn::log_entry_type::joint_quorum);
auto peers = entry.msg["msg"]["peers"]["new"];

const auto peer = std::find_if(peers.begin(), peers.end(), [&](const auto& u) { return valid_uuid==u["uuid"].asString(); });

// The new peer has been added to the quorum.
EXPECT_FALSE(peer==peers.end());
}
}
} // bzn

0 comments on commit 5c3b0fb

Please sign in to comment.