@@ -518,7 +518,7 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
518
518
{ name : 'negativeMaxSafeIntegerCounter' , count : - Number . MAX_SAFE_INTEGER } ,
519
519
] ;
520
520
521
- const stateSyncSequenceScanarios = [
521
+ const stateSyncSequenceScenarios = [
522
522
{
523
523
description : 'STATE_SYNC sequence with state object "tombstone" property creates tombstoned object' ,
524
524
action : async ( ctx ) => {
@@ -2060,9 +2060,292 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
2060
2060
} ,
2061
2061
] ;
2062
2062
2063
+ const writeApiScenarios = [
2064
+ {
2065
+ description : 'LiveCounter.increment sends COUNTER_INC operation' ,
2066
+ action : async ( ctx ) => {
2067
+ const { root, liveObjectsHelper, channelName } = ctx ;
2068
+
2069
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2070
+ mapObjectId : 'root' ,
2071
+ key : 'counter' ,
2072
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2073
+ } ) ;
2074
+
2075
+ const counter = root . get ( 'counter' ) ;
2076
+ const increments = [
2077
+ 1 , // value=1
2078
+ 10 , // value=11
2079
+ - 11 , // value=0
2080
+ - 1 , // value=-1
2081
+ - 10 , // value=-11
2082
+ 11 , // value=0
2083
+ Number . MAX_SAFE_INTEGER , // value=9007199254740991
2084
+ - Number . MAX_SAFE_INTEGER , // value=0
2085
+ - Number . MAX_SAFE_INTEGER , // value=-9007199254740991
2086
+ ] ;
2087
+ let expectedCounterValue = 0 ;
2088
+
2089
+ for ( let i = 0 ; i < increments . length ; i ++ ) {
2090
+ const increment = increments [ i ] ;
2091
+ expectedCounterValue += increment ;
2092
+ await counter . increment ( increment ) ;
2093
+
2094
+ expect ( counter . value ( ) ) . to . equal (
2095
+ expectedCounterValue ,
2096
+ `Check counter has correct value after ${ i + 1 } LiveCounter.increment calls` ,
2097
+ ) ;
2098
+ }
2099
+ } ,
2100
+ } ,
2101
+
2102
+ {
2103
+ description : 'LiveCounter.increment throws on invalid input' ,
2104
+ action : async ( ctx ) => {
2105
+ const { root, liveObjectsHelper, channelName } = ctx ;
2106
+
2107
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2108
+ mapObjectId : 'root' ,
2109
+ key : 'counter' ,
2110
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2111
+ } ) ;
2112
+
2113
+ const counter = root . get ( 'counter' ) ;
2114
+
2115
+ expect ( ( ) => counter . increment ( ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2116
+ expect ( ( ) => counter . increment ( null ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2117
+ expect ( ( ) => counter . increment ( 'foo' ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2118
+ expect ( ( ) => counter . increment ( BigInt ( 1 ) ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2119
+ expect ( ( ) => counter . increment ( true ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2120
+ expect ( ( ) => counter . increment ( Symbol ( ) ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2121
+ expect ( ( ) => counter . increment ( { } ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2122
+ expect ( ( ) => counter . increment ( [ ] ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2123
+ expect ( ( ) => counter . increment ( counter ) ) . to . throw ( 'Counter value increment should be a number' ) ;
2124
+ } ,
2125
+ } ,
2126
+
2127
+ {
2128
+ description : 'LiveCounter.decrement sends COUNTER_INC operation' ,
2129
+ action : async ( ctx ) => {
2130
+ const { root, liveObjectsHelper, channelName } = ctx ;
2131
+
2132
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2133
+ mapObjectId : 'root' ,
2134
+ key : 'counter' ,
2135
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2136
+ } ) ;
2137
+
2138
+ const counter = root . get ( 'counter' ) ;
2139
+ const decrements = [
2140
+ 1 , // value=-1
2141
+ 10 , // value=-11
2142
+ - 11 , // value=0
2143
+ - 1 , // value=1
2144
+ - 10 , // value=11
2145
+ 11 , // value=0
2146
+ Number . MAX_SAFE_INTEGER , // value=-9007199254740991
2147
+ - Number . MAX_SAFE_INTEGER , // value=0
2148
+ - Number . MAX_SAFE_INTEGER , // value=9007199254740991
2149
+ ] ;
2150
+ let expectedCounterValue = 0 ;
2151
+
2152
+ for ( let i = 0 ; i < decrements . length ; i ++ ) {
2153
+ const decrement = decrements [ i ] ;
2154
+ expectedCounterValue -= decrement ;
2155
+ await counter . decrement ( decrement ) ;
2156
+
2157
+ expect ( counter . value ( ) ) . to . equal (
2158
+ expectedCounterValue ,
2159
+ `Check counter has correct value after ${ i + 1 } LiveCounter.decrement calls` ,
2160
+ ) ;
2161
+ }
2162
+ } ,
2163
+ } ,
2164
+
2165
+ {
2166
+ description : 'LiveCounter.decrement throws on invalid input' ,
2167
+ action : async ( ctx ) => {
2168
+ const { root, liveObjectsHelper, channelName } = ctx ;
2169
+
2170
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2171
+ mapObjectId : 'root' ,
2172
+ key : 'counter' ,
2173
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2174
+ } ) ;
2175
+
2176
+ const counter = root . get ( 'counter' ) ;
2177
+
2178
+ expect ( ( ) => counter . decrement ( ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2179
+ expect ( ( ) => counter . decrement ( null ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2180
+ expect ( ( ) => counter . decrement ( 'foo' ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2181
+ expect ( ( ) => counter . decrement ( BigInt ( 1 ) ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2182
+ expect ( ( ) => counter . decrement ( true ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2183
+ expect ( ( ) => counter . decrement ( Symbol ( ) ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2184
+ expect ( ( ) => counter . decrement ( { } ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2185
+ expect ( ( ) => counter . decrement ( [ ] ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2186
+ expect ( ( ) => counter . decrement ( counter ) ) . to . throw ( 'Counter value decrement should be a number' ) ;
2187
+ } ,
2188
+ } ,
2189
+
2190
+ {
2191
+ description : 'LiveMap.set sends MAP_SET operation for primitive values' ,
2192
+ action : async ( ctx ) => {
2193
+ const { root } = ctx ;
2194
+
2195
+ await Promise . all (
2196
+ primitiveKeyData . map ( async ( keyData ) => {
2197
+ const value = keyData . data . encoding ? BufferUtils . base64Decode ( keyData . data . value ) : keyData . data . value ;
2198
+ await root . set ( keyData . key , value ) ;
2199
+ } ) ,
2200
+ ) ;
2201
+
2202
+ // check everything is applied correctly
2203
+ primitiveKeyData . forEach ( ( keyData ) => {
2204
+ if ( keyData . data . encoding ) {
2205
+ expect (
2206
+ BufferUtils . areBuffersEqual ( root . get ( keyData . key ) , BufferUtils . base64Decode ( keyData . data . value ) ) ,
2207
+ `Check root has correct value for "${ keyData . key } " key after LiveMap.set call` ,
2208
+ ) . to . be . true ;
2209
+ } else {
2210
+ expect ( root . get ( keyData . key ) ) . to . equal (
2211
+ keyData . data . value ,
2212
+ `Check root has correct value for "${ keyData . key } " key after LiveMap.set call` ,
2213
+ ) ;
2214
+ }
2215
+ } ) ;
2216
+ } ,
2217
+ } ,
2218
+
2219
+ {
2220
+ description : 'LiveMap.set sends MAP_SET operation with reference to another LiveObject' ,
2221
+ action : async ( ctx ) => {
2222
+ const { root, liveObjectsHelper, channelName } = ctx ;
2223
+
2224
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2225
+ mapObjectId : 'root' ,
2226
+ key : 'counter' ,
2227
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2228
+ } ) ;
2229
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2230
+ mapObjectId : 'root' ,
2231
+ key : 'map' ,
2232
+ createOp : liveObjectsHelper . counterCreateOp ( ) ,
2233
+ } ) ;
2234
+
2235
+ const counter = root . get ( 'counter' ) ;
2236
+ const map = root . get ( 'map' ) ;
2237
+
2238
+ await root . set ( 'counter2' , counter ) ;
2239
+ await root . set ( 'map2' , map ) ;
2240
+
2241
+ expect ( root . get ( 'counter2' ) ) . to . equal (
2242
+ counter ,
2243
+ 'Check can set a reference to a LiveCounter object on a root via a LiveMap.set call' ,
2244
+ ) ;
2245
+ expect ( root . get ( 'map2' ) ) . to . equal (
2246
+ map ,
2247
+ 'Check can set a reference to a LiveMap object on a root via a LiveMap.set call' ,
2248
+ ) ;
2249
+ } ,
2250
+ } ,
2251
+
2252
+ {
2253
+ description : 'LiveMap.set throws on invalid input' ,
2254
+ action : async ( ctx ) => {
2255
+ const { root, liveObjectsHelper, channelName } = ctx ;
2256
+
2257
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2258
+ mapObjectId : 'root' ,
2259
+ key : 'map' ,
2260
+ createOp : liveObjectsHelper . mapCreateOp ( ) ,
2261
+ } ) ;
2262
+
2263
+ const map = root . get ( 'map' ) ;
2264
+
2265
+ expect ( ( ) => map . set ( ) ) . to . throw ( 'Map key should be string' ) ;
2266
+ expect ( ( ) => map . set ( null ) ) . to . throw ( 'Map key should be string' ) ;
2267
+ expect ( ( ) => map . set ( 1 ) ) . to . throw ( 'Map key should be string' ) ;
2268
+ expect ( ( ) => map . set ( BigInt ( 1 ) ) ) . to . throw ( 'Map key should be string' ) ;
2269
+ expect ( ( ) => map . set ( true ) ) . to . throw ( 'Map key should be string' ) ;
2270
+ expect ( ( ) => map . set ( Symbol ( ) ) ) . to . throw ( 'Map key should be string' ) ;
2271
+ expect ( ( ) => map . set ( { } ) ) . to . throw ( 'Map key should be string' ) ;
2272
+ expect ( ( ) => map . set ( [ ] ) ) . to . throw ( 'Map key should be string' ) ;
2273
+ expect ( ( ) => map . set ( map ) ) . to . throw ( 'Map key should be string' ) ;
2274
+
2275
+ expect ( ( ) => map . set ( 'key' ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2276
+ expect ( ( ) => map . set ( 'key' , null ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2277
+ expect ( ( ) => map . set ( 'key' , BigInt ( 1 ) ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2278
+ expect ( ( ) => map . set ( 'key' , Symbol ( ) ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2279
+ expect ( ( ) => map . set ( 'key' , { } ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2280
+ expect ( ( ) => map . set ( 'key' , [ ] ) ) . to . throw ( 'Map value data type is unsupported' ) ;
2281
+ } ,
2282
+ } ,
2283
+
2284
+ {
2285
+ description : 'LiveMap.remove sends MAP_REMOVE operation' ,
2286
+ action : async ( ctx ) => {
2287
+ const { root, liveObjectsHelper, channelName } = ctx ;
2288
+
2289
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2290
+ mapObjectId : 'root' ,
2291
+ key : 'map' ,
2292
+ createOp : liveObjectsHelper . mapCreateOp ( {
2293
+ entries : {
2294
+ foo : { data : { value : 1 } } ,
2295
+ bar : { data : { value : 1 } } ,
2296
+ baz : { data : { value : 1 } } ,
2297
+ } ,
2298
+ } ) ,
2299
+ } ) ;
2300
+
2301
+ const map = root . get ( 'map' ) ;
2302
+
2303
+ await map . remove ( 'foo' ) ;
2304
+ await map . remove ( 'bar' ) ;
2305
+
2306
+ expect ( map . get ( 'foo' ) , 'Check can remove a key from a root via a LiveMap.remove call' ) . to . not . exist ;
2307
+ expect ( map . get ( 'bar' ) , 'Check can remove a key from a root via a LiveMap.remove call' ) . to . not . exist ;
2308
+ expect (
2309
+ map . get ( 'baz' ) ,
2310
+ 'Check non-removed keys are still present on a root after LiveMap.remove call for another keys' ,
2311
+ ) . to . equal ( 1 ) ;
2312
+ } ,
2313
+ } ,
2314
+
2315
+ {
2316
+ description : 'LiveMap.remove throws on invalid input' ,
2317
+ action : async ( ctx ) => {
2318
+ const { root, liveObjectsHelper, channelName } = ctx ;
2319
+
2320
+ await liveObjectsHelper . createAndSetOnMap ( channelName , {
2321
+ mapObjectId : 'root' ,
2322
+ key : 'map' ,
2323
+ createOp : liveObjectsHelper . mapCreateOp ( ) ,
2324
+ } ) ;
2325
+
2326
+ const map = root . get ( 'map' ) ;
2327
+
2328
+ expect ( ( ) => map . remove ( ) ) . to . throw ( 'Map key should be string' ) ;
2329
+ expect ( ( ) => map . remove ( null ) ) . to . throw ( 'Map key should be string' ) ;
2330
+ expect ( ( ) => map . remove ( 1 ) ) . to . throw ( 'Map key should be string' ) ;
2331
+ expect ( ( ) => map . remove ( BigInt ( 1 ) ) ) . to . throw ( 'Map key should be string' ) ;
2332
+ expect ( ( ) => map . remove ( true ) ) . to . throw ( 'Map key should be string' ) ;
2333
+ expect ( ( ) => map . remove ( Symbol ( ) ) ) . to . throw ( 'Map key should be string' ) ;
2334
+ expect ( ( ) => map . remove ( { } ) ) . to . throw ( 'Map key should be string' ) ;
2335
+ expect ( ( ) => map . remove ( [ ] ) ) . to . throw ( 'Map key should be string' ) ;
2336
+ expect ( ( ) => map . remove ( map ) ) . to . throw ( 'Map key should be string' ) ;
2337
+ } ,
2338
+ } ,
2339
+ ] ;
2340
+
2063
2341
/** @nospec */
2064
2342
forScenarios (
2065
- [ ...stateSyncSequenceScanarios , ...applyOperationsScenarios , ...applyOperationsDuringSyncScenarios ] ,
2343
+ [
2344
+ ...stateSyncSequenceScenarios ,
2345
+ ...applyOperationsScenarios ,
2346
+ ...applyOperationsDuringSyncScenarios ,
2347
+ ...writeApiScenarios ,
2348
+ ] ,
2066
2349
async function ( helper , scenario ) {
2067
2350
const liveObjectsHelper = new LiveObjectsHelper ( helper ) ;
2068
2351
const client = RealtimeWithLiveObjects ( helper ) ;
0 commit comments