@@ -228,4 +228,97 @@ public function test_awareness_getter_is_of_correct_shape() {
228228 $ this ->assertIsInt ( $ awareness [0 ]['user_id ' ], 'Client state user_id should be an integer. ' );
229229 $ this ->assertIsInt ( $ awareness [0 ]['timestamp ' ], 'Client state timestamp should be an integer. ' );
230230 }
231+
232+ /*
233+ * Data integrity tests.
234+ */
235+
236+ public function test_get_updates_after_cursor_drops_malformed_json () {
237+ global $ wpdb ;
238+
239+ $ storage = new WP_Collaboration_Table_Storage ();
240+ $ room = __FUNCTION__ ;
241+
242+ // Advance cursor past the seed update from create_storage_post().
243+ $ storage ->get_updates_after_cursor ( $ room , 0 );
244+ $ cursor = $ storage ->get_cursor ( $ room );
245+
246+ // Insert a valid update.
247+ $ valid_update = array (
248+ 'type ' => 'update ' ,
249+ 'data ' => 'dGVzdA== ' ,
250+ 'client_id ' => '1 ' ,
251+ );
252+ $ this ->assertTrue ( $ storage ->add_update ( $ room , $ valid_update ) );
253+
254+ // Insert a malformed JSON row directly into the database.
255+ $ wpdb ->insert (
256+ $ wpdb ->collaboration ,
257+ array (
258+ 'room ' => $ room ,
259+ 'type ' => 'update ' ,
260+ 'client_id ' => '1 ' ,
261+ 'data ' => '{invalid json ' ,
262+ ),
263+ array ( '%s ' , '%s ' , '%s ' , '%s ' )
264+ );
265+
266+ // Insert another valid update after the malformed one.
267+ $ valid_update_2 = array (
268+ 'type ' => 'sync_step1 ' ,
269+ 'client_id ' => '1 ' ,
270+ 'data ' => 'c3RlcDE= ' ,
271+ );
272+ $ this ->assertTrue ( $ storage ->add_update ( $ room , $ valid_update_2 ) );
273+
274+ $ updates = $ storage ->get_updates_after_cursor ( $ room , $ cursor );
275+
276+ // The malformed row should be dropped; only the valid updates should appear.
277+ $ this ->assertCount ( 2 , $ updates );
278+ $ this ->assertSame ( $ valid_update , $ updates [0 ] );
279+ $ this ->assertSame ( $ valid_update_2 , $ updates [1 ] );
280+ }
281+
282+ public function test_duplicate_awareness_rows_coalesces_on_latest_row () {
283+ if ( ! wp_using_ext_object_cache () ) {
284+ $ this ->markTestSkipped ( 'This test requires that an external object cache is in use. ' );
285+ }
286+
287+ global $ wpdb ;
288+
289+ $ storage = new WP_Collaboration_Table_Storage ();
290+ $ room = __FUNCTION__ ;
291+
292+ // Simulate a race: insert two awareness rows directly.
293+ $ wpdb ->insert (
294+ $ wpdb ->postmeta ,
295+ array (
296+ 'room ' => $ room ,
297+ 'type ' => 'awareness ' ,
298+ 'client_id ' => '1 ' ,
299+ 'user_id ' => 1 ,
300+ 'data ' => wp_json_encode ( array ( 1 => array ( 'name ' => 'Stale ' ) ) ),
301+ ),
302+ array ( '%s ' , '%s ' , '%s ' , '%d ' , '%s ' )
303+ );
304+
305+ $ wpdb ->insert (
306+ $ wpdb ->postmeta ,
307+ array (
308+ 'room ' => $ room ,
309+ 'type ' => 'awareness ' ,
310+ 'client_id ' => '1 ' ,
311+ 'user_id ' => 1 ,
312+ 'data ' => wp_json_encode ( array ( 1 => array ( 'name ' => 'Latest ' ) ) ),
313+ ),
314+ array ( '%s ' , '%s ' , '%s ' , '%d ' , '%s ' )
315+ );
316+
317+ // get_awareness_state and set_awareness_state should target the latest row.
318+ $ awareness = $ storage ->get_awareness_state ( $ room );
319+ $ this ->assertSame ( array ( 'name ' => 'Latest ' ), $ awareness [0 ] );
320+ $ storage ->set_awareness_state ( $ room , '1 ' , array ( 'name ' => 'Current ' ), 1 );
321+ $ awareness = $ storage ->get_awareness_state ( $ room );
322+ $ this ->assertSame ( array ( 'name ' => 'Current ' ), $ awareness [0 ] );
323+ }
231324}
0 commit comments