test_appservice.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from tests import unittest
  16. from twisted.internet import defer
  17. from tests.utils import setup_test_homeserver
  18. from synapse.appservice import ApplicationService, ApplicationServiceState
  19. from synapse.server import HomeServer
  20. from synapse.storage.appservice import (
  21. ApplicationServiceStore, ApplicationServiceTransactionStore
  22. )
  23. import json
  24. import os
  25. import yaml
  26. from mock import Mock
  27. from tests.utils import SQLiteMemoryDbPool, MockClock
  28. class ApplicationServiceStoreTestCase(unittest.TestCase):
  29. @defer.inlineCallbacks
  30. def setUp(self):
  31. self.as_yaml_files = []
  32. config = Mock(
  33. app_service_config_files=self.as_yaml_files
  34. )
  35. hs = yield setup_test_homeserver(config=config)
  36. self.as_token = "token1"
  37. self.as_url = "some_url"
  38. self._add_appservice(self.as_token, self.as_url, "some_hs_token", "bob")
  39. self._add_appservice("token2", "some_url", "some_hs_token", "bob")
  40. self._add_appservice("token3", "some_url", "some_hs_token", "bob")
  41. # must be done after inserts
  42. self.store = ApplicationServiceStore(hs)
  43. def tearDown(self):
  44. # TODO: suboptimal that we need to create files for tests!
  45. for f in self.as_yaml_files:
  46. try:
  47. os.remove(f)
  48. except:
  49. pass
  50. def _add_appservice(self, as_token, url, hs_token, sender):
  51. as_yaml = dict(url=url, as_token=as_token, hs_token=hs_token,
  52. sender_localpart=sender, namespaces={})
  53. # use the token as the filename
  54. with open(as_token, 'w') as outfile:
  55. outfile.write(yaml.dump(as_yaml))
  56. self.as_yaml_files.append(as_token)
  57. @defer.inlineCallbacks
  58. def test_retrieve_unknown_service_token(self):
  59. service = yield self.store.get_app_service_by_token("invalid_token")
  60. self.assertEquals(service, None)
  61. @defer.inlineCallbacks
  62. def test_retrieval_of_service(self):
  63. stored_service = yield self.store.get_app_service_by_token(
  64. self.as_token
  65. )
  66. self.assertEquals(stored_service.token, self.as_token)
  67. self.assertEquals(stored_service.url, self.as_url)
  68. self.assertEquals(
  69. stored_service.namespaces[ApplicationService.NS_ALIASES],
  70. []
  71. )
  72. self.assertEquals(
  73. stored_service.namespaces[ApplicationService.NS_ROOMS],
  74. []
  75. )
  76. self.assertEquals(
  77. stored_service.namespaces[ApplicationService.NS_USERS],
  78. []
  79. )
  80. @defer.inlineCallbacks
  81. def test_retrieval_of_all_services(self):
  82. services = yield self.store.get_app_services()
  83. self.assertEquals(len(services), 3)
  84. class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
  85. @defer.inlineCallbacks
  86. def setUp(self):
  87. self.as_yaml_files = []
  88. config = Mock(
  89. app_service_config_files=self.as_yaml_files
  90. )
  91. hs = yield setup_test_homeserver(config=config)
  92. self.db_pool = hs.get_db_pool()
  93. self.as_list = [
  94. {
  95. "token": "token1",
  96. "url": "https://matrix-as.org",
  97. "id": "token1"
  98. },
  99. {
  100. "token": "alpha_tok",
  101. "url": "https://alpha.com",
  102. "id": "alpha_tok"
  103. },
  104. {
  105. "token": "beta_tok",
  106. "url": "https://beta.com",
  107. "id": "beta_tok"
  108. },
  109. {
  110. "token": "delta_tok",
  111. "url": "https://delta.com",
  112. "id": "delta_tok"
  113. },
  114. ]
  115. for s in self.as_list:
  116. yield self._add_service(s["url"], s["token"])
  117. self.as_yaml_files = []
  118. self.store = TestTransactionStore(hs)
  119. def _add_service(self, url, as_token):
  120. as_yaml = dict(url=url, as_token=as_token, hs_token="something",
  121. sender_localpart="a_sender", namespaces={})
  122. # use the token as the filename
  123. with open(as_token, 'w') as outfile:
  124. outfile.write(yaml.dump(as_yaml))
  125. self.as_yaml_files.append(as_token)
  126. def _set_state(self, id, state, txn=None):
  127. return self.db_pool.runQuery(
  128. "INSERT INTO application_services_state(as_id, state, last_txn) "
  129. "VALUES(?,?,?)",
  130. (id, state, txn)
  131. )
  132. def _insert_txn(self, as_id, txn_id, events):
  133. return self.db_pool.runQuery(
  134. "INSERT INTO application_services_txns(as_id, txn_id, event_ids) "
  135. "VALUES(?,?,?)",
  136. (as_id, txn_id, json.dumps([e.event_id for e in events]))
  137. )
  138. def _set_last_txn(self, as_id, txn_id):
  139. return self.db_pool.runQuery(
  140. "INSERT INTO application_services_state(as_id, last_txn, state) "
  141. "VALUES(?,?,?)",
  142. (as_id, txn_id, ApplicationServiceState.UP)
  143. )
  144. @defer.inlineCallbacks
  145. def test_get_appservice_state_none(self):
  146. service = Mock(id=999)
  147. state = yield self.store.get_appservice_state(service)
  148. self.assertEquals(None, state)
  149. @defer.inlineCallbacks
  150. def test_get_appservice_state_up(self):
  151. yield self._set_state(
  152. self.as_list[0]["id"], ApplicationServiceState.UP
  153. )
  154. service = Mock(id=self.as_list[0]["id"])
  155. state = yield self.store.get_appservice_state(service)
  156. self.assertEquals(ApplicationServiceState.UP, state)
  157. @defer.inlineCallbacks
  158. def test_get_appservice_state_down(self):
  159. yield self._set_state(
  160. self.as_list[0]["id"], ApplicationServiceState.UP
  161. )
  162. yield self._set_state(
  163. self.as_list[1]["id"], ApplicationServiceState.DOWN
  164. )
  165. yield self._set_state(
  166. self.as_list[2]["id"], ApplicationServiceState.DOWN
  167. )
  168. service = Mock(id=self.as_list[1]["id"])
  169. state = yield self.store.get_appservice_state(service)
  170. self.assertEquals(ApplicationServiceState.DOWN, state)
  171. @defer.inlineCallbacks
  172. def test_get_appservices_by_state_none(self):
  173. services = yield self.store.get_appservices_by_state(
  174. ApplicationServiceState.DOWN
  175. )
  176. self.assertEquals(0, len(services))
  177. @defer.inlineCallbacks
  178. def test_set_appservices_state_down(self):
  179. service = Mock(id=self.as_list[1]["id"])
  180. yield self.store.set_appservice_state(
  181. service,
  182. ApplicationServiceState.DOWN
  183. )
  184. rows = yield self.db_pool.runQuery(
  185. "SELECT as_id FROM application_services_state WHERE state=?",
  186. (ApplicationServiceState.DOWN,)
  187. )
  188. self.assertEquals(service.id, rows[0][0])
  189. @defer.inlineCallbacks
  190. def test_set_appservices_state_multiple_up(self):
  191. service = Mock(id=self.as_list[1]["id"])
  192. yield self.store.set_appservice_state(
  193. service,
  194. ApplicationServiceState.UP
  195. )
  196. yield self.store.set_appservice_state(
  197. service,
  198. ApplicationServiceState.DOWN
  199. )
  200. yield self.store.set_appservice_state(
  201. service,
  202. ApplicationServiceState.UP
  203. )
  204. rows = yield self.db_pool.runQuery(
  205. "SELECT as_id FROM application_services_state WHERE state=?",
  206. (ApplicationServiceState.UP,)
  207. )
  208. self.assertEquals(service.id, rows[0][0])
  209. @defer.inlineCallbacks
  210. def test_create_appservice_txn_first(self):
  211. service = Mock(id=self.as_list[0]["id"])
  212. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  213. txn = yield self.store.create_appservice_txn(service, events)
  214. self.assertEquals(txn.id, 1)
  215. self.assertEquals(txn.events, events)
  216. self.assertEquals(txn.service, service)
  217. @defer.inlineCallbacks
  218. def test_create_appservice_txn_older_last_txn(self):
  219. service = Mock(id=self.as_list[0]["id"])
  220. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  221. yield self._set_last_txn(service.id, 9643) # AS is falling behind
  222. yield self._insert_txn(service.id, 9644, events)
  223. yield self._insert_txn(service.id, 9645, events)
  224. txn = yield self.store.create_appservice_txn(service, events)
  225. self.assertEquals(txn.id, 9646)
  226. self.assertEquals(txn.events, events)
  227. self.assertEquals(txn.service, service)
  228. @defer.inlineCallbacks
  229. def test_create_appservice_txn_up_to_date_last_txn(self):
  230. service = Mock(id=self.as_list[0]["id"])
  231. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  232. yield self._set_last_txn(service.id, 9643)
  233. txn = yield self.store.create_appservice_txn(service, events)
  234. self.assertEquals(txn.id, 9644)
  235. self.assertEquals(txn.events, events)
  236. self.assertEquals(txn.service, service)
  237. @defer.inlineCallbacks
  238. def test_create_appservice_txn_up_fuzzing(self):
  239. service = Mock(id=self.as_list[0]["id"])
  240. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  241. yield self._set_last_txn(service.id, 9643)
  242. # dump in rows with higher IDs to make sure the queries aren't wrong.
  243. yield self._set_last_txn(self.as_list[1]["id"], 119643)
  244. yield self._set_last_txn(self.as_list[2]["id"], 9)
  245. yield self._set_last_txn(self.as_list[3]["id"], 9643)
  246. yield self._insert_txn(self.as_list[1]["id"], 119644, events)
  247. yield self._insert_txn(self.as_list[1]["id"], 119645, events)
  248. yield self._insert_txn(self.as_list[1]["id"], 119646, events)
  249. yield self._insert_txn(self.as_list[2]["id"], 10, events)
  250. yield self._insert_txn(self.as_list[3]["id"], 9643, events)
  251. txn = yield self.store.create_appservice_txn(service, events)
  252. self.assertEquals(txn.id, 9644)
  253. self.assertEquals(txn.events, events)
  254. self.assertEquals(txn.service, service)
  255. @defer.inlineCallbacks
  256. def test_complete_appservice_txn_first_txn(self):
  257. service = Mock(id=self.as_list[0]["id"])
  258. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  259. txn_id = 1
  260. yield self._insert_txn(service.id, txn_id, events)
  261. yield self.store.complete_appservice_txn(txn_id=txn_id, service=service)
  262. res = yield self.db_pool.runQuery(
  263. "SELECT last_txn FROM application_services_state WHERE as_id=?",
  264. (service.id,)
  265. )
  266. self.assertEquals(1, len(res))
  267. self.assertEquals(txn_id, res[0][0])
  268. res = yield self.db_pool.runQuery(
  269. "SELECT * FROM application_services_txns WHERE txn_id=?",
  270. (txn_id,)
  271. )
  272. self.assertEquals(0, len(res))
  273. @defer.inlineCallbacks
  274. def test_complete_appservice_txn_existing_in_state_table(self):
  275. service = Mock(id=self.as_list[0]["id"])
  276. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  277. txn_id = 5
  278. yield self._set_last_txn(service.id, 4)
  279. yield self._insert_txn(service.id, txn_id, events)
  280. yield self.store.complete_appservice_txn(txn_id=txn_id, service=service)
  281. res = yield self.db_pool.runQuery(
  282. "SELECT last_txn, state FROM application_services_state WHERE "
  283. "as_id=?",
  284. (service.id,)
  285. )
  286. self.assertEquals(1, len(res))
  287. self.assertEquals(txn_id, res[0][0])
  288. self.assertEquals(ApplicationServiceState.UP, res[0][1])
  289. res = yield self.db_pool.runQuery(
  290. "SELECT * FROM application_services_txns WHERE txn_id=?",
  291. (txn_id,)
  292. )
  293. self.assertEquals(0, len(res))
  294. @defer.inlineCallbacks
  295. def test_get_oldest_unsent_txn_none(self):
  296. service = Mock(id=self.as_list[0]["id"])
  297. txn = yield self.store.get_oldest_unsent_txn(service)
  298. self.assertEquals(None, txn)
  299. @defer.inlineCallbacks
  300. def test_get_oldest_unsent_txn(self):
  301. service = Mock(id=self.as_list[0]["id"])
  302. events = [Mock(event_id="e1"), Mock(event_id="e2")]
  303. other_events = [Mock(event_id="e5"), Mock(event_id="e6")]
  304. # we aren't testing store._base stuff here, so mock this out
  305. self.store._get_events_txn = Mock(return_value=events)
  306. yield self._insert_txn(self.as_list[1]["id"], 9, other_events)
  307. yield self._insert_txn(service.id, 10, events)
  308. yield self._insert_txn(service.id, 11, other_events)
  309. yield self._insert_txn(service.id, 12, other_events)
  310. txn = yield self.store.get_oldest_unsent_txn(service)
  311. self.assertEquals(service, txn.service)
  312. self.assertEquals(10, txn.id)
  313. self.assertEquals(events, txn.events)
  314. @defer.inlineCallbacks
  315. def test_get_appservices_by_state_single(self):
  316. yield self._set_state(
  317. self.as_list[0]["id"], ApplicationServiceState.DOWN
  318. )
  319. yield self._set_state(
  320. self.as_list[1]["id"], ApplicationServiceState.UP
  321. )
  322. services = yield self.store.get_appservices_by_state(
  323. ApplicationServiceState.DOWN
  324. )
  325. self.assertEquals(1, len(services))
  326. self.assertEquals(self.as_list[0]["id"], services[0].id)
  327. @defer.inlineCallbacks
  328. def test_get_appservices_by_state_multiple(self):
  329. yield self._set_state(
  330. self.as_list[0]["id"], ApplicationServiceState.DOWN
  331. )
  332. yield self._set_state(
  333. self.as_list[1]["id"], ApplicationServiceState.UP
  334. )
  335. yield self._set_state(
  336. self.as_list[2]["id"], ApplicationServiceState.DOWN
  337. )
  338. yield self._set_state(
  339. self.as_list[3]["id"], ApplicationServiceState.UP
  340. )
  341. services = yield self.store.get_appservices_by_state(
  342. ApplicationServiceState.DOWN
  343. )
  344. self.assertEquals(2, len(services))
  345. self.assertEquals(
  346. set([self.as_list[2]["id"], self.as_list[0]["id"]]),
  347. set([services[0].id, services[1].id])
  348. )
  349. # required for ApplicationServiceTransactionStoreTestCase tests
  350. class TestTransactionStore(ApplicationServiceTransactionStore,
  351. ApplicationServiceStore):
  352. def __init__(self, hs):
  353. super(TestTransactionStore, self).__init__(hs)