diff --git a/test/y-array.tests.js b/test/y-array.tests.js new file mode 100644 index 00000000..3a49fa32 --- /dev/null +++ b/test/y-array.tests.js @@ -0,0 +1,374 @@ +import { wait, initArrays, compareUsers, Y, flushAll, garbageCollectUsers, applyRandomTests } from '../tests-lib/helper.js' +import { test, proxyConsole } from 'cutest' + +proxyConsole() + +test('basic spec', async function array0 (t) { + let { users, array0 } = await initArrays(t, { users: 2 }) + + array0.delete(0, 0) + t.assert(true, 'Does not throw when deleting zero elements with position 0') + + let throwInvalidPosition = false + try { + array0.delete(1, 0) + } catch (e) { + throwInvalidPosition = true + } + t.assert(throwInvalidPosition, 'Throws when deleting zero elements with an invalid position') + + array0.insert(0, ['A']) + array0.delete(1, 0) + t.assert(true, 'Does not throw when deleting zero elements with valid position 1') + + await compareUsers(t, users) +}) + +test('insert three elements, try re-get property', async function array1 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 2 }) + array0.insert(0, [1, 2, 3]) + t.compare(array0.toArray(), [1, 2, 3], '.toArray() works') + await flushAll(t, users) + t.compare(array1.toArray(), [1, 2, 3], '.toArray() works after sync') + await compareUsers(t, users) +}) + +test('concurrent insert (handle three conflicts)', async function array2 (t) { + var { users, array0, array1, array2 } = await initArrays(t, { users: 3 }) + array0.insert(0, [0]) + array1.insert(0, [1]) + array2.insert(0, [2]) + + await compareUsers(t, users) +}) + +test('concurrent insert&delete (handle three conflicts)', async function array3 (t) { + var { users, array0, array1, array2 } = await initArrays(t, { users: 3 }) + array0.insert(0, ['x', 'y', 'z']) + await flushAll(t, users) + array0.insert(1, [0]) + array1.delete(0) + array1.delete(1, 1) + array2.insert(1, [2]) + + await compareUsers(t, users) +}) + +test('insertions work in late sync', async function array4 (t) { + var { users, array0, array1, array2 } = await initArrays(t, { users: 3 }) + array0.insert(0, ['x', 'y']) + await flushAll(t, users) + users[1].disconnect() + users[2].disconnect() + array0.insert(1, ['user0']) + array1.insert(1, ['user1']) + array2.insert(1, ['user2']) + await users[1].reconnect() + await users[2].reconnect() + await compareUsers(t, users) +}) + +test('disconnect really prevents sending messages', async function array5 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 3 }) + array0.insert(0, ['x', 'y']) + await flushAll(t, users) + users[1].disconnect() + users[2].disconnect() + array0.insert(1, ['user0']) + array1.insert(1, ['user1']) + await wait(1000) + t.compare(array0.toArray(), ['x', 'user0', 'y']) + t.compare(array1.toArray(), ['x', 'user1', 'y']) + await users[1].reconnect() + await users[2].reconnect() + await compareUsers(t, users) +}) + +test('deletions in late sync', async function array6 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 2 }) + array0.insert(0, ['x', 'y']) + await flushAll(t, users) + await users[1].disconnect() + array1.delete(1, 1) + array0.delete(0, 2) + await wait() + await users[1].reconnect() + await compareUsers(t, users) +}) + +test('insert, then marge delete on sync', async function array7 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 2 }) + array0.insert(0, ['x', 'y', 'z']) + await flushAll(t, users) + await wait() + await users[0].disconnect() + array1.delete(0, 3) + await wait() + await users[0].reconnect() + await compareUsers(t, users) +}) + +function compareEvent (t, is, should) { + for (var key in should) { + t.assert( + should[key] === is[key] || + JSON.stringify(should[key]) === JSON.stringify(is[key]) + , 'event works as expected' + ) + } +} + +test('insert & delete events', async function array8 (t) { + var { array0, users } = await initArrays(t, { users: 2 }) + var event + array0.observe(function (e) { + event = e + }) + array0.insert(0, [0, 1, 2]) + compareEvent(t, event, { + type: 'insert', + index: 0, + values: [0, 1, 2], + length: 3 + }) + array0.delete(0) + compareEvent(t, event, { + type: 'delete', + index: 0, + length: 1, + values: [0] + }) + array0.delete(0, 2) + compareEvent(t, event, { + type: 'delete', + index: 0, + length: 2, + values: [1, 2] + }) + await compareUsers(t, users) +}) + +test('insert & delete events for types', async function array9 (t) { + var { array0, users } = await initArrays(t, { users: 2 }) + var event + array0.observe(function (e) { + event = e + }) + array0.insert(0, [Y.Array]) + compareEvent(t, event, { + type: 'insert', + object: array0, + index: 0, + length: 1 + }) + var type = array0.get(0) + t.assert(type._model != null, 'Model of type is defined') + array0.delete(0) + compareEvent(t, event, { + type: 'delete', + object: array0, + index: 0, + length: 1 + }) + await compareUsers(t, users) +}) + +test('insert & delete events for types (2)', async function array10 (t) { + var { array0, users } = await initArrays(t, { users: 2 }) + var events = [] + array0.observe(function (e) { + events.push(e) + }) + array0.insert(0, ['hi', Y.Map]) + compareEvent(t, events[0], { + type: 'insert', + object: array0, + index: 0, + length: 1, + values: ['hi'] + }) + compareEvent(t, events[1], { + type: 'insert', + object: array0, + index: 1, + length: 1 + }) + array0.delete(1) + compareEvent(t, events[2], { + type: 'delete', + object: array0, + index: 1, + length: 1 + }) + await compareUsers(t, users) +}) + +test('garbage collector', async function gc1 (t) { + var { users, array0 } = await initArrays(t, { users: 3 }) + + array0.insert(0, ['x', 'y', 'z']) + await flushAll(t, users) + users[0].disconnect() + array0.delete(0, 3) + await wait() + await users[0].reconnect() + await flushAll(t, users) + await garbageCollectUsers(t, users) + await compareUsers(t, users) +}) + +test('event has correct value when setting a primitive on a YArray (same user)', async function array11 (t) { + var { array0, users } = await initArrays(t, { users: 3 }) + + var event + array0.observe(function (e) { + event = e + }) + array0.insert(0, ['stuff']) + t.assert(event.values[0] === event.object.get(0), 'compare value with get method') + t.assert(event.values[0] === 'stuff', 'check that value is actually present') + t.assert(event.values[0] === array0.toArray()[0], '.toArray works as expected') + await compareUsers(t, users) +}) + +test('event has correct value when setting a primitive on a YArray (received from another user)', async function array12 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 3 }) + + var event + array0.observe(function (e) { + event = e + }) + array1.insert(0, ['stuff']) + await flushAll(t, users) + t.assert(event.values[0] === event.object.get(0), 'compare value with get method') + t.assert(event.values[0] === 'stuff', 'check that value is actually present') + t.assert(event.values[0] === array0.toArray()[0], '.toArray works as expected') + await compareUsers(t, users) +}) + +test('event has correct value when setting a type on a YArray (same user)', async function array13 (t) { + var { array0, users } = await initArrays(t, { users: 3 }) + + var event + array0.observe(function (e) { + event = e + }) + array0.insert(0, [Y.Array]) + t.assert(event.values[0] === event.object.get(0), 'compare value with get method') + t.assert(event.values[0] != null, 'event.value exists') + t.assert(event.values[0] === array0.toArray()[0], '.toArray works as expected') + await compareUsers(t, users) +}) +test('event has correct value when setting a type on a YArray (ops received from another user)', async function array14 (t) { + var { users, array0, array1 } = await initArrays(t, { users: 3 }) + + var event + array0.observe(function (e) { + event = e + }) + array1.insert(0, [Y.Array]) + await flushAll(t, users) + t.assert(event.values[0] === event.object.get(0), 'compare value with get method') + t.assert(event.values[0] != null, 'event.value exists') + t.assert(event.values[0] === array0.toArray()[0], '.toArray works as expected') + await compareUsers(t, users) +}) + +var _uniqueNumber = 0 +function getUniqueNumber () { + return _uniqueNumber++ +} + +var arrayTransactions = [ + function insert (t, user, chance) { + var uniqueNumber = getUniqueNumber() + var content = [] + var len = chance.integer({ min: 1, max: 4 }) + for (var i = 0; i < len; i++) { + content.push(uniqueNumber) + } + var pos = chance.integer({ min: 0, max: user.share.array.length }) + user.share.array.insert(pos, content) + }, + function insertTypeArray (t, user, chance) { + var pos = chance.integer({ min: 0, max: user.share.array.length }) + user.share.array.insert(pos, [Y.Array]) + var array2 = user.share.array.get(pos) + array2.insert(0, [1, 2, 3, 4]) + }, + function insertTypeMap (t, user, chance) { + var pos = chance.integer({ min: 0, max: user.share.array.length }) + user.share.array.insert(pos, [Y.Map]) + var map = user.share.array.get(pos) + map.set('someprop', 42) + map.set('someprop', 43) + map.set('someprop', 44) + }, + function _delete (t, user, chance) { + var length = user.share.array._content.length + if (length > 0) { + var pos = chance.integer({ min: 0, max: length - 1 }) + var delLength = chance.integer({ min: 1, max: Math.min(2, length - pos) }) + if (user.share.array._content[pos].type != null) { + if (chance.bool()) { + var type = user.share.array.get(pos) + if (type instanceof Y.Array.typeDefinition.class) { + if (type._content.length > 0) { + pos = chance.integer({ min: 0, max: type._content.length - 1 }) + delLength = chance.integer({ min: 0, max: Math.min(2, type._content.length - pos) }) + type.delete(pos, delLength) + } + } else { + type.delete('someprop') + } + } else { + user.share.array.delete(pos, delLength) + } + } else { + user.share.array.delete(pos, delLength) + } + } + } +] + +test('y-array: Random tests (42)', async function randomArray42 (t) { + await applyRandomTests(t, arrayTransactions, 42) +}) + +test('y-array: Random tests (43)', async function randomArray43 (t) { + await applyRandomTests(t, arrayTransactions, 43) +}) + +test('y-array: Random tests (44)', async function randomArray44 (t) { + await applyRandomTests(t, arrayTransactions, 44) +}) + +test('y-array: Random tests (45)', async function randomArray45 (t) { + await applyRandomTests(t, arrayTransactions, 45) +}) + +test('y-array: Random tests (46)', async function randomArray46 (t) { + await applyRandomTests(t, arrayTransactions, 46) +}) + +test('y-array: Random tests (47)', async function randomArray47 (t) { + await applyRandomTests(t, arrayTransactions, 47) +}) + +/* +test('y-array: Random tests (200)', async function randomArray200 (t) { + await applyRandomTests(t, arrayTransactions, 200) +}) + +test('y-array: Random tests (300)', async function randomArray300 (t) { + await applyRandomTests(t, arrayTransactions, 300) +}) + +test('y-array: Random tests (400)', async function randomArray400 (t) { + await applyRandomTests(t, arrayTransactions, 400) +}) + +test('y-array: Random tests (500)', async function randomArray500 (t) { + await applyRandomTests(t, arrayTransactions, 500) +}) +*/ diff --git a/test/y-map.tests.js b/test/y-map.tests.js new file mode 100644 index 00000000..9b424f4a --- /dev/null +++ b/test/y-map.tests.js @@ -0,0 +1,371 @@ +import { initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.js' +import { test, proxyConsole } from 'cutest' + +proxyConsole() + +test('basic map tests', async function map0 (t) { + let { users, map0, map1, map2 } = await initArrays(t, { users: 3 }) + users[2].disconnect() + + map0.set('number', 1) + map0.set('string', 'hello Y') + map0.set('object', { key: { key2: 'value' } }) + map0.set('y-map', Y.Map) + let map = map0.get('y-map') + map.set('y-array', Y.Array) + let array = map.get('y-array') + array.insert(0, [0]) + array.insert(0, [-1]) + + t.assert(map0.get('number') === 1, 'client 0 computed the change (number)') + t.assert(map0.get('string') === 'hello Y', 'client 0 computed the change (string)') + t.compare(map0.get('object'), { key: { key2: 'value' } }, 'client 0 computed the change (object)') + t.assert(map0.get('y-map').get('y-array').get(0) === -1, 'client 0 computed the change (type)') + + await users[2].reconnect() + await flushAll(t, users) + + t.assert(map1.get('number') === 1, 'client 1 received the update (number)') + t.assert(map1.get('string') === 'hello Y', 'client 1 received the update (string)') + t.compare(map1.get('object'), { key: { key2: 'value' } }, 'client 1 received the update (object)') + t.assert(map1.get('y-map').get('y-array').get(0) === -1, 'client 1 received the update (type)') + + // compare disconnected user + t.assert(map2.get('number') === 1, 'client 2 received the update (number) - was disconnected') + t.assert(map2.get('string') === 'hello Y', 'client 2 received the update (string) - was disconnected') + t.compare(map2.get('object'), { key: { key2: 'value' } }, 'client 2 received the update (object) - was disconnected') + t.assert(map2.get('y-map').get('y-array').get(0) === -1, 'client 2 received the update (type) - was disconnected') + await compareUsers(t, users) +}) + +test('Basic get&set of Map property (converge via sync)', async function map1 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + map0.set('stuff', 'stuffy') + t.compare(map0.get('stuff'), 'stuffy') + + await flushAll(t, users) + + for (let user of users) { + var u = user.share.map + t.compare(u.get('stuff'), 'stuffy') + } + await compareUsers(t, users) +}) + +test('Map can set custom types (Map)', async function map2 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + var map = map0.set('Map', Y.Map) + map.set('one', 1) + map = map0.get('Map') + t.compare(map.get('one'), 1) + await compareUsers(t, users) +}) + +test('Map can set custom types (Map) - get also returns the type', async function map3 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + map0.set('Map', Y.Map) + var map = map0.get('Map') + map.set('one', 1) + map = map0.get('Map') + t.compare(map.get('one'), 1) + await compareUsers(t, users) +}) + +test('Map can set custom types (Array)', async function map4 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + var array = map0.set('Array', Y.Array) + array.insert(0, [1, 2, 3]) + array = map0.get('Array') + t.compare(array.toArray(), [1, 2, 3]) + await compareUsers(t, users) +}) + +test('Basic get&set of Map property (converge via update)', async function map5 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + map0.set('stuff', 'stuffy') + t.compare(map0.get('stuff'), 'stuffy') + + await flushAll(t, users) + + for (let user of users) { + var u = user.share.map + t.compare(u.get('stuff'), 'stuffy') + } + await compareUsers(t, users) +}) + +test('Basic get&set of Map property (handle conflict)', async function map6 (t) { + let { users, map0, map1 } = await initArrays(t, { users: 3 }) + map0.set('stuff', 'c0') + map1.set('stuff', 'c1') + + await flushAll(t, users) + + for (let user of users) { + var u = user.share.map + t.compare(u.get('stuff'), 'c0') + } + await compareUsers(t, users) +}) + +test('Basic get&set&delete of Map property (handle conflict)', async function map7 (t) { + let { users, map0, map1 } = await initArrays(t, { users: 3 }) + map0.set('stuff', 'c0') + map0.delete('stuff') + map1.set('stuff', 'c1') + await flushAll(t, users) + for (let user of users) { + var u = user.share.map + t.assert(u.get('stuff') === undefined) + } + await compareUsers(t, users) +}) + +test('Basic get&set of Map property (handle three conflicts)', async function map8 (t) { + let { users, map0, map1, map2 } = await initArrays(t, { users: 3 }) + map0.set('stuff', 'c0') + map1.set('stuff', 'c1') + map1.set('stuff', 'c2') + map2.set('stuff', 'c3') + await flushAll(t, users) + for (let user of users) { + var u = user.share.map + t.compare(u.get('stuff'), 'c0') + } + await compareUsers(t, users) +}) + +test('Basic get&set&delete of Map property (handle three conflicts)', async function map9 (t) { + let { users, map0, map1, map2, map3 } = await initArrays(t, { users: 4 }) + map0.set('stuff', 'c0') + map1.set('stuff', 'c1') + map1.set('stuff', 'c2') + map2.set('stuff', 'c3') + await flushAll(t, users) + map0.set('stuff', 'deleteme') + map0.delete('stuff') + map1.set('stuff', 'c1') + map2.set('stuff', 'c2') + map3.set('stuff', 'c3') + await flushAll(t, users) + for (let user of users) { + var u = user.share.map + t.assert(u.get('stuff') === undefined) + } + await compareUsers(t, users) +}) + +test('observePath properties', async function map10 (t) { + let { users, map0, map1, map2 } = await initArrays(t, { users: 3 }) + let map + map0.observePath(['map'], function (map) { + if (map != null) { + map.set('yay', 4) + } + }) + map1.set('map', Y.Map) + await flushAll(t, users) + map = map2.get('map') + t.compare(map.get('yay'), 4) + await compareUsers(t, users) +}) + +test('observe deep properties', async function map11 (t) { + let { users, map1, map2, map3 } = await initArrays(t, { users: 4 }) + var _map1 = map1.set('map', Y.Map) + var calls = 0 + var dmapid + _map1.observe(function (event) { + calls++ + t.compare(event.name, 'deepmap') + dmapid = event.object.opContents.deepmap + }) + await flushAll(t, users) + var _map3 = map3.get('map') + _map3.set('deepmap', Y.Map) + await flushAll(t, users) + var _map2 = map2.get('map') + _map2.set('deepmap', Y.Map) + await flushAll(t, users) + var dmap1 = _map1.get('deepmap') + var dmap2 = _map2.get('deepmap') + var dmap3 = _map3.get('deepmap') + t.assert(calls > 0) + t.compare(dmap1._model, dmap2._model) + t.compare(dmap1._model, dmap3._model) + t.compare(dmap1._model, dmapid) + await compareUsers(t, users) +}) + +test('observes using observePath', async function map12 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + var pathes = [] + var calls = 0 + map0.observeDeep(function (event) { + pathes.push(event.path) + calls++ + }) + map0.set('map', Y.Map) + map0.get('map').set('array', Y.Array) + map0.get('map').get('array').insert(0, ['content']) + t.assert(calls === 3) + t.compare(pathes, [[], ['map'], ['map', 'array']]) + await compareUsers(t, users) +}) + +function compareEvent (t, is, should) { + for (var key in should) { + t.assert(should[key] === is[key]) + } +} + +test('throws add & update & delete events (with type and primitive content)', async function map13 (t) { + let { users, map0 } = await initArrays(t, { users: 2 }) + var event + await flushAll(t, users) + map0.observe(function (e) { + event = e // just put it on event, should be thrown synchronously anyway + }) + map0.set('stuff', 4) + compareEvent(t, event, { + type: 'add', + object: map0, + name: 'stuff' + }) + // update, oldValue is in contents + map0.set('stuff', Y.Array) + compareEvent(t, event, { + type: 'update', + object: map0, + name: 'stuff', + oldValue: 4 + }) + var replacedArray = map0.get('stuff') + // update, oldValue is in opContents + map0.set('stuff', 5) + var array = event.oldValue + t.compare(array._model, replacedArray._model) + // delete + map0.delete('stuff') + compareEvent(t, event, { + type: 'delete', + name: 'stuff', + object: map0, + oldValue: 5 + }) + await compareUsers(t, users) +}) + +test('event has correct value when setting a primitive on a YMap (same user)', async function map14 (t) { + let { users, map0 } = await initArrays(t, { users: 3 }) + var event + await flushAll(t, users) + map0.observe(function (e) { + event = e + }) + map0.set('stuff', 2) + t.compare(event.value, event.object.get(event.name)) + await compareUsers(t, users) +}) + +test('event has correct value when setting a primitive on a YMap (received from another user)', async function map15 (t) { + let { users, map0, map1 } = await initArrays(t, { users: 3 }) + var event + await flushAll(t, users) + map0.observe(function (e) { + event = e + }) + map1.set('stuff', 2) + await flushAll(t, users) + t.compare(event.value, event.object.get(event.name)) + await compareUsers(t, users) +}) + +test('event has correct value when setting a type on a YMap (same user)', async function map16 (t) { + let { users, map0 } = await initArrays(t, { users: 3 }) + var event + await flushAll(t, users) + map0.observe(function (e) { + event = e + }) + map0.set('stuff', Y.Map) + t.compare(event.value._model, event.object.get(event.name)._model) + await compareUsers(t, users) +}) + +test('event has correct value when setting a type on a YMap (ops received from another user)', async function map17 (t) { + let { users, map0, map1 } = await initArrays(t, { users: 3 }) + var event + await flushAll(t, users) + map0.observe(function (e) { + event = e + }) + map1.set('stuff', Y.Map) + await flushAll(t, users) + t.compare(event.value._model, event.object.get(event.name)._model) + await compareUsers(t, users) +}) + +var mapTransactions = [ + function set (t, user, chance) { + let key = chance.pickone(['one', 'two']) + var value = chance.string() + user.share.map.set(key, value) + }, + function setType (t, user, chance) { + let key = chance.pickone(['one', 'two']) + var value = chance.pickone([Y.Array, Y.Map]) + let type = user.share.map.set(key, value) + if (value === Y.Array) { + type.insert(0, [1, 2, 3, 4]) + } else { + type.set('deepkey', 'deepvalue') + } + }, + function _delete (t, user, chance) { + let key = chance.pickone(['one', 'two']) + user.share.map.delete(key) + } +] + +test('y-map: Random tests (42)', async function randomMap42 (t) { + await applyRandomTests(t, mapTransactions, 42) +}) + +test('y-map: Random tests (43)', async function randomMap43 (t) { + await applyRandomTests(t, mapTransactions, 43) +}) + +test('y-map: Random tests (44)', async function randomMap44 (t) { + await applyRandomTests(t, mapTransactions, 44) +}) + +test('y-map: Random tests (45)', async function randomMap45 (t) { + await applyRandomTests(t, mapTransactions, 45) +}) + +test('y-map: Random tests (46)', async function randomMap46 (t) { + await applyRandomTests(t, mapTransactions, 46) +}) + +test('y-map: Random tests (47)', async function randomMap47 (t) { + await applyRandomTests(t, mapTransactions, 47) +}) + +/* +test('y-map: Random tests (200)', async function randomMap200 (t) { + await applyRandomTests(t, mapTransactions, 200) +}) + +test('y-map: Random tests (300)', async function randomMap300 (t) { + await applyRandomTests(t, mapTransactions, 300) +}) + +test('y-map: Random tests (400)', async function randomMap400 (t) { + await applyRandomTests(t, mapTransactions, 400) +}) + +test('y-map: Random tests (500)', async function randomMap500 (t) { + await applyRandomTests(t, mapTransactions, 500) +}) +*/ diff --git a/tests-lib/helper.js b/tests-lib/helper.js index 6a96a073..aef1c1a2 100644 --- a/tests-lib/helper.js +++ b/tests-lib/helper.js @@ -12,6 +12,9 @@ export let Y = _Y Y.extend(yMemory, yArray, yMap, yTest) +export var database = { name: 'memory' } +export var connector = { name: 'test', url: 'http://localhost:1234' } + function * getStateSet () { var ss = {} yield * this.ss.iterate(this, null, null, function * (n) { @@ -60,7 +63,16 @@ export async function compareUsers (t, users) { await wait() await flushAll(t, users) - var userTypeContents = users.map(u => u.share.array._content.map(c => c.val || JSON.stringify(c.type))) + var userArrayValues = users.map(u => u.share.array._content.map(c => c.val || JSON.stringify(c.type))) + function valueToComparable (v) { + if (v != null && v._model != null) { + return v._model + } else { + return v || null + } + } + var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable) + var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable) await users[0].db.garbageCollect() await users[0].db.garbageCollect() @@ -118,7 +130,9 @@ export async function compareUsers (t, users) { })) for (var i = 0; i < data.length - 1; i++) { await t.asyncGroup(async () => { - t.compare(userTypeContents[i], userTypeContents[i + 1], 'types') + t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types') + t.compare(userMapOneValues[i], userMapOneValues[i + 1], 'map types (propery "one")') + t.compare(userMapTwoValues[i], userMapTwoValues[i + 1], 'map types (propery "two")') t.compare(data[i].os, data[i + 1].os, 'os') t.compare(data[i].ds, data[i + 1].ds, 'ds') t.compare(data[i].ss, data[i + 1].ss, 'ss') @@ -135,17 +149,17 @@ export async function initArrays (t, opts) { } var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map' }, opts.share) var chance = opts.chance || new Chance(t.getSeed() * 1000000000) - var connector = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, opts.connector) + var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector) for (let i = 0; i < opts.users; i++) { let dbOpts let connOpts if (i === 0) { // Only one instance can gc! - dbOpts = Object.assign({ gc: false }, opts.db) - connOpts = Object.assign({ role: 'master' }, connector) + dbOpts = Object.assign({ gc: false }, database) + connOpts = Object.assign({ role: 'master' }, conn) } else { - dbOpts = Object.assign({ gc: false }, opts.db) - connOpts = Object.assign({ role: 'slave' }, connector) + dbOpts = Object.assign({ gc: false }, database) + connOpts = Object.assign({ role: 'slave' }, conn) } let y = await Y({ connector: connOpts, @@ -220,3 +234,48 @@ export function wait (t) { setTimeout(resolve, t != null ? t : 100) }) } + +export async function applyRandomTests (t, mods, iterations) { + const chance = new Chance(t.getSeed() * 1000000000) + var initInformation = await initArrays(t, { users: 5, chance: chance }) + let { users } = initInformation + for (var i = 0; i < iterations; i++) { + if (chance.bool({likelihood: 10})) { + // 10% chance to disconnect/reconnect a user + // we make sure that the first users always is connected + let user = chance.pickone(users.slice(1)) + if (user.connector.isSynced) { + if (users.filter(u => u.connector.isSynced).length > 1) { + // make sure that at least one user remains in the room + await user.disconnect() + if (users[0].connector.testRoom == null) { + await wait(100) + } + } + } else { + await user.reconnect() + if (users[0].connector.testRoom == null) { + await wait(100) + } + await new Promise(function (resolve) { + user.connector.whenSynced(resolve) + }) + } + } else if (chance.bool({likelihood: 5})) { + // 20%*!prev chance to flush all & garbagecollect + // TODO: We do not gc all users as this does not work yet + // await garbageCollectUsers(t, users) + await flushAll(t, users) + await users[0].db.emptyGarbageCollector() + await flushAll(t, users) + } else if (chance.bool({likelihood: 10})) { + // 20%*!prev chance to flush some operations + await flushSome(t, users) + } + let user = chance.pickone(users) + var test = chance.pickone(mods) + test(t, user, chance) + } + await compareUsers(t, users) + return initInformation +}