Files
synapse/tests/sync/fixtures.py
T
Erik JohnstonandClaude Fable 5.1 4cc37d280a Port RoomStreamToken and the pagination bounds to Rust
The first of the pure parts of `/sync` that the Rust sync handler needs:
`RoomStreamToken` with its three string forms and its vector clock
arithmetic (`is_before_or_eq`, `copy_and_advance`, `bound_stream_token`),
`persisted_after`, and the pagination helpers of `stream.py` that turn
tokens into bounds and filter the rows that come back by writer. The SQL
is built in the Postgres row-comparison form only, as the handler reads
through the native Postgres driver.

Tokens cross the boundary as the Python class, converted both ways.

The multi-writer filtering is where a bug would be silent, so
`tests/sync/test_rust_stream_token.py` checks every operation against the
Python on random tokens with instance maps. `tests/sync/fixtures.py`
holds what the differential tests that follow share.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194eNQP5weBtVMg8NKtc6VU
2026-10-14 13:35:25 +00:00

62 lines
1.9 KiB
Python

#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright (C) 2026 Element Creations Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#
"""Shared pieces of the differential tests of the Rust sync handler's pure
parts (`test_rust_*.py` in this package): random inputs the Python and Rust
implementations must agree on.
The random cases are seeded, so a failure reproduces.
"""
import random
from immutabledict import immutabledict
from synapse.types import RoomStreamToken
ME = "@me:test"
USERS = [ME, "@alice:test", "@bob:test", "@spam:example.com"]
ROOMS = ["!a:test", "!b:test", "!c:example.com"]
TYPES = [
"m.room.message",
"m.room.member",
"m.room.topic",
"m.receipt",
"m.typing",
"org.example.custom",
]
WRITERS = ["master", "w1", "w2", "w3"]
SEEDS = [(seed,) for seed in range(20)]
def pick_list(
rng: random.Random, values: list[str], wildcard: str | None = None
) -> list[str]:
"""A random sub-list of `values`, sometimes with a wildcard in it."""
chosen = rng.sample(values, rng.randint(0, len(values)))
if wildcard and rng.random() < 0.15:
chosen.append(wildcard)
return chosen
def random_live_token(rng: random.Random) -> RoomStreamToken:
"""A live token with a random instance map over `WRITERS`."""
stream = rng.randint(0, 20)
instance_map = {
name: stream + rng.randint(1, 10)
for name in rng.sample(WRITERS, rng.randint(0, len(WRITERS)))
}
return RoomStreamToken(stream=stream, instance_map=immutabledict(instance_map))