commit
7266c8018f
@ -35,7 +35,7 @@ FOLDER = os.path.expanduser(config.get("storage", "filesystem_folder"))
|
||||
|
||||
try:
|
||||
from dulwich.repo import Repo
|
||||
GIT_REPOSITORY = Repo(os.path.join(FOLDER, ".git"))
|
||||
GIT_REPOSITORY = Repo(FOLDER)
|
||||
except:
|
||||
GIT_REPOSITORY = None
|
||||
|
||||
@ -52,7 +52,7 @@ def open(path, mode="r"):
|
||||
# On exit
|
||||
if GIT_REPOSITORY and mode == "w":
|
||||
path = os.path.relpath(abs_path, FOLDER)
|
||||
GIT_REPOSITORY.stage([path])
|
||||
GIT_REPOSITORY.stage([path.encode("utf-8")])
|
||||
GIT_REPOSITORY.do_commit("Commit by Radicale")
|
||||
# pylint: enable=W0622
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
-- This is the database schema for PostgreSQL.
|
||||
|
||||
begin;
|
||||
|
||||
create table collection (
|
||||
path varchar primary key not null,
|
||||
parent_path varchar references collection (path));
|
||||
@ -22,12 +20,10 @@ create table line (
|
||||
value varchar not null,
|
||||
item_name varchar references item (name) not null,
|
||||
timestamp timestamp not null,
|
||||
primary key (key, item_name));
|
||||
primary key (key, value, item_name, timestamp));
|
||||
|
||||
create table property (
|
||||
key varchar not null,
|
||||
value varchar not null,
|
||||
collection_path varchar references collection (path) not null,
|
||||
primary key (key, collection_path));
|
||||
|
||||
commit;
|
||||
|
@ -21,25 +21,29 @@ Tests for Radicale.
|
||||
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from dulwich.repo import Repo
|
||||
from io import BytesIO
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
import radicale
|
||||
from radicale import config
|
||||
from radicale.auth import htpasswd
|
||||
from radicale.storage import filesystem, database
|
||||
from .helpers import get_file_content
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
|
||||
class BaseTest(object):
|
||||
"""Base class for tests."""
|
||||
|
||||
def setup(self):
|
||||
"""Setup function for each test."""
|
||||
self.application = radicale.Application()
|
||||
|
||||
def teardown(self):
|
||||
"""Teardown function for each test."""
|
||||
|
||||
def request(self, method, path, **args):
|
||||
def request(self, method, path, data=None, **args):
|
||||
"""Send a request."""
|
||||
self.application._status = None
|
||||
self.application._headers = None
|
||||
@ -49,14 +53,86 @@ class BaseTest(object):
|
||||
args[key.upper()] = args[key]
|
||||
args["REQUEST_METHOD"] = method.upper()
|
||||
args["PATH_INFO"] = path
|
||||
if data:
|
||||
args["wsgi.input"] = BytesIO(data)
|
||||
args["CONTENT_LENGTH"] = str(len(data))
|
||||
self.application._answer = self.application(args, self.start_response)
|
||||
|
||||
return (
|
||||
int(self.application._status.split()[0]),
|
||||
dict(self.application._headers),
|
||||
self.application._answer[0].decode("utf-8"))
|
||||
self.application._answer[0].decode("utf-8")
|
||||
if self.application._answer else None)
|
||||
|
||||
def start_response(self, status, headers):
|
||||
"""Put the response values into the current application."""
|
||||
self.application._status = status
|
||||
self.application._headers = headers
|
||||
|
||||
|
||||
class FileSystem(BaseTest):
|
||||
"""Base class for filesystem tests."""
|
||||
storage_type = "filesystem"
|
||||
|
||||
def setup(self):
|
||||
"""Setup function for each test."""
|
||||
self.colpath = tempfile.mkdtemp()
|
||||
config.set("storage", "type", self.storage_type)
|
||||
filesystem.FOLDER = self.colpath
|
||||
filesystem.GIT_REPOSITORY = None
|
||||
self.application = radicale.Application()
|
||||
|
||||
def teardown(self):
|
||||
"""Teardown function for each test."""
|
||||
shutil.rmtree(self.colpath)
|
||||
|
||||
|
||||
class MultiFileSystem(FileSystem):
|
||||
"""Base class for multifilesystem tests."""
|
||||
storage_type = "multifilesystem"
|
||||
|
||||
|
||||
class DataBaseSystem(BaseTest):
|
||||
"""Base class for database tests"""
|
||||
def setup(self):
|
||||
config.set("storage", "type", "database")
|
||||
config.set("storage", "database_url", "sqlite://")
|
||||
database.Session = sessionmaker()
|
||||
database.Session.configure(bind=create_engine("sqlite://"))
|
||||
session = database.Session()
|
||||
# session.execute(get_file_content("schema.sql"))
|
||||
for st in get_file_content("schema.sql").split(";"):
|
||||
session.execute(st)
|
||||
session.commit()
|
||||
self.application = radicale.Application()
|
||||
|
||||
|
||||
class GitFileSystem(FileSystem):
|
||||
"""Base class for filesystem tests using Git"""
|
||||
def setup(self):
|
||||
super(GitFileSystem, self).setup()
|
||||
Repo.init(self.colpath)
|
||||
filesystem.GIT_REPOSITORY = Repo(self.colpath)
|
||||
|
||||
|
||||
class GitMultiFileSystem(GitFileSystem, MultiFileSystem):
|
||||
"""Base class for multifilesystem tests using Git"""
|
||||
|
||||
|
||||
class HtpasswdAuthSystem(BaseTest):
|
||||
"""Base class to test Radicale with Htpasswd authentication"""
|
||||
def setup(self):
|
||||
self.colpath = tempfile.mkdtemp()
|
||||
htpasswd_file_path = os.path.join(self.colpath, ".htpasswd")
|
||||
with open(htpasswd_file_path, "w") as fd:
|
||||
fd.write('tmp:{SHA}' + base64.b64encode(
|
||||
hashlib.sha1("bépo").digest()))
|
||||
config.set("auth", "type", "htpasswd")
|
||||
self.userpass = base64.b64encode("tmp:bépo")
|
||||
self.application = radicale.Application()
|
||||
htpasswd.FILENAME = htpasswd_file_path
|
||||
htpasswd.ENCRYPTION = "sha1"
|
||||
|
||||
def teardown(self):
|
||||
config.set("auth", "type", "None")
|
||||
radicale.auth.is_authenticated = lambda *_: True
|
||||
|
38
tests/helpers.py
Normal file
38
tests/helpers.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Radicale Server - Calendar Server
|
||||
# Copyright © 2008 Nicolas Kandel
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2013 Guillaume Ayoub
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Radicale Helpers module.
|
||||
|
||||
This module offers helpers to use in tests.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
EXAMPLES_FOLDER = os.path.join(os.path.dirname(__file__), "static")
|
||||
|
||||
|
||||
def get_file_content(file_name):
|
||||
try:
|
||||
with open(os.path.join(EXAMPLES_FOLDER, file_name)) as fd:
|
||||
return fd.read()
|
||||
except IOError:
|
||||
print(u"Couldn't open the file %s" % file_name)
|
31
tests/static/put.ics
Normal file
31
tests/static/put.ics
Normal file
@ -0,0 +1,31 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:02805f81-4cc2-4d68-8d39-72768ffa02d9
|
||||
SUMMARY:Nouvel évènement
|
||||
DTSTART;TZID=Europe/Paris:20130902T180000
|
||||
DTEND;TZID=Europe/Paris:20130902T190000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
11
tests/static/putvtodo.ics
Normal file
11
tests/static/putvtodo.ics
Normal file
@ -0,0 +1,11 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTODO
|
||||
CREATED:20130903T091105Z
|
||||
LAST-MODIFIED:20130903T091108Z
|
||||
DTSTAMP:20130903T091108Z
|
||||
UID:40f8cf9b-0e62-4624-89a2-24c5e68850f5
|
||||
SUMMARY:Nouvelle tâche
|
||||
END:VTODO
|
||||
END:VCALENDAR
|
1
tests/static/schema.sql
Symbolic link
1
tests/static/schema.sql
Symbolic link
@ -0,0 +1 @@
|
||||
../../schema.sql
|
44
tests/test_auth.py
Normal file
44
tests/test_auth.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Radicale Server - Calendar Server
|
||||
# Copyright © 2012-2013 Guillaume Ayoub
|
||||
# Copyright © 2012-2013 Jean-Marc Martins
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Radicale tests with simple requests and authentication.
|
||||
|
||||
"""
|
||||
|
||||
from nose import with_setup
|
||||
from . import HtpasswdAuthSystem
|
||||
|
||||
|
||||
class TestBaseAuthRequests(HtpasswdAuthSystem):
|
||||
"""
|
||||
Tests basic requests with auth.
|
||||
|
||||
..note Only htpasswd works at the moment since
|
||||
it requires to spawn processes running servers for
|
||||
others auth methods (ldap).
|
||||
"""
|
||||
|
||||
@with_setup(HtpasswdAuthSystem.setup, HtpasswdAuthSystem.teardown)
|
||||
def test_root(self):
|
||||
"""Tests a GET request at "/"."""
|
||||
status, headers, answer = self.request(
|
||||
"GET", "/", HTTP_AUTHORIZATION=self.userpass)
|
||||
assert status == 200
|
||||
assert "Radicale works!" in answer
|
@ -21,10 +21,13 @@ Radicale tests with simple requests.
|
||||
|
||||
"""
|
||||
|
||||
from . import BaseTest
|
||||
from . import (FileSystem, MultiFileSystem, DataBaseSystem,
|
||||
GitFileSystem, GitMultiFileSystem)
|
||||
from .helpers import get_file_content
|
||||
import sys
|
||||
|
||||
|
||||
class TestBaseRequests(BaseTest):
|
||||
class BaseRequests(object):
|
||||
"""Tests with simple requests."""
|
||||
|
||||
def test_root(self):
|
||||
@ -32,3 +35,56 @@ class TestBaseRequests(BaseTest):
|
||||
status, headers, answer = self.request("GET", "/")
|
||||
assert status == 200
|
||||
assert "Radicale works!" in answer
|
||||
# Tests the creation of the collection
|
||||
status, headers, answer = self.request("GET", "/calendar.ics/")
|
||||
assert u"BEGIN:VCALENDAR" in answer
|
||||
assert u"VERSION:2.0" in answer
|
||||
assert u"END:VCALENDAR" in answer
|
||||
assert u"PRODID:-//Radicale//NONSGML Radicale Server//EN" in answer
|
||||
|
||||
def test_add_event_todo(self):
|
||||
"""Tests the add of an event and todo."""
|
||||
self.request("GET", "/calendar.ics/")
|
||||
#VEVENT test
|
||||
event = get_file_content("put.ics")
|
||||
path = "/calendar.ics/02805f81-4cc2-4d68-8d39-72768ffa02d9.ics"
|
||||
status, headers, answer = self.request("PUT", path, event)
|
||||
assert status == 201
|
||||
assert u"ETag" in headers.keys()
|
||||
status, headers, answer = self.request("GET", path)
|
||||
assert status == 200
|
||||
assert u"VEVENT" in answer
|
||||
assert u"Nouvel évènement" in answer
|
||||
assert u"UID:02805f81-4cc2-4d68-8d39-72768ffa02d9" in answer
|
||||
# VTODO test
|
||||
todo = get_file_content("putvtodo.ics")
|
||||
path = "/calendar.ics/40f8cf9b-0e62-4624-89a2-24c5e68850f5.ics"
|
||||
status, headers, answer = self.request("PUT", path, todo)
|
||||
assert status == 201
|
||||
assert u"ETag" in headers.keys()
|
||||
status, headers, answer = self.request("GET", path)
|
||||
assert u"VTODO" in answer
|
||||
assert u"Nouvelle tâche" in answer
|
||||
assert u"UID:40f8cf9b-0e62-4624-89a2-24c5e68850f5" in answer
|
||||
|
||||
def test_delete(self):
|
||||
"""Tests the deletion of an event"""
|
||||
self.request("GET", "/calendar.ics/")
|
||||
# Adds a VEVENT to be deleted
|
||||
event = get_file_content("put.ics")
|
||||
path = "/calendar.ics/02805f81-4cc2-4d68-8d39-72768ffa02d9.ics"
|
||||
status, headers, answer = self.request("PUT", path, event)
|
||||
# Then we send a DELETE request
|
||||
status, headers, answer = self.request("DELETE", path)
|
||||
assert status == 200
|
||||
assert u"<href>%s</href>" % path in answer
|
||||
status, headers, answer = self.request("GET", "/calendar.ics/")
|
||||
assert u"VEVENT" not in answer
|
||||
|
||||
# Generates Classes with different configs
|
||||
cl_list = [FileSystem, MultiFileSystem, DataBaseSystem,
|
||||
GitFileSystem, GitMultiFileSystem]
|
||||
for cl in cl_list:
|
||||
classname = "Test%s" % cl.__name__
|
||||
setattr(sys.modules[__name__],
|
||||
classname, type(classname, (BaseRequests, cl), {}))
|
||||
|
Loading…
Reference in New Issue
Block a user