|
|
|
@ -15,599 +15,258 @@ |
|
|
|
|
|
|
|
|
|
from tests import unittest |
|
|
|
|
from twisted.internet import defer |
|
|
|
|
from twisted.python.log import PythonLoggingObserver |
|
|
|
|
|
|
|
|
|
from synapse.state import StateHandler |
|
|
|
|
from synapse.storage.pdu import PduEntry |
|
|
|
|
from synapse.federation.pdu_codec import encode_event_id |
|
|
|
|
from synapse.federation.units import Pdu |
|
|
|
|
|
|
|
|
|
from collections import namedtuple |
|
|
|
|
|
|
|
|
|
from mock import Mock |
|
|
|
|
|
|
|
|
|
import mock |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ReturnType = namedtuple( |
|
|
|
|
"StateReturnType", ["new_branch", "current_branch"] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_get_power_level(power_level_list): |
|
|
|
|
def get_power_level(room_id, user_id): |
|
|
|
|
return defer.succeed(power_level_list.get(user_id, None)) |
|
|
|
|
return get_power_level |
|
|
|
|
|
|
|
|
|
class StateTestCase(unittest.TestCase): |
|
|
|
|
def setUp(self): |
|
|
|
|
self.persistence = Mock(spec=[ |
|
|
|
|
"get_unresolved_state_tree", |
|
|
|
|
"update_current_state", |
|
|
|
|
"get_latest_pdus_in_context", |
|
|
|
|
"get_current_state_pdu", |
|
|
|
|
"get_pdu", |
|
|
|
|
"get_power_level", |
|
|
|
|
]) |
|
|
|
|
self.replication = Mock(spec=["get_pdu"]) |
|
|
|
|
|
|
|
|
|
hs = Mock(spec=["get_datastore", "get_replication_layer"]) |
|
|
|
|
hs.get_datastore.return_value = self.persistence |
|
|
|
|
hs.get_replication_layer.return_value = self.replication |
|
|
|
|
hs.hostname = "bob.com" |
|
|
|
|
|
|
|
|
|
self.state = StateHandler(hs) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_new_state_key(self): |
|
|
|
|
# We've never seen anything for this state before |
|
|
|
|
new_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu], []), None) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_direct_overwrite(self): |
|
|
|
|
# We do a direct overwriting of the old state, i.e., the new state |
|
|
|
|
# points to the old state. |
|
|
|
|
|
|
|
|
|
old_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
new_pdu = new_fake_pdu("B", "test", "mem", "x", "A", "u2") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 5, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu, old_pdu], [old_pdu]), None) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
self.store = Mock( |
|
|
|
|
spec_set=[ |
|
|
|
|
"get_state_groups", |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
hs = Mock(spec=["get_datastore"]) |
|
|
|
|
hs.get_datastore.return_value = self.store |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
self.state = StateHandler(hs) |
|
|
|
|
self.event_id = 0 |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_overwrite(self): |
|
|
|
|
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", "A", "u2") |
|
|
|
|
new_pdu = new_fake_pdu("C", "test", "mem", "x", "B", "u3") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 5, |
|
|
|
|
"u3": 0, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu, old_pdu_2, old_pdu_1], [old_pdu_1]), None) |
|
|
|
|
) |
|
|
|
|
def test_annotate_with_old_message(self): |
|
|
|
|
event = self.create_event(type="test_message", name="event") |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
old_state = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
yield self.state.annotate_state_groups(event, old_state=old_state) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
for k, v in event.old_state_events.items(): |
|
|
|
|
type, state_key = k |
|
|
|
|
self.assertEqual(type, v.type) |
|
|
|
|
self.assertEqual(state_key, v.state_key) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
self.assertEqual(set(old_state), set(event.old_state_events.values())) |
|
|
|
|
self.assertDictEqual(event.old_state_events, event.state_events) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
self.assertIsNone(event.state_group) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_power_level_fail(self): |
|
|
|
|
# We try to update the state based on an outdated state, and have a |
|
|
|
|
# too low power level. |
|
|
|
|
|
|
|
|
|
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2") |
|
|
|
|
new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 5, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
|
|
|
|
|
self.assertFalse(is_new) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(0, self.persistence.update_current_state.call_count) |
|
|
|
|
def test_annotate_with_old_state(self): |
|
|
|
|
event = self.create_event(type="state", state_key="", name="event") |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_power_level_succeed(self): |
|
|
|
|
# We try to update the state based on an outdated state, but have |
|
|
|
|
# sufficient power level to force the update. |
|
|
|
|
|
|
|
|
|
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2") |
|
|
|
|
new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 15, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None) |
|
|
|
|
) |
|
|
|
|
old_state = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
yield self.state.annotate_state_groups(event, old_state=old_state) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
for k, v in event.old_state_events.items(): |
|
|
|
|
type, state_key = k |
|
|
|
|
self.assertEqual(type, v.type) |
|
|
|
|
self.assertEqual(state_key, v.state_key) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
self.assertEqual( |
|
|
|
|
set(old_state + [event]), |
|
|
|
|
set(event.old_state_events.values()) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
self.assertDictEqual(event.old_state_events, event.state_events) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
self.assertIsNone(event.state_group) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_power_level_equal_same_len(self): |
|
|
|
|
# We try to update the state based on an outdated state, the power |
|
|
|
|
# levels are the same and so are the branch lengths |
|
|
|
|
|
|
|
|
|
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2") |
|
|
|
|
new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 10, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
def test_trivial_annotate_message(self): |
|
|
|
|
event = self.create_event(type="test_message", name="event") |
|
|
|
|
event.prev_events = [] |
|
|
|
|
|
|
|
|
|
old_state = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
group_name = "group_name_1" |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
self.store.get_state_groups.return_value = { |
|
|
|
|
group_name: old_state, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
yield self.state.annotate_state_groups(event) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
for k, v in event.old_state_events.items(): |
|
|
|
|
type, state_key = k |
|
|
|
|
self.assertEqual(type, v.type) |
|
|
|
|
self.assertEqual(state_key, v.state_key) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_power_level_equal_diff_len(self): |
|
|
|
|
# We try to update the state based on an outdated state, the power |
|
|
|
|
# levels are the same but the branch length of the new one is longer. |
|
|
|
|
|
|
|
|
|
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2") |
|
|
|
|
old_pdu_3 = new_fake_pdu("C", "test", "mem", "x", "A", "u3") |
|
|
|
|
new_pdu = new_fake_pdu("D", "test", "mem", "x", "C", "u4") |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 10, |
|
|
|
|
"u4": 10, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu, old_pdu_3, old_pdu_1], |
|
|
|
|
[old_pdu_2, old_pdu_1] |
|
|
|
|
), |
|
|
|
|
None |
|
|
|
|
) |
|
|
|
|
self.assertEqual( |
|
|
|
|
set([e.event_id for e in old_state]), |
|
|
|
|
set([e.event_id for e in event.old_state_events.values()]) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
self.assertDictEqual( |
|
|
|
|
{ |
|
|
|
|
k: v.event_id |
|
|
|
|
for k, v in event.old_state_events.items() |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
k: v.event_id |
|
|
|
|
for k, v in event.state_events.items() |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
self.assertEqual(group_name, event.state_group) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_missing_pdu(self): |
|
|
|
|
# We try to update state against a PDU we haven't yet seen, |
|
|
|
|
# triggering a get_pdu request |
|
|
|
|
|
|
|
|
|
# The pdu we haven't seen |
|
|
|
|
old_pdu_1 = new_fake_pdu( |
|
|
|
|
"A", "test", "mem", "x", None, "u1", depth=0 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
old_pdu_2 = new_fake_pdu( |
|
|
|
|
"B", "test", "mem", "x", "A", "u2", depth=1 |
|
|
|
|
) |
|
|
|
|
new_pdu = new_fake_pdu( |
|
|
|
|
"C", "test", "mem", "x", "A", "u3", depth=2 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 20, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
# The return_value of `get_unresolved_state_tree`, which changes after |
|
|
|
|
# the call to get_pdu |
|
|
|
|
tree_to_return = [(ReturnType([new_pdu], [old_pdu_2]), 0)] |
|
|
|
|
|
|
|
|
|
def return_tree(p): |
|
|
|
|
return tree_to_return[0] |
|
|
|
|
|
|
|
|
|
def set_return_tree(destination, pdu_origin, pdu_id, outlier=False): |
|
|
|
|
tree_to_return[0] = ( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1] |
|
|
|
|
), |
|
|
|
|
None |
|
|
|
|
) |
|
|
|
|
return defer.succeed(None) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.side_effect = return_tree |
|
|
|
|
def test_trivial_annotate_state(self): |
|
|
|
|
event = self.create_event(type="state", state_key="", name="event") |
|
|
|
|
event.prev_events = [] |
|
|
|
|
|
|
|
|
|
old_state = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.side_effect = set_return_tree |
|
|
|
|
group_name = "group_name_1" |
|
|
|
|
|
|
|
|
|
self.persistence.get_pdu.return_value = None |
|
|
|
|
self.store.get_state_groups.return_value = { |
|
|
|
|
group_name: old_state, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
yield self.state.annotate_state_groups(event) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
for k, v in event.old_state_events.items(): |
|
|
|
|
type, state_key = k |
|
|
|
|
self.assertEqual(type, v.type) |
|
|
|
|
self.assertEqual(state_key, v.state_key) |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.assert_called_with( |
|
|
|
|
destination=new_pdu.origin, |
|
|
|
|
pdu_origin=old_pdu_1.origin, |
|
|
|
|
pdu_id=old_pdu_1.pdu_id, |
|
|
|
|
outlier=True |
|
|
|
|
self.assertEqual( |
|
|
|
|
set([e.event_id for e in old_state]), |
|
|
|
|
set([e.event_id for e in event.old_state_events.values()]) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_with( |
|
|
|
|
new_pdu |
|
|
|
|
self.assertEqual( |
|
|
|
|
set([e.event_id for e in old_state] + [event.event_id]), |
|
|
|
|
set([e.event_id for e in event.state_events.values()]) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEquals( |
|
|
|
|
2, self.persistence.get_unresolved_state_tree.call_count |
|
|
|
|
new_state = { |
|
|
|
|
k: v.event_id |
|
|
|
|
for k, v in event.state_events.items() |
|
|
|
|
} |
|
|
|
|
old_state = { |
|
|
|
|
k: v.event_id |
|
|
|
|
for k, v in event.old_state_events.items() |
|
|
|
|
} |
|
|
|
|
old_state[(event.type, event.state_key)] = event.event_id |
|
|
|
|
self.assertDictEqual( |
|
|
|
|
old_state, |
|
|
|
|
new_state |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
self.assertIsNone(event.state_group) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_missing_pdu_depth_1(self): |
|
|
|
|
# We try to update state against a PDU we haven't yet seen, |
|
|
|
|
# triggering a get_pdu request |
|
|
|
|
|
|
|
|
|
# The pdu we haven't seen |
|
|
|
|
old_pdu_1 = new_fake_pdu( |
|
|
|
|
"A", "test", "mem", "x", None, "u1", depth=0 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
old_pdu_2 = new_fake_pdu( |
|
|
|
|
"B", "test", "mem", "x", "A", "u2", depth=2 |
|
|
|
|
) |
|
|
|
|
old_pdu_3 = new_fake_pdu( |
|
|
|
|
"C", "test", "mem", "x", "B", "u3", depth=3 |
|
|
|
|
) |
|
|
|
|
new_pdu = new_fake_pdu( |
|
|
|
|
"D", "test", "mem", "x", "A", "u4", depth=4 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 10, |
|
|
|
|
"u4": 20, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
# The return_value of `get_unresolved_state_tree`, which changes after |
|
|
|
|
# the call to get_pdu |
|
|
|
|
tree_to_return = [ |
|
|
|
|
( |
|
|
|
|
ReturnType([new_pdu], [old_pdu_3]), |
|
|
|
|
0 |
|
|
|
|
), |
|
|
|
|
( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu, old_pdu_1], [old_pdu_3] |
|
|
|
|
), |
|
|
|
|
1 |
|
|
|
|
), |
|
|
|
|
( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu, old_pdu_1], [old_pdu_3, old_pdu_2, old_pdu_1] |
|
|
|
|
), |
|
|
|
|
None |
|
|
|
|
), |
|
|
|
|
def test_resolve_message_conflict(self): |
|
|
|
|
event = self.create_event(type="test_message", name="event") |
|
|
|
|
event.prev_events = [] |
|
|
|
|
|
|
|
|
|
old_state_1 = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
to_return = [0] |
|
|
|
|
|
|
|
|
|
def return_tree(p): |
|
|
|
|
return tree_to_return[to_return[0]] |
|
|
|
|
|
|
|
|
|
def set_return_tree(destination, pdu_origin, pdu_id, outlier=False): |
|
|
|
|
to_return[0] += 1 |
|
|
|
|
return defer.succeed(None) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.side_effect = return_tree |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.side_effect = set_return_tree |
|
|
|
|
|
|
|
|
|
self.persistence.get_pdu.return_value = None |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
old_state_2 = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test3", state_key="2"), |
|
|
|
|
self.create_event(type="test4", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
group_name_1 = "group_name_1" |
|
|
|
|
group_name_2 = "group_name_2" |
|
|
|
|
|
|
|
|
|
self.assertEqual(2, self.replication.get_pdu.call_count) |
|
|
|
|
self.store.get_state_groups.return_value = { |
|
|
|
|
group_name_1: old_state_1, |
|
|
|
|
group_name_2: old_state_2, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.assert_has_calls( |
|
|
|
|
[ |
|
|
|
|
mock.call( |
|
|
|
|
destination=new_pdu.origin, |
|
|
|
|
pdu_origin=old_pdu_1.origin, |
|
|
|
|
pdu_id=old_pdu_1.pdu_id, |
|
|
|
|
outlier=True |
|
|
|
|
), |
|
|
|
|
mock.call( |
|
|
|
|
destination=old_pdu_3.origin, |
|
|
|
|
pdu_origin=old_pdu_2.origin, |
|
|
|
|
pdu_id=old_pdu_2.pdu_id, |
|
|
|
|
outlier=True |
|
|
|
|
), |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
yield self.state.annotate_state_groups(event) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
self.assertEqual(len(event.old_state_events), 5) |
|
|
|
|
|
|
|
|
|
self.assertEquals( |
|
|
|
|
3, self.persistence.get_unresolved_state_tree.call_count |
|
|
|
|
self.assertEqual( |
|
|
|
|
set([e.event_id for e in event.state_events.values()]), |
|
|
|
|
set([e.event_id for e in event.old_state_events.values()]) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
self.assertIsNone(event.state_group) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_missing_pdu_depth_2(self): |
|
|
|
|
# We try to update state against a PDU we haven't yet seen, |
|
|
|
|
# triggering a get_pdu request |
|
|
|
|
|
|
|
|
|
# The pdu we haven't seen |
|
|
|
|
old_pdu_1 = new_fake_pdu( |
|
|
|
|
"A", "test", "mem", "x", None, "u1", depth=0 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
old_pdu_2 = new_fake_pdu( |
|
|
|
|
"B", "test", "mem", "x", "A", "u2", depth=2 |
|
|
|
|
) |
|
|
|
|
old_pdu_3 = new_fake_pdu( |
|
|
|
|
"C", "test", "mem", "x", "B", "u3", depth=3 |
|
|
|
|
) |
|
|
|
|
new_pdu = new_fake_pdu( |
|
|
|
|
"D", "test", "mem", "x", "A", "u4", depth=1 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 10, |
|
|
|
|
"u2": 10, |
|
|
|
|
"u3": 10, |
|
|
|
|
"u4": 20, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
# The return_value of `get_unresolved_state_tree`, which changes after |
|
|
|
|
# the call to get_pdu |
|
|
|
|
tree_to_return = [ |
|
|
|
|
( |
|
|
|
|
ReturnType([new_pdu], [old_pdu_3]), |
|
|
|
|
1, |
|
|
|
|
), |
|
|
|
|
( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu], [old_pdu_3, old_pdu_2] |
|
|
|
|
), |
|
|
|
|
0, |
|
|
|
|
), |
|
|
|
|
( |
|
|
|
|
ReturnType( |
|
|
|
|
[new_pdu, old_pdu_1], [old_pdu_3, old_pdu_2, old_pdu_1] |
|
|
|
|
), |
|
|
|
|
None |
|
|
|
|
), |
|
|
|
|
def test_resolve_state_conflict(self): |
|
|
|
|
event = self.create_event(type="test4", state_key="", name="event") |
|
|
|
|
event.prev_events = [] |
|
|
|
|
|
|
|
|
|
old_state_1 = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test1", state_key="2"), |
|
|
|
|
self.create_event(type="test2", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
to_return = [0] |
|
|
|
|
|
|
|
|
|
def return_tree(p): |
|
|
|
|
return tree_to_return[to_return[0]] |
|
|
|
|
|
|
|
|
|
def set_return_tree(destination, pdu_origin, pdu_id, outlier=False): |
|
|
|
|
to_return[0] += 1 |
|
|
|
|
return defer.succeed(None) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.side_effect = return_tree |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.side_effect = set_return_tree |
|
|
|
|
|
|
|
|
|
self.persistence.get_pdu.return_value = None |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
|
|
|
|
|
self.assertEqual(2, self.replication.get_pdu.call_count) |
|
|
|
|
|
|
|
|
|
self.replication.get_pdu.assert_has_calls( |
|
|
|
|
[ |
|
|
|
|
mock.call( |
|
|
|
|
destination=old_pdu_3.origin, |
|
|
|
|
pdu_origin=old_pdu_2.origin, |
|
|
|
|
pdu_id=old_pdu_2.pdu_id, |
|
|
|
|
outlier=True |
|
|
|
|
), |
|
|
|
|
mock.call( |
|
|
|
|
destination=new_pdu.origin, |
|
|
|
|
pdu_origin=old_pdu_1.origin, |
|
|
|
|
pdu_id=old_pdu_1.pdu_id, |
|
|
|
|
outlier=True |
|
|
|
|
), |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_with( |
|
|
|
|
new_pdu |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEquals( |
|
|
|
|
3, self.persistence.get_unresolved_state_tree.call_count |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_no_common_ancestor(self): |
|
|
|
|
# We do a direct overwriting of the old state, i.e., the new state |
|
|
|
|
# points to the old state. |
|
|
|
|
old_state_2 = [ |
|
|
|
|
self.create_event(type="test1", state_key="1"), |
|
|
|
|
self.create_event(type="test3", state_key="2"), |
|
|
|
|
self.create_event(type="test4", state_key=""), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
old_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u1") |
|
|
|
|
new_pdu = new_fake_pdu("B", "test", "mem", "x", None, "u2") |
|
|
|
|
group_name_1 = "group_name_1" |
|
|
|
|
group_name_2 = "group_name_2" |
|
|
|
|
|
|
|
|
|
self.persistence.get_power_level.side_effect = _gen_get_power_level({ |
|
|
|
|
"u1": 5, |
|
|
|
|
"u2": 10, |
|
|
|
|
}) |
|
|
|
|
self.store.get_state_groups.return_value = { |
|
|
|
|
group_name_1: old_state_1, |
|
|
|
|
group_name_2: old_state_2, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.return_value = ( |
|
|
|
|
(ReturnType([new_pdu], [old_pdu]), None) |
|
|
|
|
) |
|
|
|
|
yield self.state.annotate_state_groups(event) |
|
|
|
|
|
|
|
|
|
is_new = yield self.state.handle_new_state(new_pdu) |
|
|
|
|
self.assertEqual(len(event.old_state_events), 5) |
|
|
|
|
|
|
|
|
|
self.assertTrue(is_new) |
|
|
|
|
expected_new = event.old_state_events |
|
|
|
|
expected_new[(event.type, event.state_key)] = event |
|
|
|
|
|
|
|
|
|
self.persistence.get_unresolved_state_tree.assert_called_once_with( |
|
|
|
|
new_pdu |
|
|
|
|
self.assertEqual( |
|
|
|
|
set([e.event_id for e in expected_new.values()]), |
|
|
|
|
set([e.event_id for e in event.state_events.values()]), |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self.assertEqual(1, self.persistence.update_current_state.call_count) |
|
|
|
|
|
|
|
|
|
self.assertFalse(self.replication.get_pdu.called) |
|
|
|
|
|
|
|
|
|
@defer.inlineCallbacks |
|
|
|
|
def test_new_event(self): |
|
|
|
|
event = Mock() |
|
|
|
|
event.event_id = "12123123@test" |
|
|
|
|
self.assertIsNone(event.state_group) |
|
|
|
|
|
|
|
|
|
state_pdu = new_fake_pdu("C", "test", "mem", "x", "A", 20) |
|
|
|
|
def create_event(self, name=None, type=None, state_key=None): |
|
|
|
|
self.event_id += 1 |
|
|
|
|
event_id = str(self.event_id) |
|
|
|
|
|
|
|
|
|
snapshot = Mock() |
|
|
|
|
snapshot.prev_state_pdu = state_pdu |
|
|
|
|
event_id = "pdu_id@origin.com" |
|
|
|
|
if not name: |
|
|
|
|
if state_key is not None: |
|
|
|
|
name = "<%s-%s>" % (type, state_key) |
|
|
|
|
else: |
|
|
|
|
name = "<%s>" % (type, ) |
|
|
|
|
|
|
|
|
|
def fill_out_prev_events(event): |
|
|
|
|
event.prev_events = [event_id] |
|
|
|
|
event.depth = 6 |
|
|
|
|
snapshot.fill_out_prev_events = fill_out_prev_events |
|
|
|
|
event = Mock(name=name, spec=[]) |
|
|
|
|
event.type = type |
|
|
|
|
|
|
|
|
|
yield self.state.handle_new_event(event, snapshot) |
|
|
|
|
|
|
|
|
|
self.assertLess(5, event.depth) |
|
|
|
|
|
|
|
|
|
self.assertEquals(1, len(event.prev_events)) |
|
|
|
|
|
|
|
|
|
prev_id = event.prev_events[0] |
|
|
|
|
|
|
|
|
|
self.assertEqual(event_id, prev_id) |
|
|
|
|
|
|
|
|
|
self.assertEqual( |
|
|
|
|
encode_event_id(state_pdu.pdu_id, state_pdu.origin), |
|
|
|
|
event.prev_state |
|
|
|
|
) |
|
|
|
|
if state_key is not None: |
|
|
|
|
event.state_key = state_key |
|
|
|
|
event.event_id = event_id |
|
|
|
|
|
|
|
|
|
event.user_id = "@user_id:example.com" |
|
|
|
|
event.room_id = "!room_id:example.com" |
|
|
|
|
|
|
|
|
|
def new_fake_pdu(pdu_id, context, pdu_type, state_key, prev_state_id, |
|
|
|
|
user_id, depth=0): |
|
|
|
|
new_pdu = Pdu( |
|
|
|
|
pdu_id=pdu_id, |
|
|
|
|
pdu_type=pdu_type, |
|
|
|
|
state_key=state_key, |
|
|
|
|
user_id=user_id, |
|
|
|
|
prev_state_id=prev_state_id, |
|
|
|
|
origin="example.com", |
|
|
|
|
context="context", |
|
|
|
|
origin_server_ts=1405353060021, |
|
|
|
|
depth=depth, |
|
|
|
|
content_json="{}", |
|
|
|
|
unrecognized_keys="{}", |
|
|
|
|
outlier=True, |
|
|
|
|
is_state=True, |
|
|
|
|
prev_state_origin="example.com", |
|
|
|
|
have_processed=True, |
|
|
|
|
content={}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
return new_pdu |
|
|
|
|
return event |
|
|
|
|