Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback key test #67

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
10 changes: 7 additions & 3 deletions trafficlight/internals/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
import logging
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, Optional, Union, cast

from trafficlight.homerunner import HomeServer

Expand Down Expand Up @@ -174,10 +174,11 @@ async def accept_crosssign(self) -> None:
async def verify_crosssign(self) -> None:
await self._perform_action({"action": "verify_crosssign_emoji", "data": {}})

async def create_room(self, room_name: str) -> None:
await self._perform_action(
async def create_room(self, room_name: str) -> str:
response = await self._perform_action(
{"action": "create_room", "data": {"name": room_name}}
)
return cast(str, response["response"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a comment on the related PR in the adapter; i think we should have a little structure here to allow us to extend later (rather than only returning a string). If we change it in the adapter we'll need to change it here.


async def create_dm(self, user_id: str) -> None:
await self._perform_action({"action": "create_dm", "data": {"userId": user_id}})
Expand Down Expand Up @@ -255,3 +256,6 @@ async def advance_clock(self, duration: int) -> None:
await self._perform_action(
{"action": "advance_clock", "data": {"milliseconds": duration}}
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see where go_offline is being used - do we need this still - is the test OK with using the network_proxy to make the client be offline?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this selected the wrong bit of code; the code in question is just below this line; sorry)

async def go_offline(self) -> None:
await self._perform_action({"action": "go_offline", "data": {}})
85 changes: 85 additions & 0 deletions trafficlight/tests/fallback_key_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import asyncio
import logging

from nio import AsyncClient, AsyncClientConfig, SyncResponse, store

from trafficlight.client_types import ElementWebStable
from trafficlight.homerunner import HomeServer
from trafficlight.internals.client import MatrixClient, NetworkProxyClient
from trafficlight.internals.test import Test
from trafficlight.server_types import SynapseDevelop


# CLIENT_COUNT=2 REQUIRES_PROXY=true CYPRESS_BASE_URL="https://app.element.io" ./trafficlight/scripts-dev/run-localdev-setup.sh && tmux kill-server
# Passing Test
class FallbackKeyTest(Test):
def __init__(self) -> None:
super().__init__()
self._client_under_test([ElementWebStable()], "alice")
self._client_under_test([ElementWebStable()], "bob")
self._network_proxy("alice_proxy")
self._server_under_test(SynapseDevelop(), ["server"])

async def run(
self,
alice: MatrixClient,
bob: MatrixClient,
server: HomeServer,
alice_proxy: NetworkProxyClient,
) -> None:
await alice_proxy.proxy_to(server)
await asyncio.gather(alice.register(alice_proxy), bob.register(server))
room_id = await alice.create_room("fallback test room")
logging.info(f"Got room-id as {room_id}")
await alice.invite_user(f"{bob.localpart}:{server.server_name}")
await bob.accept_invite()
# disable sync for alice, so she can't upload more device keys
await alice_proxy.disable_endpoint("/_matrix/client/r0/sync")
for i in range(60):
await login_and_send_message_in_room(
server, bob, room_id, f"Hello world {i + 1}!"
)
await alice_proxy.enable_endpoint("/_matrix/client/r0/sync")
# last message would have exhausted the OTKs,
# so we should have fallen back to the fallback key
await alice.verify_message_in_timeline("Hello world 60!")


async def login_and_send_message_in_room(
server: HomeServer, user: MatrixClient, room_id: str, message: str
) -> None:
# need to install python-olm and pip install "matrix-nio[e2e]
logging.info(
f"trying to login as @{user.localpart}:{server.server_name} with password ${user.password} and send a message in #{room_id}..."
)
user_id = f"@{user.localpart}:{server.server_name}"
client = AsyncClient(
server.cs_api,
user_id,
config=AsyncClientConfig(
encryption_enabled=True, store=store.SqliteMemoryStore
),
)

# This method will be called after each sync
async def handle_sync(_: None) -> None:
# Send the message after initial sync
await client.room_send(
room_id,
message_type="m.room.message",
content={"msgtype": "m.text", "body": message},
ignore_unverified_devices=True,
)
# Stop syncing
task.cancel()

try:
client.add_response_callback(handle_sync, SyncResponse)
await client.login(user.password)
# sync_forever must be used for encryption to work
task = asyncio.create_task(client.sync_forever(timeout=30000))
await task
except Exception as e:
logging.exception(str(e))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exception are we catching, logging and dropping here?

If we do it like this, the exception handling of the test suite as a whole won't mark the test as failed - are we using this to hide an exception that isn't fatal to the test?

A short note on what we're catching/why would be good if we do need to hide exceptions that aren't failures.

finally:
await client.close()