309 lines
12 KiB
Lua
309 lines
12 KiB
Lua
-- Unit tests for mod_muc_domain_mapper.lua
|
|
-- Run with busted from resources/prosody-plugins/:
|
|
-- busted spec/lua/
|
|
--
|
|
-- Stubs every Prosody dependency so no Prosody installation is needed.
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Stanza / session helpers
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local function make_stanza(name, attrs, children)
|
|
local s = { name = name, attr = attrs or {}, _children = children or {} }
|
|
function s:get_child(cname, _ns)
|
|
for _, c in ipairs(self._children) do
|
|
if c.name == cname then return c end
|
|
end
|
|
return nil
|
|
end
|
|
return s
|
|
end
|
|
|
|
local function make_session(session_type)
|
|
local logs = {}
|
|
return {
|
|
type = session_type or "c2s",
|
|
log_calls = logs,
|
|
-- session.log is called as session.log(level, msg, ...) (not method syntax)
|
|
log = function(level, _msg, ...)
|
|
table.insert(logs, level)
|
|
end,
|
|
}
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Call-tracking rewrite stubs (reset before each test via clear helpers)
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local rewrite_to_calls = {}
|
|
local rewrite_from_calls = {}
|
|
|
|
local function clear_calls()
|
|
for i = #rewrite_to_calls, 1, -1 do rewrite_to_calls[i] = nil end
|
|
for i = #rewrite_from_calls, 1, -1 do rewrite_from_calls[i] = nil end
|
|
end
|
|
|
|
local mock_util = {
|
|
room_jid_match_rewrite = function(jid, _stanza)
|
|
table.insert(rewrite_to_calls, jid)
|
|
return "MAPPED:" .. (jid or "nil")
|
|
end,
|
|
internal_room_jid_match_rewrite = function(jid, _stanza)
|
|
table.insert(rewrite_from_calls, jid)
|
|
return "UNMAPPED:" .. (jid or "nil")
|
|
end,
|
|
}
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Package preloads — must be registered before dofile()
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
package.preload['util.filters'] = function()
|
|
return {
|
|
add_filter_hook = function(_fn) end,
|
|
remove_filter_hook = function(_fn) end,
|
|
add_filter = function(_session, _type, _fn) end,
|
|
}
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Global stubs required by the module at load time
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
_G.prosody = {
|
|
events = { add_handler = function() end },
|
|
hosts = {},
|
|
}
|
|
|
|
_G.module = {
|
|
host = "localhost",
|
|
log = function() end,
|
|
get_option_string = function(_, key, default)
|
|
if key == "muc_mapper_domain_base" then return "localhost" end
|
|
return default
|
|
end,
|
|
get_option_boolean = function(_, key, default)
|
|
if key == "muc_mapper_log_not_allowed_errors" then return true end
|
|
return default
|
|
end,
|
|
require = function(_, name)
|
|
if name == "util" then return mock_util end
|
|
return nil
|
|
end,
|
|
context = function(_, _host)
|
|
return { hook = function() end }
|
|
end,
|
|
}
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Load the module under test
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local ok, err = pcall(dofile, "mod_muc_domain_mapper.lua")
|
|
if not ok then
|
|
describe("mod_muc_domain_mapper", function()
|
|
it("skipped — failed to load", function()
|
|
pending(tostring(err):match("([^\n]+)") or tostring(err))
|
|
end)
|
|
end)
|
|
return
|
|
end
|
|
|
|
local filter_stanza = _G.filter_stanza
|
|
assert(filter_stanza, "filter_stanza must be a global after dofile()")
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Tests
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
describe("mod_muc_domain_mapper", function()
|
|
|
|
before_each(clear_calls)
|
|
|
|
-- -----------------------------------------------------------------------
|
|
describe("filter_stanza", function()
|
|
|
|
-- -------------------------------------------------------------------
|
|
describe("message stanzas", function()
|
|
|
|
it("rewrites 'to' via room_jid_match_rewrite", function()
|
|
local stanza = make_stanza("message", { to = "room@conference.sub.example.com" })
|
|
local session = make_session("c2s")
|
|
|
|
local result = filter_stanza(stanza, session)
|
|
|
|
assert.equal(1, #rewrite_to_calls)
|
|
assert.equal("room@conference.sub.example.com", rewrite_to_calls[1])
|
|
assert.equal("MAPPED:room@conference.sub.example.com", result.attr.to)
|
|
end)
|
|
|
|
it("rewrites 'from' via internal_room_jid_match_rewrite", function()
|
|
local stanza = make_stanza("message", { from = "[sub]room@conference.example.com/nick" })
|
|
local session = make_session("c2s")
|
|
|
|
local result = filter_stanza(stanza, session)
|
|
|
|
assert.equal(1, #rewrite_from_calls)
|
|
assert.equal("[sub]room@conference.example.com/nick", rewrite_from_calls[1])
|
|
assert.equal("UNMAPPED:[sub]room@conference.example.com/nick", result.attr.from)
|
|
end)
|
|
|
|
it("rewrites both 'to' and 'from' when both are set", function()
|
|
local stanza = make_stanza("message", {
|
|
to = "room@conference.sub.example.com",
|
|
from = "[sub]room@conference.example.com/nick",
|
|
})
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(1, #rewrite_to_calls)
|
|
assert.equal(1, #rewrite_from_calls)
|
|
end)
|
|
|
|
it("does not call rewrites when 'to' and 'from' are absent", function()
|
|
local stanza = make_stanza("message", {})
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(0, #rewrite_to_calls)
|
|
assert.equal(0, #rewrite_from_calls)
|
|
end)
|
|
|
|
end)
|
|
|
|
-- -------------------------------------------------------------------
|
|
describe("presence stanzas", function()
|
|
|
|
it("rewrites 'to' and 'from'", function()
|
|
local stanza = make_stanza("presence", {
|
|
to = "room@conference.sub.example.com/nick",
|
|
from = "[sub]room@conference.example.com/nick",
|
|
})
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(1, #rewrite_to_calls)
|
|
assert.equal(1, #rewrite_from_calls)
|
|
end)
|
|
|
|
end)
|
|
|
|
-- -------------------------------------------------------------------
|
|
describe("iq stanzas", function()
|
|
|
|
it("rewrites 'to' and 'from'", function()
|
|
local stanza = make_stanza("iq", {
|
|
to = "room@conference.sub.example.com",
|
|
from = "[sub]room@conference.example.com",
|
|
})
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(1, #rewrite_to_calls)
|
|
assert.equal(1, #rewrite_from_calls)
|
|
end)
|
|
|
|
it("rewrites <conference room> attribute when child is present", function()
|
|
local conf = make_stanza("conference", { room = "room@conference.sub.example.com" })
|
|
local stanza = make_stanza("iq", { to = "focus@example.com" }, { conf })
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
-- room attr on the child gets rewritten
|
|
assert.equal("MAPPED:room@conference.sub.example.com", conf.attr.room)
|
|
-- 'to' also gets rewritten (focus@example.com passes through the mock)
|
|
assert.equal(2, #rewrite_to_calls)
|
|
end)
|
|
|
|
it("does not touch stanzas with no <conference> child", function()
|
|
local stanza = make_stanza("iq", { to = "focus@example.com" })
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(1, #rewrite_to_calls)
|
|
assert.equal(0, #rewrite_from_calls)
|
|
end)
|
|
|
|
end)
|
|
|
|
-- -------------------------------------------------------------------
|
|
describe("skipped stanzas", function()
|
|
|
|
it("passes stanza through unchanged when skipMapping is true", function()
|
|
local stanza = make_stanza("message", { to = "room@conference.sub.example.com" })
|
|
stanza.skipMapping = true
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(0, #rewrite_to_calls)
|
|
assert.equal("room@conference.sub.example.com", stanza.attr.to)
|
|
end)
|
|
|
|
it("passes stanza through unchanged for s2sout sessions", function()
|
|
local stanza = make_stanza("message", { to = "room@conference.sub.example.com" })
|
|
filter_stanza(stanza, make_session("s2sout"))
|
|
|
|
assert.equal(0, #rewrite_to_calls)
|
|
end)
|
|
|
|
it("passes non-message/iq/presence stanzas through unchanged", function()
|
|
local stanza = make_stanza("stream:features", { to = "example.com" })
|
|
filter_stanza(stanza, make_session("c2s"))
|
|
|
|
assert.equal(0, #rewrite_to_calls)
|
|
assert.equal(0, #rewrite_from_calls)
|
|
end)
|
|
|
|
end)
|
|
|
|
-- -------------------------------------------------------------------
|
|
describe("log_not_allowed_errors", function()
|
|
|
|
local function make_not_allowed_presence()
|
|
local not_allowed = make_stanza("not-allowed",
|
|
{ xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" })
|
|
local error_el = make_stanza("error", { type = "cancel" }, { not_allowed })
|
|
return make_stanza("presence", { type = "error" }, { error_el })
|
|
end
|
|
|
|
it("logs a not-allowed presence error once per session", function()
|
|
local stanza = make_not_allowed_presence()
|
|
local session = make_session("c2s")
|
|
|
|
filter_stanza(stanza, session)
|
|
|
|
assert.equal(1, #session.log_calls)
|
|
assert.equal("error", session.log_calls[1])
|
|
assert.is_true(session.jitsi_not_allowed_logged)
|
|
end)
|
|
|
|
it("does not log a second time on the same session", function()
|
|
local stanza = make_not_allowed_presence()
|
|
local session = make_session("c2s")
|
|
|
|
filter_stanza(stanza, session)
|
|
filter_stanza(stanza, session)
|
|
|
|
assert.equal(1, #session.log_calls)
|
|
end)
|
|
|
|
it("does not log for non-error presence", function()
|
|
local stanza = make_stanza("presence", { to = "room@conference.sub.example.com" })
|
|
local session = make_session("c2s")
|
|
|
|
filter_stanza(stanza, session)
|
|
|
|
assert.equal(0, #session.log_calls)
|
|
end)
|
|
|
|
it("does not log when error type is not 'cancel'", function()
|
|
local not_allowed = make_stanza("not-allowed",
|
|
{ xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" })
|
|
local error_el = make_stanza("error", { type = "auth" }, { not_allowed })
|
|
local stanza = make_stanza("presence", { type = "error" }, { error_el })
|
|
local session = make_session("c2s")
|
|
|
|
filter_stanza(stanza, session)
|
|
|
|
assert.equal(0, #session.log_calls)
|
|
end)
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
end)
|