# # 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: # . # """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)