libervia-backend/tests/unit/test_plugin_misc_pubsub_contacts.py
Goffi b4a13ce51e tests (unit): Add tests for pubsub contacts:
Tests the models and plugin data behaviour.

rel 475
2026-02-23 13:15:16 +01:00

420 lines
14 KiB
Python

#!/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