~j5-dev/+junk/cherrypy3-3.2.0rc1

« back to all changes in this revision

Viewing changes to cherrypy/test/test_dynamicobjectmapping.py

  • Committer: steveh at sjsoft
  • Date: 2010-07-01 13:07:15 UTC
  • Revision ID: steveh@sjsoft.com-20100701130715-w56oim8346qzqlka
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from cherrypy.test import test
 
2
from cherrypy._cptree import Application
 
3
test.prefer_parent_path()
 
4
 
 
5
import cherrypy
 
6
 
 
7
script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
 
8
 
 
9
def setup_server():
 
10
    class SubSubRoot:
 
11
        def index(self):
 
12
            return "SubSubRoot index"
 
13
        index.exposed = True
 
14
 
 
15
        def default(self, *args):
 
16
            return "SubSubRoot default"
 
17
        default.exposed = True
 
18
 
 
19
        def handler(self):
 
20
            return "SubSubRoot handler"
 
21
        handler.exposed = True
 
22
 
 
23
        def dispatch(self):
 
24
            return "SubSubRoot dispatch"
 
25
        dispatch.exposed = True
 
26
 
 
27
    subsubnodes = {
 
28
        '1': SubSubRoot(),
 
29
        '2': SubSubRoot(),
 
30
    }
 
31
 
 
32
    class SubRoot:
 
33
        def index(self):
 
34
            return "SubRoot index"
 
35
        index.exposed = True
 
36
 
 
37
        def default(self, *args):
 
38
            return "SubRoot %s" % (args,)
 
39
        default.exposed = True
 
40
 
 
41
        def handler(self):
 
42
            return "SubRoot handler"
 
43
        handler.exposed = True
 
44
 
 
45
        def _cp_dispatch(self, vpath):
 
46
            return subsubnodes.get(vpath[0], None)
 
47
 
 
48
    subnodes = {
 
49
        '1': SubRoot(),
 
50
        '2': SubRoot(),
 
51
    }
 
52
    class Root:
 
53
        def index(self):
 
54
            return "index"
 
55
        index.exposed = True
 
56
 
 
57
        def default(self, *args):
 
58
            return "default %s" % (args,)
 
59
        default.exposed = True
 
60
 
 
61
        def handler(self):
 
62
            return "handler"
 
63
        handler.exposed = True
 
64
 
 
65
        def _cp_dispatch(self, vpath):
 
66
            return subnodes.get(vpath[0])
 
67
 
 
68
    #--------------------------------------------------------------------------
 
69
    # DynamicNodeAndMethodDispatcher example.
 
70
    # This example exposes a fairly naive HTTP api
 
71
    class User(object):
 
72
        def __init__(self, id, name):
 
73
            self.id = id
 
74
            self.name = name
 
75
 
 
76
        def __unicode__(self):
 
77
            return unicode(self.name)
 
78
 
 
79
    user_lookup = {
 
80
        1: User(1, 'foo'),
 
81
        2: User(2, 'bar'),
 
82
    }
 
83
 
 
84
    def make_user(name, id=None):
 
85
        if not id:
 
86
            id = max(*user_lookup.keys()) + 1
 
87
        user_lookup[id] = User(id, name)
 
88
        return id
 
89
 
 
90
    class UserContainerNode(object):
 
91
        exposed = True
 
92
 
 
93
        def POST(self, name):
 
94
            """
 
95
            Allow the creation of a new Object
 
96
            """
 
97
            return "POST %d" % make_user(name)
 
98
 
 
99
        def GET(self):
 
100
            keys = user_lookup.keys()
 
101
            keys.sort()
 
102
            return unicode(keys)
 
103
 
 
104
        def dynamic_dispatch(self, vpath):
 
105
            try:
 
106
                id = int(vpath[0])
 
107
            except ValueError:
 
108
                return None
 
109
            return UserInstanceNode(id)
 
110
 
 
111
    class UserInstanceNode(object):
 
112
        exposed = True
 
113
        def __init__(self, id):
 
