~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web2/dav/test/test_prop.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##
 
2
# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
 
3
#
 
4
# Permission is hereby granted, free of charge, to any person obtaining a copy
 
5
# of this software and associated documentation files (the "Software"), to deal
 
6
# in the Software without restriction, including without limitation the rights
 
7
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
8
# copies of the Software, and to permit persons to whom the Software is
 
9
# furnished to do so, subject to the following conditions:
 
10
 
11
# The above copyright notice and this permission notice shall be included in all
 
12
# copies or substantial portions of the Software.
 
13
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
15
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
16
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
17
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
18
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
20
# SOFTWARE.
 
21
#
 
22
# DRI: Wilfredo Sanchez, wsanchez@apple.com
 
23
##
 
24
 
 
25
import random
 
26
 
 
27
from twisted.trial.unittest import SkipTest
 
28
from twisted.web2 import responsecode
 
29
from twisted.web2.iweb import IResponse
 
30
from twisted.web2.stream import MemoryStream
 
31
from twisted.web2 import http_headers
 
32
from twisted.web2.dav import davxml
 
33
from twisted.web2.dav.resource import DAVResource
 
34
from twisted.web2.dav.davxml import dav_namespace, lookupElement
 
35
from twisted.web2.dav.util import davXMLFromStream
 
36
from twisted.web2.test.test_server import SimpleRequest
 
37
from twisted.web2.dav.test.util import serialize
 
38
import twisted.web2.dav.test.util
 
39
 
 
40
live_properties = [lookupElement(qname)() for qname in DAVResource.liveProperties if qname[0] == dav_namespace]
 
41
 
 
42
#
 
43
# See whether dead properties are available
 
44
#
 
45
from twisted.web2.dav.noneprops import NonePropertyStore
 
46
from twisted.web2.dav.static import DeadPropertyStore
 
47
if DeadPropertyStore == NonePropertyStore:
 
48
    have_dead_properties = False
 
49
else:
 
50
    have_dead_properties = True
 
51
 
 
52
class PROP(twisted.web2.dav.test.util.TestCase):
 
53
    """
 
54
    PROPFIND, PROPPATCH requests
 
55
    """
 
56
    def test_PROPFIND_basic(self):
 
57
        """
 
58
        PROPFIND request
 
59
        """
 
60
        def check_result(response):
 
61
            response = IResponse(response)
 
62
 
 
63
            if response.code != responsecode.MULTI_STATUS:
 
64
                self.fail("Incorrect response code for PROPFIND (%s != %s)"
 
65
                          % (response.code, responsecode.MULTI_STATUS))
 
66
 
 
67
            content_type = response.headers.getHeader("content-type")
 
68
            if content_type not in (http_headers.MimeType("text", "xml"),
 
69
                                    http_headers.MimeType("application", "xml")):
 
70
                self.fail("Incorrect content-type for PROPFIND response (%r not in %r)"
 
71
                          % (content_type, (http_headers.MimeType("text", "xml"),
 
72
                                            http_headers.MimeType("application", "xml"))))
 
73
 
 
74
            return davXMLFromStream(response.stream).addCallback(check_xml)
 
75
 
 
76
        def check_xml(doc):
 
77
            multistatus = doc.root_element
 
78
 
 
79
            if not isinstance(multistatus, davxml.MultiStatus):
 
80
                self.fail("PROPFIND response XML root element is not multistatus: %r" % (multistatus,))
 
81
 
 
82
            for response in multistatus.childrenOfType(davxml.PropertyStatusResponse):
 
83
                if response.childOfType(davxml.HRef) == "/":
 
84
                    for propstat in response.childrenOfType(davxml.PropertyStatus):
 
85
                        status = propstat.childOfType(davxml.Status)
 
86
                        properties = propstat.childOfType(davxml.PropertyContainer).children
 
87
 
 
88
                        if status.code != responsecode.OK:
 
89
                            self.fail("PROPFIND failed (status %s) to locate live properties: %s"
 
90
                                      % (status.code, properties))
 
91
 
 
92
                        properties_to_find = [p.qname() for p in live_properties]
 
93
 
 
94
                        for property in properties:
 
95
                            qname = property.qname()
 
96
                            if qname in properties_to_find:
 
97
                                properties_to_find.remove(qname)
 
98
                            else:
 
99
                                self.fail("PROPFIND found property we didn't ask for: %r" % (property,))
 
100
 
 
101
                        if properties_to_find:
 
102
                            self.fail("PROPFIND failed to find properties: %r" % (properties_to_find,))
 
103
 
 
104
                    break
 
105
 
 
106
            else:
 
107
                self.fail("No response for URI /")
 
108
 
 
109
        query = davxml.PropertyFind(davxml.PropertyContainer(*live_properties))
 
110
 
 
111
        request = SimpleRequest(self.site, "PROPFIND", "/")
 
112
 
 
113
        depth = random.choice(("0", "1", "infinity", None))
 
114
        if depth is not None:
 
