565 lines
19 KiB
Python
565 lines
19 KiB
Python
import datetime
|
|
import re
|
|
import urllib
|
|
|
|
import icalendar
|
|
import pytest
|
|
import responses
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.utils.dateparse import parse_date, parse_datetime
|
|
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
|
|
|
|
import tests.utils
|
|
from passerelle.apps.caldav.models import CalDAV
|
|
from passerelle.base.models import AccessRight, ApiUser
|
|
|
|
APIERROR_CLS = 'passerelle.utils.jsonresponse.APIError'
|
|
DAV_URL = 'http://test.caldav.notld/somedav/'
|
|
EVENT_PATH_FMT = '%(username)s/calendar/%(uid)s.ics'
|
|
|
|
PROPFIND_REPLY = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<D:multistatus xmlns:D="DAV:">
|
|
<D:response xmlns:ns0="urn:uuid:02481234-abcd-4321-abcd-00aa00bb00cc/" xmlns:ns2="urn:ietf:params:xml:ns:carddav">
|
|
<D:href>/somedave/</D:href>
|
|
<D:propstat>
|
|
<D:prop>
|
|
<D:displayname>Test Caldav</D:displayname>
|
|
<D:owner/>
|
|
<D:resourcetype><D:collection /></D:resourcetype>
|
|
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
|
|
<D:current-user-principal><D:href>/somedav/principals/users/apiaccess/</D:href></D:current-user-principal>
|
|
<D:principal-collection-set><D:href>/somedav/principals/</D:href></D:principal-collection-set>
|
|
<D:getetag>"none"</D:getetag>
|
|
<D:getcontentlength/>
|
|
<D:getlastmodified/>
|
|
<ns2:addressbook-home-set><D:href>/somedav/apiaccess/</D:href></ns2:addressbook-home-set>
|
|
<ns2:principal-address/>
|
|
<ns2:directory-gateway><D:href>/somedav/addressbook/</D:href></ns2:directory-gateway>
|
|
</D:prop>
|
|
<D:status>HTTP/1.1 200 OK</D:status>
|
|
</D:propstat>
|
|
</D:response>
|
|
</D:multistatus>
|
|
'''
|
|
|
|
FAKE_ICS = '''BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
PRODID:-//EGroupware//NONSGML EGroupware Calendar 17.1.003//FR
|
|
BEGIN:VTIMEZONE
|
|
TZID:Europe/Paris
|
|
BEGIN:DAYLIGHT
|
|
TZOFFSETFROM:+0100
|
|
TZOFFSETTO:+0200
|
|
TZNAME:CEST
|
|
DTSTART:19700329T020000
|
|
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
END:DAYLIGHT
|
|
BEGIN:STANDARD
|
|
TZOFFSETFROM:+0200
|
|
TZOFFSETTO:+0100
|
|
TZNAME:CET
|
|
DTSTART:19701025T030000
|
|
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
END:STANDARD
|
|
END:VTIMEZONE
|
|
BEGIN:VEVENT
|
|
DTSTART;VALUE=DATE:20240227
|
|
DTEND;VALUE=DATE:20240228
|
|
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
|
|
SUMMARY:Yann Weber off
|
|
RRULE:FREQ=WEEKLY;BYDAY=TU
|
|
TRANSP:TRANSPARENT
|
|
CATEGORIES:Journées off
|
|
UID:01234567-abcd
|
|
STATUS:CONFIRMED
|
|
CREATED:20240320T095450Z
|
|
LAST-MODIFIED:20240320T095450Z
|
|
DTSTAMP:20240320T095513Z
|
|
END:VEVENT
|
|
END:VCALENDAR
|
|
'''
|
|
|
|
# Sometime EGW will add this line somewhere in its replies
|
|
SQL_LOG = '==> SQL => SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname=\'egw_cal\' order by d.adnum<br>'
|
|
|
|
SOME_EVENTS = [
|
|
{'DTSTART': '2020-02-20T20:02', 'DTEND': '2020-02-22T22:43', 'SUMMARY': 'Test'},
|
|
{
|
|
'DTSTART': '2020-02-20',
|
|
'DTEND': '2020-02-22',
|
|
'SUMMARY': 'Test',
|
|
'TRANSP': False,
|
|
'CATEGORY': 'Foobar',
|
|
'RRULE': {'FREQ': 'MONTHLY', 'BYDAY': ['FR', 'MO']},
|
|
},
|
|
{
|
|
'DTSTART': '2020-02-20',
|
|
'DTEND': '2020-02-22',
|
|
'SUMMARY': 'Test',
|
|
'LOCATION': 'Foo bar',
|
|
'TRANSP': True,
|
|
'DESCRIPTION': 'test',
|
|
},
|
|
{
|
|
'DTSTART': '2020-02-20T07:00',
|
|
'DTEND': '2020-02-20T19:00',
|
|
'SUMMARY': 'Test',
|
|
'TRANSP': True,
|
|
'CATEGORY': 'Test',
|
|
},
|
|
]
|
|
|
|
SOME_UPDATES = [
|
|
*SOME_EVENTS,
|
|
{'DTSTART': '2020-01-01'},
|
|
{'RRULE': {'FREQ': 'WEEKLY', 'BYDAY': ['FR'], 'UNTIL': '2025-01-01'}},
|
|
{'SUMMARY': 'Foobar', 'CATEGORY': 'Toto'},
|
|
{'TRANSP': False},
|
|
{'CATEGORY': 'SomeCategory'},
|
|
]
|
|
|
|
|
|
@pytest.fixture()
|
|
def caldav_conn(db):
|
|
user = ApiUser.objects.create(username='all', keytype='', key='')
|
|
cdav = CalDAV.objects.create(dav_url=DAV_URL, slug='test', dav_login='foo', dav_password='hackmeplz')
|
|
content_type = ContentType.objects.get_for_model(cdav)
|
|
AccessRight.objects.create(
|
|
codename='can_access', apiuser=user, resource_type=content_type, resource_pk=cdav.pk
|
|
)
|
|
return cdav
|
|
|
|
|
|
def qs_url(url, params):
|
|
'''Append query string to URL'''
|
|
return url + '?' + urllib.parse.urlencode(params)
|
|
|
|
|
|
def app_post(app, url, params, data, status=None):
|
|
'''Allows to post data on an URL with query string params and JSON data'''
|
|
return app.post_json(qs_url(url, params), params=data, status=status)
|
|
|
|
|
|
def get_event_path(username, uid=None):
|
|
ret = '%s/calendar/' % username
|
|
if uid:
|
|
ret += '%s.ics' % uid
|
|
return ret
|
|
|
|
|
|
def event_url_regex(username):
|
|
return re.compile(DAV_URL + username + '/calendar/([^/]+)$')
|
|
|
|
|
|
def assert_match_vevent(vevent, expt_event):
|
|
for k, v in expt_event.items():
|
|
if k in ('DTSTART', 'DTEND'):
|
|
assert (parse_date(v) or parse_datetime(v)) == vevent.decoded(k)
|
|
elif k == 'TRANSP':
|
|
assert (b'TRANSPARENT' if v else b'OPAQUE') == vevent.decoded(k)
|
|
elif k == 'RRULE':
|
|
rrule = vevent.decoded(k)
|
|
for rk, rv in v.items():
|
|
if rk == 'UNTIL':
|
|
assert [(parse_date(rv) or parse_datetime(rv))] == rrule[rk]
|
|
elif isinstance(rrule[rk], list) and not isinstance(rv, list):
|
|
assert rv == rrule[rk][0]
|
|
else:
|
|
assert set(rv) == set(rrule[rk])
|
|
elif k == 'CATEGORY':
|
|
assert v.encode() == vevent['CATEGORIES'].to_ical()
|
|
elif isinstance(v, str):
|
|
assert v.encode() == vevent.decoded(k)
|
|
else:
|
|
assert v == vevent.decoded(k)
|
|
|
|
|
|
def add_egw_sql_log(content, lineno=0):
|
|
'''Adds the EGW's random SQL log to some content
|
|
- lineno : indicate where to insert the line (-1 for last line)
|
|
'''
|
|
spl = content.split('\n')
|
|
if lineno == -1:
|
|
spl.append(SQL_LOG)
|
|
else:
|
|
if lineno < 0:
|
|
lineno += 1
|
|
spl.insert(lineno, SQL_LOG)
|
|
return '\n'.join(spl)
|
|
|
|
|
|
#
|
|
# Tests
|
|
#
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_check_status_ok(app, caldav_conn):
|
|
responses.add('PROPFIND', DAV_URL, headers={'Content-Type': 'text/xml'}, body=PROPFIND_REPLY)
|
|
caldav_conn.check_status()
|
|
assert len(responses.calls) == 1
|
|
assert responses.calls[0].request.method == 'PROPFIND'
|
|
assert responses.calls[0].request.url == DAV_URL
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_check_status_login_failed(app, caldav_conn):
|
|
responses.add(
|
|
'PROPFIND',
|
|
DAV_URL,
|
|
headers={'Content-Type': 'text/xml'},
|
|
body=PROPFIND_REPLY,
|
|
status=401,
|
|
)
|
|
with pytest.raises(Exception) as expt:
|
|
caldav_conn.check_status()
|
|
assert str(expt) == 'Not authorized: bad login/password ?'
|
|
assert len(responses.calls) == 1
|
|
assert responses.calls[0].request.method == 'PROPFIND'
|
|
assert responses.calls[0].request.url == DAV_URL
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_check_status_fails(app, caldav_conn):
|
|
responses.add('PROPFIND', DAV_URL, status=500)
|
|
with pytest.raises(Exception):
|
|
caldav_conn.check_status()
|
|
assert len(responses.calls) == 1
|
|
assert responses.calls[0].request.method == 'PROPFIND'
|
|
assert responses.calls[0].request.url == DAV_URL
|
|
|
|
|
|
@pytest.mark.parametrize('requests_expt', (ConnectTimeout, ReadTimeout, ConnectionError))
|
|
@responses.activate
|
|
def test_caldav_check_status_timeout(app, caldav_conn, requests_expt):
|
|
propfind_mock = responses.add( # pylint: disable=assignment-from-none
|
|
'PROPFIND', DAV_URL, body=requests_expt('Do not work as expected')
|
|
)
|
|
get_mock = responses.add( # pylint: disable=assignment-from-none
|
|
'GET', DAV_URL, body=requests_expt('Do not work as expected')
|
|
)
|
|
with pytest.raises(Exception):
|
|
caldav_conn.check_status()
|
|
assert len(responses.calls) == 2
|
|
assert len(propfind_mock.calls) == 1
|
|
assert len(get_mock.calls) == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'event',
|
|
SOME_EVENTS,
|
|
)
|
|
@responses.activate
|
|
def test_caldav_event_create_ok(app, caldav_conn, event):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/create')
|
|
username = 'foo'
|
|
qs_params = {'username': username}
|
|
responses.add(responses.PUT, event_url_regex(username), status=204, body='')
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] == 0
|
|
assert len(responses.calls) == 1
|
|
calendar = icalendar.Calendar.from_ical(responses.calls[0].request.body)
|
|
vevent = calendar.walk('VEVENT')[0]
|
|
|
|
expt_event = event.copy()
|
|
expt_event['SEQUENCE'] = 0
|
|
assert_match_vevent(vevent, event)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'expt', [ConnectionError('refused'), ConnectTimeout('timeout'), ReadTimeout('timeout')]
|
|
)
|
|
@responses.activate
|
|
def test_caldav_event_create_ok_net_err(app, caldav_conn, expt):
|
|
event = SOME_EVENTS[0]
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/create')
|
|
username = 'toto'
|
|
qs_params = {'username': username}
|
|
responses.add(responses.PUT, event_url_regex(username), body=expt)
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'] == 'Error sending creation request to caldav server'
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'dtstart,dtend,until',
|
|
[
|
|
('2020-13-13', '2021-01-01', '2021-12-01'),
|
|
('2020-01-01T01:02:03', '2020-01-01T02:64:02', '2021-12-01'),
|
|
('23-01-12', '2023-01-13', '2021-12-01'),
|
|
('13:30', '2023-01-01T14:00', '2021-12-01'),
|
|
('2023-10-02', '2023-10-03', '2024-13-12'),
|
|
],
|
|
)
|
|
def test_caldav_event_create_bad_dates(app, caldav_conn, dtstart, dtend, until):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/create')
|
|
username = 'toto'
|
|
event = {
|
|
'DTSTART': dtstart,
|
|
'DTEND': dtend,
|
|
'SUMMARY': 'hello',
|
|
'RRULE': {'FREQ': 'WEEKLY', 'BYDAY': ['FR'], 'UNTIL': until},
|
|
}
|
|
qs_params = {'username': username}
|
|
|
|
resp = app_post(app, url, qs_params, event, status=400)
|
|
|
|
assert resp.status_code == 400
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'].startswith('Unable to convert field')
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_create_bad_username(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/create')
|
|
username = 'foo'
|
|
qs_params = {'username': username}
|
|
responses.add(responses.PUT, event_url_regex(username), status=404)
|
|
event = SOME_EVENTS[0]
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'] == 'Error creating event'
|
|
assert len(responses.calls) == 2
|
|
|
|
|
|
@pytest.mark.parametrize('event_update', SOME_UPDATES)
|
|
@responses.activate
|
|
def test_caldav_event_update_ok(app, caldav_conn, event_update):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'toto'
|
|
evt_id = '01234567-abcd'
|
|
evt_url = DAV_URL + get_event_path(username, evt_id)
|
|
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
responses.add(responses.GET, evt_url, body=FAKE_ICS)
|
|
responses.add(responses.PUT, evt_url, status=204, body='')
|
|
|
|
resp = app_post(app, url, qs_params, event_update)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['data']['event_id'] == evt_id
|
|
|
|
assert len(responses.calls) == 2
|
|
assert responses.calls[1].request.method == 'PUT'
|
|
|
|
calendar = icalendar.Calendar.from_ical(responses.calls[1].request.body)
|
|
vevent = calendar.walk('VEVENT')[0]
|
|
|
|
assert_match_vevent(vevent, event_update)
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_update_sequence_ok(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'foobar'
|
|
evt_id = '1234567890'
|
|
evt_url = DAV_URL + get_event_path(username, evt_id)
|
|
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
responses.add(responses.GET, evt_url, body=FAKE_ICS)
|
|
responses.add(responses.PUT, evt_url, status=204, body='')
|
|
|
|
# testing SEQUENCE initialization : if missing we add it
|
|
dtend = datetime.date.fromisoformat('2024-02-29')
|
|
resp = app_post(app, url, qs_params, {'DTEND': dtend.isoformat()})
|
|
assert resp.json['err'] == 0
|
|
assert len(responses.calls) == 2
|
|
put_mock = responses.calls[1]
|
|
assert put_mock.request.method == 'PUT'
|
|
raw_ics = put_mock.request.body
|
|
calendar = icalendar.Calendar.from_ical(raw_ics)
|
|
vevent = calendar.walk('VEVENT')[0]
|
|
assert vevent['SEQUENCE'] == 1
|
|
|
|
for expt_sequence in range(2, 10):
|
|
# testing SEQUENCE incrementation on modification
|
|
responses.reset()
|
|
responses.add(responses.GET, evt_url, body=raw_ics)
|
|
responses.add(responses.PUT, evt_url, status=204, body='')
|
|
|
|
dtend += datetime.timedelta(days=1)
|
|
resp = app_post(app, url, qs_params, {'DTEND': dtend.isoformat()})
|
|
assert resp.json['err'] == 0
|
|
assert len(responses.calls) == 2
|
|
assert responses.calls[1].request.method == 'PUT'
|
|
raw_ics = responses.calls[1].request.body
|
|
calendar = icalendar.Calendar.from_ical(raw_ics)
|
|
vevent = calendar.walk('VEVENT')[0]
|
|
assert vevent['SEQUENCE'] == expt_sequence
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_update_notfound(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'foo-user'
|
|
evt_id = '42'
|
|
evt_path = get_event_path(username, evt_id)
|
|
|
|
responses.add(responses.GET, DAV_URL + evt_path, status=404)
|
|
|
|
event = {'DTSTART': '2020-02-20', 'DTEND': '2020-03-30', 'SUMMARY': 'foobar'}
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Unable to get event %r in calendar owned by %r' % (
|
|
evt_id,
|
|
username,
|
|
)
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_update_bad_evt(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'foo-user'
|
|
evt_id = '42'
|
|
evt_path = get_event_path(username, evt_id)
|
|
|
|
responses.add(
|
|
responses.GET, DAV_URL + evt_path, status=200, body=b'BEGIN:VCALENDAR\nVERSION:2.0\nEND:VCALENDAR'
|
|
)
|
|
|
|
event = {'DTSTART': '2020-02-20', 'DTEND': '2020-03-30', 'SUMMARY': 'foobar'}
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Given event (user:%r uid:%r) do not contains VEVENT component' % (
|
|
username,
|
|
evt_id,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'expt', [ConnectionError('refused'), ConnectTimeout('timeout'), ReadTimeout('timeout')]
|
|
)
|
|
@responses.activate
|
|
def test_caldav_event_update_net_err(app, caldav_conn, expt):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'toto'
|
|
evt_id = '012345678'
|
|
event = {'DTSTART': '2020-02-20', 'DTEND': '2020-03-30', 'SUMMARY': 'foobar'}
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
responses.add(responses.GET, DAV_URL + get_event_path(username, evt_id), body=expt)
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Unable to communicate with caldav server while fetching event'
|
|
|
|
responses.reset()
|
|
responses.add(responses.GET, DAV_URL + get_event_path(username, evt_id), body=FAKE_ICS)
|
|
responses.add(responses.PUT, DAV_URL + get_event_path(username, evt_id), body=expt)
|
|
|
|
resp = app_post(app, url, qs_params, event)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Error sending update request to caldav server'
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_delete_ok(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/delete')
|
|
username = 'toto'
|
|
evt_id = '012345678-dcba'
|
|
evt_path = get_event_path(username, evt_id)
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
responses.add(responses.GET, DAV_URL + evt_path, body=FAKE_ICS)
|
|
responses.add(responses.DELETE, DAV_URL + evt_path, status=204, body='')
|
|
|
|
resp = app.delete(qs_url(url, qs_params))
|
|
|
|
assert resp.json['err'] == 0
|
|
assert len(responses.calls) == 2
|
|
assert responses.calls[1].request.method == 'DELETE'
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'expt', [ConnectionError('refused'), ConnectTimeout('timeout'), ReadTimeout('timeout')]
|
|
)
|
|
@responses.activate
|
|
def test_caldav_event_delete_net_err(app, caldav_conn, expt):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/delete')
|
|
username = 'toto'
|
|
evt_id = '012345678-dcba'
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
evt_path = get_event_path(username, evt_id)
|
|
|
|
responses.add(responses.GET, DAV_URL + evt_path, body=expt)
|
|
resp = app.delete(qs_url(url, qs_params))
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'] == 'Unable to communicate with caldav server while fetching event'
|
|
|
|
responses.add(responses.GET, DAV_URL + evt_path, body=FAKE_ICS)
|
|
responses.add(responses.DELETE, DAV_URL + evt_path, body=expt)
|
|
resp = app.delete(qs_url(url, qs_params))
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'] == 'Error sending deletion request to caldav server'
|
|
|
|
|
|
@responses.activate
|
|
def test_caldav_event_delete_get_event_fail(app, caldav_conn):
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/delete')
|
|
username = 'toto'
|
|
evt_id = '012345678-dcba'
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
evt_path = get_event_path(username, evt_id)
|
|
|
|
responses.add(responses.GET, DAV_URL + evt_path, body='', status=404)
|
|
resp = app.delete(qs_url(url, qs_params))
|
|
assert resp.json['err'] != 0
|
|
assert resp.json['err_class'] == APIERROR_CLS
|
|
assert resp.json['err_desc'] == 'Unable to get event %r in calendar owned by %r' % (evt_id, username)
|
|
|
|
|
|
@pytest.mark.parametrize('lineno', (0, 10, -1))
|
|
@responses.activate
|
|
def test_egw_sql_log_propfind(app, caldav_conn, lineno):
|
|
response_body = add_egw_sql_log(PROPFIND_REPLY, lineno)
|
|
|
|
responses.add('PROPFIND', DAV_URL, body=response_body, content_type='text/xml')
|
|
|
|
caldav_conn.check_status()
|
|
assert len(responses.calls) == 1
|
|
assert responses.calls[0].request.method == 'PROPFIND'
|
|
assert responses.calls[0].request.url == DAV_URL
|
|
|
|
|
|
@pytest.mark.parametrize('lineno', (0, 10, -1))
|
|
@responses.activate
|
|
def test_egw_sql_log_update(app, caldav_conn, lineno):
|
|
event_update = SOME_UPDATES[0]
|
|
|
|
response_body = add_egw_sql_log(FAKE_ICS, lineno)
|
|
|
|
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
|
|
username = 'toto'
|
|
evt_id = '01234567-abcd'
|
|
evt_url = DAV_URL + get_event_path(username, evt_id)
|
|
|
|
qs_params = {'username': username, 'event_id': evt_id}
|
|
|
|
responses.add(responses.GET, evt_url, body=response_body)
|
|
responses.add(responses.PUT, evt_url, status=204, body='')
|
|
|
|
resp = app_post(app, url, qs_params, event_update)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['data']['event_id'] == evt_id
|
|
|
|
assert len(responses.calls) == 2
|
|
assert responses.calls[1].request.method == 'PUT'
|
|
|
|
calendar = icalendar.Calendar.from_ical(responses.calls[1].request.body)
|
|
vevent = calendar.walk('VEVENT')[0]
|
|
|
|
assert_match_vevent(vevent, event_update)
|