view spec/util_smqueue_spec.lua @ 14226:59c50c76860b

util.prosodyctl.check: Show TLSA records in DNS Doesn't yet check that these match your certificates, but can be compared to 'check certs'
author Kim Alvefur <zash@zash.se>
date Fri, 12 Jun 2026 23:08:09 +0200
parents 73903352de86
children
line wrap: on
line source

describe("util.smqueue", function()

	local smqueue
	setup(function() smqueue = require "util.smqueue"; end)

	describe("#new()", function()
		it("should work", function()
			assert.has_error(function () smqueue.new(-1) end);
			assert.has_error(function () smqueue.new(0) end);
			assert.not_has_error(function () smqueue.new(1) end);
			local q = smqueue.new(10);
			assert.truthy(q);
		end)
	end)

	describe("#push()", function()
		it("should allow pushing many items", function()
			local q = smqueue.new(10);
			for i = 1, 20 do q:push(i); end
			assert.equal(20, q:count_unacked());
		end)
	end)

	describe("#resumable()", function()
		it("returns true while the queue is small", function()
			local q = smqueue.new(10);
			for i = 1, 10 do q:push(i); end
			assert.truthy(q:resumable());
			q:push(11);
			assert.falsy(q:resumable());
		end)
	end)

	describe("#ack", function()
		it("allows removing items", function()
			local q = smqueue.new(10);
			for i = 1, 10 do q:push(i); end
			assert.same({ 1; 2; 3 }, q:ack(3));
			assert.same({ 4; 5; 6 }, q:ack(6));
			assert.falsy(q:ack(3), "can't go backwards")
			assert.falsy(q:ack(100), "can't ack too many")
			for i = 11, 20 do q:push(i); end
			assert.same({ 11; 12 }, q:ack(12), "items are dropped");
		end)
	end)

	describe("#resume", function()
		it("iterates over current items", function()
			local q = smqueue.new(10);
			for i = 1, 12 do q:push(i); end
			assert.same({ 3; 4; 5; 6 }, q:ack(6));
			assert.truthy(q:resumable());
			local resume = {}
			for _, i in q:resume() do resume[i] = true end
			assert.same({ [7] = true; [8] = true; [9] = true; [10] = true; [11] = true; [12] = true }, resume);
		end)
	end)

	describe("#table", function ()
		it("produces a compat layer", function ()
			local q = smqueue.new(10);
			for i = 1,10 do q:push(i); end
			do
				local t = q:table();
				assert.same({ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 }, t);
			end
			do
				for i = 11,20 do q:push(i); end
				local t = q:table();
				assert.same({ 11; 12; 13; 14; 15; 16; 17; 18; 19; 20 }, t);
			end
			do
				q:ack(15);
				local t = q:table();
				assert.same({ 16; 17; 18; 19; 20 }, t);
			end
			do
				q:ack(20);
				local t = q:table();
				assert.same({}, t);
			end
		end)
	end)

	describe("#add_checkpoint", function()
		it("fires the callback when ack reaches the checkpoint", function()
			local q = smqueue.new(10);
			for i = 1, 4 do q:push(i); end
			local called = false;
			q:add_checkpoint(function() called = true; end);
			assert.falsy(called, "should not fire before ack");
			q:ack(4);
			assert.truthy(called, "should fire when ack reaches checkpoint");
		end)

		it("fires when ack exceeds the checkpoint", function()
			local q = smqueue.new(10);
			for i = 1, 4 do q:push(i); end
			local called = false;
			q:add_checkpoint(function() called = true; end);
			for i = 5, 8 do q:push(i); end
			q:ack(7); -- acks past the checkpoint at 4
			assert.truthy(called);
		end)

		it("does not fire for acks below the checkpoint threshold", function()
			local q = smqueue.new(10);
			for i = 1, 5 do q:push(i); end
			local called = false;
			q:add_checkpoint(function() called = true; end);
			q:ack(4);
			assert.falsy(called, "should not fire when ack is below checkpoint");
		end)

		it("fires the callback with optional data argument", function()
			local q = smqueue.new(10);
			for i = 1, 4 do q:push(i); end
			local received_data;
			q:add_checkpoint(function(d) received_data = d; end, "hello");
			q:ack(4);
			assert.equal("hello", received_data);
		end)

		it("fires multiple checkpoints in a single ack call", function()
			local q = smqueue.new(10);
			for i = 1, 3 do q:push(i); end
			local fired = {};
			q:add_checkpoint(function() fired[#fired+1] = "first"; end);
			q:push(4);
			q:add_checkpoint(function() fired[#fired+1] = "second-1"; end);
			q:add_checkpoint(function() fired[#fired+1] = "second-2"; end);
			q:push(5);
			q:add_checkpoint(function() fired[#fired+1] = "third"; end);
			q:ack(5); -- should call all checkpoints, in order
			assert.same({ "first", "second-1", "second-2", "third" }, fired);
		end)

		it("fires checkpoints in order across multiple acks", function()
			local q = smqueue.new(10);
			for i = 1, 3 do q:push(i); end
			local order = {};
			q:add_checkpoint(function() order[#order+1] = 1; end);
			q:push(4);
			q:add_checkpoint(function() order[#order+1] = 2; end);
			q:push(5);
			q:add_checkpoint(function() order[#order+1] = 3; end);
			q:ack(3); -- fires only first
			assert.same({ 1 }, order);
			q:ack(5); -- fires second and third
			assert.same({ 1, 2, 3 }, order);
		end)

		it("fires each checkpoint exactly once", function()
			local q = smqueue.new(10);
			for i = 1, 4 do q:push(i); end
			local count = 0;
			q:add_checkpoint(function() count = count + 1; end);
			q:ack(4);
			q:ack(4); -- duplicate ack
			for i = 5, 8 do q:push(i); end
			q:ack(8);
			assert.equal(1, count);
		end)
	end)
end);