test_memcache.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test the memcache client protocol.
  5. """
  6. from __future__ import absolute_import, division
  7. from twisted.internet.error import ConnectionDone
  8. from twisted.protocols.memcache import MemCacheProtocol, NoSuchCommand
  9. from twisted.protocols.memcache import ClientError, ServerError
  10. from twisted.trial.unittest import TestCase
  11. from twisted.test.proto_helpers import StringTransportWithDisconnection
  12. from twisted.internet.task import Clock
  13. from twisted.internet.defer import Deferred, gatherResults, TimeoutError
  14. from twisted.internet.defer import DeferredList
  15. class CommandMixin:
  16. """
  17. Setup and tests for basic invocation of L{MemCacheProtocol} commands.
  18. """
  19. def _test(self, d, send, recv, result):
  20. """
  21. Helper test method to test the resulting C{Deferred} of a
  22. L{MemCacheProtocol} command.
  23. """
  24. raise NotImplementedError()
  25. def test_get(self):
  26. """
  27. L{MemCacheProtocol.get} returns a L{Deferred} which is called back with
  28. the value and the flag associated with the given key if the server
  29. returns a successful result.
  30. """
  31. return self._test(
  32. self.proto.get(b"foo"), b"get foo\r\n",
  33. b"VALUE foo 0 3\r\nbar\r\nEND\r\n", (0, b"bar"))
  34. def test_emptyGet(self):
  35. """
  36. Test getting a non-available key: it succeeds but return L{None} as
  37. value and C{0} as flag.
  38. """
  39. return self._test(
  40. self.proto.get(b"foo"), b"get foo\r\n",
  41. b"END\r\n", (0, None))
  42. def test_getMultiple(self):
  43. """
  44. L{MemCacheProtocol.getMultiple} returns a L{Deferred} which is called
  45. back with a dictionary of flag, value for each given key.
  46. """
  47. return self._test(
  48. self.proto.getMultiple([b'foo', b'cow']),
  49. b"get foo cow\r\n",
  50. b"VALUE foo 0 3\r\nbar\r\nVALUE cow 0 7\r\nchicken\r\nEND\r\n",
  51. {b'cow': (0, b'chicken'), b'foo': (0, b'bar')})
  52. def test_getMultipleWithEmpty(self):
  53. """
  54. When L{MemCacheProtocol.getMultiple} is called with non-available keys,
  55. the corresponding tuples are (0, None).
  56. """
  57. return self._test(
  58. self.proto.getMultiple([b'foo', b'cow']),
  59. b"get foo cow\r\n",
  60. b"VALUE cow 1 3\r\nbar\r\nEND\r\n",
  61. {b'cow': (1, b'bar'), b'foo': (0, None)})
  62. def test_set(self):
  63. """
  64. L{MemCacheProtocol.set} returns a L{Deferred} which is called back with
  65. C{True} when the operation succeeds.
  66. """
  67. return self._test(
  68. self.proto.set(b"foo", b"bar"),
  69. b"set foo 0 0 3\r\nbar\r\n", b"STORED\r\n", True)
  70. def test_add(self):
  71. """
  72. L{MemCacheProtocol.add} returns a L{Deferred} which is called back with
  73. C{True} when the operation succeeds.
  74. """
  75. return self._test(
  76. self.proto.add(b"foo", b"bar"),
  77. b"add foo 0 0 3\r\nbar\r\n", b"STORED\r\n", True)
  78. def test_replace(self):
  79. """
  80. L{MemCacheProtocol.replace} returns a L{Deferred} which is called back
  81. with C{True} when the operation succeeds.
  82. """
  83. return self._test(
  84. self.proto.replace(b"foo", b"bar"),
  85. b"replace foo 0 0 3\r\nbar\r\n", b"STORED\r\n", True)
  86. def test_errorAdd(self):
  87. """
  88. Test an erroneous add: if a L{MemCacheProtocol.add} is called but the
  89. key already exists on the server, it returns a B{NOT STORED} answer,
  90. which calls back the resulting L{Deferred} with C{False}.
  91. """
  92. return self._test(
  93. self.proto.add(b"foo", b"bar"),
  94. b"add foo 0 0 3\r\nbar\r\n", b"NOT STORED\r\n", False)
  95. def test_errorReplace(self):
  96. """
  97. Test an erroneous replace: if a L{MemCacheProtocol.replace} is called
  98. but the key doesn't exist on the server, it returns a B{NOT STORED}
  99. answer, which calls back the resulting L{Deferred} with C{False}.
  100. """
  101. return self._test(
  102. self.proto.replace(b"foo", b"bar"),
  103. b"replace foo 0 0 3\r\nbar\r\n", b"NOT STORED\r\n", False)
  104. def test_delete(self):
  105. """
  106. L{MemCacheProtocol.delete} returns a L{Deferred} which is called back
  107. with C{True} when the server notifies a success.
  108. """
  109. return self._test(
  110. self.proto.delete(b"bar"), b"delete bar\r\n", b"DELETED\r\n", True)
  111. def test_errorDelete(self):
  112. """
  113. Test an error during a delete: if key doesn't exist on the server, it
  114. returns a B{NOT FOUND} answer which calls back the resulting
  115. L{Deferred} with C{False}.
  116. """
  117. return self._test(
  118. self.proto.delete(b"bar"),
  119. b"delete bar\r\n", b"NOT FOUND\r\n", False)
  120. def test_increment(self):
  121. """
  122. Test incrementing a variable: L{MemCacheProtocol.increment} returns a
  123. L{Deferred} which is called back with the incremented value of the
  124. given key.
  125. """
  126. return self._test(
  127. self.proto.increment(b"foo"), b"incr foo 1\r\n", b"4\r\n", 4)
  128. def test_decrement(self):
  129. """
  130. Test decrementing a variable: L{MemCacheProtocol.decrement} returns a
  131. L{Deferred} which is called back with the decremented value of the
  132. given key.
  133. """
  134. return self._test(
  135. self.proto.decrement(b"foo"), b"decr foo 1\r\n", b"5\r\n", 5)
  136. def test_incrementVal(self):
  137. """
  138. L{MemCacheProtocol.increment} takes an optional argument C{value} which
  139. replaces the default value of 1 when specified.
  140. """
  141. return self._test(
  142. self.proto.increment(b"foo", 8), b"incr foo 8\r\n", b"4\r\n", 4)
  143. def test_decrementVal(self):
  144. """
  145. L{MemCacheProtocol.decrement} takes an optional argument C{value} which
  146. replaces the default value of 1 when specified.
  147. """
  148. return self._test(
  149. self.proto.decrement(b"foo", 3), b"decr foo 3\r\n", b"5\r\n", 5)
  150. def test_stats(self):
  151. """
  152. Test retrieving server statistics via the L{MemCacheProtocol.stats}
  153. command: it parses the data sent by the server and calls back the
  154. resulting L{Deferred} with a dictionary of the received statistics.
  155. """
  156. return self._test(
  157. self.proto.stats(), b"stats\r\n",
  158. b"STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
  159. {b"foo": b"bar", b"egg": b"spam"})
  160. def test_statsWithArgument(self):
  161. """
  162. L{MemCacheProtocol.stats} takes an optional C{bytes} argument which,
  163. if specified, is sent along with the I{STAT} command. The I{STAT}
  164. responses from the server are parsed as key/value pairs and returned
  165. as a C{dict} (as in the case where the argument is not specified).
  166. """
  167. return self._test(
  168. self.proto.stats(b"blah"), b"stats blah\r\n",
  169. b"STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
  170. {b"foo": b"bar", b"egg": b"spam"})
  171. def test_version(self):
  172. """
  173. Test version retrieval via the L{MemCacheProtocol.version} command: it
  174. returns a L{Deferred} which is called back with the version sent by the
  175. server.
  176. """
  177. return self._test(
  178. self.proto.version(), b"version\r\n", b"VERSION 1.1\r\n", b"1.1")
  179. def test_flushAll(self):
  180. """
  181. L{MemCacheProtocol.flushAll} returns a L{Deferred} which is called back
  182. with C{True} if the server acknowledges success.
  183. """
  184. return self._test(
  185. self.proto.flushAll(), b"flush_all\r\n", b"OK\r\n", True)
  186. class MemCacheTests(CommandMixin, TestCase):
  187. """
  188. Test client protocol class L{MemCacheProtocol}.
  189. """
  190. def setUp(self):
  191. """
  192. Create a memcache client, connect it to a string protocol, and make it
  193. use a deterministic clock.
  194. """
  195. self.proto = MemCacheProtocol()
  196. self.clock = Clock()
  197. self.proto.callLater = self.clock.callLater
  198. self.transport = StringTransportWithDisconnection()
  199. self.transport.protocol = self.proto
  200. self.proto.makeConnection(self.transport)
  201. def _test(self, d, send, recv, result):
  202. """
  203. Implementation of C{_test} which checks that the command sends C{send}
  204. data, and that upon reception of C{recv} the result is C{result}.
  205. @param d: the resulting deferred from the memcache command.
  206. @type d: C{Deferred}
  207. @param send: the expected data to be sent.
  208. @type send: C{bytes}
  209. @param recv: the data to simulate as reception.
  210. @type recv: C{bytes}
  211. @param result: the expected result.
  212. @type result: C{any}
  213. """
  214. def cb(res):
  215. self.assertEqual(res, result)
  216. self.assertEqual(self.transport.value(), send)
  217. d.addCallback(cb)
  218. self.proto.dataReceived(recv)
  219. return d
  220. def test_invalidGetResponse(self):
  221. """
  222. If the value returned doesn't match the expected key of the current
  223. C{get} command, an error is raised in L{MemCacheProtocol.dataReceived}.
  224. """
  225. self.proto.get(b"foo")
  226. self.assertRaises(
  227. RuntimeError, self.proto.dataReceived,
  228. b"VALUE bar 0 7\r\nspamegg\r\nEND\r\n")
  229. def test_invalidMultipleGetResponse(self):
  230. """
  231. If the value returned doesn't match one the expected keys of the
  232. current multiple C{get} command, an error is raised error in
  233. L{MemCacheProtocol.dataReceived}.
  234. """
  235. self.proto.getMultiple([b"foo", b"bar"])
  236. self.assertRaises(
  237. RuntimeError, self.proto.dataReceived,
  238. b"VALUE egg 0 7\r\nspamegg\r\nEND\r\n")
  239. def test_invalidEndResponse(self):
  240. """
  241. If an END is received in response to an operation that isn't C{get},
  242. C{gets}, or C{stats}, an error is raised in
  243. L{MemCacheProtocol.dataReceived}.
  244. """
  245. self.proto.set(b"key", b"value")
  246. self.assertRaises(
  247. RuntimeError, self.proto.dataReceived,
  248. b"END\r\n")
  249. def test_timeOut(self):
  250. """
  251. Test the timeout on outgoing requests: when timeout is detected, all
  252. current commands fail with a L{TimeoutError}, and the connection is
  253. closed.
  254. """
  255. d1 = self.proto.get(b"foo")
  256. d2 = self.proto.get(b"bar")
  257. d3 = Deferred()
  258. self.proto.connectionLost = d3.callback
  259. self.clock.advance(self.proto.persistentTimeOut)
  260. self.assertFailure(d1, TimeoutError)
  261. self.assertFailure(d2, TimeoutError)
  262. def checkMessage(error):
  263. self.assertEqual(str(error), "Connection timeout")
  264. d1.addCallback(checkMessage)
  265. self.assertFailure(d3, ConnectionDone)
  266. return gatherResults([d1, d2, d3])
  267. def test_timeoutRemoved(self):
  268. """
  269. When a request gets a response, no pending timeout call remains around.
  270. """
  271. d = self.proto.get(b"foo")
  272. self.clock.advance(self.proto.persistentTimeOut - 1)
  273. self.proto.dataReceived(b"VALUE foo 0 3\r\nbar\r\nEND\r\n")
  274. def check(result):
  275. self.assertEqual(result, (0, b"bar"))
  276. self.assertEqual(len(self.clock.calls), 0)
  277. d.addCallback(check)
  278. return d
  279. def test_timeOutRaw(self):
  280. """
  281. Test the timeout when raw mode was started: the timeout is not reset
  282. until all the data has been received, so we can have a L{TimeoutError}
  283. when waiting for raw data.
  284. """
  285. d1 = self.proto.get(b"foo")
  286. d2 = Deferred()
  287. self.proto.connectionLost = d2.callback
  288. self.proto.dataReceived(b"VALUE foo 0 10\r\n12345")
  289. self.clock.advance(self.proto.persistentTimeOut)
  290. self.assertFailure(d1, TimeoutError)
  291. self.assertFailure(d2, ConnectionDone)
  292. return gatherResults([d1, d2])
  293. def test_timeOutStat(self):
  294. """
  295. Test the timeout when stat command has started: the timeout is not
  296. reset until the final B{END} is received.
  297. """
  298. d1 = self.proto.stats()
  299. d2 = Deferred()
  300. self.proto.connectionLost = d2.callback
  301. self.proto.dataReceived(b"STAT foo bar\r\n")
  302. self.clock.advance(self.proto.persistentTimeOut)
  303. self.assertFailure(d1, TimeoutError)
  304. self.assertFailure(d2, ConnectionDone)
  305. return gatherResults([d1, d2])
  306. def test_timeoutPipelining(self):
  307. """
  308. When two requests are sent, a timeout call remains around for the
  309. second request, and its timeout time is correct.
  310. """
  311. d1 = self.proto.get(b"foo")
  312. d2 = self.proto.get(b"bar")
  313. d3 = Deferred()
  314. self.proto.connectionLost = d3.callback
  315. self.clock.advance(self.proto.persistentTimeOut - 1)
  316. self.proto.dataReceived(b"VALUE foo 0 3\r\nbar\r\nEND\r\n")
  317. def check(result):
  318. self.assertEqual(result, (0, b"bar"))
  319. self.assertEqual(len(self.clock.calls), 1)
  320. for i in range(self.proto.persistentTimeOut):
  321. self.clock.advance(1)
  322. return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
  323. def checkTime(ignored):
  324. # Check that the timeout happened C{self.proto.persistentTimeOut}
  325. # after the last response
  326. self.assertEqual(
  327. self.clock.seconds(), 2 * self.proto.persistentTimeOut - 1)
  328. d1.addCallback(check)
  329. self.assertFailure(d3, ConnectionDone)
  330. return d1
  331. def test_timeoutNotReset(self):
  332. """
  333. Check that timeout is not resetted for every command, but keep the
  334. timeout from the first command without response.
  335. """
  336. d1 = self.proto.get(b"foo")
  337. d3 = Deferred()
  338. self.proto.connectionLost = d3.callback
  339. self.clock.advance(self.proto.persistentTimeOut - 1)
  340. d2 = self.proto.get(b"bar")
  341. self.clock.advance(1)
  342. self.assertFailure(d1, TimeoutError)
  343. self.assertFailure(d2, TimeoutError)
  344. self.assertFailure(d3, ConnectionDone)
  345. return gatherResults([d1, d2, d3])
  346. def test_timeoutCleanDeferreds(self):
  347. """
  348. C{timeoutConnection} cleans the list of commands that it fires with
  349. C{TimeoutError}: C{connectionLost} doesn't try to fire them again, but
  350. sets the disconnected state so that future commands fail with a
  351. C{RuntimeError}.
  352. """
  353. d1 = self.proto.get(b"foo")
  354. self.clock.advance(self.proto.persistentTimeOut)
  355. self.assertFailure(d1, TimeoutError)
  356. d2 = self.proto.get(b"bar")
  357. self.assertFailure(d2, RuntimeError)
  358. return gatherResults([d1, d2])
  359. def test_connectionLost(self):
  360. """
  361. When disconnection occurs while commands are still outstanding, the
  362. commands fail.
  363. """
  364. d1 = self.proto.get(b"foo")
  365. d2 = self.proto.get(b"bar")
  366. self.transport.loseConnection()
  367. done = DeferredList([d1, d2], consumeErrors=True)
  368. def checkFailures(results):
  369. for success, result in results:
  370. self.assertFalse(success)
  371. result.trap(ConnectionDone)
  372. return done.addCallback(checkFailures)
  373. def test_tooLongKey(self):
  374. """
  375. An error is raised when trying to use a too long key: the called
  376. command returns a L{Deferred} which fails with a L{ClientError}.
  377. """
  378. d1 = self.assertFailure(
  379. self.proto.set(b"a" * 500, b"bar"), ClientError)
  380. d2 = self.assertFailure(self.proto.increment(b"a" * 500), ClientError)
  381. d3 = self.assertFailure(self.proto.get(b"a" * 500), ClientError)
  382. d4 = self.assertFailure(
  383. self.proto.append(b"a" * 500, b"bar"), ClientError)
  384. d5 = self.assertFailure(
  385. self.proto.prepend(b"a" * 500, b"bar"), ClientError)
  386. d6 = self.assertFailure(
  387. self.proto.getMultiple([b"foo", b"a" * 500]), ClientError)
  388. return gatherResults([d1, d2, d3, d4, d5, d6])
  389. def test_invalidCommand(self):
  390. """
  391. When an unknown command is sent directly (not through public API), the
  392. server answers with an B{ERROR} token, and the command fails with
  393. L{NoSuchCommand}.
  394. """
  395. d = self.proto._set(b"egg", b"foo", b"bar", 0, 0, b"")
  396. self.assertEqual(self.transport.value(), b"egg foo 0 0 3\r\nbar\r\n")
  397. self.assertFailure(d, NoSuchCommand)
  398. self.proto.dataReceived(b"ERROR\r\n")
  399. return d
  400. def test_clientError(self):
  401. """
  402. Test the L{ClientError} error: when the server sends a B{CLIENT_ERROR}
  403. token, the originating command fails with L{ClientError}, and the error
  404. contains the text sent by the server.
  405. """
  406. a = b"eggspamm"
  407. d = self.proto.set(b"foo", a)
  408. self.assertEqual(self.transport.value(),
  409. b"set foo 0 0 8\r\neggspamm\r\n")
  410. self.assertFailure(d, ClientError)
  411. def check(err):
  412. self.assertEqual(str(err), repr(b"We don't like egg and spam"))
  413. d.addCallback(check)
  414. self.proto.dataReceived(b"CLIENT_ERROR We don't like egg and spam\r\n")
  415. return d
  416. def test_serverError(self):
  417. """
  418. Test the L{ServerError} error: when the server sends a B{SERVER_ERROR}
  419. token, the originating command fails with L{ServerError}, and the error
  420. contains the text sent by the server.
  421. """
  422. a = b"eggspamm"
  423. d = self.proto.set(b"foo", a)
  424. self.assertEqual(self.transport.value(),
  425. b"set foo 0 0 8\r\neggspamm\r\n")
  426. self.assertFailure(d, ServerError)
  427. def check(err):
  428. self.assertEqual(str(err), repr(b"zomg"))
  429. d.addCallback(check)
  430. self.proto.dataReceived(b"SERVER_ERROR zomg\r\n")
  431. return d
  432. def test_unicodeKey(self):
  433. """
  434. Using a non-string key as argument to commands raises an error.
  435. """
  436. d1 = self.assertFailure(self.proto.set(u"foo", b"bar"), ClientError)
  437. d2 = self.assertFailure(self.proto.increment(u"egg"), ClientError)
  438. d3 = self.assertFailure(self.proto.get(1), ClientError)
  439. d4 = self.assertFailure(self.proto.delete(u"bar"), ClientError)
  440. d5 = self.assertFailure(self.proto.append(u"foo", b"bar"), ClientError)
  441. d6 = self.assertFailure(
  442. self.proto.prepend(u"foo", b"bar"), ClientError)
  443. d7 = self.assertFailure(
  444. self.proto.getMultiple([b"egg", 1]), ClientError)
  445. return gatherResults([d1, d2, d3, d4, d5, d6, d7])
  446. def test_unicodeValue(self):
  447. """
  448. Using a non-string value raises an error.
  449. """
  450. return self.assertFailure(self.proto.set(b"foo", u"bar"), ClientError)
  451. def test_pipelining(self):
  452. """
  453. Multiple requests can be sent subsequently to the server, and the
  454. protocol orders the responses correctly and dispatch to the
  455. corresponding client command.
  456. """
  457. d1 = self.proto.get(b"foo")
  458. d1.addCallback(self.assertEqual, (0, b"bar"))
  459. d2 = self.proto.set(b"bar", b"spamspamspam")
  460. d2.addCallback(self.assertEqual, True)
  461. d3 = self.proto.get(b"egg")
  462. d3.addCallback(self.assertEqual, (0, b"spam"))
  463. self.assertEqual(
  464. self.transport.value(),
  465. b"get foo\r\nset bar 0 0 12\r\nspamspamspam\r\nget egg\r\n")
  466. self.proto.dataReceived(b"VALUE foo 0 3\r\nbar\r\nEND\r\n"
  467. b"STORED\r\n"
  468. b"VALUE egg 0 4\r\nspam\r\nEND\r\n")
  469. return gatherResults([d1, d2, d3])
  470. def test_getInChunks(self):
  471. """
  472. If the value retrieved by a C{get} arrive in chunks, the protocol
  473. is able to reconstruct it and to produce the good value.
  474. """
  475. d = self.proto.get(b"foo")
  476. d.addCallback(self.assertEqual, (0, b"0123456789"))
  477. self.assertEqual(self.transport.value(), b"get foo\r\n")
  478. self.proto.dataReceived(b"VALUE foo 0 10\r\n0123456")
  479. self.proto.dataReceived(b"789")
  480. self.proto.dataReceived(b"\r\nEND")
  481. self.proto.dataReceived(b"\r\n")
  482. return d
  483. def test_append(self):
  484. """
  485. L{MemCacheProtocol.append} behaves like a L{MemCacheProtocol.set}
  486. method: it returns a L{Deferred} which is called back with C{True} when
  487. the operation succeeds.
  488. """
  489. return self._test(
  490. self.proto.append(b"foo", b"bar"),
  491. b"append foo 0 0 3\r\nbar\r\n", b"STORED\r\n", True)
  492. def test_prepend(self):
  493. """
  494. L{MemCacheProtocol.prepend} behaves like a L{MemCacheProtocol.set}
  495. method: it returns a L{Deferred} which is called back with C{True} when
  496. the operation succeeds.
  497. """
  498. return self._test(
  499. self.proto.prepend(b"foo", b"bar"),
  500. b"prepend foo 0 0 3\r\nbar\r\n", b"STORED\r\n", True)
  501. def test_gets(self):
  502. """
  503. L{MemCacheProtocol.get} handles an additional cas result when
  504. C{withIdentifier} is C{True} and forward it in the resulting
  505. L{Deferred}.
  506. """
  507. return self._test(
  508. self.proto.get(b"foo", True), b"gets foo\r\n",
  509. b"VALUE foo 0 3 1234\r\nbar\r\nEND\r\n", (0, b"1234", b"bar"))
  510. def test_emptyGets(self):
  511. """
  512. Test getting a non-available key with gets: it succeeds but return
  513. L{None} as value, C{0} as flag and an empty cas value.
  514. """
  515. return self._test(
  516. self.proto.get(b"foo", True), b"gets foo\r\n",
  517. b"END\r\n", (0, b"", None))
  518. def test_getsMultiple(self):
  519. """
  520. L{MemCacheProtocol.getMultiple} handles an additional cas field in the
  521. returned tuples if C{withIdentifier} is C{True}.
  522. """
  523. return self._test(
  524. self.proto.getMultiple([b"foo", b"bar"], True),
  525. b"gets foo bar\r\n",
  526. b"VALUE foo 0 3 1234\r\negg\r\n"
  527. b"VALUE bar 0 4 2345\r\nspam\r\nEND\r\n",
  528. {b'bar': (0, b'2345', b'spam'), b'foo': (0, b'1234', b'egg')})
  529. def test_getsMultipleIterableKeys(self):
  530. """
  531. L{MemCacheProtocol.getMultiple} accepts any iterable of keys.
  532. """
  533. return self._test(
  534. self.proto.getMultiple(iter([b"foo", b"bar"]), True),
  535. b"gets foo bar\r\n",
  536. b"VALUE foo 0 3 1234\r\negg\r\n"
  537. b"VALUE bar 0 4 2345\r\nspam\r\nEND\r\n",
  538. {b'bar': (0, b'2345', b'spam'), b'foo': (0, b'1234', b'egg')})
  539. def test_getsMultipleWithEmpty(self):
  540. """
  541. When getting a non-available key with L{MemCacheProtocol.getMultiple}
  542. when C{withIdentifier} is C{True}, the other keys are retrieved
  543. correctly, and the non-available key gets a tuple of C{0} as flag,
  544. L{None} as value, and an empty cas value.
  545. """
  546. return self._test(
  547. self.proto.getMultiple([b"foo", b"bar"], True),
  548. b"gets foo bar\r\n",
  549. b"VALUE foo 0 3 1234\r\negg\r\nEND\r\n",
  550. {b'bar': (0, b'', None), b'foo': (0, b'1234', b'egg')})
  551. def test_checkAndSet(self):
  552. """
  553. L{MemCacheProtocol.checkAndSet} passes an additional cas identifier
  554. that the server handles to check if the data has to be updated.
  555. """
  556. return self._test(
  557. self.proto.checkAndSet(b"foo", b"bar", cas=b"1234"),
  558. b"cas foo 0 0 3 1234\r\nbar\r\n", b"STORED\r\n", True)
  559. def test_casUnknowKey(self):
  560. """
  561. When L{MemCacheProtocol.checkAndSet} response is C{EXISTS}, the
  562. resulting L{Deferred} fires with C{False}.
  563. """
  564. return self._test(
  565. self.proto.checkAndSet(b"foo", b"bar", cas=b"1234"),
  566. b"cas foo 0 0 3 1234\r\nbar\r\n", b"EXISTS\r\n", False)
  567. class CommandFailureTests(CommandMixin, TestCase):
  568. """
  569. Tests for correct failure of commands on a disconnected
  570. L{MemCacheProtocol}.
  571. """
  572. def setUp(self):
  573. """
  574. Create a disconnected memcache client, using a deterministic clock.
  575. """
  576. self.proto = MemCacheProtocol()
  577. self.clock = Clock()
  578. self.proto.callLater = self.clock.callLater
  579. self.transport = StringTransportWithDisconnection()
  580. self.transport.protocol = self.proto
  581. self.proto.makeConnection(self.transport)
  582. self.transport.loseConnection()
  583. def _test(self, d, send, recv, result):
  584. """
  585. Implementation of C{_test} which checks that the command fails with
  586. C{RuntimeError} because the transport is disconnected. All the
  587. parameters except C{d} are ignored.
  588. """
  589. return self.assertFailure(d, RuntimeError)