114
            self.id = id
 
115
            self.user = user_lookup.get(id, None)
 
116
 
 
117
            # For all but PUT methods there MUST be a valid user identified
 
118
            # by self.id
 
119
            if not self.user and cherrypy.request.method != 'PUT':
 
120
                raise cherrypy.HTTPError(404)
 
121
 
 
122
        def GET(self, *args, **kwargs):
 
123
            """
 
124
            Return the appropriate representation of the instance.
 
125
            """
 
126
            return unicode(self.user)
 
127
 
 
128
        def POST(self, name):
 
129
            """
 
130
            Update the fields of the user instance.
 
131
            """
 
132
            self.user.name = name
 
133
            return "POST %d" % self.user.id
 
134
 
 
135
        def PUT(self, name):
 
136
            """
 
137
            Create a new user with the specified id, or edit it if it already exists
 
138
            """
 
139
            if self.user:
 
140
                # Edit the current user
 
141
                self.user.name = name
 
142
                return "PUT %d" % self.user.id
 
143
            else:
 
144
                # Make a new user with said attributes.
 
145
                return "PUT %d" % make_user(name, self.id)
 
146
 
 
147
        def DELETE(self):
 
148
            """
 
149
            Delete the user specified at the id.
 
150
            """
 
151
            id = self.user.id
 
152
            del user_lookup[self.user.id]
 
153
            del self.user
 
154
            return "DELETE %d" % id
 
155
 
 
156
 
 
157
    Root.users = UserContainerNode()
 
158
 
 
159
    md = cherrypy.dispatch.MethodDispatcher('dynamic_dispatch')
 
160
    for url in script_names:
 
161
        conf = {'/': {
 
162
                    'user': (url or "/").split("/")[-2],
 
163
                },
 
164
                '/users': {
 
165
                    'request.dispatch': md
 
166
                },
 
167
            }
 
168
        cherrypy.tree.mount(Root(), url, conf)
 
169
 
 
170
 
 
171
from cherrypy.test import helper
 
172
 
 
173
class DynamicObjectMappingTest(helper.CPWebCase):
 
174
 
 
175
    def testObjectMapping(self):
 
176
        for url in script_names:
 
177
            prefix = self.script_name = url
 
178
 
 
179
            self.getPage('/')
 
180
            self.assertBody('index')
 
181
 
 
182
            self.getPage('/handler')
 
183
            self.assertBody('handler')
 
184
 
 
185
            # Dynamic dispatch will succeed here for the subnodes
 
186
            # so the subroot gets called
 
187
            self.getPage('/1/')
 
188
            self.assertBody('SubRoot index')
 
189
 
 
190
            self.getPage('/2/')
 
191
            self.assertBody('SubRoot index')
 
192
 
 
193
            self.getPage('/1/handler')
 
194
            self.assertBody('SubRoot handler')
 
195
 
 
196
            self.getPage('/2/handler')
 
197
            self.assertBody('SubRoot handler')
 
198
 
 
199
            # Dynamic dispatch will fail here for the subnodes
 
200
            # so the default gets called
 
201
            self.getPage('/asdf/')
 
202
            self.assertBody("default ('asdf',)")
 
203
 
 
204
            self.getPage('/asdf/asdf')
 
205
            self.assertBody("default ('asdf', 'asdf')")
 
206
 
 
207
            self.getPage('/asdf/handler')
 
208
            self.assertBody("default ('asdf', 'handler')")
 
209
 
 
210
            # Dynamic dispatch will succeed here for the subsubnodes
 
211
            # so the subsubroot gets called
 
212
            self.getPage('/1/1/')
 
213
            self.assertBody('SubSubRoot index')
 
214
 
 
215
            self.getPage('/2/2/')
 
216
            self.assertBody('SubSubRoot index')
 
217
 
 
218
            self.getPage('/1/1/handler')
 
219
            self.assertBody('SubSubRoot handler')
 
220
 
 
221
            self.getPage('/2/2/handler')
 
222
            self.assertBody('SubSubRoot handler')
 
