Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_memcache.py 25KB

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