@firebase/database-compat 0.1.1-2021823172527 → 0.1.2-canary.0b3ca78eb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -8
- package/dist/database-compat/src/index.d.ts +0 -9
- package/dist/database-compat/src/index.node.d.ts +0 -53
- package/dist/database-compat/src/index.standalone.d.ts +52 -0
- package/dist/index.esm2017.js +5 -6
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +2 -3
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.js +10 -77
- package/dist/index.js.map +1 -1
- package/dist/index.standalone.js +15150 -0
- package/dist/index.standalone.js.map +1 -0
- package/dist/node-esm/database-compat/src/api/Database.d.ts +72 -0
- package/dist/node-esm/database-compat/src/api/Reference.d.ts +201 -0
- package/{karma.conf.js → dist/node-esm/database-compat/src/api/TransactionResult.d.ts} +26 -34
- package/dist/node-esm/database-compat/src/api/internal.d.ts +39 -0
- package/dist/node-esm/database-compat/src/api/onDisconnect.d.ts +27 -0
- package/dist/node-esm/database-compat/src/index.d.ts +32 -0
- package/dist/node-esm/database-compat/src/index.node.d.ts +14 -0
- package/dist/node-esm/database-compat/src/index.standalone.d.ts +52 -0
- package/{src/util/util.ts → dist/node-esm/database-compat/src/util/util.d.ts} +17 -25
- package/dist/node-esm/database-compat/src/util/validation.d.ts +18 -0
- package/{src/api/TransactionResult.ts → dist/node-esm/database-compat/test/browser/crawler_support.test.d.ts} +17 -34
- package/dist/node-esm/database-compat/test/database.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/datasnapshot.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/helpers/events.d.ts +34 -0
- package/dist/node-esm/database-compat/test/helpers/util.d.ts +42 -0
- package/dist/node-esm/database-compat/test/info.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/order.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/order_by.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/promise.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/query.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/servervalues.test.d.ts +17 -0
- package/dist/node-esm/database-compat/test/transaction.test.d.ts +17 -0
- package/dist/node-esm/index.js +868 -0
- package/dist/node-esm/index.js.map +1 -0
- package/dist/node-esm/package.json +1 -0
- package/package.json +34 -9
- package/standalone/package.json +7 -0
- package/.eslintrc.js +0 -64
- package/rollup.config.js +0 -125
- package/src/api/Database.ts +0 -123
- package/src/api/Reference.ts +0 -790
- package/src/api/internal.ts +0 -91
- package/src/api/onDisconnect.ts +0 -112
- package/src/index.node.ts +0 -142
- package/src/index.ts +0 -88
- package/src/util/validation.ts +0 -59
- package/test/browser/crawler_support.test.ts +0 -206
- package/test/database.test.ts +0 -306
- package/test/datasnapshot.test.ts +0 -249
- package/test/helpers/events.ts +0 -258
- package/test/helpers/util.ts +0 -177
- package/test/info.test.ts +0 -222
- package/test/order.test.ts +0 -592
- package/test/order_by.test.ts +0 -530
- package/test/promise.test.ts +0 -261
- package/test/query.test.ts +0 -4670
- package/test/servervalues.test.ts +0 -152
- package/test/transaction.test.ts +0 -1534
- package/tsconfig.json +0 -11
package/test/transaction.test.ts
DELETED
|
@@ -1,1534 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2017 Google LLC
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import firebase from '@firebase/app-compat';
|
|
19
|
-
import { _TEST_ACCESS_hijackHash as hijackHash } from '@firebase/database';
|
|
20
|
-
import { Deferred } from '@firebase/util';
|
|
21
|
-
import { expect } from 'chai';
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
EventAccumulator,
|
|
25
|
-
EventAccumulatorFactory
|
|
26
|
-
} from '../../database/test/helpers/EventAccumulator';
|
|
27
|
-
import { Reference } from '../src/api/Reference';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
import { eventTestHelper } from './helpers/events';
|
|
31
|
-
import {
|
|
32
|
-
canCreateExtraConnections,
|
|
33
|
-
getFreshRepoFromReference,
|
|
34
|
-
getRandomNode,
|
|
35
|
-
getVal
|
|
36
|
-
} from './helpers/util';
|
|
37
|
-
|
|
38
|
-
import '../src/index';
|
|
39
|
-
|
|
40
|
-
describe('Transaction Tests', () => {
|
|
41
|
-
// Tests that use hijackHash() should set restoreHash to the restore function
|
|
42
|
-
// and be sure to call it (and set restoreHash back to null) before the test
|
|
43
|
-
// exits. In the case that the test fails to do so, we'll log a warning and
|
|
44
|
-
// call restoreHash() manually to ensure subsequent tests aren't affected.
|
|
45
|
-
let restoreHash = null;
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
if (restoreHash) {
|
|
49
|
-
console.warn("Prior test didn't properly call restoreHash()!");
|
|
50
|
-
restoreHash();
|
|
51
|
-
restoreHash = null;
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('New value is immediately visible.', () => {
|
|
56
|
-
const node = getRandomNode() as Reference;
|
|
57
|
-
node.child('foo').transaction(() => {
|
|
58
|
-
return 42;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
let val = null;
|
|
62
|
-
node.child('foo').on('value', snap => {
|
|
63
|
-
val = snap.val();
|
|
64
|
-
});
|
|
65
|
-
expect(val).to.equal(42);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('Event is raised for new value.', () => {
|
|
69
|
-
const node = getRandomNode() as Reference;
|
|
70
|
-
const fooNode = node.child('foo');
|
|
71
|
-
const eventHelper = eventTestHelper([[fooNode, ['value', '']]]);
|
|
72
|
-
|
|
73
|
-
node.child('foo').transaction(() => {
|
|
74
|
-
return 42;
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
expect(eventHelper.waiter()).to.equal(true);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('Transaction result can be converted to JSON.', () => {
|
|
81
|
-
const node = getRandomNode() as Reference;
|
|
82
|
-
|
|
83
|
-
return node
|
|
84
|
-
.transaction(() => {
|
|
85
|
-
return 42;
|
|
86
|
-
})
|
|
87
|
-
.then(transactionResult => {
|
|
88
|
-
expect(transactionResult.toJSON()).to.deep.equal({
|
|
89
|
-
committed: true,
|
|
90
|
-
snapshot: 42
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('Non-aborted transaction sets committed to true in callback.', done => {
|
|
96
|
-
const node = getRandomNode() as Reference;
|
|
97
|
-
|
|
98
|
-
node.transaction(
|
|
99
|
-
() => {
|
|
100
|
-
return 42;
|
|
101
|
-
},
|
|
102
|
-
(error, committed, snapshot) => {
|
|
103
|
-
expect(error).to.equal(null);
|
|
104
|
-
expect(committed).to.equal(true);
|
|
105
|
-
expect(snapshot.val()).to.equal(42);
|
|
106
|
-
done();
|
|
107
|
-
}
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('Aborted transaction sets committed to false in callback.', done => {
|
|
112
|
-
const node = getRandomNode() as Reference;
|
|
113
|
-
|
|
114
|
-
node.transaction(
|
|
115
|
-
() => {},
|
|
116
|
-
(error, committed, snapshot) => {
|
|
117
|
-
expect(error).to.equal(null);
|
|
118
|
-
expect(committed).to.equal(false);
|
|
119
|
-
expect(snapshot.val()).to.be.null;
|
|
120
|
-
done();
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('Tetris bug test - set data, reconnect, do transaction that aborts once data arrives, verify correct events.', async () => {
|
|
126
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
127
|
-
let node = nodePair[0];
|
|
128
|
-
let eventsReceived = 0;
|
|
129
|
-
const ea = EventAccumulatorFactory.waitsForCount(2);
|
|
130
|
-
|
|
131
|
-
await node.child('foo').set(42);
|
|
132
|
-
|
|
133
|
-
node = nodePair[1];
|
|
134
|
-
node.child('foo').on('value', snap => {
|
|
135
|
-
if (eventsReceived === 0) {
|
|
136
|
-
expect(snap.val()).to.equal('temp value');
|
|
137
|
-
} else if (eventsReceived === 1) {
|
|
138
|
-
expect(snap.val()).to.equal(42);
|
|
139
|
-
} else {
|
|
140
|
-
// Extra event detected.
|
|
141
|
-
expect(true).to.equal(false);
|
|
142
|
-
}
|
|
143
|
-
eventsReceived++;
|
|
144
|
-
ea.addEvent();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
node.child('foo').transaction(
|
|
148
|
-
value => {
|
|
149
|
-
if (value === null) {
|
|
150
|
-
return 'temp value';
|
|
151
|
-
} else {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
(error, committed, snapshot) => {
|
|
156
|
-
expect(error).to.equal(null);
|
|
157
|
-
expect(committed).to.equal(false);
|
|
158
|
-
expect(snapshot.val()).to.equal(42);
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return ea.promise;
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('Use transaction to create a node, make sure exactly one event is received.', () => {
|
|
166
|
-
const node = getRandomNode() as Reference;
|
|
167
|
-
let events = 0,
|
|
168
|
-
done = false;
|
|
169
|
-
|
|
170
|
-
const ea = new EventAccumulator(() => done && events === 1);
|
|
171
|
-
|
|
172
|
-
node.child('a').on('value', () => {
|
|
173
|
-
events++;
|
|
174
|
-
ea.addEvent();
|
|
175
|
-
if (events > 1) {
|
|
176
|
-
throw 'Expected 1 event on a, but got two.';
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
node.child('a').transaction(
|
|
181
|
-
() => {
|
|
182
|
-
return 42;
|
|
183
|
-
},
|
|
184
|
-
() => {
|
|
185
|
-
done = true;
|
|
186
|
-
ea.addEvent();
|
|
187
|
-
}
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
return ea.promise;
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it(
|
|
194
|
-
'Use transaction to update one of two existing child nodes. ' +
|
|
195
|
-
'Make sure events are only raised for the changed node.',
|
|
196
|
-
async () => {
|
|
197
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
198
|
-
let node = nodePair[0].child('foo');
|
|
199
|
-
|
|
200
|
-
await Promise.all([node.child('a').set(42), node.child('b').set(42)]);
|
|
201
|
-
|
|
202
|
-
node = nodePair[1].child('foo');
|
|
203
|
-
const eventHelper = eventTestHelper([
|
|
204
|
-
[node.child('a'), ['value', '']],
|
|
205
|
-
[node.child('b'), ['value', '']]
|
|
206
|
-
]);
|
|
207
|
-
|
|
208
|
-
await eventHelper.promise;
|
|
209
|
-
|
|
210
|
-
eventHelper.addExpectedEvents([[node.child('b'), ['value', '']]]);
|
|
211
|
-
|
|
212
|
-
const transaction = node.transaction(
|
|
213
|
-
() => {
|
|
214
|
-
return { a: 42, b: 87 };
|
|
215
|
-
},
|
|
216
|
-
(error, committed, snapshot) => {
|
|
217
|
-
expect(error).to.be.null;
|
|
218
|
-
expect(committed).to.equal(true);
|
|
219
|
-
expect(snapshot.val()).to.deep.equal({ a: 42, b: 87 });
|
|
220
|
-
}
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
return Promise.all([eventHelper.promise, transaction]);
|
|
224
|
-
}
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
it('Transaction is only called once when initializing an empty node.', () => {
|
|
228
|
-
const node = getRandomNode() as Reference;
|
|
229
|
-
let updateCalled = 0;
|
|
230
|
-
|
|
231
|
-
const ea = EventAccumulatorFactory.waitsForCount(1);
|
|
232
|
-
node.transaction(value => {
|
|
233
|
-
expect(value).to.equal(null);
|
|
234
|
-
updateCalled++;
|
|
235
|
-
ea.addEvent();
|
|
236
|
-
if (updateCalled > 1) {
|
|
237
|
-
throw 'Transaction called too many times.';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (value === null) {
|
|
241
|
-
return { a: 5, b: 3 };
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
return ea.promise;
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('Second transaction gets run immediately on previous output and only runs once.', done => {
|
|
249
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
250
|
-
let firstRun = false,
|
|
251
|
-
firstDone = false,
|
|
252
|
-
secondRun = false,
|
|
253
|
-
secondDone = false;
|
|
254
|
-
|
|
255
|
-
function onComplete() {
|
|
256
|
-
if (firstDone && secondDone) {
|
|
257
|
-
nodePair[1].on('value', snap => {
|
|
258
|
-
expect(snap.val()).to.equal(84);
|
|
259
|
-
done();
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
nodePair[0].transaction(
|
|
265
|
-
() => {
|
|
266
|
-
expect(firstRun).to.equal(false);
|
|
267
|
-
firstRun = true;
|
|
268
|
-
return 42;
|
|
269
|
-
},
|
|
270
|
-
(error, committed, snapshot) => {
|
|
271
|
-
expect(error).to.equal(null);
|
|
272
|
-
expect(committed).to.equal(true);
|
|
273
|
-
firstDone = true;
|
|
274
|
-
onComplete();
|
|
275
|
-
}
|
|
276
|
-
);
|
|
277
|
-
expect(firstRun).to.equal(true);
|
|
278
|
-
|
|
279
|
-
nodePair[0].transaction(
|
|
280
|
-
value => {
|
|
281
|
-
expect(secondRun).to.equal(false);
|
|
282
|
-
secondRun = true;
|
|
283
|
-
expect(value).to.equal(42);
|
|
284
|
-
return 84;
|
|
285
|
-
},
|
|
286
|
-
(error, committed, snapshot) => {
|
|
287
|
-
expect(error).to.equal(null);
|
|
288
|
-
expect(committed).to.equal(true);
|
|
289
|
-
secondDone = true;
|
|
290
|
-
onComplete();
|
|
291
|
-
}
|
|
292
|
-
);
|
|
293
|
-
expect(secondRun).to.equal(true);
|
|
294
|
-
|
|
295
|
-
expect(getVal(nodePair[0])).to.equal(84);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('Set() cancels pending transactions and re-runs affected transactions.', async () => {
|
|
299
|
-
// We do 3 transactions: 1) At /foo, 2) At /, and 3) At /bar.
|
|
300
|
-
// Only #1 is sent to the server immediately (since 2 depends on 1 and 3 depends on 2).
|
|
301
|
-
// We set /foo to 0.
|
|
302
|
-
// Transaction #1 should complete as planned (since it was already sent).
|
|
303
|
-
// Transaction #2 should be aborted by the set.
|
|
304
|
-
// Transaction #3 should be re-run after #2 is reverted, and then be sent to the server and succeed.
|
|
305
|
-
let firstDone = false,
|
|
306
|
-
secondDone = false,
|
|
307
|
-
thirdDone = false;
|
|
308
|
-
const node = getRandomNode() as Reference;
|
|
309
|
-
let nodeSnap = null;
|
|
310
|
-
let nodeFooSnap = null;
|
|
311
|
-
|
|
312
|
-
node.on('value', s => {
|
|
313
|
-
const str = JSON.stringify(s.val());
|
|
314
|
-
nodeSnap = s;
|
|
315
|
-
});
|
|
316
|
-
node.child('foo').on('value', s => {
|
|
317
|
-
const str = JSON.stringify(s.val());
|
|
318
|
-
nodeFooSnap = s;
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
let firstRun = false,
|
|
322
|
-
secondRun = false,
|
|
323
|
-
thirdRunCount = 0;
|
|
324
|
-
const ea = new EventAccumulator(() => firstDone && thirdDone);
|
|
325
|
-
node.child('foo').transaction(
|
|
326
|
-
() => {
|
|
327
|
-
expect(firstRun).to.equal(false);
|
|
328
|
-
firstRun = true;
|
|
329
|
-
return 42;
|
|
330
|
-
},
|
|
331
|
-
(error, committed, snapshot) => {
|
|
332
|
-
expect(error).to.equal(null);
|
|
333
|
-
expect(committed).to.equal(true);
|
|
334
|
-
expect(snapshot.val()).to.equal(42);
|
|
335
|
-
firstDone = true;
|
|
336
|
-
ea.addEvent();
|
|
337
|
-
}
|
|
338
|
-
);
|
|
339
|
-
expect(nodeFooSnap.val()).to.deep.equal(42);
|
|
340
|
-
|
|
341
|
-
node.transaction(
|
|
342
|
-
() => {
|
|
343
|
-
expect(secondRun).to.equal(false);
|
|
344
|
-
secondRun = true;
|
|
345
|
-
return { foo: 84, bar: 1 };
|
|
346
|
-
},
|
|
347
|
-
(error, committed, snapshot) => {
|
|
348
|
-
expect(committed).to.equal(false);
|
|
349
|
-
secondDone = true;
|
|
350
|
-
ea.addEvent();
|
|
351
|
-
}
|
|
352
|
-
);
|
|
353
|
-
expect(secondRun).to.equal(true);
|
|
354
|
-
expect(nodeSnap.val()).to.deep.equal({ foo: 84, bar: 1 });
|
|
355
|
-
|
|
356
|
-
node.child('bar').transaction(
|
|
357
|
-
val => {
|
|
358
|
-
thirdRunCount++;
|
|
359
|
-
if (thirdRunCount === 1) {
|
|
360
|
-
expect(val).to.equal(1);
|
|
361
|
-
return 'first';
|
|
362
|
-
} else if (thirdRunCount === 2) {
|
|
363
|
-
expect(val).to.equal(null);
|
|
364
|
-
return 'second';
|
|
365
|
-
} else {
|
|
366
|
-
throw new Error('Called too many times!');
|
|
367
|
-
}
|
|
368
|
-
},
|
|
369
|
-
(error, committed, snapshot) => {
|
|
370
|
-
expect(error).to.equal(null);
|
|
371
|
-
expect(committed).to.equal(true);
|
|
372
|
-
expect(snapshot.val()).to.equal('second');
|
|
373
|
-
thirdDone = true;
|
|
374
|
-
ea.addEvent();
|
|
375
|
-
}
|
|
376
|
-
);
|
|
377
|
-
expect(thirdRunCount).to.equal(1);
|
|
378
|
-
expect(nodeSnap.val()).to.deep.equal({ foo: 84, bar: 'first' });
|
|
379
|
-
|
|
380
|
-
// This rolls back the second transaction, and triggers a re-run of the third.
|
|
381
|
-
// However, a new value event won't be triggered until the listener is complete,
|
|
382
|
-
// so we're left with the last value event
|
|
383
|
-
node.child('foo').set(0);
|
|
384
|
-
|
|
385
|
-
expect(firstDone).to.equal(false);
|
|
386
|
-
|
|
387
|
-
// Wait for the `onComplete` callbacks to be invoked. This is no longer
|
|
388
|
-
// happening synchronously, as the underlying database@exp implementation
|
|
389
|
-
// uses promises.
|
|
390
|
-
await ea.promise;
|
|
391
|
-
|
|
392
|
-
expect(secondDone).to.equal(true);
|
|
393
|
-
expect(thirdRunCount).to.equal(2);
|
|
394
|
-
// Note that the set actually raises two events, one overlaid on top of the original transaction value, and a
|
|
395
|
-
// second one with the re-run value from the third transaction
|
|
396
|
-
expect(nodeSnap.val()).to.deep.equal({ foo: 0, bar: 'second' });
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it('transaction(), set(), set() should work.', done => {
|
|
400
|
-
const ref = getRandomNode() as Reference;
|
|
401
|
-
ref.transaction(
|
|
402
|
-
curr => {
|
|
403
|
-
expect(curr).to.equal(null);
|
|
404
|
-
return 'hi!';
|
|
405
|
-
},
|
|
406
|
-
(error, committed) => {
|
|
407
|
-
expect(error).to.equal(null);
|
|
408
|
-
expect(committed).to.equal(true);
|
|
409
|
-
done();
|
|
410
|
-
}
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
ref.set('foo');
|
|
414
|
-
ref.set('bar');
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('Priority is preserved when setting data.', async () => {
|
|
418
|
-
const node = getRandomNode() as Reference;
|
|
419
|
-
let complete = false;
|
|
420
|
-
let snap;
|
|
421
|
-
node.on('value', s => {
|
|
422
|
-
snap = s;
|
|
423
|
-
});
|
|
424
|
-
node.setWithPriority('test', 5);
|
|
425
|
-
expect(snap.getPriority()).to.equal(5);
|
|
426
|
-
|
|
427
|
-
const promise = node.transaction(
|
|
428
|
-
() => {
|
|
429
|
-
return 'new value';
|
|
430
|
-
},
|
|
431
|
-
() => {
|
|
432
|
-
complete = true;
|
|
433
|
-
}
|
|
434
|
-
);
|
|
435
|
-
|
|
436
|
-
expect(snap.val()).to.equal('new value');
|
|
437
|
-
expect(snap.getPriority()).to.equal(5);
|
|
438
|
-
|
|
439
|
-
await promise;
|
|
440
|
-
expect(snap.getPriority()).to.equal(5);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('Tetris bug test - Can do transactions from transaction callback.', async () => {
|
|
444
|
-
const nodePair = getRandomNode(2) as Reference[],
|
|
445
|
-
writeDone = false;
|
|
446
|
-
await nodePair[0].child('foo').set(42);
|
|
447
|
-
|
|
448
|
-
const node = nodePair[1];
|
|
449
|
-
|
|
450
|
-
return new Promise(resolve => {
|
|
451
|
-
node.child('foo').transaction(
|
|
452
|
-
val => {
|
|
453
|
-
if (val === null) {
|
|
454
|
-
return 84;
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
() => {
|
|
458
|
-
node.child('bar').transaction(val => {
|
|
459
|
-
resolve();
|
|
460
|
-
return 168;
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
);
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
it('Resulting snapshot is passed to onComplete callback.', async () => {
|
|
468
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
469
|
-
await nodePair[0].transaction(
|
|
470
|
-
v => {
|
|
471
|
-
if (v === null) {
|
|
472
|
-
return 'hello!';
|
|
473
|
-
}
|
|
474
|
-
},
|
|
475
|
-
(error, committed, snapshot) => {
|
|
476
|
-
expect(error).to.equal(null);
|
|
477
|
-
expect(committed).to.equal(true);
|
|
478
|
-
expect(snapshot.val()).to.equal('hello!');
|
|
479
|
-
}
|
|
480
|
-
);
|
|
481
|
-
|
|
482
|
-
// Do it again for the aborted case.
|
|
483
|
-
await nodePair[0].transaction(
|
|
484
|
-
v => {
|
|
485
|
-
if (v === null) {
|
|
486
|
-
return 'hello!';
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
(error, committed, snapshot) => {
|
|
490
|
-
expect(committed).to.equal(false);
|
|
491
|
-
expect(snapshot.val()).to.equal('hello!');
|
|
492
|
-
}
|
|
493
|
-
);
|
|
494
|
-
|
|
495
|
-
// Do it again on a fresh connection, for the aborted case.
|
|
496
|
-
await nodePair[1].transaction(
|
|
497
|
-
v => {
|
|
498
|
-
if (v === null) {
|
|
499
|
-
return 'hello!';
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
(error, committed, snapshot) => {
|
|
503
|
-
expect(committed).to.equal(false);
|
|
504
|
-
expect(snapshot.val()).to.equal('hello!');
|
|
505
|
-
}
|
|
506
|
-
);
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
it('Transaction aborts after 25 retries.', done => {
|
|
510
|
-
restoreHash = hijackHash(() => {
|
|
511
|
-
return 'duck, duck, goose.';
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
const node = getRandomNode() as Reference;
|
|
515
|
-
let tries = 0;
|
|
516
|
-
node.transaction(
|
|
517
|
-
curr => {
|
|
518
|
-
expect(tries).to.be.lessThan(25);
|
|
519
|
-
tries++;
|
|
520
|
-
return 'hello!';
|
|
521
|
-
},
|
|
522
|
-
(error, committed, snapshot) => {
|
|
523
|
-
expect(error.message).to.equal('maxretry');
|
|
524
|
-
expect(committed).to.equal(false);
|
|
525
|
-
expect(tries).to.equal(25);
|
|
526
|
-
restoreHash();
|
|
527
|
-
restoreHash = null;
|
|
528
|
-
done();
|
|
529
|
-
}
|
|
530
|
-
);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it('Set should cancel already sent transactions that come back as datastale.', done => {
|
|
534
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
535
|
-
let transactionCalls = 0;
|
|
536
|
-
nodePair[0].set(5, () => {
|
|
537
|
-
nodePair[1].transaction(
|
|
538
|
-
old => {
|
|
539
|
-
expect(transactionCalls).to.equal(0);
|
|
540
|
-
expect(old).to.equal(null);
|
|
541
|
-
transactionCalls++;
|
|
542
|
-
return 72;
|
|
543
|
-
},
|
|
544
|
-
(error, committed, snapshot) => {
|
|
545
|
-
expect(error.message).to.equal('set');
|
|
546
|
-
expect(committed).to.equal(false);
|
|
547
|
-
done();
|
|
548
|
-
}
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
// Transaction should get sent but fail due to stale data, and then aborted because of the below set().
|
|
552
|
-
nodePair[1].set(32);
|
|
553
|
-
});
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('Update should not cancel unrelated transactions', async () => {
|
|
557
|
-
const node = getRandomNode() as Reference;
|
|
558
|
-
const fooTransactionDone = false;
|
|
559
|
-
let barTransactionDone = false;
|
|
560
|
-
restoreHash = hijackHash(() => {
|
|
561
|
-
return 'foobar';
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
await node.child('foo').set(5);
|
|
565
|
-
|
|
566
|
-
const deferred = new Deferred<void>();
|
|
567
|
-
|
|
568
|
-
// 'foo' gets overwritten in the update so the transaction gets cancelled.
|
|
569
|
-
node.child('foo').transaction(
|
|
570
|
-
old => {
|
|
571
|
-
return 72;
|
|
572
|
-
},
|
|
573
|
-
(error, committed, snapshot) => {
|
|
574
|
-
expect(error.message).to.equal('set');
|
|
575
|
-
expect(committed).to.equal(false);
|
|
576
|
-
deferred.resolve();
|
|
577
|
-
}
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
// 'bar' does not get touched during the update and the transaction succeeds.
|
|
581
|
-
node.child('bar').transaction(
|
|
582
|
-
old => {
|
|
583
|
-
return 72;
|
|
584
|
-
},
|
|
585
|
-
(error, committed, snapshot) => {
|
|
586
|
-
expect(error).to.equal(null);
|
|
587
|
-
expect(committed).to.equal(true);
|
|
588
|
-
barTransactionDone = true;
|
|
589
|
-
}
|
|
590
|
-
);
|
|
591
|
-
|
|
592
|
-
await node.update({
|
|
593
|
-
foo: 'newValue',
|
|
594
|
-
boo: 'newValue',
|
|
595
|
-
loo: {
|
|
596
|
-
doo: {
|
|
597
|
-
boo: 'newValue'
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
await deferred.promise;
|
|
603
|
-
expect(barTransactionDone).to.equal(false);
|
|
604
|
-
restoreHash();
|
|
605
|
-
restoreHash = null;
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it('Test transaction on wacky unicode data.', done => {
|
|
609
|
-
const nodePair = getRandomNode(2) as Reference[];
|
|
610
|
-
nodePair[0].set('♜♞♝♛♚♝♞♜', () => {
|
|
611
|
-
nodePair[1].transaction(
|
|
612
|
-
current => {
|
|
613
|
-
if (current !== null) {
|
|
614
|
-
expect(current).to.equal('♜♞♝♛♚♝♞♜');
|
|
615
|
-
}
|
|
616
|
-
return '♖♘♗♕♔♗♘♖';
|
|
617
|
-
},
|
|
618
|
-
(error, committed, snapshot) => {
|
|
619
|
-
expect(error).to.equal(null);
|
|
620
|
-
expect(committed).to.equal(true);
|
|
621
|
-
done();
|
|
622
|
-
}
|
|
623
|
-
);
|
|
624
|
-
});
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
it('Test immediately aborted transaction.', done => {
|
|
628
|
-
const node = getRandomNode() as Reference;
|
|
629
|
-
// without callback.
|
|
630
|
-
node.transaction(curr => {
|
|
631
|
-
return;
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
// with callback.
|
|
635
|
-
node.transaction(
|
|
636
|
-
curr => {
|
|
637
|
-
return;
|
|
638
|
-
},
|
|
639
|
-
(error, committed, snapshot) => {
|
|
640
|
-
expect(committed).to.equal(false);
|
|
641
|
-
done();
|
|
642
|
-
}
|
|
643
|
-
);
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
it('Test adding to an array with a transaction.', done => {
|
|
647
|
-
const node = getRandomNode() as Reference;
|
|
648
|
-
node.set(['cat', 'horse'], () => {
|
|
649
|
-
node.transaction(
|
|
650
|
-
(current: string[] | null) => {
|
|
651
|
-
if (current) {
|
|
652
|
-
current.push('dog');
|
|
653
|
-
} else {
|
|
654
|
-
current = ['dog'];
|
|
655
|
-
}
|
|
656
|
-
return current;
|
|
657
|
-
},
|
|
658
|
-
(error, committed, snapshot) => {
|
|
659
|
-
expect(error).to.equal(null);
|
|
660
|
-
expect(committed).to.equal(true);
|
|
661
|
-
expect(snapshot.val()).to.deep.equal(['cat', 'horse', 'dog']);
|
|
662
|
-
done();
|
|
663
|
-
}
|
|
664
|
-
);
|
|
665
|
-
});
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
it('Merged transactions have correct snapshot in onComplete.', async () => {
|
|
669
|
-
const nodePair = getRandomNode(2) as Reference[],
|
|
670
|
-
node1 = nodePair[0],
|
|
671
|
-
node2 = nodePair[1];
|
|
672
|
-
let transaction1Done, transaction2Done;
|
|
673
|
-
await node1.set({ a: 0 });
|
|
674
|
-
|
|
675
|
-
const tx1 = node2.transaction(
|
|
676
|
-
val => {
|
|
677
|
-
if (val !== null) {
|
|
678
|
-
expect(val).to.deep.equal({ a: 0 });
|
|
679
|
-
}
|
|
680
|
-
return { a: 1 };
|
|
681
|
-
},
|
|
682
|
-
(error, committed, snapshot) => {
|
|
683
|
-
expect(error).to.equal(null);
|
|
684
|
-
expect(committed).to.equal(true);
|
|
685
|
-
expect(snapshot.key).to.equal(node2.key);
|
|
686
|
-
// Per new behavior, will include the accepted value of the transaction, if it was successful.
|
|
687
|
-
expect(snapshot.val()).to.deep.equal({ a: 1 });
|
|
688
|
-
transaction1Done = true;
|
|
689
|
-
}
|
|
690
|
-
);
|
|
691
|
-
|
|
692
|
-
const tx2 = node2.child('a').transaction(
|
|
693
|
-
val => {
|
|
694
|
-
if (val !== null) {
|
|
695
|
-
expect(val).to.equal(1); // should run after the first transaction.
|
|
696
|
-
}
|
|
697
|
-
return 2;
|
|
698
|
-
},
|
|
699
|
-
(error, committed, snapshot) => {
|
|
700
|
-
expect(error).to.equal(null);
|
|
701
|
-
expect(committed).to.equal(true);
|
|
702
|
-
expect(snapshot.key).to.equal('a');
|
|
703
|
-
expect(snapshot.val()).to.deep.equal(2);
|
|
704
|
-
transaction2Done = true;
|
|
705
|
-
}
|
|
706
|
-
);
|
|
707
|
-
|
|
708
|
-
return Promise.all([tx1, tx2]);
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
it('Doing set() in successful transaction callback works. Case 870.', done => {
|
|
712
|
-
const node = getRandomNode() as Reference;
|
|
713
|
-
let transactionCalled = false;
|
|
714
|
-
let callbackCalled = false;
|
|
715
|
-
node.transaction(
|
|
716
|
-
val => {
|
|
717
|
-
expect(transactionCalled).to.not.be.ok;
|
|
718
|
-
transactionCalled = true;
|
|
719
|
-
return 'hi';
|
|
720
|
-
},
|
|
721
|
-
() => {
|
|
722
|
-
expect(callbackCalled).to.not.be.ok;
|
|
723
|
-
callbackCalled = true;
|
|
724
|
-
node.set('transaction done', () => {
|
|
725
|
-
done();
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
);
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
it('Doing set() in aborted transaction callback works. Case 870.', done => {
|
|
732
|
-
const nodePair = getRandomNode(2) as Reference[],
|
|
733
|
-
node1 = nodePair[0],
|
|
734
|
-
node2 = nodePair[1];
|
|
735
|
-
|
|
736
|
-
node1.set('initial', () => {
|
|
737
|
-
let transactionCalled = false;
|
|
738
|
-
let callbackCalled = false;
|
|
739
|
-
node2.transaction(
|
|
740
|
-
val => {
|
|
741
|
-
// Return dummy value until we're called with the actual current value.
|
|
742
|
-
if (val === null) {
|
|
743
|
-
return 'hi';
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
expect(transactionCalled).to.not.be.ok;
|
|
747
|
-
transactionCalled = true;
|
|
748
|
-
return;
|
|
749
|
-
},
|
|
750
|
-
(error, committed, snapshot) => {
|
|
751
|
-
expect(callbackCalled).to.not.be.ok;
|
|
752
|
-
callbackCalled = true;
|
|
753
|
-
node2.set('transaction done', () => {
|
|
754
|
-
done();
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
);
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
it('Pending transactions are canceled on disconnect.', done => {
|
|
762
|
-
const ref = getRandomNode() as Reference;
|
|
763
|
-
|
|
764
|
-
// wait to be connected and some data set.
|
|
765
|
-
ref.set('initial', () => {
|
|
766
|
-
ref.transaction(
|
|
767
|
-
current => {
|
|
768
|
-
return 'new';
|
|
769
|
-
},
|
|
770
|
-
(error, committed, snapshot) => {
|
|
771
|
-
expect(committed).to.equal(false);
|
|
772
|
-
expect(error.message).to.equal('disconnect');
|
|
773
|
-
done();
|
|
774
|
-
}
|
|
775
|
-
);
|
|
776
|
-
|
|
777
|
-
// Kill the connection, which should cancel the outstanding transaction, since we don't know if it was
|
|
778
|
-
// committed on the server or not.
|
|
779
|
-
ref.database.goOffline();
|
|
780
|
-
ref.database.goOnline();
|
|
781
|
-
});
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
it('Transaction without local events (1)', async () => {
|
|
785
|
-
const ref = getRandomNode() as Reference,
|
|
786
|
-
actions = [];
|
|
787
|
-
let ea = EventAccumulatorFactory.waitsForCount(1);
|
|
788
|
-
|
|
789
|
-
ref.on('value', s => {
|
|
790
|
-
actions.push('value ' + s.val());
|
|
791
|
-
ea.addEvent();
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
await ea.promise;
|
|
795
|
-
|
|
796
|
-
ea = new EventAccumulator(() => actions.length >= 4);
|
|
797
|
-
|
|
798
|
-
ref.transaction(
|
|
799
|
-
() => {
|
|
800
|
-
return 'hello!';
|
|
801
|
-
},
|
|
802
|
-
(error, committed, snapshot) => {
|
|
803
|
-
expect(error).to.be.null;
|
|
804
|
-
expect(committed).to.equal(true);
|
|
805
|
-
expect(snapshot.val()).to.equal('hello!');
|
|
806
|
-
|
|
807
|
-
actions.push('txn completed');
|
|
808
|
-
ea.addEvent();
|
|
809
|
-
},
|
|
810
|
-
/*applyLocally=*/ false
|
|
811
|
-
);
|
|
812
|
-
|
|
813
|
-
// Shouldn't have gotten any events yet.
|
|
814
|
-
expect(actions).to.deep.equal(['value null']);
|
|
815
|
-
actions.push('txn run');
|
|
816
|
-
ea.addEvent();
|
|
817
|
-
|
|
818
|
-
await ea.promise;
|
|
819
|
-
|
|
820
|
-
expect(actions).to.deep.equal([
|
|
821
|
-
'value null',
|
|
822
|
-
'txn run',
|
|
823
|
-
'value hello!',
|
|
824
|
-
'txn completed'
|
|
825
|
-
]);
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
// This test is meant to ensure that with applyLocally=false, while the transaction is outstanding, we continue
|
|
829
|
-
// to get events from other clients.
|
|
830
|
-
// TODO(mikelehen): Unfortunately this test is currently flaky. It's inherently a racey test since it's
|
|
831
|
-
// trying to do 4 sets before the transaction retries 25 times (and fails), using two different connections.
|
|
832
|
-
// Disabling for now until we rework the approach.
|
|
833
|
-
it.skip('Transaction without local events (2)', done => {
|
|
834
|
-
const refPair = getRandomNode(2) as Reference[],
|
|
835
|
-
ref1 = refPair[0],
|
|
836
|
-
ref2 = refPair[1];
|
|
837
|
-
restoreHash = hijackHash(() => {
|
|
838
|
-
return 'badhash';
|
|
839
|
-
});
|
|
840
|
-
const SETS = 4;
|
|
841
|
-
const events = [];
|
|
842
|
-
let retries = 0;
|
|
843
|
-
let setsDone = 0;
|
|
844
|
-
|
|
845
|
-
function txn1(next) {
|
|
846
|
-
// Do a transaction on the first connection which will keep retrying (cause we hijacked the hash).
|
|
847
|
-
// Make sure we're getting events for the sets happening on the second connection.
|
|
848
|
-
ref1.transaction(
|
|
849
|
-
current => {
|
|
850
|
-
retries++;
|
|
851
|
-
// We should be getting server events while the transaction is outstanding.
|
|
852
|
-
for (let i = 0; i < (current || 0); i++) {
|
|
853
|
-
expect(events[i]).to.equal(i);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
if (current === SETS - 1) {
|
|
857
|
-
restoreHash();
|
|
858
|
-
restoreHash = null;
|
|
859
|
-
}
|
|
860
|
-
return 'txn result';
|
|
861
|
-
},
|
|
862
|
-
(error, committed, snapshot) => {
|
|
863
|
-
expect(error).to.equal(null);
|
|
864
|
-
expect(committed).to.equal(true);
|
|
865
|
-
|
|
866
|
-
expect(snapshot && snapshot.val()).to.equal('txn result');
|
|
867
|
-
next();
|
|
868
|
-
},
|
|
869
|
-
/*applyLocally=*/ false
|
|
870
|
-
);
|
|
871
|
-
|
|
872
|
-
// Meanwhile, do sets from the second connection.
|
|
873
|
-
const doSet = function () {
|
|
874
|
-
ref2.set(setsDone, () => {
|
|
875
|
-
setsDone++;
|
|
876
|
-
if (setsDone < SETS) {
|
|
877
|
-
doSet();
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
};
|
|
881
|
-
doSet();
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
ref1.set(0, () => {
|
|
885
|
-
ref1.on('value', snap => {
|
|
886
|
-
events.push(snap.val());
|
|
887
|
-
if (events.length === 1 && events[0] === 0) {
|
|
888
|
-
txn1(() => {
|
|
889
|
-
// Sanity check stuff.
|
|
890
|
-
expect(setsDone).to.equal(SETS);
|
|
891
|
-
if (retries === 0) {
|
|
892
|
-
throw 'Transaction should have had to retry!';
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// Validate we got the correct events.
|
|
896
|
-
for (let i = 0; i < SETS; i++) {
|
|
897
|
-
expect(events[i]).to.equal(i);
|
|
898
|
-
}
|
|
899
|
-
expect(events[SETS]).to.equal('txn result');
|
|
900
|
-
|
|
901
|
-
if (restoreHash) {
|
|
902
|
-
restoreHash();
|
|
903
|
-
restoreHash = null;
|
|
904
|
-
}
|
|
905
|
-
done();
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
});
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
it('Transaction from value callback.', done => {
|
|
913
|
-
const ref = getRandomNode() as Reference;
|
|
914
|
-
const COUNT = 1;
|
|
915
|
-
let transactionsOutstanding = 0;
|
|
916
|
-
ref.on('value', snap => {
|
|
917
|
-
let shouldCommit = true;
|
|
918
|
-
transactionsOutstanding++;
|
|
919
|
-
ref.transaction(
|
|
920
|
-
current => {
|
|
921
|
-
if (current == null) {
|
|
922
|
-
return 0;
|
|
923
|
-
} else if (current < COUNT) {
|
|
924
|
-
return (current as number) + 1;
|
|
925
|
-
} else {
|
|
926
|
-
shouldCommit = false;
|
|
927
|
-
}
|
|
928
|
-
},
|
|
929
|
-
(error, committed, snap) => {
|
|
930
|
-
expect(committed).to.equal(shouldCommit);
|
|
931
|
-
transactionsOutstanding--;
|
|
932
|
-
if (transactionsOutstanding === 0) {
|
|
933
|
-
done();
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
);
|
|
937
|
-
});
|
|
938
|
-
});
|
|
939
|
-
|
|
940
|
-
it('Transaction runs on null only once after reconnect (Case 1981).', async () => {
|
|
941
|
-
if (!canCreateExtraConnections()) {
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const ref = getRandomNode() as Reference;
|
|
946
|
-
await ref.set(42);
|
|
947
|
-
const newRef = getFreshRepoFromReference(ref);
|
|
948
|
-
let run = 0;
|
|
949
|
-
return newRef.transaction(
|
|
950
|
-
curr => {
|
|
951
|
-
run++;
|
|
952
|
-
if (run === 1) {
|
|
953
|
-
expect(curr).to.equal(null);
|
|
954
|
-
} else if (run === 2) {
|
|
955
|
-
expect(curr).to.equal(42);
|
|
956
|
-
}
|
|
957
|
-
return 3.14;
|
|
958
|
-
},
|
|
959
|
-
(error, committed, resultSnapshot) => {
|
|
960
|
-
expect(error).to.equal(null);
|
|
961
|
-
expect(committed).to.equal(true);
|
|
962
|
-
expect(run).to.equal(2);
|
|
963
|
-
expect(resultSnapshot.val()).to.equal(3.14);
|
|
964
|
-
}
|
|
965
|
-
);
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
// Provided by bk@thinkloop.com, this was failing when we sent puts before listens, but passes now.
|
|
969
|
-
it('makeFriends user test case.', () => {
|
|
970
|
-
const ea = EventAccumulatorFactory.waitsForCount(12);
|
|
971
|
-
if (!canCreateExtraConnections()) {
|
|
972
|
-
return;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function makeFriends(accountID, friendAccountIDs, firebase) {
|
|
976
|
-
let friendAccountID;
|
|
977
|
-
|
|
978
|
-
// add friend relationships
|
|
979
|
-
for (const i in friendAccountIDs) {
|
|
980
|
-
if (friendAccountIDs.hasOwnProperty(i)) {
|
|
981
|
-
friendAccountID = friendAccountIDs[i];
|
|
982
|
-
makeFriend(friendAccountID, accountID, firebase);
|
|
983
|
-
makeFriend(accountID, friendAccountID, firebase);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
function makeFriend(accountID, friendAccountID, firebase) {
|
|
989
|
-
firebase
|
|
990
|
-
.child(accountID)
|
|
991
|
-
.child(friendAccountID)
|
|
992
|
-
.transaction(
|
|
993
|
-
r => {
|
|
994
|
-
if (r == null) {
|
|
995
|
-
r = {
|
|
996
|
-
accountID,
|
|
997
|
-
friendAccountID,
|
|
998
|
-
percentCommon: 0
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
return r;
|
|
1003
|
-
},
|
|
1004
|
-
(error, committed, snapshot) => {
|
|
1005
|
-
if (error) {
|
|
1006
|
-
throw error;
|
|
1007
|
-
} else if (!committed) {
|
|
1008
|
-
throw 'All should be committed!';
|
|
1009
|
-
} else {
|
|
1010
|
-
count++;
|
|
1011
|
-
ea.addEvent();
|
|
1012
|
-
snapshot.ref.setPriority(snapshot.val().percentCommon);
|
|
1013
|
-
}
|
|
1014
|
-
},
|
|
1015
|
-
false
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
const firebase = getRandomNode() as Reference;
|
|
1020
|
-
firebase.database.goOffline();
|
|
1021
|
-
firebase.database.goOnline();
|
|
1022
|
-
let count = 0;
|
|
1023
|
-
makeFriends('a1', ['a2', 'a3'], firebase);
|
|
1024
|
-
makeFriends('a2', ['a1', 'a3'], firebase);
|
|
1025
|
-
makeFriends('a3', ['a1', 'a2'], firebase);
|
|
1026
|
-
return ea.promise;
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
it('transaction() respects .priority.', done => {
|
|
1030
|
-
const ref = getRandomNode() as Reference;
|
|
1031
|
-
const values = [];
|
|
1032
|
-
ref.on('value', s => {
|
|
1033
|
-
values.push(s.exportVal());
|
|
1034
|
-
});
|
|
1035
|
-
|
|
1036
|
-
ref.transaction(
|
|
1037
|
-
curr => {
|
|
1038
|
-
expect(curr).to.equal(null);
|
|
1039
|
-
return { '.value': 5, '.priority': 5 };
|
|
1040
|
-
},
|
|
1041
|
-
() => {
|
|
1042
|
-
ref.transaction(
|
|
1043
|
-
curr => {
|
|
1044
|
-
expect(curr).to.equal(5);
|
|
1045
|
-
return { '.value': 10, '.priority': 10 };
|
|
1046
|
-
},
|
|
1047
|
-
() => {
|
|
1048
|
-
expect(values).to.deep.equal([
|
|
1049
|
-
{ '.value': 5, '.priority': 5 },
|
|
1050
|
-
{ '.value': 10, '.priority': 10 }
|
|
1051
|
-
]);
|
|
1052
|
-
done();
|
|
1053
|
-
}
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
);
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
it('Transaction properly reverts data when you add a deeper listen.', done => {
|
|
1060
|
-
const refPair = getRandomNode(2) as Reference[],
|
|
1061
|
-
ref1 = refPair[0],
|
|
1062
|
-
ref2 = refPair[1];
|
|
1063
|
-
ref1.child('y').set('test', () => {
|
|
1064
|
-
ref2.transaction(curr => {
|
|
1065
|
-
if (curr === null) {
|
|
1066
|
-
return { x: 1 };
|
|
1067
|
-
}
|
|
1068
|
-
});
|
|
1069
|
-
|
|
1070
|
-
ref2.child('y').on('value', s => {
|
|
1071
|
-
if (s.val() === 'test') {
|
|
1072
|
-
done();
|
|
1073
|
-
}
|
|
1074
|
-
});
|
|
1075
|
-
});
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
it('Transaction with integer keys', done => {
|
|
1079
|
-
const ref = getRandomNode() as Reference;
|
|
1080
|
-
ref.set({ 1: 1, 5: 5, 10: 10, 20: 20 }, () => {
|
|
1081
|
-
ref.transaction(
|
|
1082
|
-
current => {
|
|
1083
|
-
return 42;
|
|
1084
|
-
},
|
|
1085
|
-
(error, committed) => {
|
|
1086
|
-
expect(error).to.be.null;
|
|
1087
|
-
expect(committed).to.equal(true);
|
|
1088
|
-
done();
|
|
1089
|
-
}
|
|
1090
|
-
);
|
|
1091
|
-
});
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
it('Return null from first run of transaction.', done => {
|
|
1095
|
-
const ref = getRandomNode() as Reference;
|
|
1096
|
-
ref.transaction(
|
|
1097
|
-
c => {
|
|
1098
|
-
return null;
|
|
1099
|
-
},
|
|
1100
|
-
(error, committed) => {
|
|
1101
|
-
expect(error).to.equal(null);
|
|
1102
|
-
expect(committed).to.equal(true);
|
|
1103
|
-
done();
|
|
1104
|
-
}
|
|
1105
|
-
);
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
// https://app.asana.com/0/5673976843758/9259161251948
|
|
1109
|
-
it('Bubble-app transaction bug.', done => {
|
|
1110
|
-
const ref = getRandomNode() as Reference;
|
|
1111
|
-
ref.child('a').transaction(() => {
|
|
1112
|
-
return 1;
|
|
1113
|
-
});
|
|
1114
|
-
ref.child('a').transaction((current: number) => {
|
|
1115
|
-
return current + 42;
|
|
1116
|
-
});
|
|
1117
|
-
ref.child('b').transaction(() => {
|
|
1118
|
-
return 7;
|
|
1119
|
-
});
|
|
1120
|
-
ref.transaction(
|
|
1121
|
-
(current: { a?: number; b?: number } | null) => {
|
|
1122
|
-
if (current && current.a && current.b) {
|
|
1123
|
-
return current.a + current.b;
|
|
1124
|
-
} else {
|
|
1125
|
-
return 'dummy';
|
|
1126
|
-
}
|
|
1127
|
-
},
|
|
1128
|
-
(error, committed, snap) => {
|
|
1129
|
-
expect(error).to.equal(null);
|
|
1130
|
-
expect(committed).to.equal(true);
|
|
1131
|
-
expect(snap.val()).to.deep.equal(50);
|
|
1132
|
-
done();
|
|
1133
|
-
}
|
|
1134
|
-
);
|
|
1135
|
-
});
|
|
1136
|
-
|
|
1137
|
-
it('Transaction and priority: Can set priority in transaction on empty node', async () => {
|
|
1138
|
-
const ref = getRandomNode() as Reference;
|
|
1139
|
-
|
|
1140
|
-
await ref.transaction(current => {
|
|
1141
|
-
return { '.value': 42, '.priority': 7 };
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
return ref.once('value', s => {
|
|
1145
|
-
expect(s.exportVal()).to.deep.equal({ '.value': 42, '.priority': 7 });
|
|
1146
|
-
});
|
|
1147
|
-
});
|
|
1148
|
-
|
|
1149
|
-
it("Transaction and priority: Transaction doesn't change priority.", async () => {
|
|
1150
|
-
const ref = getRandomNode() as Reference;
|
|
1151
|
-
|
|
1152
|
-
await ref.set({ '.value': 42, '.priority': 7 });
|
|
1153
|
-
|
|
1154
|
-
await ref.transaction(current => {
|
|
1155
|
-
return 12;
|
|
1156
|
-
});
|
|
1157
|
-
|
|
1158
|
-
const snap = await ref.once('value');
|
|
1159
|
-
|
|
1160
|
-
expect(snap.exportVal()).to.deep.equal({ '.value': 12, '.priority': 7 });
|
|
1161
|
-
});
|
|
1162
|
-
|
|
1163
|
-
it('Transaction and priority: Transaction can change priority on non-empty node.', async () => {
|
|
1164
|
-
const ref = getRandomNode() as Reference;
|
|
1165
|
-
|
|
1166
|
-
await ref.set({ '.value': 42, '.priority': 7 });
|
|
1167
|
-
|
|
1168
|
-
await ref.transaction(current => {
|
|
1169
|
-
return { '.value': 43, '.priority': 8 };
|
|
1170
|
-
});
|
|
1171
|
-
|
|
1172
|
-
return ref.once('value', s => {
|
|
1173
|
-
expect(s.exportVal()).to.deep.equal({ '.value': 43, '.priority': 8 });
|
|
1174
|
-
});
|
|
1175
|
-
});
|
|
1176
|
-
|
|
1177
|
-
it('Transaction and priority: Changing priority on siblings.', async () => {
|
|
1178
|
-
const ref = getRandomNode() as Reference;
|
|
1179
|
-
|
|
1180
|
-
await ref.set({
|
|
1181
|
-
a: { '.value': 'a', '.priority': 'a' },
|
|
1182
|
-
b: { '.value': 'b', '.priority': 'b' }
|
|
1183
|
-
});
|
|
1184
|
-
|
|
1185
|
-
const tx1 = ref.child('a').transaction(current => {
|
|
1186
|
-
return { '.value': 'a2', '.priority': 'a2' };
|
|
1187
|
-
});
|
|
1188
|
-
|
|
1189
|
-
const tx2 = ref.child('b').transaction(current => {
|
|
1190
|
-
return { '.value': 'b2', '.priority': 'b2' };
|
|
1191
|
-
});
|
|
1192
|
-
|
|
1193
|
-
await Promise.all([tx1, tx2]);
|
|
1194
|
-
|
|
1195
|
-
return ref.once('value', s => {
|
|
1196
|
-
expect(s.exportVal()).to.deep.equal({
|
|
1197
|
-
a: { '.value': 'a2', '.priority': 'a2' },
|
|
1198
|
-
b: { '.value': 'b2', '.priority': 'b2' }
|
|
1199
|
-
});
|
|
1200
|
-
});
|
|
1201
|
-
});
|
|
1202
|
-
|
|
1203
|
-
it('Transaction and priority: Leaving priority on siblings.', async () => {
|
|
1204
|
-
const ref = getRandomNode() as Reference;
|
|
1205
|
-
|
|
1206
|
-
await ref.set({
|
|
1207
|
-
a: { '.value': 'a', '.priority': 'a' },
|
|
1208
|
-
b: { '.value': 'b', '.priority': 'b' }
|
|
1209
|
-
});
|
|
1210
|
-
|
|
1211
|
-
const tx1 = ref.child('a').transaction(current => {
|
|
1212
|
-
return 'a2';
|
|
1213
|
-
});
|
|
1214
|
-
|
|
1215
|
-
const tx2 = ref.child('b').transaction(current => {
|
|
1216
|
-
return 'b2';
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
|
-
await Promise.all([tx1, tx2]);
|
|
1220
|
-
|
|
1221
|
-
return ref.once('value', s => {
|
|
1222
|
-
expect(s.exportVal()).to.deep.equal({
|
|
1223
|
-
a: { '.value': 'a2', '.priority': 'a' },
|
|
1224
|
-
b: { '.value': 'b2', '.priority': 'b' }
|
|
1225
|
-
});
|
|
1226
|
-
});
|
|
1227
|
-
});
|
|
1228
|
-
|
|
1229
|
-
it("transaction() doesn't pick up cached data from previous once().", done => {
|
|
1230
|
-
const refPair = getRandomNode(2) as Reference[];
|
|
1231
|
-
const me = refPair[0],
|
|
1232
|
-
other = refPair[1];
|
|
1233
|
-
me.set('not null', () => {
|
|
1234
|
-
me.once('value', snapshot => {
|
|
1235
|
-
other.set(null, () => {
|
|
1236
|
-
me.transaction(
|
|
1237
|
-
snapshot => {
|
|
1238
|
-
if (snapshot === null) {
|
|
1239
|
-
return 'it was null!';
|
|
1240
|
-
} else {
|
|
1241
|
-
return 'it was not null!';
|
|
1242
|
-
}
|
|
1243
|
-
},
|
|
1244
|
-
(err, committed, snapshot) => {
|
|
1245
|
-
expect(err).to.equal(null);
|
|
1246
|
-
expect(committed).to.equal(true);
|
|
1247
|
-
expect(snapshot.val()).to.deep.equal('it was null!');
|
|
1248
|
-
done();
|
|
1249
|
-
}
|
|
1250
|
-
);
|
|
1251
|
-
});
|
|
1252
|
-
});
|
|
1253
|
-
});
|
|
1254
|
-
});
|
|
1255
|
-
|
|
1256
|
-
it("transaction() doesn't pick up cached data from previous transaction.", done => {
|
|
1257
|
-
const refPair = getRandomNode(2) as Reference[];
|
|
1258
|
-
const me = refPair[0],
|
|
1259
|
-
other = refPair[1];
|
|
1260
|
-
me.transaction(
|
|
1261
|
-
() => {
|
|
1262
|
-
return 'not null';
|
|
1263
|
-
},
|
|
1264
|
-
(err, committed) => {
|
|
1265
|
-
expect(err).to.equal(null);
|
|
1266
|
-
expect(committed).to.equal(true);
|
|
1267
|
-
other.set(null, () => {
|
|
1268
|
-
me.transaction(
|
|
1269
|
-
snapshot => {
|
|
1270
|
-
if (snapshot === null) {
|
|
1271
|
-
return 'it was null!';
|
|
1272
|
-
} else {
|
|
1273
|
-
return 'it was not null!';
|
|
1274
|
-
}
|
|
1275
|
-
},
|
|
1276
|
-
(err, committed, snapshot) => {
|
|
1277
|
-
expect(err).to.equal(null);
|
|
1278
|
-
expect(committed).to.equal(true);
|
|
1279
|
-
expect(snapshot.val()).to.deep.equal('it was null!');
|
|
1280
|
-
done();
|
|
1281
|
-
}
|
|
1282
|
-
);
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
);
|
|
1286
|
-
});
|
|
1287
|
-
|
|
1288
|
-
it('server values: local timestamp should eventually (but not immediately) match the server with txns', done => {
|
|
1289
|
-
const refPair = getRandomNode(2) as Reference[],
|
|
1290
|
-
writer = refPair[0],
|
|
1291
|
-
reader = refPair[1],
|
|
1292
|
-
readSnaps = [],
|
|
1293
|
-
writeSnaps = [];
|
|
1294
|
-
|
|
1295
|
-
const evaluateCompletionCriteria = function () {
|
|
1296
|
-
if (readSnaps.length === 1 && writeSnaps.length === 2) {
|
|
1297
|
-
expect(
|
|
1298
|
-
Math.abs(new Date().getTime() - writeSnaps[0].val()) < 10000
|
|
1299
|
-
).to.equal(true);
|
|
1300
|
-
expect(
|
|
1301
|
-
Math.abs(new Date().getTime() - writeSnaps[0].getPriority()) < 10000
|
|
1302
|
-
).to.equal(true);
|
|
1303
|
-
expect(
|
|
1304
|
-
Math.abs(new Date().getTime() - writeSnaps[1].val()) < 10000
|
|
1305
|
-
).to.equal(true);
|
|
1306
|
-
expect(
|
|
1307
|
-
Math.abs(new Date().getTime() - writeSnaps[1].getPriority()) < 10000
|
|
1308
|
-
).to.equal(true);
|
|
1309
|
-
|
|
1310
|
-
expect(writeSnaps[0].val() === writeSnaps[1].val()).to.equal(false);
|
|
1311
|
-
expect(
|
|
1312
|
-
writeSnaps[0].getPriority() === writeSnaps[1].getPriority()
|
|
1313
|
-
).to.equal(false);
|
|
1314
|
-
expect(writeSnaps[1].val() === readSnaps[0].val()).to.equal(true);
|
|
1315
|
-
expect(
|
|
1316
|
-
writeSnaps[1].getPriority() === readSnaps[0].getPriority()
|
|
1317
|
-
).to.equal(true);
|
|
1318
|
-
done();
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1321
|
-
|
|
1322
|
-
// 1st non-null event = actual server timestamp
|
|
1323
|
-
reader.on('value', snap => {
|
|
1324
|
-
if (snap.val() === null) {
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
readSnaps.push(snap);
|
|
1328
|
-
evaluateCompletionCriteria();
|
|
1329
|
-
});
|
|
1330
|
-
|
|
1331
|
-
// 1st non-null event = local timestamp estimate
|
|
1332
|
-
// 2nd non-null event = actual server timestamp
|
|
1333
|
-
writer.on('value', snap => {
|
|
1334
|
-
if (snap.val() === null) {
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
writeSnaps.push(snap);
|
|
1338
|
-
evaluateCompletionCriteria();
|
|
1339
|
-
});
|
|
1340
|
-
|
|
1341
|
-
// Generate the server value offline to make sure there's a time gap between the client's guess of the timestamp
|
|
1342
|
-
// and the server's actual timestamp.
|
|
1343
|
-
writer.database.goOffline();
|
|
1344
|
-
|
|
1345
|
-
writer.transaction(current => {
|
|
1346
|
-
return {
|
|
1347
|
-
'.value': (firebase as any).database.ServerValue.TIMESTAMP,
|
|
1348
|
-
'.priority': (firebase as any).database.ServerValue.TIMESTAMP
|
|
1349
|
-
};
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
|
-
writer.database.goOnline();
|
|
1353
|
-
});
|
|
1354
|
-
|
|
1355
|
-
it("transaction() still works when there's a query listen.", done => {
|
|
1356
|
-
const ref = getRandomNode() as Reference;
|
|
1357
|
-
|
|
1358
|
-
ref.set(
|
|
1359
|
-
{
|
|
1360
|
-
a: 1,
|
|
1361
|
-
b: 2
|
|
1362
|
-
},
|
|
1363
|
-
() => {
|
|
1364
|
-
ref.limitToFirst(1).on('child_added', () => {});
|
|
1365
|
-
|
|
1366
|
-
ref.child('a').transaction(
|
|
1367
|
-
current => {
|
|
1368
|
-
return current;
|
|
1369
|
-
},
|
|
1370
|
-
(error, committed, snapshot) => {
|
|
1371
|
-
expect(error).to.equal(null);
|
|
1372
|
-
expect(committed).to.equal(true);
|
|
1373
|
-
if (!error) {
|
|
1374
|
-
expect(snapshot.val()).to.deep.equal(1);
|
|
1375
|
-
}
|
|
1376
|
-
done();
|
|
1377
|
-
},
|
|
1378
|
-
false
|
|
1379
|
-
);
|
|
1380
|
-
}
|
|
1381
|
-
);
|
|
1382
|
-
});
|
|
1383
|
-
|
|
1384
|
-
it("transaction() on queried location doesn't run initially on null (firebase-worker-queue depends on this).", done => {
|
|
1385
|
-
const ref = getRandomNode() as Reference;
|
|
1386
|
-
ref.push({ a: 1, b: 2 }, () => {
|
|
1387
|
-
ref
|
|
1388
|
-
.startAt()
|
|
1389
|
-
.limitToFirst(1)
|
|
1390
|
-
.on('child_added', snap => {
|
|
1391
|
-
snap.ref.transaction(
|
|
1392
|
-
current => {
|
|
1393
|
-
expect(current).to.deep.equal({ a: 1, b: 2 });
|
|
1394
|
-
return null;
|
|
1395
|
-
},
|
|
1396
|
-
(error, committed, snapshot) => {
|
|
1397
|
-
expect(error).to.equal(null);
|
|
1398
|
-
expect(committed).to.equal(true);
|
|
1399
|
-
expect(snapshot.val()).to.equal(null);
|
|
1400
|
-
done();
|
|
1401
|
-
}
|
|
1402
|
-
);
|
|
1403
|
-
});
|
|
1404
|
-
});
|
|
1405
|
-
});
|
|
1406
|
-
|
|
1407
|
-
it('transactions raise correct child_changed events on queries', async () => {
|
|
1408
|
-
const ref = getRandomNode() as Reference;
|
|
1409
|
-
|
|
1410
|
-
const value = { foo: { value: 1 } };
|
|
1411
|
-
const snapshots = [];
|
|
1412
|
-
|
|
1413
|
-
await ref.set(value);
|
|
1414
|
-
|
|
1415
|
-
const query = ref.endAt(Number.MIN_VALUE);
|
|
1416
|
-
query.on('child_added', snapshot => {
|
|
1417
|
-
snapshots.push(snapshot);
|
|
1418
|
-
});
|
|
1419
|
-
|
|
1420
|
-
query.on('child_changed', snapshot => {
|
|
1421
|
-
snapshots.push(snapshot);
|
|
1422
|
-
});
|
|
1423
|
-
|
|
1424
|
-
await ref.child('foo').transaction(
|
|
1425
|
-
current => {
|
|
1426
|
-
return { value: 2 };
|
|
1427
|
-
},
|
|
1428
|
-
(error, committed, snapshot) => {
|
|
1429
|
-
expect(error).to.equal(null);
|
|
1430
|
-
expect(committed).to.equal(true);
|
|
1431
|
-
},
|
|
1432
|
-
false
|
|
1433
|
-
);
|
|
1434
|
-
|
|
1435
|
-
expect(snapshots.length).to.equal(2);
|
|
1436
|
-
const addedSnapshot = snapshots[0];
|
|
1437
|
-
expect(addedSnapshot.key).to.equal('foo');
|
|
1438
|
-
expect(addedSnapshot.val()).to.deep.equal({ value: 1 });
|
|
1439
|
-
const changedSnapshot = snapshots[1];
|
|
1440
|
-
expect(changedSnapshot.key).to.equal('foo');
|
|
1441
|
-
expect(changedSnapshot.val()).to.deep.equal({ value: 2 });
|
|
1442
|
-
});
|
|
1443
|
-
|
|
1444
|
-
it('transactions can use local merges', done => {
|
|
1445
|
-
const ref = getRandomNode() as Reference;
|
|
1446
|
-
|
|
1447
|
-
ref.update({ foo: 'bar' });
|
|
1448
|
-
|
|
1449
|
-
ref.child('foo').transaction(
|
|
1450
|
-
current => {
|
|
1451
|
-
expect(current).to.equal('bar');
|
|
1452
|
-
return current;
|
|
1453
|
-
},
|
|
1454
|
-
(error, committed, snapshot) => {
|
|
1455
|
-
expect(error).to.equal(null);
|
|
1456
|
-
expect(committed).to.equal(true);
|
|
1457
|
-
done();
|
|
1458
|
-
}
|
|
1459
|
-
);
|
|
1460
|
-
});
|
|
1461
|
-
|
|
1462
|
-
it('transactions works with merges without the transaction path', done => {
|
|
1463
|
-
const ref = getRandomNode() as Reference;
|
|
1464
|
-
|
|
1465
|
-
ref.update({ foo: 'bar' });
|
|
1466
|
-
|
|
1467
|
-
ref.child('non-foo').transaction(
|
|
1468
|
-
current => {
|
|
1469
|
-
expect(current).to.equal(null);
|
|
1470
|
-
return current;
|
|
1471
|
-
},
|
|
1472
|
-
(error, committed, snapshot) => {
|
|
1473
|
-
expect(error).to.equal(null);
|
|
1474
|
-
expect(committed).to.equal(true);
|
|
1475
|
-
done();
|
|
1476
|
-
}
|
|
1477
|
-
);
|
|
1478
|
-
});
|
|
1479
|
-
|
|
1480
|
-
//See https://app.asana.com/0/15566422264127/23303789496881
|
|
1481
|
-
it('out of order remove writes are handled correctly', done => {
|
|
1482
|
-
const ref = getRandomNode() as Reference;
|
|
1483
|
-
|
|
1484
|
-
ref.set({ foo: 'bar' });
|
|
1485
|
-
ref.transaction(
|
|
1486
|
-
() => {
|
|
1487
|
-
return 'transaction-1';
|
|
1488
|
-
},
|
|
1489
|
-
() => {}
|
|
1490
|
-
);
|
|
1491
|
-
ref.transaction(
|
|
1492
|
-
() => {
|
|
1493
|
-
return 'transaction-2';
|
|
1494
|
-
},
|
|
1495
|
-
() => {}
|
|
1496
|
-
);
|
|
1497
|
-
|
|
1498
|
-
// This will trigger an abort of the transaction which should not cause the client to crash
|
|
1499
|
-
ref.update({ qux: 'quu' }, error => {
|
|
1500
|
-
expect(error).to.equal(null);
|
|
1501
|
-
done();
|
|
1502
|
-
});
|
|
1503
|
-
});
|
|
1504
|
-
|
|
1505
|
-
it('Can listen to transaction changes', async () => {
|
|
1506
|
-
// Repro for https://github.com/firebase/firebase-js-sdk/issues/5195
|
|
1507
|
-
let latestValue = 0;
|
|
1508
|
-
|
|
1509
|
-
const ref = getRandomNode() as Reference;
|
|
1510
|
-
|
|
1511
|
-
let deferred = new Deferred<void>();
|
|
1512
|
-
ref.on('value', snap => {
|
|
1513
|
-
latestValue = snap.val() as number;
|
|
1514
|
-
deferred.resolve();
|
|
1515
|
-
});
|
|
1516
|
-
|
|
1517
|
-
async function incrementViaTransaction() {
|
|
1518
|
-
deferred = new Deferred<void>();
|
|
1519
|
-
await ref.transaction(currentData => {
|
|
1520
|
-
return (currentData as number) + 1;
|
|
1521
|
-
});
|
|
1522
|
-
// Wait for the snapshot listener to fire. They are not invoked inline
|
|
1523
|
-
// for transactions.
|
|
1524
|
-
await deferred.promise;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
expect(latestValue).to.equal(0);
|
|
1528
|
-
|
|
1529
|
-
await incrementViaTransaction();
|
|
1530
|
-
expect(latestValue).to.equal(1);
|
|
1531
|
-
await incrementViaTransaction();
|
|
1532
|
-
expect(latestValue).to.equal(2);
|
|
1533
|
-
});
|
|
1534
|
-
});
|