@@ -109,6 +109,8 @@ pub trait StateStoreIntegrationTests {
109109 async fn test_thread_subscriptions ( & self ) -> TestResult ;
110110 /// Test thread subscription bumpstamp semantics.
111111 async fn test_thread_subscriptions_bumpstamps ( & self ) -> TestResult ;
112+ /// Test thread subscriptions bulk upsert, including bumpstamp semantics.
113+ async fn test_thread_subscriptions_bulk_upsert ( & self ) -> TestResult ;
112114}
113115
114116impl StateStoreIntegrationTests for DynStateStore {
@@ -1955,6 +1957,183 @@ impl StateStoreIntegrationTests for DynStateStore {
19551957
19561958 Ok ( ( ) )
19571959 }
1960+
1961+ async fn test_thread_subscriptions_bulk_upsert ( & self ) -> TestResult {
1962+ let threads = [
1963+ event_id ! ( "$t1" ) ,
1964+ event_id ! ( "$t2" ) ,
1965+ event_id ! ( "$t3" ) ,
1966+ event_id ! ( "$t4" ) ,
1967+ event_id ! ( "$t5" ) ,
1968+ event_id ! ( "$t6" ) ,
1969+ ] ;
1970+ // Helper for building the input for `upsert_thread_subscriptions()`,
1971+ // which is of the type: Vec<(&RoomId, &EventId, StoredThreadSubscription)>
1972+ let build_subscription_updates = |subs : & [ StoredThreadSubscription ] | {
1973+ threads
1974+ . iter ( )
1975+ . zip ( subs)
1976+ . map ( |( & event_id, & sub) | ( room_id ( ) , event_id, sub) )
1977+ . collect :: < Vec < _ > > ( )
1978+ } ;
1979+
1980+ // Test bump_stamp logic
1981+ let initial_subscriptions = build_subscription_updates ( & [
1982+ StoredThreadSubscription {
1983+ status : ThreadSubscriptionStatus :: Unsubscribed ,
1984+ bump_stamp : None ,
1985+ } ,
1986+ StoredThreadSubscription {
1987+ status : ThreadSubscriptionStatus :: Unsubscribed ,
1988+ bump_stamp : Some ( 14 ) ,
1989+ } ,
1990+ StoredThreadSubscription {
1991+ status : ThreadSubscriptionStatus :: Unsubscribed ,
1992+ bump_stamp : None ,
1993+ } ,
1994+ StoredThreadSubscription {
1995+ status : ThreadSubscriptionStatus :: Unsubscribed ,
1996+ bump_stamp : Some ( 210 ) ,
1997+ } ,
1998+ StoredThreadSubscription {
1999+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2000+ bump_stamp : Some ( 5 ) ,
2001+ } ,
2002+ StoredThreadSubscription {
2003+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2004+ bump_stamp : Some ( 100 ) ,
2005+ } ,
2006+ ] ) ;
2007+
2008+ let update_subscriptions = build_subscription_updates ( & [
2009+ StoredThreadSubscription {
2010+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2011+ bump_stamp : None ,
2012+ } ,
2013+ StoredThreadSubscription {
2014+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2015+ bump_stamp : None ,
2016+ } ,
2017+ StoredThreadSubscription {
2018+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2019+ bump_stamp : Some ( 1101 ) ,
2020+ } ,
2021+ StoredThreadSubscription {
2022+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2023+ bump_stamp : Some ( 222 ) ,
2024+ } ,
2025+ StoredThreadSubscription {
2026+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2027+ bump_stamp : Some ( 1 ) ,
2028+ } ,
2029+ StoredThreadSubscription {
2030+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2031+ bump_stamp : Some ( 100 ) ,
2032+ } ,
2033+ ] ) ;
2034+
2035+ let expected_subscriptions = build_subscription_updates ( & [
2036+ // Status should be updated, because prev and new bump_stamp are both None
2037+ StoredThreadSubscription {
2038+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2039+ bump_stamp : None ,
2040+ } ,
2041+ // Status should be updated, but keep initial bump_stamp (new is None)
2042+ StoredThreadSubscription {
2043+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2044+ bump_stamp : Some ( 14 ) ,
2045+ } ,
2046+ // Status should be updated and also bump_stamp should be updated (inital was None)
2047+ StoredThreadSubscription {
2048+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2049+ bump_stamp : Some ( 1101 ) ,
2050+ } ,
2051+ // Status should be updated and also bump_stamp should be updated (inital was lower)
2052+ StoredThreadSubscription {
2053+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2054+ bump_stamp : Some ( 222 ) ,
2055+ } ,
2056+ // Status shouldn't change, as new bump_stamp is lower
2057+ StoredThreadSubscription {
2058+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2059+ bump_stamp : Some ( 5 ) ,
2060+ } ,
2061+ // Status shouldn't change, as bump_stamp is equal to the previous one
2062+ StoredThreadSubscription {
2063+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2064+ bump_stamp : Some ( 100 ) ,
2065+ } ,
2066+ ] ) ;
2067+
2068+ // Set the initial subscriptions
2069+ self . upsert_thread_subscriptions ( initial_subscriptions. clone ( ) ) . await ?;
2070+
2071+ // Assert the subscriptions have been added
2072+ for ( room_id, thread_id, expected_sub) in & initial_subscriptions {
2073+ let stored_subscription = self . load_thread_subscription ( room_id, thread_id) . await ?;
2074+ assert_eq ! ( stored_subscription, Some ( * expected_sub) ) ;
2075+ }
2076+
2077+ // Update subscriptions
2078+ self . upsert_thread_subscriptions ( update_subscriptions) . await ?;
2079+
2080+ // Assert the expected subscriptions and bump_stamps
2081+ for ( room_id, thread_id, expected_sub) in & expected_subscriptions {
2082+ let stored_subscription = self . load_thread_subscription ( room_id, thread_id) . await ?;
2083+ assert_eq ! ( stored_subscription, Some ( * expected_sub) ) ;
2084+ }
2085+
2086+ // Test just state changes, but first remove previous subscriptions
2087+ for ( room_id, thread_id, _) in & expected_subscriptions {
2088+ self . remove_thread_subscription ( room_id, thread_id) . await ?;
2089+ }
2090+
2091+ let initial_subscriptions = build_subscription_updates ( & [
2092+ StoredThreadSubscription {
2093+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2094+ bump_stamp : Some ( 1 ) ,
2095+ } ,
2096+ StoredThreadSubscription {
2097+ status : ThreadSubscriptionStatus :: Subscribed { automatic : false } ,
2098+ bump_stamp : Some ( 1 ) ,
2099+ } ,
2100+ StoredThreadSubscription {
2101+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2102+ bump_stamp : Some ( 1 ) ,
2103+ } ,
2104+ ] ) ;
2105+
2106+ self . upsert_thread_subscriptions ( initial_subscriptions. clone ( ) ) . await ?;
2107+
2108+ for ( room_id, thread_id, expected_sub) in & initial_subscriptions {
2109+ let stored_subscription = self . load_thread_subscription ( room_id, thread_id) . await ?;
2110+ assert_eq ! ( stored_subscription, Some ( * expected_sub) ) ;
2111+ }
2112+
2113+ let update_subscriptions = build_subscription_updates ( & [
2114+ StoredThreadSubscription {
2115+ status : ThreadSubscriptionStatus :: Subscribed { automatic : true } ,
2116+ bump_stamp : Some ( 2 ) ,
2117+ } ,
2118+ StoredThreadSubscription {
2119+ status : ThreadSubscriptionStatus :: Unsubscribed ,
2120+ bump_stamp : Some ( 2 ) ,
2121+ } ,
2122+ StoredThreadSubscription {
2123+ status : ThreadSubscriptionStatus :: Subscribed { automatic : false } ,
2124+ bump_stamp : Some ( 2 ) ,
2125+ } ,
2126+ ] ) ;
2127+
2128+ self . upsert_thread_subscriptions ( update_subscriptions. clone ( ) ) . await ?;
2129+
2130+ for ( room_id, thread_id, expected_sub) in & update_subscriptions {
2131+ let stored_subscription = self . load_thread_subscription ( room_id, thread_id) . await ?;
2132+ assert_eq ! ( stored_subscription, Some ( * expected_sub) ) ;
2133+ }
2134+
2135+ Ok ( ( ) )
2136+ }
19582137}
19592138
19602139/// Macro building to allow your StateStore implementation to run the entire
@@ -2141,6 +2320,12 @@ macro_rules! statestore_integration_tests {
21412320 let store = get_store( ) . await ?. into_state_store( ) ;
21422321 store. test_thread_subscriptions_bumpstamps( ) . await
21432322 }
2323+
2324+ #[ async_test]
2325+ async fn test_thread_subscriptions_bulk_upsert( ) -> TestResult {
2326+ let store = get_store( ) . await ?. into_state_store( ) ;
2327+ store. test_thread_subscriptions_bulk_upsert( ) . await
2328+ }
21442329 }
21452330 } ;
21462331}
0 commit comments