spec/util_hashring_spec.lua
author Kim Alvefur <zash@zash.se>
Thu, 28 Mar 2024 15:26:57 +0100
changeset 13472 98806cac64c3
parent 12799 87424cbedc55
permissions -rw-r--r--
MUC: Switch to official XEP-0317 namespace for Hats (including compat) (thanks nicoco)
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
10011
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     1
local hashring = require "util.hashring";
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     2
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     3
describe("util.hashring", function ()
12798
249b01adc54a util.hashring: tests: don't randomize order - they are written in a sequential style
Matthew Wild <mwild1@gmail.com>
parents: 10011
diff changeset
     4
	randomize(false);
10011
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     5
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     6
	local sha256 = require "util.hashes".sha256;
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     7
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     8
	local ring = hashring.new(128, sha256);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     9
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    10
	it("should fail to get a node that does not exist", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    11
		assert.is_nil(ring:get_node("foo"))
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    12
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    13
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    14
	it("should support adding nodes", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    15
		ring:add_node("node1");
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    16
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    17
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    18
	it("should return a single node for all keys if only one node exists", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    19
		for i = 1, 100 do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    20
			assert.is_equal("node1", ring:get_node(tostring(i)))
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    21
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    22
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    23
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    24
	it("should support adding a second node", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    25
		ring:add_node("node2");
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    26
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    27
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    28
	it("should fail to remove a non-existent node", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    29
		assert.is_falsy(ring:remove_node("node3"));
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    30
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    31
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    32
	it("should succeed to remove a node", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    33
		assert.is_truthy(ring:remove_node("node1"));
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    34
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    35
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    36
	it("should return the only node for all keys", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    37
		for i = 1, 100 do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    38
			assert.is_equal("node2", ring:get_node(tostring(i)))
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    39
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    40
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    41
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    42
	it("should support adding multiple nodes", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    43
		ring:add_nodes({ "node1", "node3", "node4", "node5" });
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    44
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    45
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    46
	it("should disrupt a minimal number of keys on node removal", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    47
		local orig_ring = ring:clone();
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    48
		local node_tallies = {};
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    49
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    50
		local n = 1000;
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    51
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    52
		for i = 1, n do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    53
			local key = tostring(i);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    54
			local node = ring:get_node(key);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    55
			node_tallies[node] = (node_tallies[node] or 0) + 1;
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    56
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    57
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    58
		--[[
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    59
		for node, key_count in pairs(node_tallies) do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    60
			print(node, key_count, ("%.2f%%"):format((key_count/n)*100));
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    61
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    62
		]]
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    63
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    64
		ring:remove_node("node5");
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    65
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    66
		local disrupted_keys = 0;
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    67
		for i = 1, n do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    68
			local key = tostring(i);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    69
			if orig_ring:get_node(key) ~= ring:get_node(key) then
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    70
				disrupted_keys = disrupted_keys + 1;
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    71
			end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    72
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    73
		assert.is_equal(node_tallies["node5"], disrupted_keys);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    74
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    75
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    76
	it("should support removing multiple nodes", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    77
		ring:remove_nodes({"node2", "node3", "node4", "node5"});
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    78
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    79
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    80
	it("should return a single node for all keys if only one node remains", function ()
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    81
		for i = 1, 100 do
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    82
			assert.is_equal("node1", ring:get_node(tostring(i)))
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    83
		end
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    84
	end);
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    85
12799
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    86
	it("should support values associated with nodes", function ()
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    87
		local r = hashring.new(128, sha256);
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    88
		r:add_node("node1", { a = 1 });
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    89
		local node, value = r:get_node("foo");
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    90
		assert.is_equal("node1", node);
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    91
		assert.same({ a = 1 }, value);
87424cbedc55 util.hashring: Support associating arbitrary data with nodes
Matthew Wild <mwild1@gmail.com>
parents: 12798
diff changeset
    92
	end);
10011
de43ca319184 util.hashring: Add tests
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    93
end);