115
            request.headers.setHeader("depth", depth)
 
116
 
 
117
        request.stream = MemoryStream(query.toxml())
 
118
 
 
119
        return self.send(request, check_result)
 
120
 
 
121
    def test_PROPFIND_list(self):
 
122
        """
 
123
        PROPFIND with allprop, propname
 
124
        """
 
125
        def check_result(which):
 
126
            def _check_result(response):
 
127
                response = IResponse(response)
 
128
 
 
129
                if response.code != responsecode.MULTI_STATUS:
 
130
                    self.fail("Incorrect response code for PROPFIND (%s != %s)"
 
131
                              % (response.code, responsecode.MULTI_STATUS))
 
132
 
 
133
                return davXMLFromStream(response.stream).addCallback(check_xml, which)
 
134
            return _check_result
 
135
 
 
136
        def check_xml(doc, which):
 
137
            response = doc.root_element.childOfType(davxml.PropertyStatusResponse)
 
138
 
 
139
            self.failUnless(
 
140
                response.childOfType(davxml.HRef) == "/",
 
141
                "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef),)
 
142
            )
 
143
 
 
144
            for propstat in response.childrenOfType(davxml.PropertyStatus):
 
145
                status = propstat.childOfType(davxml.Status)
 
146
                properties = propstat.childOfType(davxml.PropertyContainer).children
 
147
 
 
148
                if status.code != responsecode.OK:
 
149
                    self.fail("PROPFIND failed (status %s) to locate live properties: %s"
 
150
                              % (status.code, properties))
 
151
 
 
152
                if which.name == "allprop":
 
153
                    properties_to_find = [p.qname() for p in live_properties if not p.hidden]
 
154
                else:
 
155
                    properties_to_find = [p.qname() for p in live_properties]
 
156
 
 
157
                for property in properties:
 
158
                    qname = property.qname()
 
159
                    if qname in properties_to_find:
 
160
                        properties_to_find.remove(qname)
 
161
                    elif qname[0] != dav_namespace:
 
162
                        pass
 
163
                    else:
 
164
                        self.fail("PROPFIND with %s found property we didn't expect: %r" % (which.name, property))
 
165
 
 
166
                    if which.name == "propname":
 
167
                        # Element should be empty
 
168
                        self.failUnless(len(property.children) == 0)
 
169
                    else:
 
170
                        # Element should have a value
 
171
                        # Actually, this isn't necessarily true, but it is for the live
 
172
                        # properties we've defined so far...
 
173
                        self.failIf(len(property.children) == 0)
 
174
 
 
175
                if properties_to_find:
 
176
                    self.fail("PROPFIND with %s failed to find properties: %r" % (which.name, properties_to_find))
 
177
 
 
178
            properties = propstat.childOfType(davxml.PropertyContainer).children
 
179
 
 
180
        def work():
 
181
            for which in (davxml.AllProperties(), davxml.PropertyName()):
 
182
                query = davxml.PropertyFind(which)
 
183
 
 
184
                request = SimpleRequest(self.site, "PROPFIND", "/")
 
185
                request.headers.setHeader("depth", "0")
 
186
                request.stream = MemoryStream(query.toxml())
 
187
 
 
188
                yield (request, check_result(which))
 
189
 
 
190
        return serialize(self.send, work())
 
191
 
 
192
    def test_PROPPATCH_basic(self):
 
193
        """
 
194
        PROPPATCH
 
195
        """
 
196
        # FIXME:
 
197
        # Do PROPFIND to make sure it's still there
 
198
        # Test nonexistant resource
 
199
        # Test None namespace in property
 
200
 
 
201
        def check_patch_response(response):
 
202
            response = IResponse(response)
 
203
 
 
204
            if response.code != responsecode.MULTI_STATUS:
 
205
                self.fail("Incorrect response code for PROPFIND (%s != %s)"
 
206
                          % (response.code, responsecode.MULTI_STATUS))
 
207
 
 
208
            content_type = response.headers.getHeader("content-type")
 
209
            if content_type not in (http_headers.MimeType("text", "xml"),
 
210
                                    http_headers.MimeType("application", "xml")):
 
211
                self.fail("Incorrect content-type for PROPPATCH response (%r not in %r)"
 
212
                          % (content_type, (http_headers.MimeType("text", "xml"),
 
213
                                            http_headers.MimeType("application", "xml"))))
 
214
 
 
215
            return davXMLFromStream(response.stream).addCallback(check_patch_xml)
 
216
 
 
217
        def check_patch_xml(doc):
 
218
            multistatus = doc.root_element
 
219
 
 
220
            if not isinstance(multistatus, davxml.MultiStatus):
 
221
                self.fail("PROPFIND response XML root element is not multistatus: %r" % (multistatus,))
 
222
 
 
223
            # Requested a property change one resource, so there should be exactly one response
 
224
            response = multistatus.childOfType(davxml.Response)
 
225
 
 
226
            # Should have a response description (its contents are arbitrary)
 
