Skip to content

Commit c6eedae

Browse files
committed
Move storage class tests to own test suite.
1 parent b26c3da commit c6eedae

2 files changed

Lines changed: 191 additions & 168 deletions

File tree

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
<?php
2+
/**
3+
* Tests for the WP_Collaboration_Table_Storage class.
4+
*
5+
* Covers the storage implementation contract: cache bypass, data integrity,
6+
* malformed data handling, and race-condition safety.
7+
*
8+
* @package WordPress
9+
* @subpackage Collaboration
10+
*
11+
* @group collaboration
12+
* @group cache
13+
*/
14+
class Tests_Collaboration_WpCollaborationTableStorage extends WP_UnitTestCase {
15+
16+
public function set_up() {
17+
parent::set_up();
18+
add_filter( 'pre_option_wp_collaboration_enabled', '__return_true' );
19+
}
20+
21+
/**
22+
* Returns the number of awareness rows in the collaboration table.
23+
*
24+
* @return positive-int Row count.
25+
*/
26+
private function get_awareness_row_count(): int {
27+
global $wpdb;
28+
29+
return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->collaboration} WHERE type = 'awareness'" );
30+
}
31+
32+
/**
33+
* @ticket 64696
34+
*/
35+
public function test_collaboration_storage_add_update_rejects_empty_room(): void {
36+
$storage = new WP_Collaboration_Table_Storage();
37+
$result = $storage->add_update(
38+
'',
39+
array(
40+
'type' => 'update',
41+
'client_id' => '1',
42+
'data' => 'test',
43+
)
44+
);
45+
$this->assertFalse( $result, 'add_update should reject an empty room.' );
46+
}
47+
48+
/**
49+
* @ticket 64696
50+
*/
51+
public function test_collaboration_storage_add_update_rejects_empty_type(): void {
52+
$storage = new WP_Collaboration_Table_Storage();
53+
$result = $storage->add_update(
54+
'postType/post:1',
55+
array(
56+
'type' => '',
57+
'client_id' => '1',
58+
'data' => 'test',
59+
)
60+
);
61+
$this->assertFalse( $result, 'add_update should reject an empty type.' );
62+
}
63+
64+
/**
65+
* @ticket 64696
66+
*/
67+
public function test_collaboration_storage_add_update_rejects_empty_client_id(): void {
68+
$storage = new WP_Collaboration_Table_Storage();
69+
$result = $storage->add_update(
70+
'postType/post:1',
71+
array(
72+
'type' => 'update',
73+
'client_id' => '',
74+
'data' => 'test',
75+
)
76+
);
77+
$this->assertFalse( $result, 'add_update should reject an empty client_id.' );
78+
}
79+
80+
/**
81+
* @ticket 64696
82+
*/
83+
public function test_collaboration_storage_set_awareness_rejects_empty_room(): void {
84+
$storage = new WP_Collaboration_Table_Storage();
85+
$result = $storage->set_awareness_state( '', '1', array( 'user' => 'test' ), 1 );
86+
$this->assertFalse( $result, 'set_awareness_state should reject an empty room.' );
87+
}
88+
89+
/**
90+
* @ticket 64696
91+
*/
92+
public function test_collaboration_storage_set_awareness_rejects_empty_client_id(): void {
93+
$storage = new WP_Collaboration_Table_Storage();
94+
$result = $storage->set_awareness_state( 'postType/post:1', '', array( 'user' => 'test' ), 1 );
95+
$this->assertFalse( $result, 'set_awareness_state should reject an empty client_id.' );
96+
}
97+
/**
98+
* Ensure awareness updates are not stored in the DB for sites using a persistent cache.
99+
*/
100+
public function test_awareness_uses_persistent_object_cache() {
101+
if ( ! wp_using_ext_object_cache() ) {
102+
$this->markTestSkipped( 'This test requires that an external object cache is in use.' );
103+
}
104+
105+
$storage = new WP_Collaboration_Table_Storage();
106+
$db_calls_initial = get_num_queries();
107+
$storage->set_awareness_state( 'test-room', 'test-client', array( 'name' => 'Test Client' ), 1 );
108+
$db_calls_after = get_num_queries();
109+
110+
$this->assertSame( 0, $db_calls_after - $db_calls_initial, 'Awareness update should not trigger database queries when using persistent object cache.' );
111+
$this->assertSame( 0, $this->get_awareness_row_count(), 'Awareness row should not be stored in database when using persistent object cache.' );
112+
}
113+
114+
/**
115+
* Ensure awareness retrieval uses in-memory cache within a single request, even when a persistent cache is in use.
116+
*/
117+
public function test_awareness_uses_in_memory_cache() {
118+
if ( wp_using_ext_object_cache() ) {
119+
$this->markTestSkipped( 'This test requires that an external object cache is not in use.' );
120+
}
121+
122+
$storage = new WP_Collaboration_Table_Storage();
123+
$db_calls_initial = get_num_queries();
124+
$storage->set_awareness_state( 'test-room', 'test-client', array( 'name' => 'Test Client' ), 1 );
125+
$db_calls_after = get_num_queries();
126+
127+
$this->assertSame( 3, $db_calls_after - $db_calls_initial, 'Awareness update should not trigger database queries when using persistent object cache.' );
128+
$this->assertSame( 1, $this->get_awareness_row_count(), 'Awareness row should not be stored in database when using persistent object cache.' );
129+
130+
$db_calls_initial = get_num_queries();
131+
$storage->get_awareness_state( 'test-room' );
132+
$db_calls_after = get_num_queries();
133+
134+
$this->assertSame( 1, $db_calls_after - $db_calls_initial, 'Initial awareness retrieval should query database.' );
135+
136+
$db_calls_initial = get_num_queries();
137+
$storage->get_awareness_state( 'test-room' );
138+
$db_calls_after = get_num_queries();
139+
140+
$this->assertSame( 0, $db_calls_after - $db_calls_initial, 'Subsequent awareness retrieval should use in-memory cache and not query database.' );
141+
}
142+
143+
/**
144+
* Ensure adding subsequent client does not remove existing clients from room.
145+
*/
146+
public function test_awareness_updates_for_multiple_users() {
147+
$storage = new WP_Collaboration_Table_Storage();
148+
149+
// User 1 sets awareness.
150+
$storage->set_awareness_state( 'test-room', 'client-1', array( 'name' => 'Client 1' ), 1 );
151+
152+
// User 2 sets awareness.
153+
$storage->set_awareness_state( 'test-room', 'client-2', array( 'name' => 'Client 2' ), 2 );
154+
155+
// Retrieve awareness state and verify both users are present.
156+
$awareness = $storage->get_awareness_state( 'test-room' );
157+
$clients = wp_list_pluck( $awareness, 'client_id' );
158+
159+
$this->assertContains( 'client-1', $clients, 'Client 1 should be present in awareness state.' );
160+
$this->assertContains( 'client-2', $clients, 'Client 2 should be present in awareness state.' );
161+
}
162+
163+
/**
164+
* Ensure awareness does not include out of date clients from cached results.
165+
*/
166+
public function test_awareness_excludes_expired_clients_from_cached_results() {
167+
$storage = new WP_Collaboration_Table_Storage();
168+
$cached_data = array(
169+
array(
170+
'client_id' => 'client-1',
171+
'state' => array( 'name' => 'Client 1' ),
172+
'timestamp' => time() - 120, // Simulate expired client.
173+
),
174+
array(
175+
'client_id' => 'client-2',
176+
'state' => array( 'name' => 'Client 2' ),
177+
'timestamp' => time(), // Active client.
178+
),
179+
);
180+
181+
// Manually set cached awareness data.
182+
wp_cache_set( 'awareness::test-room', $cached_data, 'collaboration', HOUR_IN_SECONDS );
183+
184+
$awareness = $storage->get_awareness_state( 'test-room' );
185+
$clients = wp_list_pluck( $awareness, 'client_id' );
186+
187+
$this->assertNotContains( 'client-1', $clients, 'Expired client should not be present in awareness state.' );
188+
$this->assertContains( 'client-2', $clients, 'Active client should be present in awareness state.' );
189+
$this->assertCount( 1, $awareness, 'Only one active client should be present in awareness state.' );
190+
}
191+
}

