tests (unit): Add tests for pubsub contacts:
Tests the models and plugin data behaviour. rel 475
This commit is contained in:
parent
1a0cc787f8
commit
b4a13ce51e
2 changed files with 427 additions and 1 deletions
|
|
@ -28,7 +28,13 @@ from pytest_twisted import ensureDeferred as ed
|
|||
|
||||
from libervia.backend.core import exceptions
|
||||
from libervia.backend.tools.common import data_format
|
||||
from libervia.frontends.tools import webrtc as webrtc_mod
|
||||
try:
|
||||
from libervia.frontends.tools import webrtc as webrtc_mod
|
||||
except exceptions.MissingModule as e:
|
||||
pytest.skip(
|
||||
"Can't import webrtc due to missing modules: {e}",
|
||||
allow_module_level=True
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
420
tests/unit/test_plugin_misc_pubsub_contacts.py
Normal file
420
tests/unit/test_plugin_misc_pubsub_contacts.py
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Tests for Libervia's Pubsub Contacts plugin
|
||||
# Copyright (C) 2009-2026 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from twisted.words.xish import domish
|
||||
from wokkel.pubsub import NS_PUBSUB
|
||||
|
||||
from libervia.backend.plugins.plugin_misc_pubsub_contacts import (
|
||||
Contact,
|
||||
Contacts,
|
||||
ContactsData,
|
||||
Group,
|
||||
Groups,
|
||||
NS_CONTACTS,
|
||||
)
|
||||
|
||||
|
||||
class TestGroup:
|
||||
"""Tests for Group model."""
|
||||
|
||||
def test_from_element(self):
|
||||
"""Group is correctly parsed from element."""
|
||||
element = domish.Element((NS_CONTACTS, "group"))
|
||||
element["id"] = "group123"
|
||||
element["name"] = "My Group"
|
||||
desc_elt = element.addElement((NS_CONTACTS, "description"))
|
||||
desc_elt.addContent("A test group")
|
||||
|
||||
group = Group.from_element(element)
|
||||
|
||||
assert group.id == "group123"
|
||||
assert group.name == "My Group"
|
||||
assert group.description == "A test group"
|
||||
|
||||
def test_from_element_nested_in_parent(self):
|
||||
"""Group is correctly parsed from <group> element's parent."""
|
||||
parent = domish.Element((None, "parent"))
|
||||
group_elt = parent.addElement((NS_CONTACTS, "group"))
|
||||
group_elt["id"] = "group123"
|
||||
group_elt["name"] = "Test Group"
|
||||
|
||||
group = Group.from_element(parent)
|
||||
|
||||
assert group.id == "group123"
|
||||
assert group.name == "Test Group"
|
||||
|
||||
def test_from_element_set_encrypted(self):
|
||||
""" "encrypted" flag is correctly set when present in metadata."""
|
||||
element = domish.Element((NS_CONTACTS, "group"))
|
||||
element["id"] = "group123"
|
||||
metadata = {"encrypted": {"group123": True}}
|
||||
|
||||
group = Group.from_element(element, metadata)
|
||||
|
||||
assert group.encrypted is True
|
||||
|
||||
def test_from_element_invalid_type_raises_error(self):
|
||||
"""ValueError is raised when element is not a group element."""
|
||||
element = domish.Element((NS_CONTACTS, "contact"))
|
||||
|
||||
try:
|
||||
Group.from_element(element)
|
||||
assert False, "Expected ValueError"
|
||||
except ValueError as e:
|
||||
assert "Invalid element" in str(e)
|
||||
|
||||
def test_to_element(self):
|
||||
"""Group element is correctly created with all fields."""
|
||||
group = Group(
|
||||
id="group123", name="My Group", description="A test group", encrypted=True
|
||||
)
|
||||
element = group.to_element()
|
||||
|
||||
assert element.uri == NS_CONTACTS
|
||||
assert element.name == "group"
|
||||
assert element["id"] == "group123"
|
||||
assert element["name"] == "My Group"
|
||||
|
||||
desc_elements = list(element.elements(NS_CONTACTS, "description"))
|
||||
assert len(desc_elements) == 1
|
||||
assert str(desc_elements[0]) == "A test group"
|
||||
|
||||
def test_to_element_missing_name_raises_error(self):
|
||||
"""ValueError is raised when name is missing and id_only is False."""
|
||||
group = Group(id="group123")
|
||||
|
||||
try:
|
||||
group.to_element(id_only=False)
|
||||
assert False, "Expected ValueError"
|
||||
except ValueError as e:
|
||||
assert "name must be set" in str(e)
|
||||
|
||||
|
||||
class TestGroups:
|
||||
"""Tests for Groups model."""
|
||||
|
||||
def test_append_and_iterate(self):
|
||||
"""Groups can be appended and iterated."""
|
||||
groups = Groups([])
|
||||
groups.append(Group(id="g1", name="Group 1"))
|
||||
groups.append(Group(id="g2", name="Group 2"))
|
||||
|
||||
group_list = list(groups)
|
||||
assert len(group_list) == 2
|
||||
assert group_list[0].id == "g1"
|
||||
assert group_list[1].id == "g2"
|
||||
|
||||
def test_len_and_remove(self):
|
||||
"""Groups len and remove work correctly."""
|
||||
groups = Groups([])
|
||||
groups.append(Group(id="g1", name="Group 1"))
|
||||
groups.append(Group(id="g2", name="Group 2"))
|
||||
|
||||
assert len(groups) == 2
|
||||
groups.remove(groups[0])
|
||||
assert len(groups) == 1
|
||||
assert groups[0].id == "g2"
|
||||
|
||||
def test_extend(self):
|
||||
"""Groups extend works correctly."""
|
||||
groups = Groups([])
|
||||
groups.extend([Group(id="g1"), Group(id="g2")])
|
||||
|
||||
assert len(groups) == 2
|
||||
|
||||
def test_clear(self):
|
||||
"""Groups clear works correctly."""
|
||||
groups = Groups([Group(id="g1"), Group(id="g2")])
|
||||
groups.clear()
|
||||
|
||||
assert len(groups) == 0
|
||||
|
||||
def test_sort(self):
|
||||
"""Groups sort works correctly."""
|
||||
groups = Groups([Group(id="g2"), Group(id="g1")])
|
||||
groups.sort(key=lambda g: g.id)
|
||||
|
||||
assert groups[0].id == "g1"
|
||||
assert groups[1].id == "g2"
|
||||
|
||||
def test_getitem(self):
|
||||
"""Groups __getitem__ works correctly."""
|
||||
groups = Groups([Group(id="g1"), Group(id="g2")])
|
||||
assert groups[0].id == "g1"
|
||||
assert groups[1].id == "g2"
|
||||
|
||||
|
||||
class TestContact:
|
||||
"""Tests for Contact model."""
|
||||
|
||||
def test_from_element_with_jid_id(self):
|
||||
"""Contact is correctly parsed from element with jid identity."""
|
||||
contact_elt = domish.Element((NS_CONTACTS, "contact"))
|
||||
contact_elt["name"] = "Test Name"
|
||||
identity_elt = contact_elt.addElement((NS_CONTACTS, "identity"))
|
||||
identity_elt["type"] = "jid"
|
||||
identity_elt.addContent("user@example.com")
|
||||
|
||||
contact = Contact.from_element(contact_elt, ContactsData())
|
||||
|
||||
assert contact.identities == {"jid": "user@example.com"}
|
||||
assert contact.name == "Test Name"
|
||||
assert len(contact.groups) == 0
|
||||
|
||||
def test_from_element_with_group(self):
|
||||
"""Contact is correctly parsed with group reference."""
|
||||
parent = domish.Element((NS_PUBSUB, "items"))
|
||||
item_elt = parent.addElement((NS_PUBSUB, "item"))
|
||||
item_elt["id"] = "contact123"
|
||||
contact_elt = item_elt.addElement((NS_CONTACTS, "contact"))
|
||||
identity_elt = contact_elt.addElement((NS_CONTACTS, "identity"))
|
||||
identity_elt["type"] = "jid"
|
||||
identity_elt.addContent("user@example.com")
|
||||
group_elt = contact_elt.addElement((NS_CONTACTS, "group"))
|
||||
group_elt["id"] = "g1"
|
||||
|
||||
contacts_data = ContactsData()
|
||||
contacts_data.groups = Groups([Group(id="g1", name="Friends")])
|
||||
|
||||
contact = Contact.from_element(contact_elt, contacts_data)
|
||||
|
||||
assert contact.id == "contact123"
|
||||
assert len(contact.groups) == 1
|
||||
assert contact.groups[0].id == "g1"
|
||||
assert contact.groups[0].name == "Friends"
|
||||
|
||||
def test_from_element_with_ancestor_item_id(self):
|
||||
"""Contact id is extracted from ancestor item element."""
|
||||
parent = domish.Element((NS_PUBSUB, "items"))
|
||||
item_elt = parent.addElement((NS_PUBSUB, "item"))
|
||||
item_elt["id"] = "contact123"
|
||||
contact_elt = item_elt.addElement((NS_CONTACTS, "contact"))
|
||||
identity_elt = contact_elt.addElement((NS_CONTACTS, "identity"))
|
||||
identity_elt["type"] = "jid"
|
||||
identity_elt.addContent("user@example.com")
|
||||
|
||||
contact = Contact.from_element(contact_elt, ContactsData())
|
||||
|
||||
assert contact.id == "contact123"
|
||||
|
||||
def test_from_element_with_ancestor_item_encrypted(self):
|
||||
"""Contact encrypted flag is set from item metadata."""
|
||||
parent = domish.Element((NS_PUBSUB, "items"))
|
||||
item_elt = parent.addElement((NS_PUBSUB, "item"))
|
||||
item_elt["id"] = "contact123"
|
||||
item_elt["encrypted"] = "true"
|
||||
contact_elt = item_elt.addElement((NS_CONTACTS, "contact"))
|
||||
identity_elt = contact_elt.addElement((NS_CONTACTS, "identity"))
|
||||
identity_elt["type"] = "jid"
|
||||
identity_elt.addContent("user@example.com")
|
||||
|
||||
metadata = {"encrypted": {"contact123": True}}
|
||||
contact = Contact.from_element(contact_elt, ContactsData(), metadata)
|
||||
|
||||
assert contact.encrypted is True
|
||||
|
||||
def test_to_element(self):
|
||||
"""Contact element is correctly created."""
|
||||
contact = Contact(
|
||||
id="contact123",
|
||||
name="Test Name",
|
||||
identities={"jid": "user@example.com"},
|
||||
groups=Groups([Group(id="g1", name="Friends")]),
|
||||
)
|
||||
element = contact.to_element()
|
||||
|
||||
assert element.uri == NS_CONTACTS
|
||||
assert element.name == "contact"
|
||||
assert element["name"] == "Test Name"
|
||||
|
||||
identity_elements = list(element.elements(NS_CONTACTS, "identity"))
|
||||
assert len(identity_elements) == 1
|
||||
assert identity_elements[0]["type"] == "jid"
|
||||
assert str(identity_elements[0]) == "user@example.com"
|
||||
|
||||
group_elements = list(element.elements(NS_CONTACTS, "group"))
|
||||
assert len(group_elements) == 1
|
||||
group_element = group_elements[0]
|
||||
assert group_element["id"] == "g1"
|
||||
assert not group_element.hasAttribute(
|
||||
"name"
|
||||
), "name attribute should not be present in group with id_only"
|
||||
|
||||
class TestContacts:
|
||||
"""Tests for Contacts root model."""
|
||||
|
||||
def test_append_and_iterate(self):
|
||||
"""Contacts can be appended and iterated."""
|
||||
contacts = Contacts([])
|
||||
contacts.append(Contact(identities={"jid": "user1@example.com"}, id="c1"))
|
||||
contacts.append(Contact(identities={"jid": "user2@example.com"}, id="c2"))
|
||||
|
||||
contact_list = list(contacts)
|
||||
assert len(contact_list) == 2
|
||||
assert contact_list[0].id == "c1"
|
||||
assert contact_list[1].id == "c2"
|
||||
|
||||
def test_len_and_remove(self):
|
||||
"""Contacts len and remove work correctly."""
|
||||
contacts = Contacts([])
|
||||
contacts.append(Contact(identities={"jid": "user1@example.com"}, id="c1"))
|
||||
contacts.append(Contact(identities={"jid": "user2@example.com"}, id="c2"))
|
||||
|
||||
assert len(contacts) == 2
|
||||
contacts.remove(contacts[0])
|
||||
assert len(contacts) == 1
|
||||
assert contacts[0].id == "c2"
|
||||
|
||||
def test_extend(self):
|
||||
"""Contacts extend works correctly."""
|
||||
contacts = Contacts([])
|
||||
contacts.extend(
|
||||
[
|
||||
Contact(identities={"jid": "user1@example.com"}),
|
||||
Contact(identities={"jid": "user2@example.com"}),
|
||||
]
|
||||
)
|
||||
|
||||
assert len(contacts) == 2
|
||||
|
||||
def test_clear(self):
|
||||
"""Contacts clear works correctly."""
|
||||
contacts = Contacts(
|
||||
[
|
||||
Contact(identities={"jid": "user1@example.com"}),
|
||||
Contact(identities={"jid": "user2@example.com"}),
|
||||
]
|
||||
)
|
||||
contacts.clear()
|
||||
|
||||
assert len(contacts) == 0
|
||||
|
||||
def test_sort(self):
|
||||
"""Contacts sort works correctly."""
|
||||
contacts = Contacts(
|
||||
[
|
||||
Contact(identities={"jid": "user2@example.com"}, id="c2"),
|
||||
Contact(identities={"jid": "user1@example.com"}, id="c1"),
|
||||
]
|
||||
)
|
||||
contacts.sort(key=lambda c: c.id)
|
||||
|
||||
assert contacts[0].id == "c1"
|
||||
assert contacts[1].id == "c2"
|
||||
|
||||
def test_getitem(self):
|
||||
"""Contacts __getitem__ works correctly."""
|
||||
contacts = Contacts(
|
||||
[
|
||||
Contact(identities={"jid": "user1@example.com"}, id="c1"),
|
||||
Contact(identities={"jid": "user2@example.com"}, id="c2"),
|
||||
]
|
||||
)
|
||||
assert contacts[0].id == "c1"
|
||||
assert contacts[1].id == "c2"
|
||||
|
||||
|
||||
class TestContactsData:
|
||||
"""Tests for ContactsData class."""
|
||||
|
||||
def test_jid_to_contacts_empty(self):
|
||||
"""jid_to_contacts returns empty dict when contacts is empty."""
|
||||
data = ContactsData()
|
||||
assert data.jid_to_contacts == {}
|
||||
|
||||
def test_jid_to_contacts(self):
|
||||
"""jid_to_contacts correctly maps JIDs to contacts."""
|
||||
contact1 = Contact(
|
||||
id="c1",
|
||||
identities={"jid": "user1@example.com"},
|
||||
)
|
||||
contact2 = Contact(
|
||||
id="c2",
|
||||
identities={"jid": "user2@example.com"},
|
||||
)
|
||||
data = ContactsData(contacts=Contacts([contact1, contact2]))
|
||||
|
||||
jid_map = data.jid_to_contacts
|
||||
|
||||
assert len(jid_map) == 2
|
||||
assert jid_map["user1@example.com"].id == "c1"
|
||||
assert jid_map["user2@example.com"].id == "c2"
|
||||
|
||||
def test_jid_to_contacts_skips_contact_without_id(self):
|
||||
"""jid_to_contacts skips contacts without id."""
|
||||
contact1 = Contact(
|
||||
id="c1",
|
||||
identities={"jid": "user1@example.com"},
|
||||
)
|
||||
contact2 = Contact(
|
||||
id="c2",
|
||||
identities={"jid": "user2@example.com"},
|
||||
)
|
||||
contact_no_id = Contact(
|
||||
id=None,
|
||||
identities={"jid": "user3@example.com"},
|
||||
)
|
||||
data = ContactsData(contacts=Contacts([contact1, contact2, contact_no_id]))
|
||||
|
||||
jid_map = data.jid_to_contacts
|
||||
|
||||
assert len(jid_map) == 2
|
||||
assert "user1@example.com" in jid_map
|
||||
assert "user2@example.com" in jid_map
|
||||
assert "user3@example.com" not in jid_map
|
||||
|
||||
def test_ids_to_groups(self):
|
||||
"""ids_to_groups correctly maps group IDs to groups."""
|
||||
group1 = Group(id="g1", name="Group 1")
|
||||
group2 = Group(id="g2", name="Group 2")
|
||||
data = ContactsData(groups=Groups([group1, group2]))
|
||||
|
||||
id_map = data.ids_to_groups
|
||||
|
||||
assert len(id_map) == 2
|
||||
assert id_map["g1"].name == "Group 1"
|
||||
assert id_map["g2"].name == "Group 2"
|
||||
|
||||
def test_ids_to_groups_skips_missing_id(self):
|
||||
"""ids_to_groups skips groups without ID."""
|
||||
group = Group(id="g1", name="Group 1")
|
||||
group_no_id = Group(name="No ID Group")
|
||||
data = ContactsData(groups=Groups([group, group_no_id]))
|
||||
|
||||
id_map = data.ids_to_groups
|
||||
|
||||
assert len(id_map) == 1
|
||||
assert "g1" in id_map
|
||||
|
||||
def test_reset(self):
|
||||
"""Reset clears cached mappings."""
|
||||
contact = Contact(
|
||||
id="c1",
|
||||
identities={"jid": "user1@example.com"},
|
||||
)
|
||||
group = Group(id="g1", name="Group 1")
|
||||
data = ContactsData(contacts=Contacts([contact]), groups=Groups([group]))
|
||||
|
||||
# Access the mappings to populate caches, and check that they are not empty.
|
||||
assert len(data.jid_to_contacts) == 1
|
||||
assert len(data.ids_to_groups) == 1
|
||||
|
||||
data.reset()
|
||||
|
||||
assert data._jid_to_contacts is None
|
||||
assert data._ids_to_groups is None
|
||||
Loading…
Reference in a new issue