227
            response.childOfType(davxml.ResponseDescription)
 
228
 
 
229
            # Requested property change was on /
 
230
            self.failUnless(
 
231
                response.childOfType(davxml.HRef) == "/",
 
232
                "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef),)
 
233
            )
 
234
 
 
235
            # Requested one property change, so there should be exactly one property status
 
236
            propstat = response.childOfType(davxml.PropertyStatus)
 
237
 
 
238
            # And the contained property should be a SpiffyProperty
 
239
            self.failIf(
 
240
                propstat.childOfType(davxml.PropertyContainer).childOfType(SpiffyProperty) is None,
 
241
                "Not a SpiffyProperty in PROPPATCH property status: %s" % (propstat.toxml())
 
242
            )
 
243
 
 
244
            if not have_dead_properties:
 
245
                raise SkipTest(
 
246
                    "No dead property store available for DAVFile.  "
 
247
                    "Install xattr (http://undefined.org/python/#xattr) to enable use of dead properties."
 
248
                )
 
249
 
 
250
            # And the status should be 200
 
251
            self.failUnless(
 
252
                propstat.childOfType(davxml.Status).code == responsecode.OK,
 
253
                "Incorrect status code for PROPPATCH of property %s: %s != %s"
 
254
                % (propstat.childOfType(davxml.PropertyContainer).toxml(),
 
255
                   propstat.childOfType(davxml.Status).code, responsecode.OK)
 
256
            )
 
257
 
 
258
        patch = davxml.PropertyUpdate(
 
259
            davxml.Set(
 
260
                davxml.PropertyContainer(
 
261
                    SpiffyProperty.fromString("This is a spiffy resource.")
 
262
                )
 
263
            )
 
264
        )
 
265
 
 
266
        request = SimpleRequest(self.site, "PROPPATCH", "/")
 
267
        request.stream = MemoryStream(patch.toxml())
 
268
        return self.send(request, check_patch_response)
 
269
 
 
270
    def test_PROPPATCH_liveprop(self):
 
271
        """
 
272
        PROPPATCH on a live property
 
273
        """
 
274
        prop = davxml.GETETag.fromString("some-etag-string")
 
275
        patch = davxml.PropertyUpdate(davxml.Set(davxml.PropertyContainer(prop)))
 
276
 
 
277
        return self._simple_PROPPATCH(patch, prop, responsecode.FORBIDDEN, "edit of live property")
 
278
 
 
279
    def test_PROPPATCH_exists_not(self):
 
280
        """
 
281
        PROPPATCH remove a non-existant property
 
282
        """
 
283
        prop = davxml.Timeout() # Timeout isn't a valid property, so it won't exist.
 
284
        patch = davxml.PropertyUpdate(davxml.Remove(davxml.PropertyContainer(prop)))
 
285
 
 
286
        return self._simple_PROPPATCH(patch, prop, responsecode.OK, "remove of non-existant property")
 
287
 
 
288
    def _simple_PROPPATCH(self, patch, prop, expected_code, what):
 
289
        def check_result(response):
 
290
            response = IResponse(response)
 
291
 
 
292
            if response.code != responsecode.MULTI_STATUS:
 
293
                self.fail("Incorrect response code for PROPPATCH (%s != %s)"
 
294
                          % (response.code, responsecode.MULTI_STATUS))
 
295
 
 
296
            return davXMLFromStream(response.stream).addCallback(check_xml)
 
297
 
 
298
        def check_xml(doc):
 
299
            response = doc.root_element.childOfType(davxml.Response)
 
300
            propstat = response.childOfType(davxml.PropertyStatus)
 
301
 
 
302
            self.failUnless(
 
303
                response.childOfType(davxml.HRef) == "/",
 
304
                "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef),)
 
305
            )
 
306
 
 
307
            self.failIf(
 
308
                propstat.childOfType(davxml.PropertyContainer).childOfType(prop) is None,
 
309
                "Not a %s in PROPPATCH property status: %s" % (prop.sname(), propstat.toxml())
 
310
            )
 
311
 
 
312
            if not have_dead_properties:
 
313
                raise SkipTest(
 
314
                    "No dead property store available for DAVFile.  "
 
315
                    "Install xattr (http://undefined.org/python/#xattr) to enable use of dead properties."
 
316
                )
 
317
 
 
318
            self.failUnless(
 
319
                propstat.childOfType(davxml.Status).code == expected_code,
 
320
                "Incorrect status code for PROPPATCH %s: %s != %s"
 
321
                % (what, propstat.childOfType(davxml.Status).code, expected_code)
 
322
            )
 
323
 
 
324
        request = SimpleRequest(self.site, "PROPPATCH", "/")
 
325
        request.stream = MemoryStream(patch.toxml())
 
326
        return self.send(request, check_result)
 
327
 
 
328
class SpiffyProperty (davxml.WebDAVTextElement):
 
329
    namespace = "http://twistedmatrix.com/ns/private/tests"
 
330
    name = "spiffyproperty"