223
 
 
224
            self.getPage('/2/2/dispatch')
 
225
            self.assertBody('SubSubRoot dispatch')
 
226
 
 
227
            # The exposed dispatch will not be called as a dispatch
 
228
            # method.
 
229
            self.getPage('/2/2/foo/foo')
 
230
            self.assertBody("SubSubRoot default")
 
231
 
 
232
            # Dynamic dispatch will fail here for the subsubnodes
 
233
            # so the SubRoot gets called
 
234
            self.getPage('/1/asdf/')
 
235
            self.assertBody("SubRoot ('asdf',)")
 
236
 
 
237
            self.getPage('/1/asdf/asdf')
 
238
            self.assertBody("SubRoot ('asdf', 'asdf')")
 
239
 
 
240
            self.getPage('/1/asdf/handler')
 
241
            self.assertBody("SubRoot ('asdf', 'handler')")
 
242
 
 
243
    def testMethodDispatch(self):
 
244
        # GET acts like a container
 
245
        self.getPage("/users")
 
246
        self.assertBody("[1, 2]")
 
247
        self.assertHeader('Allow', 'GET, HEAD, POST')
 
248
 
 
249
        # POST to the container URI allows creation
 
250
        self.getPage("/users", method="POST", body="name=baz")
 
251
        self.assertBody("POST 3")
 
252
        self.assertHeader('Allow', 'GET, HEAD, POST')
 
253
 
 
254
        # POST to a specific instanct URI results in a 404
 
255
        # as the resource does not exit.
 
256
        self.getPage("/users/5", method="POST", body="name=baz")
 
257
        self.assertStatus(404)
 
258
 
 
259
        # PUT to a specific instanct URI results in creation
 
260
        self.getPage("/users/5", method="PUT", body="name=boris")
 
261
        self.assertBody("PUT 5")
 
262
        self.assertHeader('Allow', 'DELETE, GET, HEAD, POST, PUT')
 
263
 
 
264
        # GET acts like a container
 
265
        self.getPage("/users")
 
266
        self.assertBody("[1, 2, 3, 5]")
 
267
        self.assertHeader('Allow', 'GET, HEAD, POST')
 
268
 
 
269
        test_cases = (
 
270
            (1, 'foo', 'fooupdated', 'DELETE, GET, HEAD, POST, PUT'),
 
271
            (2, 'bar', 'barupdated', 'DELETE, GET, HEAD, POST, PUT'),
 
272
            (3, 'baz', 'bazupdated', 'DELETE, GET, HEAD, POST, PUT'),
 
273
            (5, 'boris', 'borisupdated', 'DELETE, GET, HEAD, POST, PUT'),
 
274
        )
 
275
        for id, name, updatedname, headers in test_cases:
 
276
            self.getPage("/users/%d" % id)
 
277
            self.assertBody(name)
 
278
            self.assertHeader('Allow', headers)
 
279
 
 
280
            # Make sure POSTs update already existings resources
 
281
            self.getPage("/users/%d" % id, method='POST', body="name=%s" % updatedname)
 
282
            self.assertBody("POST %d" % id)
 
283
            self.assertHeader('Allow', headers)
 
284
 
 
285
            # Make sure PUTs Update already existing resources.
 
286
            self.getPage("/users/%d" % id, method='PUT', body="name=%s" % updatedname)
 
287
            self.assertBody("PUT %d" % id)
 
288
            self.assertHeader('Allow', headers)
 
289
 
 
290
            # Make sure DELETES Remove already existing resources.
 
291
            self.getPage("/users/%d" % id, method='DELETE')
 
292
            self.assertBody("DELETE %d" % id)
 
293
            self.assertHeader('Allow', headers)
 
294
 
 
295
 
 
296
        # GET acts like a container
 
297
        self.getPage("/users")
 
298
        self.assertBody("[]")
 
299
        self.assertHeader('Allow', 'GET, HEAD, POST')
 
300
 
 
301
if __name__ == "__main__":
 
302
    helper.testmain()