Merge lp:~mithrandi/txfluiddb/object-operations into lp:~txfluiddb-maint/txfluiddb/trunk-obsolete-1.6
- object-operations
- Merge into trunk-obsolete-1.6
Status: | Merged |
---|---|
Merged at revision: | 3 |
Proposed branch: | lp:~mithrandi/txfluiddb/object-operations |
Merge into: | lp:~txfluiddb-maint/txfluiddb/trunk-obsolete-1.6 |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~mithrandi/txfluiddb/object-operations |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tristan Seligmann | Needs Resubmitting | ||
Jonathan Jacobs (community) | Needs Fixing | ||
Review via email: mp+10500@code.launchpad.net |
Commit message
Description of the change
Tristan Seligmann (mithrandi) wrote : | # |
Jonathan Jacobs (jjacobs) wrote : | # |
1. `Endpoint.submit`'s parameters need documentation, specifically the "data" parameter probably needs some careful and clear documentation.
2. The `agent` parameter to `getPage` should probably include a version.
3. There are some klaxons going off in `Object.get`. You test for the "valueType" key, and then read "valueEncoding", test it against a value and then create a blob with "valueType". It looks like "valueEncoding" was a brainfart. Also, why not use the "encoding" value (that you tested) as the parameter to `decode`? See point 5.
4. `Object.query`'s return value is not documented.
5. The description of the return value for `Object.getTags` is slightly confusing.
6. There isn't any test coverage for the potential exception / errback in `Object.get`.
7. `ObjectTests.
8. In "ObjectTests", `test_setTag`, `test_setBlob`, `test_createObj
9. There are a handful of lines longer than 80 columns.
Jonathan Jacobs (jjacobs) wrote : | # |
> parameter to `decode`? See point 5.
I meant point 6.
- 13. By Tristan Seligmann <tristan@viridian>
-
Improve Endpoint.submit documentation.
- 14. By Tristan Seligmann <tristan@viridian>
-
Document Object.query return value.
- 15. By Tristan Seligmann <tristan@viridian>
-
Fix test docstring.
- 16. By Tristan Seligmann <tristan@viridian>
-
Tests for encoding stuff.
- 17. By Tristan Seligmann
-
Test unsupported encodings.
- 18. By Tristan Seligmann
-
Split long lines.
Tristan Seligmann (mithrandi) wrote : | # |
I believe I've addressed all of these points except for point 8; I'm not sure how to write a useful docstring for those tests. Any suggestions?
Tristan Seligmann (mithrandi) wrote : | # |
Testing something.
Jonathan Jacobs (jjacobs) wrote : | # |
> I believe I've addressed all of these points except for point 8; I'm not sure
> how to write a useful docstring for those tests. Any suggestions?
My only suggestion is the obvious one, attempt to concisely explain the desired behaviour of the code. What is supposed to happen? etc.
I noticed that several other tests are missing docstrings too.
- 19. By Tristan Seligmann
-
Docstrings.
Preview Diff
1 | === modified file 'LICENSE' |
2 | --- LICENSE 2009-08-17 23:05:23 +0000 |
3 | +++ LICENSE 2009-08-21 00:05:49 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -Copyright (c) 2009 Tristan Seligmann |
6 | +Copyright © 2009 Tristan Seligmann |
7 | |
8 | Permission is hereby granted, free of charge, to any person obtaining |
9 | a copy of this software and associated documentation files (the |
10 | |
11 | === modified file 'txfluiddb/client.py' |
12 | --- txfluiddb/client.py 2009-08-19 20:33:58 +0000 |
13 | +++ txfluiddb/client.py 2009-08-21 00:42:26 +0000 |
14 | @@ -15,7 +15,7 @@ |
15 | @type components: C{list} of C{unicode} |
16 | @ivar components: A list of path components for this identifier. |
17 | |
18 | - @type collectionName: C{str} |
19 | + @type collectionName: C{unicode} |
20 | @cvar collectionName: The top-level path component under which these |
21 | identifiers exist. |
22 | |
23 | @@ -66,13 +66,19 @@ |
24 | return u'/'.join(self.components) |
25 | |
26 | |
27 | - def getURL(self, endpoint, prefix=None): |
28 | + def getURL(self, endpoint, prefix=None, suffix=[]): |
29 | """ |
30 | Get the URL for this path as accessed through the given endpoint. |
31 | |
32 | @type endpoint: L{Endpoint} |
33 | @param endpoint: The endpoint to operate through. |
34 | |
35 | + @type prefix: C{list} of C{unicode} |
36 | + @param prefix: A list of components to prepend before this path. |
37 | + |
38 | + @type suffix: C{list} of C{unicode} |
39 | + @param suffix: A list of components to append after this path. |
40 | + |
41 | @rtype: C{str} |
42 | """ |
43 | if self.collectionName is None: |
44 | @@ -83,12 +89,11 @@ |
45 | components.append(self.collectionName) |
46 | else: |
47 | components.extend(prefix) |
48 | - |
49 | - for component in self.components: |
50 | - encoded = component.encode('utf-8') |
51 | - quoted = quote(encoded, safe='') |
52 | - components.append(quoted) |
53 | - |
54 | + components.extend(self.components) |
55 | + components.extend(suffix) |
56 | + |
57 | + components = [quote(component.encode('utf-8'), safe='') |
58 | + for component in components] |
59 | return endpoint.getRootURL() + '/'.join(components) |
60 | |
61 | |
62 | @@ -97,7 +102,7 @@ |
63 | """ |
64 | Representation of a FluidDB namespace. |
65 | """ |
66 | - collectionName = 'namespaces' |
67 | + collectionName = u'namespaces' |
68 | |
69 | |
70 | def child(self, name): |
71 | @@ -141,7 +146,7 @@ |
72 | @rtype: C{Deferred} -> C{unicode} |
73 | @return: The description. |
74 | """ |
75 | - url = self.getURL(endpoint) + '?' + urlencode({'returnDescription': 'true'}) |
76 | + url = self.getURL(endpoint) + '?returnDescription=true' |
77 | d = endpoint.submit(url=url, method='GET') |
78 | return d.addCallback(lambda res: res[u'description']) |
79 | |
80 | @@ -179,7 +184,7 @@ |
81 | namespaces.append(self.child(name)) |
82 | return namespaces |
83 | |
84 | - url = self.getURL(endpoint) + '?' + urlencode({'returnNamespaces': 'true'}) |
85 | + url = self.getURL(endpoint) + '?returnNamespaces=true' |
86 | d = endpoint.submit(url=url, method='GET') |
87 | return d.addCallback(_parseResponse) |
88 | |
89 | @@ -200,7 +205,7 @@ |
90 | namespaces.append(self.tag(name)) |
91 | return namespaces |
92 | |
93 | - url = self.getURL(endpoint) + '?' + urlencode({'returnTags': 'true'}) |
94 | + url = self.getURL(endpoint) + '?returnTags=true' |
95 | d = endpoint.submit(url=url, method='GET') |
96 | return d.addCallback(_parseResponse) |
97 | |
98 | @@ -273,7 +278,7 @@ |
99 | """ |
100 | Representation of a FluidDB tag. |
101 | """ |
102 | - collectionName = 'tags' |
103 | + collectionName = u'tags' |
104 | |
105 | |
106 | def getDescription(self, endpoint): |
107 | @@ -286,7 +291,7 @@ |
108 | @rtype: C{Deferred} -> C{unicode} |
109 | @return: The description. |
110 | """ |
111 | - url = self.getURL(endpoint) + '?' + urlencode({'returnDescription': 'true'}) |
112 | + url = self.getURL(endpoint) + '?returnDescription=true' |
113 | d = endpoint.submit(url=url, method='GET') |
114 | return d.addCallback(lambda res: res[u'description']) |
115 | |
116 | @@ -356,12 +361,15 @@ |
117 | return getPage(*a, **kw) |
118 | |
119 | |
120 | - def submit(self, url, method, data=None): |
121 | + def submit(self, url, method, data=None, headers=None): |
122 | """ |
123 | Submit a request through this endpoint. |
124 | """ |
125 | - headers = {'Accept': 'application/json'} |
126 | - if data is not None: |
127 | + if headers is None: |
128 | + headers = {} |
129 | + |
130 | + headers['Accept'] = 'application/json' |
131 | + if data is not None and not isinstance(data, str): |
132 | data = json.dumps(data) |
133 | headers['Content-Type'] = 'application/json' |
134 | |
135 | @@ -369,5 +377,202 @@ |
136 | if res: |
137 | return json.loads(unicode(res, 'utf-8')) |
138 | |
139 | - d = self.getPage(url=url, method=method, postdata=data, headers=headers, agent='txFluidDB') |
140 | + d = self.getPage(url=url, |
141 | + method=method, |
142 | + postdata=data, |
143 | + headers=headers, |
144 | + agent='txFluidDB') |
145 | return d.addCallback(_parse) |
146 | + |
147 | + |
148 | + |
149 | +class Object(_HasPath): |
150 | + """ |
151 | + A FluidDB object. |
152 | + |
153 | + @type uuid: C{unicode} |
154 | + @ivar uuid: The UUID of the object. |
155 | + """ |
156 | + collectionName = u'objects' |
157 | + |
158 | + |
159 | + def __init__(self, uuid): |
160 | + self.uuid = uuid |
161 | + |
162 | + |
163 | + @property |
164 | + def components(self): |
165 | + """ |
166 | + Our only path component is the object UUID. |
167 | + """ |
168 | + return [self.uuid] |
169 | + |
170 | + |
171 | + @classmethod |
172 | + def create(cls, endpoint, about=None): |
173 | + """ |
174 | + Create a new object. |
175 | + |
176 | + @type endpoint: L{Endpoint} |
177 | + @param endpoint: The endpoint to operate through. |
178 | + |
179 | + @type about: C{unicode} or C{None} |
180 | + @param about: The value for the about tag, if desired. |
181 | + |
182 | + @rtype: C{Deferred} -> L{Object} |
183 | + @return: The newly created object. |
184 | + """ |
185 | + url = endpoint.getRootURL() + 'objects' |
186 | + data = {} |
187 | + if about is not None: |
188 | + data[u'about'] = about |
189 | + |
190 | + def _parseResponse(response): |
191 | + return Object(response[u'id']) |
192 | + |
193 | + d = endpoint.submit(url=url, method='POST', data=data) |
194 | + return d.addCallback(_parseResponse) |
195 | + |
196 | + |
197 | + @classmethod |
198 | + def query(cls, endpoint, query): |
199 | + """ |
200 | + Search for objects that match a query. |
201 | + |
202 | + @type endpoint: L{Endpoint} |
203 | + @param endpoint: The endpoint to operate through. |
204 | + |
205 | + @type query: C{unicode} |
206 | + @param query: A query string to search on. |
207 | + """ |
208 | + qs = '?' + urlencode({'query': query.encode('utf-8')}) |
209 | + url = endpoint.getRootURL() + 'objects' + qs |
210 | + |
211 | + def _parseResponse(response): |
212 | + return [Object(uuid) for uuid in response['ids']] |
213 | + |
214 | + d = endpoint.submit(url=url, method='GET') |
215 | + return d.addCallback(_parseResponse) |
216 | + |
217 | + |
218 | + def getTags(self, endpoint): |
219 | + """ |
220 | + Get the visible tags on this object. |
221 | + |
222 | + @type endpoint: L{Endpoint} |
223 | + @param endpoint: The endpoint to operate through. |
224 | + |
225 | + @rtype: C{Deferred} -> (C{unicode}, C{list} of L{Tag}) |
226 | + @return: A tuple of the about tag value and the list of tags. |
227 | + """ |
228 | + def _parseResponse(response): |
229 | + about = response[u'about'] |
230 | + tags = [Tag(*path.split(u'/')) for path in response[u'tagPaths']] |
231 | + return (about, tags) |
232 | + |
233 | + url = self.getURL(endpoint) |
234 | + d = endpoint.submit(url=url, |
235 | + method='GET', |
236 | + data={u'showAbout': True}) |
237 | + return d.addCallback(_parseResponse) |
238 | + |
239 | + |
240 | + def get(self, endpoint, tag): |
241 | + """ |
242 | + Get the value of a tag on this object. |
243 | + |
244 | + @type endpoint: L{Endpoint} |
245 | + @param endpoint: The endpoint to operate through. |
246 | + |
247 | + @type tag: L{Tag} |
248 | + @param tag: The tag to retrieve. |
249 | + |
250 | + @rtype: Varies depending on what value is stored. |
251 | + @return: The stored value. |
252 | + """ |
253 | + def _parseResponse(response): |
254 | + value = response[u'value'] |
255 | + if u'valueType' in response: |
256 | + encoding = response[u'valueEncoding'] |
257 | + if encoding != u'base-64': |
258 | + raise ValueError('Unsupported encoding received: %r' % encoding) |
259 | + value = value.decode('base-64') |
260 | + return Blob(response[u'valueType'], value) |
261 | + return value |
262 | + |
263 | + url = self.getURL(endpoint, suffix=tag.components) + '?format=json' |
264 | + d = endpoint.submit(url=url, |
265 | + method='GET', |
266 | + headers={'Accept-Encoding': 'base-64'}) |
267 | + return d.addCallback(_parseResponse) |
268 | + |
269 | + |
270 | + def set(self, endpoint, tag, value): |
271 | + """ |
272 | + Set the value of a tag on this object. |
273 | + |
274 | + @type endpoint: L{Endpoint} |
275 | + @param endpoint: The endpoint to operate through. |
276 | + |
277 | + @type tag: L{Tag} |
278 | + @param tag: The tag to set or replace. |
279 | + |
280 | + @type value: Any JSON-encodable value |
281 | + @param value: The value to store. |
282 | + """ |
283 | + url = self.getURL(endpoint, suffix=tag.components) + '?format=json' |
284 | + return endpoint.submit(url=url, |
285 | + method='PUT', |
286 | + data={u'value': value}) |
287 | + |
288 | + |
289 | + def setBlob(self, endpoint, tag, value): |
290 | + """ |
291 | + Set the value of a tag on this object to a blob. |
292 | + |
293 | + @type endpoint: L{Endpoint} |
294 | + @param endpoint: The endpoint to operate through. |
295 | + |
296 | + @type tag: L{Tag} |
297 | + @param tag: The tag to set or replace. |
298 | + |
299 | + @type value: L{Blob} |
300 | + @param value: The value to store. |
301 | + """ |
302 | + url = self.getURL(endpoint, suffix=tag.components) |
303 | + return endpoint.submit(url=url, |
304 | + method='PUT', |
305 | + headers={'Content-Type': value.contentType}, |
306 | + data=value.data) |
307 | + |
308 | + |
309 | + def delete(self, endpoint, tag): |
310 | + """ |
311 | + Delete a tag from this object. |
312 | + |
313 | + Note that FluidDB does not support deleting objects themselves. |
314 | + |
315 | + @type endpoint: L{Endpoint} |
316 | + @param endpoint: The endpoint to operate through. |
317 | + |
318 | + @type tag: L{Tag} |
319 | + @param tag: The tag to retrieve. |
320 | + """ |
321 | + url = self.getURL(endpoint, suffix=tag.components) |
322 | + return endpoint.submit(url=url, method='DELETE') |
323 | + |
324 | + |
325 | + |
326 | +class Blob(object): |
327 | + """ |
328 | + A binary blob with a MIME content type. |
329 | + |
330 | + @type contentType: C{unicode} |
331 | + @ivar contentType: The MIME content-type of the data. |
332 | + |
333 | + @type data: C{str} |
334 | + @ivar data: The actual data. |
335 | + """ |
336 | + def __init__(self, contentType, data): |
337 | + self.contentType = contentType |
338 | + self.data = data |
339 | |
340 | === modified file 'txfluiddb/test/test_client.py' |
341 | --- txfluiddb/test/test_client.py 2009-08-19 20:33:58 +0000 |
342 | +++ txfluiddb/test/test_client.py 2009-08-21 00:42:26 +0000 |
343 | @@ -4,7 +4,7 @@ |
344 | import simplejson as json |
345 | |
346 | from txfluiddb.errors import InvalidName |
347 | -from txfluiddb.client import Namespace, Tag, Endpoint, _HasPath |
348 | +from txfluiddb.client import Namespace, Tag, Endpoint, _HasPath, Object, Blob |
349 | |
350 | |
351 | |
352 | @@ -44,6 +44,28 @@ |
353 | 'http://fluiddb.test.url/tests/foo') |
354 | |
355 | |
356 | + def test_getURLPrefix(self): |
357 | + """ |
358 | + Passing prefix to getURL prepends the given components to the path. |
359 | + """ |
360 | + endpoint = Endpoint('http://fluiddb.test.url/') |
361 | + path = _HasPath(u'foo') |
362 | + path.collectionName = 'tests' |
363 | + self.assertEqual(path.getURL(endpoint, prefix=[u'quux', u'42']), |
364 | + 'http://fluiddb.test.url/quux/42/foo') |
365 | + |
366 | + |
367 | + def test_getURLSuffix(self): |
368 | + """ |
369 | + Passing suffix to getURL appends the given components to the path. |
370 | + """ |
371 | + endpoint = Endpoint('http://fluiddb.test.url/') |
372 | + path = _HasPath(u'foo') |
373 | + path.collectionName = 'tests' |
374 | + self.assertEqual(path.getURL(endpoint, suffix=[u'quux', u'42']), |
375 | + 'http://fluiddb.test.url/tests/foo/quux/42') |
376 | + |
377 | + |
378 | def test_urlEncoding(self): |
379 | """ |
380 | URL encoding is done according to IRI rules. |
381 | @@ -374,3 +396,199 @@ |
382 | endpoint = Endpoint('http://fluiddb.test.url/', '20090817') |
383 | self.assertEqual(endpoint.getRootURL(), |
384 | 'http://fluiddb.test.url/20090817/') |
385 | + |
386 | + |
387 | + |
388 | +class ObjectTests(TestCase): |
389 | + """ |
390 | + Tests for L{Object}. |
391 | + """ |
392 | + def setUp(self): |
393 | + self.endpoint = MockEndpoint('http://fluiddb.test.url/') |
394 | + |
395 | + |
396 | + def test_getTags(self): |
397 | + """ |
398 | + The getTags method returns the about value and the list of tags. |
399 | + """ |
400 | + response = {u'about': u'Testing object', |
401 | + u'tagPaths': [u'test/tag1', u'test/tag2']} |
402 | + self.endpoint.response = json.dumps(response) |
403 | + |
404 | + def _gotResponse(response): |
405 | + about, tags = response |
406 | + self.assertEqual(about, u'Testing object') |
407 | + self.assertEqual(len(tags), 2) |
408 | + tag1, tag2 = tags |
409 | + self.assertIsInstance(tag1, Tag) |
410 | + self.assertEqual(tag1.getPath(), u'test/tag1') |
411 | + self.assertIsInstance(tag2, Tag) |
412 | + self.assertEqual(tag2.getPath(), u'test/tag2') |
413 | + |
414 | + obj = Object(u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
415 | + d = obj.getTags(self.endpoint).addCallback(_gotResponse) |
416 | + self.assertEqual(self.endpoint.method, 'GET') |
417 | + self.assertEqual( |
418 | + self.endpoint.url, |
419 | + 'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
420 | + self.assertEqual(json.loads(self.endpoint.data), |
421 | + {u'showAbout': True}) |
422 | + return d |
423 | + |
424 | + |
425 | + def makeGetRequest(self, response): |
426 | + """ |
427 | + Utility wrapper for testing L{Object.get} operations. |
428 | + """ |
429 | + self.endpoint.response = json.dumps(response) |
430 | + obj = Object(u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
431 | + tag = Namespace(u'test').child(u'tag') |
432 | + d = obj.get(self.endpoint, tag) |
433 | + self.assertEqual(self.endpoint.method, 'GET') |
434 | + self.assertEqual(self.endpoint.data, None) |
435 | + self.assertEqual( |
436 | + self.endpoint.url, |
437 | + 'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx/test/tag?format=json') |
438 | + return d |
439 | + |
440 | + |
441 | + def test_getString(self): |
442 | + """ |
443 | + Retrieving a JSON string value results in a C{unicode} object. |
444 | + """ |
445 | + response = {u'value': u'Data goes where?'} |
446 | + def _gotResponse(response): |
447 | + self.assertIsInstance(response, unicode) |
448 | + self.assertEqual(response, u'Data goes where?') |
449 | + return self.makeGetRequest(response).addCallback(_gotResponse) |
450 | + |
451 | + |
452 | + def test_getInteger(self): |
453 | + """ |
454 | + Retrieving a JSON integer value results in an C{integer} object. |
455 | + """ |
456 | + response = {u'value': 42} |
457 | + def _gotResponse(response): |
458 | + self.assertEqual(response, 42) |
459 | + return self.makeGetRequest(response).addCallback(_gotResponse) |
460 | + |
461 | + |
462 | + def test_getBlob(self): |
463 | + """ |
464 | + Retrieving a binary value results in a C{Blob} object. |
465 | + """ |
466 | + response = {u'value': u'PHA+Zm9vPC9wPg==', |
467 | + u'valueEncoding': u'base-64', |
468 | + u'valueType': u'text/html'} |
469 | + def _gotResponse(response): |
470 | + self.assertEqual(self.endpoint.headers['Accept-Encoding'], 'base-64') |
471 | + self.assertEqual(response.contentType, u'text/html') |
472 | + self.assertEqual(response.data, '<p>foo</p>') |
473 | + return self.makeGetRequest(response).addCallback(_gotResponse) |
474 | + |
475 | + |
476 | + def test_deleteTag(self): |
477 | + """ |
478 | + Deleting a tag returns nothing if successful. |
479 | + """ |
480 | + self.endpoint.response = '' |
481 | + |
482 | + obj = Object(u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
483 | + tag = Namespace(u'test').child(u'tag') |
484 | + d = obj.delete(self.endpoint, tag) |
485 | + self.assertEqual(self.endpoint.method, 'DELETE') |
486 | + self.assertEqual( |
487 | + self.endpoint.url, |
488 | + 'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx/test/tag') |
489 | + self.assertEqual(self.endpoint.data, None) |
490 | + return d |
491 | + |
492 | + |
493 | + def test_setTag(self): |
494 | + self.endpoint.response = '' |
495 | + |
496 | + obj = Object(u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
497 | + tag = Namespace(u'test').child(u'tag') |
498 | + d = obj.set(self.endpoint, tag, 42) |
499 | + self.assertEqual(self.endpoint.method, 'PUT') |
500 | + self.assertEqual( |
501 | + self.endpoint.url, |
502 | + 'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx/test/tag?format=json') |
503 | + self.assertEqual(json.loads(self.endpoint.data), |
504 | + {u'value': 42}) |
505 | + return d |
506 | + |
507 | + |
508 | + def test_setBlob(self): |
509 | + self.endpoint.response = '' |
510 | + |
511 | + obj = Object(u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
512 | + tag = Namespace(u'test').child(u'tag') |
513 | + blob = Blob(u'text/html', '<p>foo</p>') |
514 | + d = obj.setBlob(self.endpoint, tag, blob) |
515 | + self.assertEqual(self.endpoint.method, 'PUT') |
516 | + self.assertEqual( |
517 | + self.endpoint.url, |
518 | + 'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx/test/tag') |
519 | + self.assertEqual(self.endpoint.data, '<p>foo</p>') |
520 | + self.assertEqual(self.endpoint.headers['Content-Type'], 'text/html') |
521 | + return d |
522 | + |
523 | + |
524 | + def test_createObject(self): |
525 | + """ |
526 | + Creating an object returns the newly created object. |
527 | + """ |
528 | + response = {u'URI': u'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', |
529 | + u'id': u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'} |
530 | + self.endpoint.response = json.dumps(response) |
531 | + |
532 | + def _gotResponse(obj): |
533 | + self.assertIsInstance(obj, Object) |
534 | + self.assertEqual(obj.uuid, u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
535 | + |
536 | + d = Object.create(self.endpoint).addCallback(_gotResponse) |
537 | + self.assertEqual(self.endpoint.method, 'POST') |
538 | + self.assertEqual(self.endpoint.url, 'http://fluiddb.test.url/objects') |
539 | + self.assertEqual(json.loads(self.endpoint.data), {}) |
540 | + return d |
541 | + |
542 | + |
543 | + def test_createObjectWithAbout(self): |
544 | + response = {u'URI': u'http://fluiddb.test.url/objects/xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', |
545 | + u'id': u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'} |
546 | + self.endpoint.response = json.dumps(response) |
547 | + |
548 | + def _gotResponse(obj): |
549 | + self.assertIsInstance(obj, Object) |
550 | + self.assertEqual(obj.uuid, u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx') |
551 | + |
552 | + d = Object.create(self.endpoint, u'New object').addCallback(_gotResponse) |
553 | + self.assertEqual(self.endpoint.method, 'POST') |
554 | + self.assertEqual(self.endpoint.url, 'http://fluiddb.test.url/objects') |
555 | + self.assertEqual(json.loads(self.endpoint.data), |
556 | + {u'about': u'New object'}) |
557 | + return d |
558 | + |
559 | + |
560 | + def test_query(self): |
561 | + """ |
562 | + Querying returns all objects matching the query. |
563 | + """ |
564 | + response = {u'ids': [u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxx1', |
565 | + u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxx2']} |
566 | + self.endpoint.response = json.dumps(response) |
567 | + |
568 | + def _gotResponse(objs): |
569 | + self.assertEqual(len(objs), 2) |
570 | + obj1, obj2 = objs |
571 | + self.assertIsInstance(obj1, Object) |
572 | + self.assertEqual(obj1.uuid, u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxx1') |
573 | + self.assertIsInstance(obj2, Object) |
574 | + self.assertEqual(obj2.uuid, u'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxx2') |
575 | + |
576 | + d = Object.query(self.endpoint, u'has fluiddb/about').addCallback(_gotResponse) |
577 | + self.assertEqual(self.endpoint.method, 'GET') |
578 | + self.assertEqual(self.endpoint.url, 'http://fluiddb.test.url/objects?query=has+fluiddb%2Fabout') |
579 | + self.assertEqual(self.endpoint.data, None) |
580 | + return d |
Implement operations on objects.