# /****************************************************************************
# *
# * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
# *
# * This program is free software; you can redistribute it and/or
# * modify it under the terms of version 2 of the GNU General Public License
# * as published by the Free Software Foundation.
# *
# * 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 General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, contact Novell, Inc.
# *
# * To contact Novell about this file by physical or electronic mail, you
# * may find current contact information at www.novell.com.
# *
# ****************************************************************************/
import io
from pathlib import Path
import tempfile
from types import SimpleNamespace
import unittest
from unittest import mock
from bongo import table
from bongo.Console import wrap
from bongo.external.simpletal import simpleTAL, simpleTALES
from bongo.store.StoreClient import CalendarACL, StoreClient
from bongo.storetool.CalendarCommands import (_collection_name,
_event_filename,
_event_payload)
from bongo.storetool.Connection import connect_store
from bongo.storetool.ContactCommands import AddressbookContactsCommand
from bongo.storetool.ExportMailbox import MaildirMailbox, MboxMailbox
class _Response:
def __init__(self, code, message=""):
self.code = code
self.message = message
class _Stream:
def __init__(self, responses):
self.responses = list(responses)
self.lines = []
self.raw = []
def Write(self, value):
self.lines.append(value)
def WriteRaw(self, value):
self.raw.append(value)
def GetResponse(self):
return self.responses.pop(0)
class Python3RuntimeTests(unittest.TestCase):
@mock.patch("bongo.storetool.Connection.StoreClient")
def test_storetool_connection_uses_every_global_option(self, client):
options = SimpleNamespace(
user="alice", store="alice-store", host="store.example.test",
port="1689", password="secret")
connect_store(options)
client.assert_called_once_with(
"alice", "alice-store", host="store.example.test", port=1689,
authPassword="secret")
def test_calendar_import_splits_events_and_normalizes_collection_names(self):
import vobject
source = vobject.readOne(
"BEGIN:VCALENDAR\r\n"
"VERSION:2.0\r\n"
"BEGIN:VEVENT\r\n"
"UID:event@example.test\r\n"
"DTSTART:20260724T080000Z\r\n"
"END:VEVENT\r\n"
"END:VCALENDAR\r\n")
event = source.vevent
payload = _event_payload(source, event)
self.assertEqual(_collection_name("/calendars/personal"), "personal")
self.assertIn("BEGIN:VEVENT", payload)
self.assertIn("UID:event@example.test", payload)
self.assertRegex(_event_filename(event), r"^import-[0-9a-f]{64}$")
def test_contact_listing_reads_document_bodies(self):
addressbook = SimpleNamespace(uid="addressbook-guid")
contact = SimpleNamespace(uid="contact-guid")
store = mock.Mock()
store.List.side_effect = [
[SimpleNamespace(filename="/addressbook/personal",
uid=addressbook.uid)],
[contact],
]
store.Read.return_value = b'{"fn": "\\u00c4nne Example"}'
command = AddressbookContactsCommand()
with mock.patch(
"bongo.storetool.ContactCommands.connect_store",
return_value=store), \
mock.patch("builtins.print") as output:
command.Run(SimpleNamespace(), ["personal"])
store.List.assert_has_calls([
mock.call("/addressbook"),
mock.call(addressbook.uid),
])
store.Read.assert_called_once_with(contact.uid)
self.assertIn("\u00c4nne Example", output.call_args.args[0])
def test_console_table_and_unicode_template_are_text(self):
self.assertIsInstance(wrap("Grüße 世界", width=8), str)
self.assertEqual(table.format_table(["Name"], [["Müller"]]),
"Name\n------\nMüller")
context = simpleTALES.Context()
context.addGlobal("name", "A&B")
template = simpleTAL.compileHTMLTemplate(
'
placeholder
')
output = io.StringIO()
template.expand(context, output)
self.assertEqual(output.getvalue(), "A&B
")
def test_store_wire_lengths_are_encoded_byte_lengths(self):
client = object.__new__(StoreClient)
client.stream = _Stream([
_Response(2002), _Response(1000, "guid ok"),
_Response(2002), _Response(1000),
_Response(2002), _Response(1000, "guid ok"),
])
client.Write("/mail/INBOX", 2, "Grüße")
client.PropSet("guid", "display", "Grüße")
client.Replace("guid", "Grüße")
expected = "Grüße".encode("utf-8")
self.assertIn(" 7", client.stream.lines[0])
self.assertTrue(client.stream.lines[1].endswith(" 7"))
self.assertTrue(client.stream.lines[2].endswith(" 7"))
self.assertEqual(client.stream.raw, [expected, expected, expected])
def test_store_info_treats_missing_document_as_normal_absence(self):
client = object.__new__(StoreClient)
client.stream = _Stream([_Response(4224, "Document doesn't exist")])
self.assertIsNone(client.Info("/events/new-event"))
def test_calendar_share_token_uses_python3_hashlib(self):
token = CalendarACL(None)._GetUid("calendar")
self.assertEqual(len(token), 32)
int(token, 16)
def test_mailbox_writers_accept_store_bytes(self):
message = (
b"From: sender@example.test\r\n"
b"To: user@example.test\r\n"
b"Subject: Python 3\r\n\r\nBody\r\n")
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
mbox_path = root / "mailbox"
writer = MboxMailbox(str(mbox_path))
writer.add(message)
writer.write()
self.assertIn(b"Subject: Python 3", mbox_path.read_bytes())
maildir_path = root / "maildir"
writer = MaildirMailbox(str(maildir_path))
writer.add(message)
writer.write()
payloads = [
path.read_bytes()
for folder in ("new", "cur")
for path in (maildir_path / folder).iterdir()
]
self.assertEqual(len(payloads), 1)
self.assertIn(b"Subject: Python 3", payloads[0])
if __name__ == "__main__":
unittest.main()