1
0
Fork 0
mirror of https://github.com/Kozea/Radicale.git synced 2025-06-26 16:45:52 +00:00
Radicale/radicale/tests/__init__.py

166 lines
6.1 KiB
Python
Raw Normal View History

2012-09-15 10:00:13 +02:00
# This file is part of Radicale Server - Calendar Server
2017-05-27 17:28:07 +02:00
# Copyright © 2012-2017 Guillaume Ayoub
2019-06-17 04:13:24 +02:00
# Copyright © 2017-2018 Unrud <unrud@outlook.com>
2012-09-15 10:00:13 +02:00
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library 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 Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Tests for Radicale.
"""
2020-04-22 19:20:36 +02:00
import base64
2018-08-21 18:43:44 +02:00
import logging
2012-09-15 10:00:13 +02:00
import sys
2013-09-05 15:13:31 +02:00
from io import BytesIO
2012-09-15 10:00:13 +02:00
import defusedxml.ElementTree as DefusedET
2019-06-15 09:01:55 +02:00
import radicale
2020-02-20 10:41:33 +01:00
from radicale import xmlutils
2018-09-09 07:28:36 +02:00
2018-08-21 18:43:44 +02:00
# Enable debug output
radicale.log.logger.setLevel(logging.DEBUG)
2012-09-15 10:00:13 +02:00
class BaseTest:
2012-09-15 10:00:13 +02:00
"""Base class for tests."""
2020-04-22 19:20:36 +02:00
def request(self, method, path, data=None, login=None, **args):
2012-09-15 10:00:13 +02:00
"""Send a request."""
for key in args:
args[key.upper()] = args[key]
2020-04-22 19:20:36 +02:00
if login:
args["HTTP_AUTHORIZATION"] = "Basic " + base64.b64encode(
login.encode()).decode()
2012-09-15 10:00:13 +02:00
args["REQUEST_METHOD"] = method.upper()
args["PATH_INFO"] = path
2013-09-05 15:13:31 +02:00
if data:
2020-01-19 18:13:05 +01:00
data = data.encode()
2013-09-05 15:13:31 +02:00
args["wsgi.input"] = BytesIO(data)
args["CONTENT_LENGTH"] = str(len(data))
2018-08-16 07:59:56 +02:00
args["wsgi.errors"] = sys.stderr
2018-04-29 23:06:18 +02:00
status = headers = None
def start_response(status_, headers_):
nonlocal status, headers
status = status_
headers = headers_
answer = self.application(args, start_response)
return (int(status.split()[0]), dict(headers),
2020-01-19 18:13:05 +01:00
answer[0].decode() if answer else None)
@staticmethod
def parse_responses(text):
xml = DefusedET.fromstring(text)
assert xml.tag == xmlutils.make_clark("D:multistatus")
path_responses = {}
for response in xml.findall(xmlutils.make_clark("D:response")):
href = response.find(xmlutils.make_clark("D:href"))
assert href.text not in path_responses
prop_respones = {}
for propstat in response.findall(
xmlutils.make_clark("D:propstat")):
status = propstat.find(xmlutils.make_clark("D:status"))
assert status.text.startswith("HTTP/1.1 ")
status_code = int(status.text.split(" ")[1])
for prop in propstat.findall(xmlutils.make_clark("D:prop")):
for element in prop:
human_tag = xmlutils.make_human_tag(element.tag)
assert human_tag not in prop_respones
prop_respones[human_tag] = (status_code, element)
status = response.find(xmlutils.make_clark("D:status"))
if status is not None:
assert not prop_respones
assert status.text.startswith("HTTP/1.1 ")
status_code = int(status.text.split(" ")[1])
path_responses[href.text] = status_code
else:
path_responses[href.text] = prop_respones
return path_responses
@staticmethod
def _check_status(status, good_status, check=True):
2020-04-22 19:20:36 +02:00
if check is True:
assert status == good_status
elif check is not False:
assert status == check
return status == good_status
def get(self, path, check=True, **args):
status, _, answer = self.request("GET", path, **args)
self._check_status(status, 200, check)
return status, answer
def put(self, path, data, check=True, **args):
status, _, answer = self.request("PUT", path, data, **args)
self._check_status(status, 201, check)
return status, answer
def propfind(self, path, data=None, check=True, **args):
status, _, answer = self.request("PROPFIND", path, data, **args)
if not self._check_status(status, 207, check):
return status, None
responses = self.parse_responses(answer)
if args.get("HTTP_DEPTH", 0) == 0:
assert len(responses) == 1 and path in responses
return status, responses
def proppatch(self, path, data=None, check=True, **args):
status, _, answer = self.request("PROPPATCH", path, data, **args)
if not self._check_status(status, 207, check):
return status, None
responses = self.parse_responses(answer)
assert len(responses) == 1 and path in responses
return status, responses
def report(self, path, data, check=True, **args):
status, _, answer = self.request("REPORT", path, data, **args)
if not self._check_status(status, 207, check):
return status, None
return status, self.parse_responses(answer)
def delete(self, path, check=True, **args):
status, _, answer = self.request("DELETE", path, **args)
if not self._check_status(status, 200, check):
return status, None
responses = self.parse_responses(answer)
assert len(responses) == 1 and path in responses
return status, responses
def mkcalendar(self, path, data=None, check=True, **args):
status, _, answer = self.request("MKCALENDAR", path, data, **args)
self._check_status(status, 201, check)
return status, answer
def mkcol(self, path, data=None, check=True, **args):
status, _, _ = self.request("MKCOL", path, data, **args)
self._check_status(status, 201, check)
return status
def create_addressbook(self, path, check=True, **args):
return self.mkcol(path, """\
<?xml version="1.0" encoding="UTF-8" ?>
<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
<set>
<prop>
<resourcetype>
<collection />
<CR:addressbook />
</resourcetype>
</prop>
</set>
</create>""", check=check, **args)