test_state.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  1. # Copyright 2018-2021 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import logging
  15. from frozendict import frozendict
  16. from synapse.api.constants import EventTypes, Membership
  17. from synapse.api.room_versions import RoomVersions
  18. from synapse.storage.state import StateFilter
  19. from synapse.types import RoomID, UserID
  20. from tests.unittest import HomeserverTestCase, TestCase
  21. logger = logging.getLogger(__name__)
  22. class StateStoreTestCase(HomeserverTestCase):
  23. def prepare(self, reactor, clock, hs):
  24. self.store = hs.get_datastores().main
  25. self.storage = hs.get_storage_controllers()
  26. self.state_datastore = self.storage.state.stores.state
  27. self.event_builder_factory = hs.get_event_builder_factory()
  28. self.event_creation_handler = hs.get_event_creation_handler()
  29. self.u_alice = UserID.from_string("@alice:test")
  30. self.u_bob = UserID.from_string("@bob:test")
  31. self.room = RoomID.from_string("!abc123:test")
  32. self.get_success(
  33. self.store.store_room(
  34. self.room.to_string(),
  35. room_creator_user_id="@creator:text",
  36. is_public=True,
  37. room_version=RoomVersions.V1,
  38. )
  39. )
  40. def inject_state_event(self, room, sender, typ, state_key, content):
  41. builder = self.event_builder_factory.for_room_version(
  42. RoomVersions.V1,
  43. {
  44. "type": typ,
  45. "sender": sender.to_string(),
  46. "state_key": state_key,
  47. "room_id": room.to_string(),
  48. "content": content,
  49. },
  50. )
  51. event, context = self.get_success(
  52. self.event_creation_handler.create_new_client_event(builder)
  53. )
  54. self.get_success(self.storage.persistence.persist_event(event, context))
  55. return event
  56. def assertStateMapEqual(self, s1, s2):
  57. for t in s1:
  58. # just compare event IDs for simplicity
  59. self.assertEqual(s1[t].event_id, s2[t].event_id)
  60. self.assertEqual(len(s1), len(s2))
  61. def test_get_state_groups_ids(self):
  62. e1 = self.inject_state_event(self.room, self.u_alice, EventTypes.Create, "", {})
  63. e2 = self.inject_state_event(
  64. self.room, self.u_alice, EventTypes.Name, "", {"name": "test room"}
  65. )
  66. state_group_map = self.get_success(
  67. self.storage.state.get_state_groups_ids(self.room, [e2.event_id])
  68. )
  69. self.assertEqual(len(state_group_map), 1)
  70. state_map = list(state_group_map.values())[0]
  71. self.assertDictEqual(
  72. state_map,
  73. {(EventTypes.Create, ""): e1.event_id, (EventTypes.Name, ""): e2.event_id},
  74. )
  75. def test_get_state_groups(self):
  76. e1 = self.inject_state_event(self.room, self.u_alice, EventTypes.Create, "", {})
  77. e2 = self.inject_state_event(
  78. self.room, self.u_alice, EventTypes.Name, "", {"name": "test room"}
  79. )
  80. state_group_map = self.get_success(
  81. self.storage.state.get_state_groups(self.room, [e2.event_id])
  82. )
  83. self.assertEqual(len(state_group_map), 1)
  84. state_list = list(state_group_map.values())[0]
  85. self.assertEqual({ev.event_id for ev in state_list}, {e1.event_id, e2.event_id})
  86. def test_get_state_for_event(self):
  87. # this defaults to a linear DAG as each new injection defaults to whatever
  88. # forward extremities are currently in the DB for this room.
  89. e1 = self.inject_state_event(self.room, self.u_alice, EventTypes.Create, "", {})
  90. e2 = self.inject_state_event(
  91. self.room, self.u_alice, EventTypes.Name, "", {"name": "test room"}
  92. )
  93. e3 = self.inject_state_event(
  94. self.room,
  95. self.u_alice,
  96. EventTypes.Member,
  97. self.u_alice.to_string(),
  98. {"membership": Membership.JOIN},
  99. )
  100. e4 = self.inject_state_event(
  101. self.room,
  102. self.u_bob,
  103. EventTypes.Member,
  104. self.u_bob.to_string(),
  105. {"membership": Membership.JOIN},
  106. )
  107. e5 = self.inject_state_event(
  108. self.room,
  109. self.u_bob,
  110. EventTypes.Member,
  111. self.u_bob.to_string(),
  112. {"membership": Membership.LEAVE},
  113. )
  114. # check we get the full state as of the final event
  115. state = self.get_success(self.storage.state.get_state_for_event(e5.event_id))
  116. self.assertIsNotNone(e4)
  117. self.assertStateMapEqual(
  118. {
  119. (e1.type, e1.state_key): e1,
  120. (e2.type, e2.state_key): e2,
  121. (e3.type, e3.state_key): e3,
  122. # e4 is overwritten by e5
  123. (e5.type, e5.state_key): e5,
  124. },
  125. state,
  126. )
  127. # check we can filter to the m.room.name event (with a '' state key)
  128. state = self.get_success(
  129. self.storage.state.get_state_for_event(
  130. e5.event_id, StateFilter.from_types([(EventTypes.Name, "")])
  131. )
  132. )
  133. self.assertStateMapEqual({(e2.type, e2.state_key): e2}, state)
  134. # check we can filter to the m.room.name event (with a wildcard None state key)
  135. state = self.get_success(
  136. self.storage.state.get_state_for_event(
  137. e5.event_id, StateFilter.from_types([(EventTypes.Name, None)])
  138. )
  139. )
  140. self.assertStateMapEqual({(e2.type, e2.state_key): e2}, state)
  141. # check we can grab the m.room.member events (with a wildcard None state key)
  142. state = self.get_success(
  143. self.storage.state.get_state_for_event(
  144. e5.event_id, StateFilter.from_types([(EventTypes.Member, None)])
  145. )
  146. )
  147. self.assertStateMapEqual(
  148. {(e3.type, e3.state_key): e3, (e5.type, e5.state_key): e5}, state
  149. )
  150. # check we can grab a specific room member without filtering out the
  151. # other event types
  152. state = self.get_success(
  153. self.storage.state.get_state_for_event(
  154. e5.event_id,
  155. state_filter=StateFilter(
  156. types=frozendict(
  157. {EventTypes.Member: frozenset({self.u_alice.to_string()})}
  158. ),
  159. include_others=True,
  160. ),
  161. )
  162. )
  163. self.assertStateMapEqual(
  164. {
  165. (e1.type, e1.state_key): e1,
  166. (e2.type, e2.state_key): e2,
  167. (e3.type, e3.state_key): e3,
  168. },
  169. state,
  170. )
  171. # check that we can grab everything except members
  172. state = self.get_success(
  173. self.storage.state.get_state_for_event(
  174. e5.event_id,
  175. state_filter=StateFilter(
  176. types=frozendict({EventTypes.Member: frozenset()}),
  177. include_others=True,
  178. ),
  179. )
  180. )
  181. self.assertStateMapEqual(
  182. {(e1.type, e1.state_key): e1, (e2.type, e2.state_key): e2}, state
  183. )
  184. #######################################################
  185. # _get_state_for_group_using_cache tests against a full cache
  186. #######################################################
  187. room_id = self.room.to_string()
  188. group_ids = self.get_success(
  189. self.storage.state.get_state_groups_ids(room_id, [e5.event_id])
  190. )
  191. group = list(group_ids.keys())[0]
  192. # test _get_state_for_group_using_cache correctly filters out members
  193. # with types=[]
  194. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  195. self.state_datastore._state_group_cache,
  196. group,
  197. state_filter=StateFilter(
  198. types=frozendict({EventTypes.Member: frozenset()}), include_others=True
  199. ),
  200. )
  201. self.assertEqual(is_all, True)
  202. self.assertDictEqual(
  203. {
  204. (e1.type, e1.state_key): e1.event_id,
  205. (e2.type, e2.state_key): e2.event_id,
  206. },
  207. state_dict,
  208. )
  209. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  210. self.state_datastore._state_group_members_cache,
  211. group,
  212. state_filter=StateFilter(
  213. types=frozendict({EventTypes.Member: frozenset()}), include_others=True
  214. ),
  215. )
  216. self.assertEqual(is_all, True)
  217. self.assertDictEqual({}, state_dict)
  218. # test _get_state_for_group_using_cache correctly filters in members
  219. # with wildcard types
  220. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  221. self.state_datastore._state_group_cache,
  222. group,
  223. state_filter=StateFilter(
  224. types=frozendict({EventTypes.Member: None}), include_others=True
  225. ),
  226. )
  227. self.assertEqual(is_all, True)
  228. self.assertDictEqual(
  229. {
  230. (e1.type, e1.state_key): e1.event_id,
  231. (e2.type, e2.state_key): e2.event_id,
  232. },
  233. state_dict,
  234. )
  235. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  236. self.state_datastore._state_group_members_cache,
  237. group,
  238. state_filter=StateFilter(
  239. types=frozendict({EventTypes.Member: None}), include_others=True
  240. ),
  241. )
  242. self.assertEqual(is_all, True)
  243. self.assertDictEqual(
  244. {
  245. (e3.type, e3.state_key): e3.event_id,
  246. # e4 is overwritten by e5
  247. (e5.type, e5.state_key): e5.event_id,
  248. },
  249. state_dict,
  250. )
  251. # test _get_state_for_group_using_cache correctly filters in members
  252. # with specific types
  253. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  254. self.state_datastore._state_group_cache,
  255. group,
  256. state_filter=StateFilter(
  257. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  258. include_others=True,
  259. ),
  260. )
  261. self.assertEqual(is_all, True)
  262. self.assertDictEqual(
  263. {
  264. (e1.type, e1.state_key): e1.event_id,
  265. (e2.type, e2.state_key): e2.event_id,
  266. },
  267. state_dict,
  268. )
  269. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  270. self.state_datastore._state_group_members_cache,
  271. group,
  272. state_filter=StateFilter(
  273. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  274. include_others=True,
  275. ),
  276. )
  277. self.assertEqual(is_all, True)
  278. self.assertDictEqual({(e5.type, e5.state_key): e5.event_id}, state_dict)
  279. # test _get_state_for_group_using_cache correctly filters in members
  280. # with specific types
  281. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  282. self.state_datastore._state_group_members_cache,
  283. group,
  284. state_filter=StateFilter(
  285. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  286. include_others=False,
  287. ),
  288. )
  289. self.assertEqual(is_all, True)
  290. self.assertDictEqual({(e5.type, e5.state_key): e5.event_id}, state_dict)
  291. #######################################################
  292. # deliberately remove e2 (room name) from the _state_group_cache
  293. cache_entry = self.state_datastore._state_group_cache.get(group)
  294. state_dict_ids = cache_entry.value
  295. self.assertEqual(cache_entry.full, True)
  296. self.assertEqual(cache_entry.known_absent, set())
  297. self.assertDictEqual(
  298. state_dict_ids,
  299. {
  300. (e1.type, e1.state_key): e1.event_id,
  301. (e2.type, e2.state_key): e2.event_id,
  302. },
  303. )
  304. state_dict_ids.pop((e2.type, e2.state_key))
  305. self.state_datastore._state_group_cache.invalidate(group)
  306. self.state_datastore._state_group_cache.update(
  307. sequence=self.state_datastore._state_group_cache.sequence,
  308. key=group,
  309. value=state_dict_ids,
  310. # list fetched keys so it knows it's partial
  311. fetched_keys=((e1.type, e1.state_key),),
  312. )
  313. cache_entry = self.state_datastore._state_group_cache.get(group)
  314. state_dict_ids = cache_entry.value
  315. self.assertEqual(cache_entry.full, False)
  316. self.assertEqual(cache_entry.known_absent, set())
  317. self.assertDictEqual(state_dict_ids, {})
  318. ############################################
  319. # test that things work with a partial cache
  320. # test _get_state_for_group_using_cache correctly filters out members
  321. # with types=[]
  322. room_id = self.room.to_string()
  323. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  324. self.state_datastore._state_group_cache,
  325. group,
  326. state_filter=StateFilter(
  327. types=frozendict({EventTypes.Member: frozenset()}), include_others=True
  328. ),
  329. )
  330. self.assertEqual(is_all, False)
  331. self.assertDictEqual({}, state_dict)
  332. room_id = self.room.to_string()
  333. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  334. self.state_datastore._state_group_members_cache,
  335. group,
  336. state_filter=StateFilter(
  337. types=frozendict({EventTypes.Member: frozenset()}), include_others=True
  338. ),
  339. )
  340. self.assertEqual(is_all, True)
  341. self.assertDictEqual({}, state_dict)
  342. # test _get_state_for_group_using_cache correctly filters in members
  343. # wildcard types
  344. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  345. self.state_datastore._state_group_cache,
  346. group,
  347. state_filter=StateFilter(
  348. types=frozendict({EventTypes.Member: None}), include_others=True
  349. ),
  350. )
  351. self.assertEqual(is_all, False)
  352. self.assertDictEqual({}, state_dict)
  353. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  354. self.state_datastore._state_group_members_cache,
  355. group,
  356. state_filter=StateFilter(
  357. types=frozendict({EventTypes.Member: None}), include_others=True
  358. ),
  359. )
  360. self.assertEqual(is_all, True)
  361. self.assertDictEqual(
  362. {
  363. (e3.type, e3.state_key): e3.event_id,
  364. (e5.type, e5.state_key): e5.event_id,
  365. },
  366. state_dict,
  367. )
  368. # test _get_state_for_group_using_cache correctly filters in members
  369. # with specific types
  370. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  371. self.state_datastore._state_group_cache,
  372. group,
  373. state_filter=StateFilter(
  374. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  375. include_others=True,
  376. ),
  377. )
  378. self.assertEqual(is_all, False)
  379. self.assertDictEqual({}, state_dict)
  380. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  381. self.state_datastore._state_group_members_cache,
  382. group,
  383. state_filter=StateFilter(
  384. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  385. include_others=True,
  386. ),
  387. )
  388. self.assertEqual(is_all, True)
  389. self.assertDictEqual({(e5.type, e5.state_key): e5.event_id}, state_dict)
  390. # test _get_state_for_group_using_cache correctly filters in members
  391. # with specific types
  392. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  393. self.state_datastore._state_group_cache,
  394. group,
  395. state_filter=StateFilter(
  396. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  397. include_others=False,
  398. ),
  399. )
  400. self.assertEqual(is_all, False)
  401. self.assertDictEqual({}, state_dict)
  402. (state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
  403. self.state_datastore._state_group_members_cache,
  404. group,
  405. state_filter=StateFilter(
  406. types=frozendict({EventTypes.Member: frozenset({e5.state_key})}),
  407. include_others=False,
  408. ),
  409. )
  410. self.assertEqual(is_all, True)
  411. self.assertDictEqual({(e5.type, e5.state_key): e5.event_id}, state_dict)
  412. class StateFilterDifferenceTestCase(TestCase):
  413. def assert_difference(
  414. self, minuend: StateFilter, subtrahend: StateFilter, expected: StateFilter
  415. ):
  416. self.assertEqual(
  417. minuend.approx_difference(subtrahend),
  418. expected,
  419. f"StateFilter difference not correct:\n\n\t{minuend!r}\nminus\n\t{subtrahend!r}\nwas\n\t{minuend.approx_difference(subtrahend)}\nexpected\n\t{expected}",
  420. )
  421. def test_state_filter_difference_no_include_other_minus_no_include_other(self):
  422. """
  423. Tests the StateFilter.approx_difference method
  424. where, in a.approx_difference(b), both a and b do not have the
  425. include_others flag set.
  426. """
  427. # (wildcard on state keys) - (wildcard on state keys):
  428. self.assert_difference(
  429. StateFilter.freeze(
  430. {EventTypes.Member: None, EventTypes.Create: None},
  431. include_others=False,
  432. ),
  433. StateFilter.freeze(
  434. {EventTypes.Member: None, EventTypes.CanonicalAlias: None},
  435. include_others=False,
  436. ),
  437. StateFilter.freeze({EventTypes.Create: None}, include_others=False),
  438. )
  439. # (wildcard on state keys) - (specific state keys)
  440. # This one is an over-approximation because we can't represent
  441. # 'all state keys except a few named examples'
  442. self.assert_difference(
  443. StateFilter.freeze({EventTypes.Member: None}, include_others=False),
  444. StateFilter.freeze(
  445. {EventTypes.Member: {"@wombat:spqr"}},
  446. include_others=False,
  447. ),
  448. StateFilter.freeze({EventTypes.Member: None}, include_others=False),
  449. )
  450. # (wildcard on state keys) - (no state keys)
  451. self.assert_difference(
  452. StateFilter.freeze(
  453. {EventTypes.Member: None},
  454. include_others=False,
  455. ),
  456. StateFilter.freeze(
  457. {
  458. EventTypes.Member: set(),
  459. },
  460. include_others=False,
  461. ),
  462. StateFilter.freeze(
  463. {EventTypes.Member: None},
  464. include_others=False,
  465. ),
  466. )
  467. # (specific state keys) - (wildcard on state keys):
  468. self.assert_difference(
  469. StateFilter.freeze(
  470. {
  471. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  472. EventTypes.CanonicalAlias: {""},
  473. },
  474. include_others=False,
  475. ),
  476. StateFilter.freeze(
  477. {EventTypes.Member: None},
  478. include_others=False,
  479. ),
  480. StateFilter.freeze(
  481. {EventTypes.CanonicalAlias: {""}},
  482. include_others=False,
  483. ),
  484. )
  485. # (specific state keys) - (specific state keys)
  486. self.assert_difference(
  487. StateFilter.freeze(
  488. {
  489. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  490. EventTypes.CanonicalAlias: {""},
  491. },
  492. include_others=False,
  493. ),
  494. StateFilter.freeze(
  495. {
  496. EventTypes.Member: {"@wombat:spqr"},
  497. },
  498. include_others=False,
  499. ),
  500. StateFilter.freeze(
  501. {
  502. EventTypes.Member: {"@spqr:spqr"},
  503. EventTypes.CanonicalAlias: {""},
  504. },
  505. include_others=False,
  506. ),
  507. )
  508. # (specific state keys) - (no state keys)
  509. self.assert_difference(
  510. StateFilter.freeze(
  511. {
  512. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  513. EventTypes.CanonicalAlias: {""},
  514. },
  515. include_others=False,
  516. ),
  517. StateFilter.freeze(
  518. {
  519. EventTypes.Member: set(),
  520. },
  521. include_others=False,
  522. ),
  523. StateFilter.freeze(
  524. {
  525. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  526. EventTypes.CanonicalAlias: {""},
  527. },
  528. include_others=False,
  529. ),
  530. )
  531. def test_state_filter_difference_include_other_minus_no_include_other(self):
  532. """
  533. Tests the StateFilter.approx_difference method
  534. where, in a.approx_difference(b), only a has the include_others flag set.
  535. """
  536. # (wildcard on state keys) - (wildcard on state keys):
  537. self.assert_difference(
  538. StateFilter.freeze(
  539. {EventTypes.Member: None, EventTypes.Create: None},
  540. include_others=True,
  541. ),
  542. StateFilter.freeze(
  543. {EventTypes.Member: None, EventTypes.CanonicalAlias: None},
  544. include_others=False,
  545. ),
  546. StateFilter.freeze(
  547. {
  548. EventTypes.Create: None,
  549. EventTypes.Member: set(),
  550. EventTypes.CanonicalAlias: set(),
  551. },
  552. include_others=True,
  553. ),
  554. )
  555. # (wildcard on state keys) - (specific state keys)
  556. # This one is an over-approximation because we can't represent
  557. # 'all state keys except a few named examples'
  558. # This also shows that the resultant state filter is normalised.
  559. self.assert_difference(
  560. StateFilter.freeze({EventTypes.Member: None}, include_others=True),
  561. StateFilter.freeze(
  562. {
  563. EventTypes.Member: {"@wombat:spqr"},
  564. EventTypes.Create: {""},
  565. },
  566. include_others=False,
  567. ),
  568. StateFilter(types=frozendict(), include_others=True),
  569. )
  570. # (wildcard on state keys) - (no state keys)
  571. self.assert_difference(
  572. StateFilter.freeze(
  573. {EventTypes.Member: None},
  574. include_others=True,
  575. ),
  576. StateFilter.freeze(
  577. {
  578. EventTypes.Member: set(),
  579. },
  580. include_others=False,
  581. ),
  582. StateFilter(
  583. types=frozendict(),
  584. include_others=True,
  585. ),
  586. )
  587. # (specific state keys) - (wildcard on state keys):
  588. self.assert_difference(
  589. StateFilter.freeze(
  590. {
  591. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  592. EventTypes.CanonicalAlias: {""},
  593. },
  594. include_others=True,
  595. ),
  596. StateFilter.freeze(
  597. {EventTypes.Member: None},
  598. include_others=False,
  599. ),
  600. StateFilter.freeze(
  601. {
  602. EventTypes.CanonicalAlias: {""},
  603. EventTypes.Member: set(),
  604. },
  605. include_others=True,
  606. ),
  607. )
  608. # (specific state keys) - (specific state keys)
  609. self.assert_difference(
  610. StateFilter.freeze(
  611. {
  612. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  613. EventTypes.CanonicalAlias: {""},
  614. },
  615. include_others=True,
  616. ),
  617. StateFilter.freeze(
  618. {
  619. EventTypes.Member: {"@wombat:spqr"},
  620. },
  621. include_others=False,
  622. ),
  623. StateFilter.freeze(
  624. {
  625. EventTypes.Member: {"@spqr:spqr"},
  626. EventTypes.CanonicalAlias: {""},
  627. },
  628. include_others=True,
  629. ),
  630. )
  631. # (specific state keys) - (no state keys)
  632. self.assert_difference(
  633. StateFilter.freeze(
  634. {
  635. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  636. EventTypes.CanonicalAlias: {""},
  637. },
  638. include_others=True,
  639. ),
  640. StateFilter.freeze(
  641. {
  642. EventTypes.Member: set(),
  643. },
  644. include_others=False,
  645. ),
  646. StateFilter.freeze(
  647. {
  648. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  649. EventTypes.CanonicalAlias: {""},
  650. },
  651. include_others=True,
  652. ),
  653. )
  654. def test_state_filter_difference_include_other_minus_include_other(self):
  655. """
  656. Tests the StateFilter.approx_difference method
  657. where, in a.approx_difference(b), both a and b have the include_others
  658. flag set.
  659. """
  660. # (wildcard on state keys) - (wildcard on state keys):
  661. self.assert_difference(
  662. StateFilter.freeze(
  663. {EventTypes.Member: None, EventTypes.Create: None},
  664. include_others=True,
  665. ),
  666. StateFilter.freeze(
  667. {EventTypes.Member: None, EventTypes.CanonicalAlias: None},
  668. include_others=True,
  669. ),
  670. StateFilter(types=frozendict(), include_others=False),
  671. )
  672. # (wildcard on state keys) - (specific state keys)
  673. # This one is an over-approximation because we can't represent
  674. # 'all state keys except a few named examples'
  675. self.assert_difference(
  676. StateFilter.freeze({EventTypes.Member: None}, include_others=True),
  677. StateFilter.freeze(
  678. {
  679. EventTypes.Member: {"@wombat:spqr"},
  680. EventTypes.CanonicalAlias: {""},
  681. },
  682. include_others=True,
  683. ),
  684. StateFilter.freeze(
  685. {EventTypes.Member: None, EventTypes.CanonicalAlias: None},
  686. include_others=False,
  687. ),
  688. )
  689. # (wildcard on state keys) - (no state keys)
  690. self.assert_difference(
  691. StateFilter.freeze(
  692. {EventTypes.Member: None},
  693. include_others=True,
  694. ),
  695. StateFilter.freeze(
  696. {
  697. EventTypes.Member: set(),
  698. },
  699. include_others=True,
  700. ),
  701. StateFilter.freeze(
  702. {EventTypes.Member: None},
  703. include_others=False,
  704. ),
  705. )
  706. # (specific state keys) - (wildcard on state keys):
  707. self.assert_difference(
  708. StateFilter.freeze(
  709. {
  710. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  711. EventTypes.CanonicalAlias: {""},
  712. },
  713. include_others=True,
  714. ),
  715. StateFilter.freeze(
  716. {EventTypes.Member: None},
  717. include_others=True,
  718. ),
  719. StateFilter(
  720. types=frozendict(),
  721. include_others=False,
  722. ),
  723. )
  724. # (specific state keys) - (specific state keys)
  725. # This one is an over-approximation because we can't represent
  726. # 'all state keys except a few named examples'
  727. self.assert_difference(
  728. StateFilter.freeze(
  729. {
  730. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  731. EventTypes.CanonicalAlias: {""},
  732. EventTypes.Create: {""},
  733. },
  734. include_others=True,
  735. ),
  736. StateFilter.freeze(
  737. {
  738. EventTypes.Member: {"@wombat:spqr"},
  739. EventTypes.Create: set(),
  740. },
  741. include_others=True,
  742. ),
  743. StateFilter.freeze(
  744. {
  745. EventTypes.Member: {"@spqr:spqr"},
  746. EventTypes.Create: {""},
  747. },
  748. include_others=False,
  749. ),
  750. )
  751. # (specific state keys) - (no state keys)
  752. self.assert_difference(
  753. StateFilter.freeze(
  754. {
  755. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  756. EventTypes.CanonicalAlias: {""},
  757. },
  758. include_others=True,
  759. ),
  760. StateFilter.freeze(
  761. {
  762. EventTypes.Member: set(),
  763. },
  764. include_others=True,
  765. ),
  766. StateFilter.freeze(
  767. {
  768. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  769. },
  770. include_others=False,
  771. ),
  772. )
  773. def test_state_filter_difference_no_include_other_minus_include_other(self):
  774. """
  775. Tests the StateFilter.approx_difference method
  776. where, in a.approx_difference(b), only b has the include_others flag set.
  777. """
  778. # (wildcard on state keys) - (wildcard on state keys):
  779. self.assert_difference(
  780. StateFilter.freeze(
  781. {EventTypes.Member: None, EventTypes.Create: None},
  782. include_others=False,
  783. ),
  784. StateFilter.freeze(
  785. {EventTypes.Member: None, EventTypes.CanonicalAlias: None},
  786. include_others=True,
  787. ),
  788. StateFilter(types=frozendict(), include_others=False),
  789. )
  790. # (wildcard on state keys) - (specific state keys)
  791. # This one is an over-approximation because we can't represent
  792. # 'all state keys except a few named examples'
  793. self.assert_difference(
  794. StateFilter.freeze({EventTypes.Member: None}, include_others=False),
  795. StateFilter.freeze(
  796. {EventTypes.Member: {"@wombat:spqr"}},
  797. include_others=True,
  798. ),
  799. StateFilter.freeze({EventTypes.Member: None}, include_others=False),
  800. )
  801. # (wildcard on state keys) - (no state keys)
  802. self.assert_difference(
  803. StateFilter.freeze(
  804. {EventTypes.Member: None},
  805. include_others=False,
  806. ),
  807. StateFilter.freeze(
  808. {
  809. EventTypes.Member: set(),
  810. },
  811. include_others=True,
  812. ),
  813. StateFilter.freeze(
  814. {EventTypes.Member: None},
  815. include_others=False,
  816. ),
  817. )
  818. # (specific state keys) - (wildcard on state keys):
  819. self.assert_difference(
  820. StateFilter.freeze(
  821. {
  822. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  823. EventTypes.CanonicalAlias: {""},
  824. },
  825. include_others=False,
  826. ),
  827. StateFilter.freeze(
  828. {EventTypes.Member: None},
  829. include_others=True,
  830. ),
  831. StateFilter(
  832. types=frozendict(),
  833. include_others=False,
  834. ),
  835. )
  836. # (specific state keys) - (specific state keys)
  837. # This one is an over-approximation because we can't represent
  838. # 'all state keys except a few named examples'
  839. self.assert_difference(
  840. StateFilter.freeze(
  841. {
  842. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  843. EventTypes.CanonicalAlias: {""},
  844. },
  845. include_others=False,
  846. ),
  847. StateFilter.freeze(
  848. {
  849. EventTypes.Member: {"@wombat:spqr"},
  850. },
  851. include_others=True,
  852. ),
  853. StateFilter.freeze(
  854. {
  855. EventTypes.Member: {"@spqr:spqr"},
  856. },
  857. include_others=False,
  858. ),
  859. )
  860. # (specific state keys) - (no state keys)
  861. self.assert_difference(
  862. StateFilter.freeze(
  863. {
  864. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  865. EventTypes.CanonicalAlias: {""},
  866. },
  867. include_others=False,
  868. ),
  869. StateFilter.freeze(
  870. {
  871. EventTypes.Member: set(),
  872. },
  873. include_others=True,
  874. ),
  875. StateFilter.freeze(
  876. {
  877. EventTypes.Member: {"@wombat:spqr", "@spqr:spqr"},
  878. },
  879. include_others=False,
  880. ),
  881. )
  882. def test_state_filter_difference_simple_cases(self):
  883. """
  884. Tests some very simple cases of the StateFilter approx_difference,
  885. that are not explicitly tested by the more in-depth tests.
  886. """
  887. self.assert_difference(StateFilter.all(), StateFilter.all(), StateFilter.none())
  888. self.assert_difference(
  889. StateFilter.all(),
  890. StateFilter.none(),
  891. StateFilter.all(),
  892. )
  893. class StateFilterTestCase(TestCase):
  894. def test_return_expanded(self):
  895. """
  896. Tests the behaviour of the return_expanded() function that expands
  897. StateFilters to include more state types (for the sake of cache hit rate).
  898. """
  899. self.assertEqual(StateFilter.all().return_expanded(), StateFilter.all())
  900. self.assertEqual(StateFilter.none().return_expanded(), StateFilter.none())
  901. # Concrete-only state filters stay the same
  902. # (Case: mixed filter)
  903. self.assertEqual(
  904. StateFilter.freeze(
  905. {
  906. EventTypes.Member: {"@wombat:test", "@alicia:test"},
  907. "some.other.state.type": {""},
  908. },
  909. include_others=False,
  910. ).return_expanded(),
  911. StateFilter.freeze(
  912. {
  913. EventTypes.Member: {"@wombat:test", "@alicia:test"},
  914. "some.other.state.type": {""},
  915. },
  916. include_others=False,
  917. ),
  918. )
  919. # Concrete-only state filters stay the same
  920. # (Case: non-member-only filter)
  921. self.assertEqual(
  922. StateFilter.freeze(
  923. {"some.other.state.type": {""}}, include_others=False
  924. ).return_expanded(),
  925. StateFilter.freeze({"some.other.state.type": {""}}, include_others=False),
  926. )
  927. # Concrete-only state filters stay the same
  928. # (Case: member-only filter)
  929. self.assertEqual(
  930. StateFilter.freeze(
  931. {
  932. EventTypes.Member: {"@wombat:test", "@alicia:test"},
  933. },
  934. include_others=False,
  935. ).return_expanded(),
  936. StateFilter.freeze(
  937. {
  938. EventTypes.Member: {"@wombat:test", "@alicia:test"},
  939. },
  940. include_others=False,
  941. ),
  942. )
  943. # Wildcard member-only state filters stay the same
  944. self.assertEqual(
  945. StateFilter.freeze(
  946. {EventTypes.Member: None},
  947. include_others=False,
  948. ).return_expanded(),
  949. StateFilter.freeze(
  950. {EventTypes.Member: None},
  951. include_others=False,
  952. ),
  953. )
  954. # If there is a wildcard in the non-member portion of the filter,
  955. # it's expanded to include ALL non-member events.
  956. # (Case: mixed filter)
  957. self.assertEqual(
  958. StateFilter.freeze(
  959. {
  960. EventTypes.Member: {"@wombat:test", "@alicia:test"},
  961. "some.other.state.type": None,
  962. },
  963. include_others=False,
  964. ).return_expanded(),
  965. StateFilter.freeze(
  966. {EventTypes.Member: {"@wombat:test", "@alicia:test"}},
  967. include_others=True,
  968. ),
  969. )
  970. # If there is a wildcard in the non-member portion of the filter,
  971. # it's expanded to include ALL non-member events.
  972. # (Case: non-member-only filter)
  973. self.assertEqual(
  974. StateFilter.freeze(
  975. {
  976. "some.other.state.type": None,
  977. },
  978. include_others=False,
  979. ).return_expanded(),
  980. StateFilter.freeze({EventTypes.Member: set()}, include_others=True),
  981. )
  982. self.assertEqual(
  983. StateFilter.freeze(
  984. {
  985. "some.other.state.type": None,
  986. "yet.another.state.type": {"wombat"},
  987. },
  988. include_others=False,
  989. ).return_expanded(),
  990. StateFilter.freeze({EventTypes.Member: set()}, include_others=True),
  991. )