2020-03-06 22:24:25 +01:00
|
|
|
-- Gossip protocol implementation tests
|
|
|
|
-- https://github.com/alexandruantochi/
|
|
|
|
local gossipSubmodules = loadfile('gossip.lua')('test');
|
|
|
|
|
|
|
|
local gossip = gossipSubmodules._gossip;
|
|
|
|
local constants = gossipSubmodules._constants;
|
|
|
|
local utils = gossipSubmodules._utils;
|
|
|
|
local network = gossipSubmodules._network;
|
|
|
|
local state = gossipSubmodules._state;
|
|
|
|
|
|
|
|
-- test constants and mocks
|
|
|
|
|
|
|
|
local function dummy() return nil; end
|
|
|
|
|
|
|
|
-- luacheck: push allow defined
|
|
|
|
tmr = {};
|
|
|
|
tmr.time = function() return 200; end
|
|
|
|
sjson = {};
|
|
|
|
sjson.decode = function(data) return data; end
|
|
|
|
file = {};
|
|
|
|
file.exists = dummy
|
|
|
|
file.putcontents = dummy
|
|
|
|
-- luacheck: pop
|
|
|
|
|
|
|
|
local Ip_1 = '192.168.0.1';
|
|
|
|
local Ip_2 = '192.168.0.2';
|
2022-07-10 17:01:21 +02:00
|
|
|
local Ip_own = '192.168.0.3';
|
2020-03-06 22:24:25 +01:00
|
|
|
|
|
|
|
-- test runner
|
|
|
|
|
|
|
|
local Test = {};
|
|
|
|
|
|
|
|
local RunTests = function()
|
|
|
|
local failures = {};
|
|
|
|
print('\nRunning tests...\n');
|
|
|
|
for testName, test in pairs(Test) do
|
|
|
|
if type(test) == 'function' then
|
|
|
|
local result = testName .. ': ';
|
|
|
|
local passed, res = pcall(test);
|
|
|
|
if passed then
|
|
|
|
result = result .. ' Passed.';
|
|
|
|
else
|
|
|
|
result = result .. ' Failed ->';
|
|
|
|
result = '>>>' .. result .. res;
|
|
|
|
table.insert(failures, testName);
|
|
|
|
end
|
|
|
|
print(result);
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if (#failures ~= 0) then
|
|
|
|
print('\n\n');
|
|
|
|
print('Failed tests (' .. #failures .. '): \n');
|
|
|
|
for k in pairs(failures) do print(failures[k]); end
|
|
|
|
print('\n');
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- utils
|
|
|
|
|
|
|
|
function Test.utils_contains()
|
|
|
|
local seedList = {};
|
|
|
|
assert(not utils.contains(seedList, Ip_1));
|
|
|
|
table.insert(seedList, Ip_1);
|
|
|
|
assert(utils.contains(seedList, Ip_1));
|
|
|
|
table.insert(seedList, Ip_2);
|
|
|
|
assert(utils.contains(seedList, Ip_1) and utils.contains(seedList, Ip_2));
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_setConfig()
|
|
|
|
local config = {
|
|
|
|
seedList = {Ip_1},
|
|
|
|
roundInterval = 1500,
|
|
|
|
comPort = 8000,
|
|
|
|
junk = 'junk'
|
|
|
|
};
|
|
|
|
gossip.config = constants.defaultConfig;
|
|
|
|
utils.setConfig(config);
|
|
|
|
|
|
|
|
assert(#gossip.config.seedList == 1, 'Config failed when adding seedList');
|
|
|
|
assert(gossip.config.seedList[1] == Ip_1,
|
|
|
|
'Config failed to add ip to seedList');
|
|
|
|
assert(gossip.config.roundInterval == 1500,
|
|
|
|
'Config failed to add round interval.');
|
|
|
|
assert(gossip.config.comPort == 8000, 'Config failed to add comPort.');
|
|
|
|
assert(gossip.config.debug == false, 'Debug should be false.');
|
|
|
|
assert(gossip.config.junk == nil, 'Junk data inserted in config.');
|
|
|
|
|
|
|
|
gossip.config = constants.defaultConfig;
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_compare()
|
|
|
|
assert(utils.compare(1, 2) == 1);
|
|
|
|
assert(utils.compare(2, 1) == -1);
|
|
|
|
assert(utils.compare(0, 0) == 0);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_compareNodeData_on_revision()
|
|
|
|
local networkData_1 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
local networkData_2 = {
|
|
|
|
revision = 2,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 1);
|
|
|
|
assert(utils.compareNodeData(networkData_2, networkData_1) == -1);
|
|
|
|
networkData_1.revision = networkData_2.revision;
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 0);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_compareNodeData_on_heartbeat()
|
|
|
|
local networkData_1 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
local networkData_2 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 600,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 1);
|
|
|
|
assert(utils.compareNodeData(networkData_2, networkData_1) == -1);
|
|
|
|
networkData_1.heartbeat = networkData_2.heartbeat;
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 0);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_compareNodeData_on_state()
|
|
|
|
local networkData_1 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
local networkData_2 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.SUSPECT
|
|
|
|
};
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 1);
|
|
|
|
assert(utils.compareNodeData(networkData_2, networkData_1) == -1);
|
|
|
|
networkData_1.state = networkData_2.state;
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 0);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_compareNodeData_on_bad_data()
|
|
|
|
local networkData_1 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = nil,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
local networkData_2 = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 600,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 1);
|
|
|
|
assert(utils.compareNodeData(networkData_2, networkData_1) == -1);
|
|
|
|
networkData_2.state = nil;
|
|
|
|
assert(utils.compareNodeData(networkData_1, networkData_2) == 0);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.utils_getMinus()
|
|
|
|
local data1 = {};
|
|
|
|
local data2 = {};
|
|
|
|
|
|
|
|
data1[Ip_1] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 500,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
data1[Ip_2] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 400,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
data2[Ip_1] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 400,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
data2[Ip_2] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 400,
|
|
|
|
state = constants.nodeState.SUSPECT;
|
|
|
|
};
|
|
|
|
|
2022-07-10 17:01:21 +02:00
|
|
|
local diff1 = utils.getMinus(data1, data2);
|
2020-03-06 22:24:25 +01:00
|
|
|
local diff2 = utils.getMinus(data2, data1);
|
|
|
|
|
2022-07-10 17:01:21 +02:00
|
|
|
assert(diff1[Ip_1] ~= nil and diff1[Ip_2] == nil);
|
2020-03-06 22:24:25 +01:00
|
|
|
assert(diff2[Ip_1] == nil and diff2[Ip_2] ~= nil);
|
|
|
|
end
|
|
|
|
|
|
|
|
-- state
|
|
|
|
|
|
|
|
function Test.state_setRev()
|
2022-07-10 17:01:21 +02:00
|
|
|
gossip.ip = Ip_own;
|
|
|
|
gossip.networkState[Ip_own] = {};
|
|
|
|
gossip.networkState[Ip_own].revision = -1;
|
2020-03-06 22:24:25 +01:00
|
|
|
assert(state.setRev() == 0, 'Revision not initialized to 0.');
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.state_tickNodeState()
|
|
|
|
local ip_1 = Ip_1;
|
|
|
|
local ip_2 = Ip_2;
|
|
|
|
gossip.networkState[ip_1] = {};
|
|
|
|
gossip.networkState[ip_2] = {};
|
|
|
|
gossip.networkState[ip_1].state = constants.nodeState.UP;
|
|
|
|
gossip.networkState[ip_2].state = constants.nodeState.DOWN;
|
|
|
|
state.tickNodeState(ip_1);
|
|
|
|
state.tickNodeState(ip_2);
|
|
|
|
assert(gossip.networkState[ip_1].state == constants.nodeState.UP +
|
|
|
|
constants.nodeState.TICK);
|
|
|
|
assert(gossip.networkState[ip_2].state == constants.nodeState.REMOVE);
|
|
|
|
state.tickNodeState(ip_1);
|
|
|
|
assert(gossip.networkState[ip_1].state == constants.nodeState.SUSPECT);
|
|
|
|
gossip.networkState = {};
|
|
|
|
end
|
|
|
|
|
|
|
|
-- network
|
|
|
|
|
|
|
|
function Test.network_updateNetworkState_no_callback()
|
2022-07-10 17:01:21 +02:00
|
|
|
gossip.ip = Ip_own;
|
2020-03-06 22:24:25 +01:00
|
|
|
local updateData = {}
|
|
|
|
updateData[Ip_1] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 400,
|
|
|
|
state = constants.nodeState.UP
|
|
|
|
};
|
|
|
|
updateData[Ip_2] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 700,
|
2022-07-10 17:01:21 +02:00
|
|
|
state = constants.nodeState.DOWN
|
|
|
|
};
|
|
|
|
updateData[Ip_own] = {
|
|
|
|
revision = 1,
|
|
|
|
heartbeat = 800,
|
|
|
|
state = constants.nodeState.DOWN
|
2020-03-06 22:24:25 +01:00
|
|
|
};
|
|
|
|
network.updateNetworkState(updateData);
|
|
|
|
-- send duplicate data
|
|
|
|
network.updateNetworkState(updateData);
|
|
|
|
assert(#gossip.config.seedList == 2);
|
|
|
|
assert(gossip.config.seedList[1] == Ip_1);
|
|
|
|
assert(gossip.config.seedList[2] == Ip_2);
|
|
|
|
assert(gossip.networkState[Ip_1] ~= nil and gossip.networkState[Ip_2] ~= nil);
|
|
|
|
gossip.networkState = {};
|
|
|
|
gossip.config = constants.defaultConfig;
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.network_updateNetworkState_with_callback()
|
|
|
|
local callbackTriggered = false;
|
|
|
|
local function updateCallback() callbackTriggered = true; end
|
|
|
|
gossip.updateCallback = updateCallback;
|
|
|
|
Test.network_updateNetworkState_no_callback();
|
|
|
|
assert(callbackTriggered);
|
|
|
|
gossip.updateCallback = nil;
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.network_receiveData_when_receive_syn()
|
|
|
|
local originalReceiveSyn = network.receiveSyn;
|
|
|
|
local receiveSynCalled = false;
|
|
|
|
network.receiveSyn = function() receiveSynCalled = true; end
|
|
|
|
network.receiveData('socket', {type = constants.updateType.SYN});
|
|
|
|
network.receiveSyn = originalReceiveSyn;
|
|
|
|
assert(receiveSynCalled);
|
|
|
|
end
|
|
|
|
|
|
|
|
function Test.network_receiveData_when_receive_ack()
|
|
|
|
local originalReceiveAck = network.receiveAck;
|
|
|
|
local receiveAckCalled = false;
|
|
|
|
network.receiveAck = function() receiveAckCalled = true; end
|
|
|
|
network.receiveData('socket', {type = constants.updateType.ACK});
|
|
|
|
network.receiveAck = originalReceiveAck;
|
|
|
|
assert(receiveAckCalled);
|
|
|
|
end
|
|
|
|
|
|
|
|
-- run tests
|
|
|
|
|
|
|
|
RunTests();
|