mirror of
https://github.com/bjc/prosody.git
synced 2025-04-03 21:27:38 +03:00
The JSON Schema specification says that schemas are objects or booleans, and that the 'type' property is optional and can be an array. This module previously allowed bare type names as schemas and did not really handle booleans. It now handles missing 'type' properties and boolean 'true' as a schema. Objects and arrays are guessed based on the presence of 'properties' or 'items' field.
243 lines
6.8 KiB
Lua
243 lines
6.8 KiB
Lua
local st
|
|
local xml
|
|
local map
|
|
|
|
setup(function()
|
|
st = require "util.stanza";
|
|
xml = require "util.xml";
|
|
map = require "util.datamapper";
|
|
end);
|
|
|
|
describe("util.datamapper", function()
|
|
|
|
local s, x, d
|
|
local disco, disco_info, disco_schema
|
|
setup(function()
|
|
|
|
-- a convenience function for simple attributes, there's a few of them
|
|
local attr = {["$ref"]="#/$defs/attr"};
|
|
s = {
|
|
["$defs"] = { attr = { type = "string"; xml = { attribute = true } } };
|
|
type = "object";
|
|
xml = {name = "message"; namespace = "jabber:client"};
|
|
properties = {
|
|
to = attr;
|
|
from = attr;
|
|
type = attr;
|
|
id = attr;
|
|
body = true; -- should be assumed to be a string
|
|
lang = {type = "string"; xml = {attribute = true; prefix = "xml"}};
|
|
delay = {
|
|
type = "object";
|
|
xml = {namespace = "urn:xmpp:delay"; name = "delay"};
|
|
properties = {stamp = attr; from = attr; reason = {type = "string"; xml = {text = true}}};
|
|
};
|
|
state = {
|
|
type = "string";
|
|
enum = {
|
|
"active",
|
|
"inactive",
|
|
"gone",
|
|
"composing",
|
|
"paused",
|
|
};
|
|
xml = {x_name_is_value = true; namespace = "http://jabber.org/protocol/chatstates"};
|
|
};
|
|
fallback = {
|
|
type = "boolean";
|
|
xml = {x_name_is_value = true; name = "fallback"; namespace = "urn:xmpp:fallback:0"};
|
|
};
|
|
origin_id = {
|
|
type = "string";
|
|
xml = {name = "origin-id"; namespace = "urn:xmpp:sid:0"; x_single_attribute = "id"};
|
|
};
|
|
react = {
|
|
type = "object";
|
|
xml = {namespace = "urn:xmpp:reactions:0"; name = "reactions"};
|
|
properties = {
|
|
to = {type = "string"; xml = {attribute = true; name = "id"}};
|
|
-- should be assumed to be array since it has 'items'
|
|
reactions = { items = { xml = { name = "reaction" } } };
|
|
};
|
|
};
|
|
stanza_ids = {
|
|
type = "array";
|
|
items = {
|
|
xml = {name = "stanza-id"; namespace = "urn:xmpp:sid:0"};
|
|
type = "object";
|
|
properties = {
|
|
id = attr;
|
|
by = attr;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
x = xml.parse [[
|
|
<message xmlns="jabber:client" xml:lang="en" to="a@test" from="b@test" type="chat" id="1">
|
|
<body>Hello</body>
|
|
<delay xmlns='urn:xmpp:delay' from='test' stamp='2021-03-07T15:59:08+00:00'>Because</delay>
|
|
<UNRELATED xmlns='http://jabber.org/protocol/chatstates'/>
|
|
<active xmlns='http://jabber.org/protocol/chatstates'/>
|
|
<fallback xmlns='urn:xmpp:fallback:0'/>
|
|
<origin-id xmlns='urn:xmpp:sid:0' id='qgkmMdPB'/>
|
|
<stanza-id xmlns='urn:xmpp:sid:0' id='abc1' by='muc'/>
|
|
<stanza-id xmlns='urn:xmpp:sid:0' id='xyz2' by='host'/>
|
|
<reactions id='744f6e18-a57a-11e9-a656-4889e7820c76' xmlns='urn:xmpp:reactions:0'>
|
|
<reaction>👋</reaction>
|
|
<reaction>🐢</reaction>
|
|
</reactions>
|
|
</message>
|
|
]];
|
|
|
|
d = {
|
|
to = "a@test";
|
|
from = "b@test";
|
|
type = "chat";
|
|
id = "1";
|
|
lang = "en";
|
|
body = "Hello";
|
|
delay = {from = "test"; stamp = "2021-03-07T15:59:08+00:00"; reason = "Because"};
|
|
state = "active";
|
|
fallback = true;
|
|
origin_id = "qgkmMdPB";
|
|
stanza_ids = {{id = "abc1"; by = "muc"}; {id = "xyz2"; by = "host"}};
|
|
react = {
|
|
to = "744f6e18-a57a-11e9-a656-4889e7820c76";
|
|
reactions = {
|
|
"👋",
|
|
"🐢",
|
|
};
|
|
};
|
|
};
|
|
|
|
disco_schema = {
|
|
["$defs"] = { attr = { type = "string"; xml = { attribute = true } } };
|
|
type = "object";
|
|
xml = {
|
|
name = "iq";
|
|
namespace = "jabber:client"
|
|
};
|
|
properties = {
|
|
to = attr;
|
|
from = attr;
|
|
type = attr;
|
|
id = attr;
|
|
disco = {
|
|
type = "object";
|
|
xml = {
|
|
name = "query";
|
|
namespace = "http://jabber.org/protocol/disco#info"
|
|
};
|
|
properties = {
|
|
features = {
|
|
type = "array";
|
|
items = {
|
|
type = "string";
|
|
xml = {
|
|
name = "feature";
|
|
x_single_attribute = "var";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
disco_info = xml.parse[[
|
|
<iq type="result" id="disco1" from="example.com">
|
|
<query xmlns="http://jabber.org/protocol/disco#info">
|
|
<feature var="urn:example:feature:1">wrong</feature>
|
|
<feature var="urn:example:feature:2"/>
|
|
<feature var="urn:example:feature:3"/>
|
|
<unrelated var="urn:example:feature:not"/>
|
|
</query>
|
|
</iq>
|
|
]];
|
|
|
|
disco = {
|
|
type="result";
|
|
id="disco1";
|
|
from="example.com";
|
|
disco = {
|
|
features = {
|
|
"urn:example:feature:1";
|
|
"urn:example:feature:2";
|
|
"urn:example:feature:3";
|
|
};
|
|
};
|
|
};
|
|
end);
|
|
|
|
describe("parse", function()
|
|
it("works", function()
|
|
assert.same(d, map.parse(s, x));
|
|
end);
|
|
|
|
it("handles arrays", function ()
|
|
assert.same(disco, map.parse(disco_schema, disco_info));
|
|
end);
|
|
|
|
it("deals with locally built stanzas", function()
|
|
-- FIXME this could also be argued to be a util.stanza problem
|
|
local ver_schema = {
|
|
type = "object";
|
|
xml = {name = "iq"};
|
|
properties = {
|
|
type = {type = "string"; xml = {attribute = true}};
|
|
id = {type = "string"; xml = {attribute = true}};
|
|
version = {
|
|
type = "object";
|
|
xml = {name = "query"; namespace = "jabber:iq:version"};
|
|
-- properties should be assumed to be strings
|
|
properties = {name = true; version = {}; os = {}};
|
|
};
|
|
};
|
|
};
|
|
local ver_st = st.iq({type = "result"; id = "v1"})
|
|
:query("jabber:iq:version")
|
|
:text_tag("name", "Prosody")
|
|
:text_tag("version", "trunk")
|
|
:text_tag("os", "Lua 5.3")
|
|
:reset();
|
|
|
|
local data = {type = "result"; id = "v1"; version = {name = "Prosody"; version = "trunk"; os = "Lua 5.3"}}
|
|
assert.same(data, map.parse(ver_schema, ver_st));
|
|
end);
|
|
|
|
end);
|
|
|
|
describe("unparse", function()
|
|
it("works", function()
|
|
local u = map.unparse(s, d);
|
|
assert.equal("message", u.name);
|
|
assert.same(x.attr, u.attr);
|
|
assert.equal(x:get_child_text("body"), u:get_child_text("body"));
|
|
assert.equal(x:get_child_text("delay", "urn:xmpp:delay"), u:get_child_text("delay", "urn:xmpp:delay"));
|
|
assert.same(x:get_child("delay", "urn:xmpp:delay").attr, u:get_child("delay", "urn:xmpp:delay").attr);
|
|
assert.same(x:get_child("origin-id", "urn:xmpp:sid:0").attr, u:get_child("origin-id", "urn:xmpp:sid:0").attr);
|
|
assert.same(x:get_child("reactions", "urn:xmpp:reactions:0").attr, u:get_child("reactions", "urn:xmpp:reactions:0").attr);
|
|
assert.same(2, #u:get_child("reactions", "urn:xmpp:reactions:0").tags);
|
|
for _, tag in ipairs(x.tags) do
|
|
if tag.name ~= "UNRELATED" then
|
|
assert.truthy(u:get_child(tag.name, tag.attr.xmlns) or u:get_child(tag.name), tag:top_tag())
|
|
end
|
|
end
|
|
assert.equal(#x.tags-1, #u.tags)
|
|
|
|
end);
|
|
|
|
it("handles arrays", function ()
|
|
local u = map.unparse(disco_schema, disco);
|
|
assert.equal("urn:example:feature:1", u:find("{http://jabber.org/protocol/disco#info}query/feature/@var"))
|
|
local n = 0;
|
|
for child in u:get_child("query", "http://jabber.org/protocol/disco#info"):childtags("feature") do
|
|
n = n + 1;
|
|
assert.equal(string.format("urn:example:feature:%d", n), child.attr.var);
|
|
end
|
|
end);
|
|
|
|
end);
|
|
end)
|