tests/phpunit/tests/rest-api/rest-collaboration-server.php

Lines changed: 0 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,101 +1342,6 @@ public function test_collaboration_awareness_client_reactivates_after_expiry():
13421342
$this->assertSame( 1, $row_count, 'Should have exactly one awareness row after reactivation.' );
13431343
}
13441344

1345-
/**
1346-
* Ensure awareness updates are not stored in the DB for sites using a persistent cache.
1347-
*/
1348-
public function test_awareness_uses_persistent_object_cache() {
1349-
if ( ! wp_using_ext_object_cache() ) {
1350-
$this->markTestSkipped( 'This test requires that an external object cache is in use.' );
1351-
}
1352-
1353-
$storage = new WP_Collaboration_Table_Storage();
1354-
$db_calls_initial = get_num_queries();
1355-
$storage->set_awareness_state( 'test-room', 'test-client', array( 'name' => 'Test Client' ), 1 );
1356-
$db_calls_after = get_num_queries();
1357-
1358-
$this->assertSame( 0, $db_calls_after - $db_calls_initial, 'Awareness update should not trigger database queries when using persistent object cache.' );
1359-
$this->assertSame( 0, $this->get_awareness_row_count(), 'Awareness row should not be stored in database when using persistent object cache.' );
1360-
}
1361-
1362-
/**
1363-
* Ensure awareness retrieval uses in-memory cache within a single request, even when a persistent cache is in use.
1364-
*/
1365-
public function test_awareness_uses_in_memory_cache() {
1366-
if ( wp_using_ext_object_cache() ) {
1367-
$this->markTestSkipped( 'This test requires that an external object cache is not in use.' );
1368-
}
1369-
1370-
$storage = new WP_Collaboration_Table_Storage();
1371-
$db_calls_initial = get_num_queries();
1372-
$storage->set_awareness_state( 'test-room', 'test-client', array( 'name' => 'Test Client' ), 1 );
1373-
$db_calls_after = get_num_queries();
1374-
1375-
$this->assertSame( 2, $db_calls_after - $db_calls_initial, 'Awareness update should not trigger database queries when using persistent object cache.' );
1376-
$this->assertSame( 1, $this->get_awareness_row_count(), 'Awareness row should not be stored in database when using persistent object cache.' );
1377-
1378-
$db_calls_initial = get_num_queries();
1379-
$storage->get_awareness_state( 'test-room' );
1380-
$db_calls_after = get_num_queries();
1381-
1382-
$this->assertSame( 1, $db_calls_after - $db_calls_initial, 'Initial awareness retrieval should query database.' );
1383-
1384-
$db_calls_initial = get_num_queries();
1385-
$storage->get_awareness_state( 'test-room' );
1386-
$db_calls_after = get_num_queries();
1387-
1388-
$this->assertSame( 0, $db_calls_after - $db_calls_initial, 'Subsequent awareness retrieval should use in-memory cache and not query database.' );
1389-
}
1390-
1391-
/**
1392-
* Ensure adding subsequent client does not remove existing clients from room.
1393-
*/
1394-
public function test_awareness_updates_for_multiple_users() {
1395-
$storage = new WP_Collaboration_Table_Storage();
1396-
1397-
// User 1 sets awareness.
1398-
$storage->set_awareness_state( 'test-room', 'client-1', array( 'name' => 'Client 1' ), 1 );
1399-
1400-
// User 2 sets awareness.
1401-
$storage->set_awareness_state( 'test-room', 'client-2', array( 'name' => 'Client 2' ), 2 );
1402-
1403-
// Retrieve awareness state and verify both users are present.
1404-
$awareness = $storage->get_awareness_state( 'test-room' );
1405-
$clients = wp_list_pluck( $awareness, 'client_id' );
1406-
1407-
$this->assertContains( 'client-1', $clients, 'Client 1 should be present in awareness state.' );
1408-
$this->assertContains( 'client-2', $clients, 'Client 2 should be present in awareness state.' );
1409-
}
1410-
1411-
/**
1412-
* Ensure awareness does not include out of date clients from cached results.
1413-
*/
1414-
public function test_awareness_excludes_expired_clients_from_cached_results() {
1415-
$storage = new WP_Collaboration_Table_Storage();
1416-
$cached_data = array(
1417-
array(
1418-
'client_id' => 'client-1',
1419-
'state' => array( 'name' => 'Client 1' ),
1420-
'timestamp' => time() - 120, // Simulate expired client.
1421-
),
1422-
array(
1423-
'client_id' => 'client-2',
1424-
'state' => array( 'name' => 'Client 2' ),
1425-
'timestamp' => time(), // Active client.
1426-
),
1427-
);
1428-
1429-
// Manually set cached awareness data.
1430-
wp_cache_set( 'awareness::test-room', $cached_data, 'collaboration', HOUR_IN_SECONDS );
1431-
1432-
$awareness = $storage->get_awareness_state( 'test-room' );
1433-
$clients = wp_list_pluck( $awareness, 'client_id' );
1434-
1435-
$this->assertNotContains( 'client-1', $clients, 'Expired client should not be present in awareness state.' );
1436-
$this->assertContains( 'client-2', $clients, 'Active client should be present in awareness state.' );
1437-
$this->assertCount( 1, $awareness, 'Only one active client should be present in awareness state.' );
1438-
}
1439-
14401345
/*
14411346
* Multiple rooms tests.
14421347
*/
@@ -3231,77 +3136,4 @@ public function test_collaboration_table_accepts_arbitrary_types(): void {
32313136
$this->assertNotNull( $row, 'Custom type row should be queryable.' );
32323137
$this->assertSame( 'persisted_crdt_doc', $row->type, 'Type column should store the custom value.' );
32333138
}
3234-
3235-
/*
3236-
* Storage validation tests.
3237-
*
3238-
* Verify that storage methods reject empty required fields
3239-
* rather than inserting rows with default empty values.
3240-
*/
3241-
3242-
/**
3243-
* @ticket 64696
3244-
*/
3245-
public function test_collaboration_storage_add_update_rejects_empty_room(): void {
3246-
$storage = new WP_Collaboration_Table_Storage();
3247-
$result = $storage->add_update(
3248-
'',
3249-
array(
3250-
'type' => 'update',
3251-
'client_id' => '1',
3252-
'data' => 'test',
3253-
)
3254-
);
3255-
$this->assertFalse( $result, 'add_update should reject an empty room.' );
3256-
}
3257-
3258-
/**
3259-
* @ticket 64696
3260-
*/
3261-
public function test_collaboration_storage_add_update_rejects_empty_type(): void {
3262-
$storage = new WP_Collaboration_Table_Storage();
3263-
$result = $storage->add_update(
3264-
'postType/post:1',
3265-
array(
3266-
'type' => '',
3267-
'client_id' => '1',
3268-
'data' => 'test',
3269-
)
3270-
);
3271-
$this->assertFalse( $result, 'add_update should reject an empty type.' );
3272-
}
3273-
3274-
/**
3275-
* @ticket 64696
3276-
*/
3277-
public function test_collaboration_storage_add_update_rejects_empty_client_id(): void {
3278-
$storage = new WP_Collaboration_Table_Storage();
3279-
$result = $storage->add_update(
3280-
'postType/post:1',
3281-
array(
3282-
'type' => 'update',
3283-
'client_id' => '',
3284-
'data' => 'test',
3285-
)
3286-
);
3287-
$this->assertFalse( $result, 'add_update should reject an empty client_id.' );
3288-
}
3289-
3290-
/**
3291-
* @ticket 64696
3292-
*/
3293-
public function test_collaboration_storage_set_awareness_rejects_empty_room(): void {
3294-
$storage = new WP_Collaboration_Table_Storage();
3295-
$result = $storage->set_awareness_state( '', '1', array( 'user' => 'test' ), 1 );
3296-
$this->assertFalse( $result, 'set_awareness_state should reject an empty room.' );
3297-
}
3298-
3299-
/**
3300-
* @ticket 64696
3301-
*/
3302-
public function test_collaboration_storage_set_awareness_rejects_empty_client_id(): void {
3303-
$storage = new WP_Collaboration_Table_Storage();
3304-
$result = $storage->set_awareness_state( 'postType/post:1', '', array( 'user' => 'test' ), 1 );
3305-
$this->assertFalse( $result, 'set_awareness_state should reject an empty client_id.' );
3306-
}
33073139
}

0 commit comments

Comments
 (0)