mirror of
https://github.com/element-hq/synapse.git
synced 2026-05-14 14:45:10 +00:00
tests: add make_test_event helper for stricter event construction
Prepare for porting the event class into Rust, where the constructor strictly validates that all format-required fields (depth, hashes, origin_server_ts, auth_events, prev_events, ...) are present. Most tests build minimal dicts that omit these fields because they only care about the fields the test exercises. Introduce make_test_event and make_test_pdu_event, which layer format-version-aware defaults on top of caller-supplied fields so individual tests don't need to spell out every required key.
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# 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>.
|
||||
#
|
||||
"""Test-only helpers for building events.
|
||||
|
||||
The Rust `Event` constructor strictly validates that all format-required
|
||||
fields are present on the event dict. Most production code paths always
|
||||
supply these, but tests routinely build minimal dicts that omit fields
|
||||
like `depth`, `hashes`, `origin_server_ts`, `auth_events`, or
|
||||
`prev_events`. This module provides `make_test_event`, which fills in
|
||||
sensible defaults for the required fields based on the event format
|
||||
version, so individual tests only need to specify the fields they
|
||||
actually care about.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from synapse.api.room_versions import (
|
||||
EventFormatVersions,
|
||||
RoomVersion,
|
||||
RoomVersions,
|
||||
)
|
||||
from synapse.events import EventBase, make_event_from_dict
|
||||
from synapse.federation.federation_base import event_from_pdu_json
|
||||
from synapse.types import JsonDict
|
||||
|
||||
|
||||
def default_event_fields(room_version: RoomVersion) -> JsonDict:
|
||||
"""Return the default values for every field required by `room_version`.
|
||||
|
||||
Tests can call this directly when they need to merge defaults into a
|
||||
builder (e.g. inside another helper) rather than constructing the
|
||||
event up-front.
|
||||
"""
|
||||
defaults: JsonDict = {
|
||||
"type": "m.test",
|
||||
"sender": "@test:test",
|
||||
"content": {},
|
||||
"depth": 1,
|
||||
"origin_server_ts": 1,
|
||||
"hashes": {"sha256": ""},
|
||||
}
|
||||
|
||||
if room_version.event_format == EventFormatVersions.ROOM_V1_V2:
|
||||
# V1 events store auth/prev as `[(event_id, hashes)]` pairs and
|
||||
# carry an explicit `event_id` and `room_id`.
|
||||
defaults["auth_events"] = []
|
||||
defaults["prev_events"] = []
|
||||
defaults["room_id"] = "!test:test"
|
||||
defaults["event_id"] = "$test:test"
|
||||
elif room_version.event_format in (
|
||||
EventFormatVersions.ROOM_V3,
|
||||
EventFormatVersions.ROOM_V4_PLUS,
|
||||
):
|
||||
# V2/V3 and V4 share the flat auth/prev list shape. V2/V3 always
|
||||
# carry a `room_id`; V4 makes it optional on create events but
|
||||
# required otherwise, so callers building non-create events
|
||||
# supply it explicitly.
|
||||
defaults["auth_events"] = []
|
||||
defaults["prev_events"] = []
|
||||
defaults["room_id"] = "!test:test"
|
||||
else:
|
||||
# V11 Hydra+ and VMSC4242 derive the room_id from the create
|
||||
# event's ID, so we never default it here — providing one would
|
||||
# break auth-event derivation for create events on these
|
||||
# versions. Callers supply room_id explicitly on non-create
|
||||
# events.
|
||||
defaults["auth_events"] = []
|
||||
defaults["prev_events"] = []
|
||||
|
||||
if room_version.msc4242_state_dags:
|
||||
defaults["prev_state_events"] = []
|
||||
|
||||
return defaults
|
||||
|
||||
|
||||
def make_test_event(
|
||||
event_dict: JsonDict | None = None,
|
||||
room_version: RoomVersion = RoomVersions.V1,
|
||||
internal_metadata_dict: JsonDict | None = None,
|
||||
rejected_reason: str | None = None,
|
||||
**fields: Any,
|
||||
) -> EventBase:
|
||||
"""Build an `EventBase` with defaults for the strict-required fields.
|
||||
|
||||
Pass an `event_dict` and/or `**fields` keyword arguments — both are
|
||||
merged on top of the format-version defaults from
|
||||
`default_event_fields`. Explicit values win over defaults, and
|
||||
`**fields` wins over `event_dict` so call sites can override a
|
||||
shared base dict with one-off tweaks.
|
||||
|
||||
Args:
|
||||
event_dict: Explicit event fields. Wins over defaults; loses to
|
||||
`**fields`.
|
||||
room_version: Determines which format-specific defaults apply.
|
||||
internal_metadata_dict: Forwarded to `make_event_from_dict`.
|
||||
rejected_reason: Forwarded to `make_event_from_dict`.
|
||||
**fields: Additional event fields. Wins over `event_dict`.
|
||||
|
||||
Returns:
|
||||
The constructed `EventBase`.
|
||||
"""
|
||||
merged: JsonDict = {
|
||||
**default_event_fields(room_version),
|
||||
**(event_dict or {}),
|
||||
**fields,
|
||||
}
|
||||
return make_event_from_dict(
|
||||
merged,
|
||||
room_version=room_version,
|
||||
internal_metadata_dict=internal_metadata_dict,
|
||||
rejected_reason=rejected_reason,
|
||||
)
|
||||
|
||||
|
||||
def make_test_pdu_event(
|
||||
pdu: JsonDict,
|
||||
room_version: RoomVersion,
|
||||
received_time: int | None = None,
|
||||
) -> EventBase:
|
||||
"""Wrapper around `event_from_pdu_json` for test PDU dicts.
|
||||
|
||||
Federation-side test fixtures often omit fields the strict Rust ctor
|
||||
requires (e.g. `hashes`, `auth_events`, `prev_events`, `depth`)
|
||||
because those tests focus on transport/auth flow rather than event
|
||||
well-formedness. This helper layers in the same format-version
|
||||
defaults as `make_test_event` before delegating.
|
||||
"""
|
||||
pdu = {**default_event_fields(room_version), **pdu}
|
||||
return event_from_pdu_json(pdu, room_version, received_time=received_time)
|
||||
Reference in New Issue
Block a user