async def test_except_hook(tmp_path: Path, capsys): assert sys.excepthook is unexpected_errors_logger async with Client(tmp_path / "1") as c1, Client(tmp_path / "2") as c2: def get_keyboard_interrupt() -> KeyboardInterrupt: try: raise KeyboardInterrupt except KeyboardInterrupt as e: return e ignored = get_keyboard_interrupt() assert ignored.__traceback__ sys.excepthook(type(ignored), ignored, ignored.__traceback__) assert "Unexpected general error" not in capsys.readouterr().err assert not await c1.current_log_file.read_text() assert not await c2.current_log_file.read_text() def get_runtime_error() -> RuntimeError: try: raise RuntimeError except RuntimeError as e: return e caught = get_runtime_error() assert caught.__traceback__ sys.excepthook(type(caught), caught, caught.__traceback__) assert "Unexpected general error" in capsys.readouterr().err assert await c1.current_log_file.read_text() assert await c2.current_log_file.read_text()
async def test_file_logging(tmp_path: Path): async def entry_match( level: str, text: str = "test", matches: Optional[str] = None, ) -> None: if matches is None: matches = await client.current_log_file.read_text() assert re.match( rf"^{re.escape(level)} \d{{4}}-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d " rf"[a-zA-Z\d_.]+:\d+\n{re.escape(text)}\n\n$", matches, ) await client.current_log_file.write_text("") async with Client(tmp_path) as client: client.debug("test") await entry_match("DEBUG") client.info("test") await entry_match("INFO") client.warn("test") await entry_match("WARNING") client.err("test") await entry_match("ERROR") client.crit("test") await entry_match("CRITICAL") err = RuntimeError("test") async def check_trace_err(entry_txt: str = "test"): text = await client.current_log_file.read_text() await client.current_log_file.write_text("") msg = "\n".join(text.splitlines()[:2]) + "\n\n" await entry_match("ERROR", entry_txt, msg) assert len(text.splitlines()) > 3 assert type(err).__name__ in text try: raise err except type(err): client.exception("test") await check_trace_err() with client.report(type(err), level="ERROR", trace=True): raise err await check_trace_err(repr(err)) # type: ignore with client.report(type(err), trace=False): raise err await entry_match("WARNING", repr(err))
def clone_client(client: Client, *args, **kwargs) -> Client: dest_dir = Path(client.base_dir).parent / str(uuid4()) new_base_dir = dest_dir / Path(client.base_dir).name dest_dir.mkdir() copytree(client.base_dir, new_base_dir) return Client(new_base_dir, *args, **kwargs)
async def test_file_retention(tmp_path: Path): for i in range(1, 11): async with Client(tmp_path) as client: client.info("test {}", i) log_dir = tmp_path / "logs" assert len(list(log_dir.iterdir())) == 10 assert len(list(log_dir.glob("????????-??????.??????.log"))) == 10 async with Client(tmp_path) as client: client.info("test 11") files = sorted(log_dir.iterdir(), key=lambda f: f.name) assert len(files) == 10 await asyncio.sleep(1) assert "test 2" in files[0].read_text() assert "test 11" in files[-1].read_text()
async def test_file_lock(alice: Client, tmp_path: Path): alice2 = Client(alice.base_dir) assert alice2._lock is None with raises(filelock.Timeout): # Try creating another client with same base_dir await alice2.load(user_id=alice.user_id, device_id=alice.device_id) assert alice2._lock and not alice2._lock.is_locked # type: ignore
async def test_terminal_logging(tmp_path: Path, capsys): def line_match( level: str, text: str = "test", matches: Optional[str] = None, ) -> None: if matches is None: matches = capsys.readouterr().err assert re.match( rf"^\d\d:\d\d:\d\d {re.escape(level)} {re.escape(text)}" rf" +[a-zA-Z\d_.]+:\d+\n$", matches, ) async with Client(tmp_path) as client: client.debug("test") assert not capsys.readouterr().err client.info("test") line_match("i") client.warn("test") line_match("!") client.err("test") line_match("X") client.crit("test") line_match("F") err = RuntimeError("test") def check_trace_err(line_re_txt: str = "test"): text = capsys.readouterr().err line_match("X", line_re_txt, text.splitlines()[0] + "\n") assert len(text.splitlines()) > 3 assert type(err).__name__ in text try: raise err except type(err): client.exception("test") check_trace_err() with client.report(type(err), level="ERROR", trace=True): raise err check_trace_err(repr(err)) # type: ignore with client.report(type(err), trace=False): raise err line_match("!", repr(err))
async def test_report_caught(tmp_path: Path): async with Client(tmp_path) as client: with client.report(ValueError, TypeError) as caught: pass assert caught == [] with client.report(ValueError, TypeError) as caught: raise TypeError assert len(caught) == 1 # type: ignore assert isinstance(caught[0], TypeError)
async def test_sending_failure(room: Room, bob: Client): new = [] cb = lambda room, event: new.append((event.id, event.sending)) # noqa room.client.rooms.callbacks[TimelineEvent].append(cb) await room.timeline.send(Text("abc")) await bob.rooms.join(room.id) # prevent room from becoming inaccessible await room.leave() # Event that has failed sending in timeline with raises(MatrixError): await room.timeline.send(Text("def"), transaction_id="123") unsent_id = EventId("$echo.123") assert new[-1] == (unsent_id, SendStep.failed) assert unsent_id in room.timeline assert list(room.timeline.unsent) == [unsent_id] assert room.timeline.unsent[unsent_id].content == Text("def") assert room.timeline.unsent[unsent_id].sending == SendStep.failed assert not room.timeline.unsent[unsent_id].historic # Failed in unsent but not timeline._data after client restart await room.client.terminate() client2 = Client(room.client.base_dir) new = [] cb = lambda room, event: new.append((event.id, event.sending)) # noqa client2.rooms.callbacks[TimelineEvent].append(cb) await client2.load() assert new == [(unsent_id, SendStep.failed)] timeline2 = client2.rooms[room.id].timeline assert unsent_id in timeline2 assert list(timeline2.unsent) == [unsent_id] assert timeline2.unsent[unsent_id].content == Text("def") assert timeline2.unsent[unsent_id].sending == SendStep.failed assert timeline2.unsent[unsent_id].historic # Retry sending that failed await client2.rooms.join(room.id) event_id = await timeline2.resend_failed(timeline2.unsent[unsent_id]) assert timeline2[event_id].sending == SendStep.sent assert not timeline2.unsent assert new[-1] == (event_id, SendStep.sent) with raises(AssertionError): await timeline2.resend_failed(timeline2[event_id])
async def login_client(client: Client) -> None: print("Creating credentials file") while client.access_token == "": user_id = input("Enter your user or user_id: ") password = input("Enter your password: "******"Enter homeserver: ") client.server = homeserver await client.auth.login_password(user=user_id, password=password) await client.save() print()
async def test_bare_init(alice: Client, tmp_path: Path): client = Client( tmp_path, alice.server, alice.device_id, alice.user_id, alice.access_token, ) assert client.path == AsyncPath(tmp_path / "client.json") assert read_json(client.path) == { "server": alice.server, "user_id": alice.user_id, "access_token": alice.access_token, "device_id": alice.device_id, }
async def new_device_from(client: Client, path: Path) -> Client: new = Client(path, client.server) return await new.auth.login_password(client.user_id, "test")
async def __getattr__(self, name: str) -> Client: name = f"{name}.{uuid4()}" path = self.path / "{user_id}.{device_id}" self.synapse.register(name) client = Client(path, self.synapse.url) return await client.auth.login_password(name, "test")
async def test_remove_terminal_logging(tmp_path: Path, capsys): async with Client(tmp_path) as client: client.remove_terminal_logging() client.crit("test") assert not capsys.readouterr().err assert await client.current_log_file.read_text()