Skip to content

Commit 45cf5b7

Browse files
authored
Merge pull request #670 from ably/fix/preserve-extras-in-send-update
fix: preserve extras and annotations in _send_update()
2 parents 787da08 + c0807ab commit 45cf5b7

5 files changed

Lines changed: 124 additions & 0 deletions

File tree

ably/realtime/channel.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,8 @@ async def _send_update(
526526
serial=message.serial,
527527
action=action,
528528
version=version,
529+
extras=message.extras,
530+
annotations=message.annotations,
529531
)
530532

531533
# Encrypt if needed

ably/rest/channel.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ async def _send_update(
194194
serial=message.serial,
195195
action=action,
196196
version=version,
197+
extras=message.extras,
198+
annotations=message.annotations,
197199
)
198200

199201
# Encrypt if needed

test/ably/realtime/realtimechannelmutablemessages_test.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,43 @@ def on_message(message):
270270
assert messages_received[1].action == MessageAction.MESSAGE_APPEND
271271
assert messages_received[1].version.serial == second_append_result.version_serial
272272

273+
# RTL32b, TM2i
274+
async def test_update_message_preserves_extras(self):
275+
"""Test that extras are preserved when updating a message"""
276+
channel = self.ably.channels[self.get_channel_name('mutable:update_extras')]
277+
278+
# Publish a message
279+
result = await channel.publish('test-event', 'original data')
280+
assert len(result.serials) > 0
281+
serial = result.serials[0]
282+
283+
messages_received = []
284+
update_received = WaitableEvent()
285+
286+
def on_message(message):
287+
if message.action == MessageAction.MESSAGE_UPDATE:
288+
messages_received.append(message)
289+
update_received.finish()
290+
291+
await channel.subscribe(on_message)
292+
293+
# Update with extras
294+
message = Message(
295+
data='updated data',
296+
serial=serial,
297+
extras={'headers': {'status': 'complete'}},
298+
)
299+
300+
update_result = await channel.update_message(message)
301+
assert update_result is not None
302+
303+
await update_received.wait()
304+
305+
assert len(messages_received) > 0
306+
received = messages_received[0]
307+
assert received.extras is not None
308+
assert received.extras['headers']['status'] == 'complete'
309+
273310
async def wait_until_message_with_action_appears(self, channel, serial, action):
274311
message: Message | None = None
275312
async def check_message_action():

test/ably/rest/restchannelmutablemessages_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,33 @@ async def test_append_message_with_string_data(self):
270270
assert appended_message.version.description == 'Appended to message'
271271
assert appended_message.serial == serial
272272

273+
# RSL15b, TM2i
274+
async def test_update_message_preserves_extras(self):
275+
"""Test that extras are preserved when updating a message"""
276+
channel = self.ably.channels[self.get_channel_name('mutable:update_extras')]
277+
278+
# Publish a message
279+
result = await channel.publish('test-event', 'original data')
280+
assert len(result.serials) > 0
281+
serial = result.serials[0]
282+
283+
# Update with extras
284+
message = Message(
285+
data='updated data',
286+
serial=serial,
287+
extras={'headers': {'status': 'complete'}},
288+
)
289+
290+
update_result = await channel.update_message(message)
291+
assert update_result is not None
292+
293+
updated_message = await self.wait_until_message_with_action_appears(
294+
channel, serial, MessageAction.MESSAGE_UPDATE
295+
)
296+
assert updated_message.data == 'updated data'
297+
assert updated_message.extras is not None
298+
assert updated_message.extras['headers']['status'] == 'complete'
299+
273300
async def wait_until_message_with_action_appears(self, channel, serial, action):
274301
message: Message | None = None
275302
async def check_message_action():

test/unit/mutable_message_test.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,62 @@ def test_message_version_serialization():
9696
assert reconstructed.description == version.description
9797
assert reconstructed.metadata == version.metadata
9898

99+
# RSL15b, RTL32b, TM2i
100+
def test_message_extras_preserved_in_as_dict():
101+
"""Test that extras are included when a Message with extras is serialized.
102+
103+
Regression test: _send_update() in both RestChannel and RealtimeChannel
104+
constructed a new Message without copying extras or annotations from the
105+
user-supplied message, violating RSL15b/RTL32b which require "whatever
106+
fields were in the user-supplied Message" to be sent.
107+
See commits 1723f5d (REST) and 0b93c10 (Realtime).
108+
"""
109+
extras = {'headers': {'status': 'complete'}}
110+
message = Message(
111+
name='test',
112+
data='updated data',
113+
serial='abc123',
114+
action=MessageAction.MESSAGE_UPDATE,
115+
extras=extras,
116+
)
117+
118+
msg_dict = message.as_dict()
119+
assert msg_dict['extras'] == extras
120+
assert msg_dict['extras']['headers']['status'] == 'complete'
121+
122+
123+
# RSL15b, RTL32b, TM2i
124+
def test_message_extras_none_excluded_from_as_dict():
125+
"""Test that extras=None does not appear in as_dict output."""
126+
message = Message(
127+
name='test',
128+
data='data',
129+
serial='abc123',
130+
action=MessageAction.MESSAGE_UPDATE,
131+
)
132+
133+
msg_dict = message.as_dict()
134+
assert 'extras' not in msg_dict
135+
136+
137+
# RSL15b, RTL32b, TM2u
138+
def test_message_annotations_preserved_in_as_dict():
139+
"""Test that annotations are included when a Message with annotations is serialized."""
140+
from ably.types.message import MessageAnnotations
141+
annotations = MessageAnnotations(summary={'reaction:distinct.v1': {'thumbsup': 5}})
142+
message = Message(
143+
name='test',
144+
data='data',
145+
serial='abc123',
146+
action=MessageAction.MESSAGE_UPDATE,
147+
annotations=annotations,
148+
)
149+
150+
msg_dict = message.as_dict()
151+
assert msg_dict['annotations'] is not None
152+
assert msg_dict['annotations']['summary']['reaction:distinct.v1'] == {'thumbsup': 5}
153+
154+
99155
def test_message_operation_serialization():
100156
"""Test MessageOperation can be serialized and deserialized"""
101157
operation = MessageOperation(

0 commit comments

Comments
 (0)