aerospike
Version:
Aerospike Client Library
1,323 lines (1,158 loc) • 54.6 kB
JavaScript
// *****************************************************************************
// Copyright 2013-2024 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// *****************************************************************************
'use strict'
/* eslint-env mocha */
/* global expect */
/* eslint-disable no-unused-expressions */
const Aerospike = require('../lib/aerospike')
const helper = require('./test_helper')
const AerospikeError = Aerospike.AerospikeError
const lists = Aerospike.lists
const ops = Aerospike.operations
const Context = Aerospike.cdt.Context
const status = Aerospike.status
const {
assertError,
assertRecordEql,
assertResultEql,
assertResultSatisfy,
cleanup,
createRecord,
expectError,
initState,
operate
} = require('./util/statefulAsyncTest')
const orderList = (bin, ctx) => {
const setListOrder = lists.setOrder(bin, lists.order.ORDERED)
if (ctx) setListOrder.withContext(ctx)
return operate(setListOrder)
}
describe('client.operate() - CDT List operations', function () {
helper.skipUnlessSupportsFeature(Aerospike.features.CDT_LIST, this)
let ListOutOfBoundsError
before(() => {
ListOutOfBoundsError = helper.cluster.isVersionInRange('>=4.6.0')
? status.ERR_OP_NOT_APPLICABLE
: status.ERR_REQUEST_INVALID
})
describe('lists.setOrder', function () {
it('changes the list order', function () {
return initState()
.then(createRecord({ list: [3, 1, 2] }))
.then(operate([
lists.setOrder('list', lists.order.ORDERED),
ops.read('list')
]))
.then(assertResultEql({ list: [1, 2, 3] }))
.then(cleanup())
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('changes the order of a nested list', function () {
return initState()
.then(createRecord({ list: [[3, 1, 2], [6, 5, 4]] }))
.then(operate([
lists.setOrder('list', lists.order.ORDERED).withContext(ctx => ctx.addListIndex(0)),
lists.setOrder('list', lists.order.ORDERED).withContext(ctx => ctx.addListIndex(1)),
ops.read('list')
]))
.then(assertResultEql({ list: [[1, 2, 3], [4, 5, 6]] }))
.then(cleanup())
})
})
})
describe('lists.sort', function () {
it('sorts the list', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 1] }))
.then(operate([
lists.sort('list', lists.sortFlags.DEFAULT),
ops.read('list')
]))
.then(assertResultEql({ list: [1, 1, 2, 3] }))
.then(cleanup())
})
context('with DROP_DUPLICATES flag', function () {
it('sorts the list and drops duplicates', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 1] }))
.then(operate([
lists.sort('list', lists.sortFlags.DROP_DUPLICATES),
ops.read('list')
]))
.then(assertResultEql({ list: [1, 2, 3] }))
.then(cleanup())
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('sorts a nested list', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [3, 1, 2, 1]] }))
.then(operate([
lists.sort('list', lists.sortFlags.DEFAULT).withContext(ctx => ctx.addListIndex(-1)),
ops.read('list')
]))
.then(assertResultEql({ list: [['a', 'b', 'c'], [1, 1, 2, 3]] }))
.then(cleanup())
})
})
})
describe('lists.append', function () {
it('appends an item to the list and returns the list size', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.append('list', 99)))
.then(assertResultEql({ list: 6 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5, 99] }))
.then(cleanup)
})
context('with add-unique flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE
}
it('returns an error when trying to append a non-unique element', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.append('list', 3, policy)))
.then(assertError(status.ERR_FAIL_ELEMENT_EXISTS))
.then(cleanup)
})
context('with no-fail flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL
}
it('returns an error when trying to append a non-unique element', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.append('list', 3, policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('appends a value to a nested list', function () {
return initState()
.then(createRecord({ list: [1, 2, ['a', 'b', 'c'], 4, 5] }))
.then(operate(lists.append('list', 'd').withContext(ctx => ctx.addListIndex(2))))
.then(assertResultEql({ list: 4 }))
.then(assertRecordEql({ list: [1, 2, ['a', 'b', 'c', 'd'], 4, 5] }))
.then(cleanup)
})
})
})
describe('lists.appendItems', function () {
it('appends the items to the list and returns the list size', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.appendItems('list', [99, 100])))
.then(assertResultEql({ list: 7 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5, 99, 100] }))
.then(cleanup)
})
it('returns an error if the value to append is not an array', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.appendItems('list', 99)))
.then(assertError(status.ERR_PARAM))
.then(cleanup)
})
context('with add-unique flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE
}
it('returns an error when appending duplicate items', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.appendItems('list', [3, 6], policy)))
.then(assertError(status.ERR_FAIL_ELEMENT_EXISTS))
.then(cleanup)
})
context('with no-fail flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL
}
it('does not append any items but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.appendItems('list', [3, 6], policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
context('with partial flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL | lists.writeFlags.PARTIAL
}
it('appends only the unique items', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.appendItems('list', [3, 6], policy)))
.then(assertResultEql({ list: 6 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5, 6] }))
.then(cleanup)
})
})
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('appends the items to a nested list', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.appendItems('map', [99, 100]).withContext(ctx => ctx.addMapKey('list'))))
.then(assertResultEql({ map: 7 }))
.then(assertRecordEql({ map: { list: [1, 2, 3, 4, 5, 99, 100] } }))
.then(cleanup)
})
})
})
describe('lists.insert', function () {
it('inserts the item at the specified index and returns the list size', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insert('list', 2, 99)))
.then(assertResultEql({ list: 6 }))
.then(assertRecordEql({ list: [1, 2, 99, 3, 4, 5] }))
.then(cleanup)
})
context('with add-unique flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE
}
it('returns an error when trying to insert a non-unique element', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.insert('list', 2, 3, policy)))
.then(assertError(status.ERR_FAIL_ELEMENT_EXISTS))
.then(cleanup)
})
context('with no-fail flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL
}
it('does not insert the item but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insert('list', 2, 3, policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
})
})
context('with insert-bounded flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = new Aerospike.ListPolicy({
writeFlags: lists.writeFlags.INSERT_BOUNDED
})
it('returns an error when trying to insert an item outside the current bounds of the list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.insert('list', 10, 99, policy)))
.then(assertError(ListOutOfBoundsError))
.then(cleanup)
})
context('with no-fail flag', function () {
const policy = new Aerospike.ListPolicy({
writeFlags: lists.writeFlags.INSERT_BOUNDED | lists.writeFlags.NO_FAIL
})
it('does not insert an item outside bounds, but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insert('list', 10, 99, policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('inserts the item at the specified index of a nested list', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.insert('map', 2, 99).withContext(ctx => ctx.addMapKey('list'))))
.then(assertResultEql({ map: 6 }))
.then(assertRecordEql({ map: { list: [1, 2, 99, 3, 4, 5] } }))
.then(cleanup)
})
})
})
describe('lists.insertItems', function () {
it('inserts the items at the specified index and returns the list size', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insertItems('list', 2, [99, 100])))
.then(assertResultEql({ list: 7 }))
.then(assertRecordEql({ list: [1, 2, 99, 100, 3, 4, 5] }))
.then(cleanup)
})
it('returns an error if the value to insert is not an array', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.insertItems('list', 2, 99)))
.then(assertError(status.ERR_PARAM))
.then(cleanup)
})
context('with add-unique flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE
}
it('returns an error when trying to insert items that already exist in the list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.insertItems('list', 2, [3, 99], policy)))
.then(assertError(status.ERR_FAIL_ELEMENT_EXISTS))
.then(cleanup)
})
context('with no-fail flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL
}
it('does not insert any items but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insertItems('list', 2, [3, 99], policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
context('with partial flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL | lists.writeFlags.PARTIAL
}
it('inserts only the unique items', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insertItems('list', 2, [3, 99], policy)))
.then(assertResultEql({ list: 6 }))
.then(assertRecordEql({ list: [1, 2, 99, 3, 4, 5] }))
.then(cleanup)
})
})
})
})
context('with insert-bounded flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = new Aerospike.ListPolicy({
writeFlags: lists.writeFlags.INSERT_BOUNDED
})
it('returns an error when trying to insert items outside the current bounds of the list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.insertItems('list', 10, [99, 100], policy)))
.then(assertError(ListOutOfBoundsError))
.then(cleanup)
})
context('with no-fail flag', function () {
const policy = new Aerospike.ListPolicy({
writeFlags: lists.writeFlags.INSERT_BOUNDED | lists.writeFlags.NO_FAIL
})
it('does not insert the items outside bounds, but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.insertItems('list', 10, [99, 100], policy)))
.then(assertResultEql({ list: 5 }))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('inserts the items at the specified index of a nested list', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.insertItems('map', 2, [99, 100]).withContext(ctx => ctx.addMapKey('list'))))
.then(assertResultEql({ map: 7 }))
.then(assertRecordEql({ map: { list: [1, 2, 99, 100, 3, 4, 5] } }))
.then(cleanup)
})
})
})
describe('lists.pop', function () {
it('removes the item at the specified index and returns it', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.pop('list', 2)))
.then(assertResultEql({ list: 3 }))
.then(assertRecordEql({ list: [1, 2, 4, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item at the specified index and returns it', function () {
return initState()
.then(createRecord({ list: [[1, 2, 3, 4, 5], [6, 7, 8]] }))
.then(operate(lists.pop('list', 2).withContext(ctx => ctx.addListIndex(0))))
.then(assertResultEql({ list: 3 }))
.then(assertRecordEql({ list: [[1, 2, 4, 5], [6, 7, 8]] }))
.then(cleanup)
})
})
})
describe('lists.popRange', function () {
it('removes the items at the specified range and returns them', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.popRange('list', 2, 2)))
.then(assertResultEql({ list: [3, 4] }))
.then(assertRecordEql({ list: [1, 2, 5] }))
.then(cleanup)
})
it('removes and returns all items starting from the specified index if count is not specified', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.popRange('list', 2)))
.then(assertResultEql({ list: [3, 4, 5] }))
.then(assertRecordEql({ list: [1, 2] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the items in the specified range and returns them', function () {
return initState()
.then(createRecord({ list: [[1, 2, 3, 4, 5], [6, 7, 8]] }))
.then(operate(lists.popRange('list', 2).withContext(ctx => ctx.addListIndex(1))))
.then(assertResultEql({ list: [8] }))
.then(assertRecordEql({ list: [[1, 2, 3, 4, 5], [6, 7]] }))
.then(cleanup)
})
})
})
describe('lists.remove', function () {
it('removes the item at the specified index and returns the number of items removed', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.remove('list', 2)))
.then(assertResultEql({ list: 1 }))
.then(assertRecordEql({ list: [1, 2, 4, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item at the specified index', function () {
return initState()
.then(createRecord({ list: [[1, 2, 3, 4, 5], [6, 7, 8]] }))
.then(operate(lists.remove('list', 2).withContext(ctx => ctx.addListIndex(1))))
.then(assertResultEql({ list: 1 }))
.then(assertRecordEql({ list: [[1, 2, 3, 4, 5], [6, 7]] }))
.then(cleanup)
})
})
})
describe('lists.removeRange', function () {
it('removes the items in the specified range and returns the number of items removed', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeRange('list', 2, 2)))
.then(assertResultEql({ list: 2 }))
.then(assertRecordEql({ list: [1, 2, 5] }))
.then(cleanup)
})
it('removes all items starting from the specified index and returns the number of items removed if count is not specified', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeRange('list', 2)))
.then(assertResultEql({ list: 3 }))
.then(assertRecordEql({ list: [1, 2] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item at the specified range', function () {
return initState()
.then(createRecord({ list: [[1, 2, 3, 4, 5], [6, 7, 8]] }))
.then(operate(lists.removeRange('list', 1, 3).withContext(ctx => ctx.addListIndex(0))))
.then(assertResultEql({ list: 3 }))
.then(assertRecordEql({ list: [[1, 5], [6, 7, 8]] }))
.then(cleanup)
})
})
})
describe('lists.removeByIndex', function () {
context('returnType=VALUE', function () {
it('removes the item at the specified index and returns the value', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeByIndex('list', 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: 3 }))
.then(assertRecordEql({ list: [1, 2, 4, 5] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item at the specified index', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.removeByIndex('map', 2).withContext(ctx => ctx.addMapKey('list'))))
.then(assertRecordEql({ map: { list: [1, 2, 4, 5] } }))
.then(cleanup)
})
})
})
describe('lists.removeByIndexRange', function () {
context('returnType=VALUE', function () {
it('removes the items in the specified range and returns the values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeByIndexRange('list', 2, 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [3, 4] }))
.then(assertRecordEql({ list: [1, 2, 5] }))
.then(cleanup)
})
it('removes the items starting from the specified index and returns the values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeByIndexRange('list', 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [3, 4, 5] }))
.then(assertRecordEql({ list: [1, 2] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item int the specified range', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.removeByIndexRange('map', 1, 3).withContext(ctx => ctx.addMapKey('list'))))
.then(assertRecordEql({ map: { list: [1, 5] } }))
.then(cleanup)
})
})
})
describe('lists.removeByValue', function () {
context('returnType=INDEX', function () {
it('removes all items with the specified value and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.removeByValue('list', 3).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [2, 5] }))
.then(assertRecordEql({ list: [1, 2, 1, 2] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all items with the specified value', function () {
return initState()
.then(createRecord({ list: [[3, 2, 1], [1, 2, 3, 1, 2, 3]] }))
.then(operate(lists.removeByValue('list', 3).withContext(ctx => ctx.addListValue([3, 2, 1]))))
.then(assertRecordEql({ list: [[2, 1], [1, 2, 3, 1, 2, 3]] }))
.then(cleanup)
})
})
})
describe('lists.removeByValueList', function () {
context('returnType=INDEX', function () {
it('removes all items with the specified values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.removeByValueList('list', [1, 3]).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [0, 2, 3, 5] }))
.then(assertRecordEql({ list: [2, 2] }))
.then(cleanup)
})
})
context('invert results', function () {
it('removes all items except with the specified values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.removeByValueList('list', [1, 3]).invertSelection()))
.then(assertRecordEql({ list: [1, 3, 1, 3] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all items except with the specified values', function () {
return initState()
.then(createRecord({ list: [[3, 2, 1], [1, 2, 3, 1, 2, 3]] }))
.then(operate(
lists
.removeByValueList('list', [1, 4])
.withContext(ctx => ctx.addListIndex(-1))
.invertSelection()
))
.then(assertRecordEql({ list: [[3, 2, 1], [1, 1]] }))
.then(cleanup)
})
})
})
})
describe('lists.removeByValueRange', function () {
context('returnType=INDEX', function () {
it('removes all items in the specified range of values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.removeByValueRange('list', 2, 5).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [1, 2, 3] }))
.then(assertRecordEql({ list: [1, 5] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all items in the specified range of values', function () {
return initState()
.then(createRecord({ list: [[1, 2, 3, 4, 5], [6, 7, 8]] }))
.then(operate(lists.removeByValueRange('list', 2, 5).withContext(ctx => ctx.addListIndex(0))))
.then(assertRecordEql({ list: [[1, 5], [6, 7, 8]] }))
.then(cleanup)
})
})
})
describe('lists.removeByValueRelRankRange', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
context('with count', function () {
it('removes all items nearest to value and greater, by relative rank', function () {
return initState()
.then(createRecord({ list: [0, 4, 5, 9, 11, 15] }))
.then(orderList('list'))
.then(operate(lists.removeByValueRelRankRange('list', 5, 0, 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [5, 9] }))
.then(assertRecordEql({ list: [0, 4, 11, 15] }))
.then(cleanup)
})
})
context('without count', function () {
it('removes all items nearest to value and greater, by relative rank', function () {
return initState()
.then(createRecord({ list: [0, 4, 5, 9, 11, 15] }))
.then(orderList('list'))
.then(operate(lists.removeByValueRelRankRange('list', 5, 0).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [5, 9, 11, 15] }))
.then(assertRecordEql({ list: [0, 4] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all items nearest to value and greater, by relative rank', function () {
const listContext = new Context().addMapKey('list')
return initState()
.then(createRecord({ map: { list: [0, 4, 5, 9, 11, 15] } }))
.then(orderList('map', listContext))
.then(operate(lists.removeByValueRelRankRange('map', 5, 0, 2).withContext(listContext)))
.then(assertRecordEql({ map: { list: [0, 4, 11, 15] } }))
.then(cleanup)
})
})
})
describe('lists.removeByRank', function () {
context('returnType=VALUE', function () {
it('removes the item with the specified list rank and returns the value', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 4] }))
.then(operate(lists.removeByRank('list', 1).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: 2 }))
.then(assertRecordEql({ list: [3, 1, 4] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item with the specified list rank', function () {
return initState()
.then(createRecord({ list: [[2, 3, 1, 4], [3, 1, 2, 4]] }))
.then(operate(lists.removeByRank('list', 1).withContext(ctx => ctx.addListIndex(1))))
.then(assertRecordEql({ list: [[2, 3, 1, 4], [3, 1, 4]] }))
.then(cleanup)
})
})
})
describe('lists.removeByRankRange', function () {
context('returnType=VALUE', function () {
it('removes the item with the specified list rank and returns the value', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 5, 4] }))
.then(operate(lists.removeByRankRange('list', 1, 3).andReturn(lists.returnType.VALUE)))
.then(assertResultSatisfy(result => expect(result.list.sort()).to.eql([2, 3, 4])))
.then(assertRecordEql({ list: [1, 5] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes the item with the specified list rank', function () {
return initState()
.then(createRecord({ list: [[3, 1, 2, 5, 4], [1, 2, 3]] }))
.then(operate(lists.removeByRankRange('list', 1, 3).withContext(ctx => ctx.addListIndex(0))))
.then(assertRecordEql({ list: [[1, 5], [1, 2, 3]] }))
.then(cleanup)
})
})
})
describe('lists.clear', function () {
it('removes all elements from the list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.clear('list')))
.then(assertRecordEql({ list: [] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all elements from the list', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.clear('map').withContext(ctx => ctx.addMapKey('list'))))
.then(assertRecordEql({ map: { list: [] } }))
.then(cleanup)
})
})
})
describe('lists.create', function () {
it('creates a new list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('emptyList', lists.order.ORDERED)))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5], emptyList: [] }))
.then(cleanup)
})
it('creates a new list with persist index true', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('emptyList', lists.order.ORDERED, false, true)))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5], emptyList: [] }))
.then(cleanup)
})
it('creates a new list with persist index true and pad true', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('emptyList', lists.order.ORDERED, true, true)))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5], emptyList: [] }))
.then(cleanup)
})
it('creates a new list with pad true', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('emptyList', lists.order.ORDERED, true, false)))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5], emptyList: [] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('creates a new list within a map', function () {
return initState()
.then(createRecord({ map: { c: 1, b: 2, a: 3 } }))
.then(operate(lists.create('map', lists.order.ORDERED).withContext(ctx => ctx.addMapKeyCreate('nested'))))
.then(assertRecordEql({ map: { c: 1, b: 2, a: 3, nested: [] } }))
.then(cleanup)
})
it('creates a new list within a list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('list', lists.order.UNORDERED, true, false).withContext(ctx => ctx.addListIndex(10))))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5, null, null, null, null, null, []] }))
.then(cleanup)
})
it('creates a new list within a list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.create('list', lists.order.UNORDERED, true, true).withContext(ctx => ctx.addListIndex(10))))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5, null, null, null, null, null, []] }))
.then(cleanup)
})
})
})
describe('lists.set', function () {
it('sets the item at the specified index', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.set('list', 2, 99)))
.then(assertRecordEql({ list: [1, 2, 99, 4, 5] }))
.then(cleanup)
})
context('with add-unique flag', function () {
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE
}
it('fails with an error if the value already exists in the list', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.set('list', 2, 5, policy)))
.then(assertError(status.ERR_FAIL_ELEMENT_EXISTS))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
context('with no-fail flag', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
const policy = {
writeFlags: lists.writeFlags.ADD_UNIQUE | lists.writeFlags.NO_FAIL
}
it('does not set the value but returns ok', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.set('list', 2, 5, policy)))
.then(assertRecordEql({ list: [1, 2, 3, 4, 5] }))
.then(cleanup)
})
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('sets the item at the specified index', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.set('map', 2, 99).withContext(ctx => ctx.addMapKey('list'))))
.then(assertRecordEql({ map: { list: [1, 2, 99, 4, 5] } }))
.then(cleanup)
})
})
})
describe('lists.trim', function () {
it('removes all elements not within the specified range and returns the number of elements removed', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.trim('list', 1, 3)))
.then(assertResultEql({ list: 2 }))
.then(assertRecordEql({ list: [2, 3, 4] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('removes all elements not within the specified range', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 4, 5]] }))
.then(operate(lists.trim('list', 1, 3).withContext(ctx => ctx.addListValue([1, 2, 3, 4, 5]))))
.then(assertResultEql({ list: 2 }))
.then(assertRecordEql({ list: [['a', 'b', 'c'], [2, 3, 4]] }))
.then(cleanup)
})
})
})
describe('lists.get', function () {
it('returns the item at the specified index', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.get('list', 2)))
.then(assertResultEql({ list: 3 }))
.then(cleanup)
})
it('should return an error if the index is out of bounds', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(expectError())
.then(operate(lists.get('list', 99)))
.then(assertError(ListOutOfBoundsError))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('returns the item at the specified index', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 4, 5]] }))
.then(operate(lists.get('list', 2).withContext(ctx => ctx.addListIndex(1))))
.then(assertResultEql({ list: 3 }))
.then(cleanup)
})
})
})
describe('lists.getRange', function () {
it('returns the items in the specified range', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getRange('list', 1, 3)))
.then(assertResultEql({ list: [2, 3, 4] }))
.then(cleanup)
})
it('returns all items starting at the specified index if count is not specified', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getRange('list', 1)))
.then(assertResultEql({ list: [2, 3, 4, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('returns the items in the specified range', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(lists.getRange('map', 1, 3).withContext(ctx => ctx.addMapKey('list'))))
.then(assertResultEql({ map: [2, 3, 4] }))
.then(cleanup)
})
})
})
describe('lists.getByIndex', function () {
context('returnType=VALUE', function () {
it('fetches the item at the specified index and returns its value', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getByIndex('list', 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: 3 }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches the item at the specified index and returns its value', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 4, 5]] }))
.then(operate(
lists
.getByIndex('list', 2)
.withContext(ctx => ctx.addListIndex(1))
.andReturn(lists.returnType.VALUE)
))
.then(assertResultEql({ list: 3 }))
.then(cleanup)
})
})
})
})
describe('lists.getByIndexRange', function () {
context('returnType=VALUE', function () {
it('fetches the items in the specified range and returns the values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getByIndexRange('list', 2, 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [3, 4] }))
.then(cleanup)
})
it('fetches the items starting from the specified index and returns the values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getByIndexRange('list', 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [3, 4, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches the items in the specified range and returns the values', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(
lists
.getByIndexRange('map', 2, 2)
.withContext(ctx => ctx.addMapKey('list'))
.andReturn(lists.returnType.VALUE)
))
.then(assertResultEql({ map: [3, 4] }))
.then(cleanup)
})
})
})
})
describe('lists.getByValue', function () {
context('returnType=INDEX', function () {
it('fetches all items with the specified value and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.getByValue('list', 3).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [2, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches all items with the specified value and returns the indexes', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 1, 2, 3]] }))
.then(operate(
lists
.getByValue('list', 3)
.withContext(ctx => ctx.addListIndex(1))
.andReturn(lists.returnType.INDEX)
))
.then(assertResultEql({ list: [2, 5] }))
.then(cleanup)
})
})
})
})
describe('lists.getByValueList', function () {
context('returnType=INDEX', function () {
it('fetches all items with the specified values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.getByValueList('list', [1, 3]).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [0, 2, 3, 5] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches all items with the specified values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 1, 2, 3]] }))
.then(operate(
lists
.getByValueList('list', [1, 3])
.withContext(ctx => ctx.addListIndex(1))
.andReturn(lists.returnType.INDEX)
))
.then(assertResultEql({ list: [0, 2, 3, 5] }))
.then(cleanup)
})
})
})
context('returnType=EXISTS', function () {
helper.skipUnlessVersion('>= 6.1.0', this)
it('fetches all items with the specified values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.getByValueList('list', [1, 3, 5]).andReturn(lists.returnType.EXISTS)))
.then(assertResultEql({ list: true }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches all items with the specified values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [['a', 'b', 'c'], [1, 2, 3, 1, 2, 3]] }))
.then(operate(
lists
.getByValueList('list', [7, 5, 4])
.withContext(ctx => ctx.addListIndex(1))
.andReturn(lists.returnType.EXISTS)
))
.then(assertResultEql({ list: false }))
.then(cleanup)
})
})
})
context('invert results', function () {
it('fetches all items except with the specified values', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 1, 2, 3] }))
.then(operate(lists.getByValueList('list', [1, 3]).invertSelection().andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [1, 4] }))
.then(cleanup)
})
})
})
describe('lists.getByValueRange', function () {
context('returnType=INDEX', function () {
it('fetches all items in the specified range of values and returns the indexes', function () {
return initState()
.then(createRecord({ list: [1, 2, 3, 4, 5] }))
.then(operate(lists.getByValueRange('list', 2, 5).andReturn(lists.returnType.INDEX)))
.then(assertResultEql({ list: [1, 2, 3] }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches all items in the specified range of values and returns the indexes', function () {
return initState()
.then(createRecord({ map: { list: [1, 2, 3, 4, 5] } }))
.then(operate(
lists
.getByValueRange('map', 2, 5)
.withContext(ctx => ctx.addMapKey('list'))
.andReturn(lists.returnType.INDEX)
))
.then(assertResultEql({ map: [1, 2, 3] }))
.then(cleanup)
})
})
})
})
describe('lists.getByValueRelRankRange', function () {
helper.skipUnlessVersion('>= 4.3.0', this)
context('with count', function () {
it('fetches all items nearest to value and greater, by relative rank', function () {
return initState()
.then(createRecord({ list: [0, 4, 5, 9, 11, 15] }))
.then(orderList('list'))
.then(operate(lists.getByValueRelRankRange('list', 5, 0, 2).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [5, 9] }))
.then(cleanup)
})
})
context('without count', function () {
it('fetches all items nearest to value and greater, by relative rank', function () {
return initState()
.then(createRecord({ list: [0, 4, 5, 9, 11, 15] }))
.then(orderList('list'))
.then(operate(lists.getByValueRelRankRange('list', 5, 0).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: [5, 9, 11, 15] }))
.then(cleanup)
})
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches all items nearest to value and greater, by relative rank', function () {
const listContext = new Context().addMapKey('list')
return initState()
.then(createRecord({ map: { list: [0, 4, 5, 9, 11, 15] } }))
.then(orderList('map', listContext))
.then(operate(
lists
.getByValueRelRankRange('map', 5, 0, 2)
.withContext(listContext)
.andReturn(lists.returnType.VALUE)
))
.then(assertResultEql({ map: [5, 9] }))
.then(cleanup)
})
})
})
describe('lists.getByRank', function () {
context('returnType=VALUE', function () {
it('fetches the item with the specified list rank and returns the value', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 4] }))
.then(operate(lists.getByRank('list', 1).andReturn(lists.returnType.VALUE)))
.then(assertResultEql({ list: 2 }))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches the item with the specified list rank and returns the value', function () {
return initState()
.then(createRecord({ list: [[3, 1, 2, 4], ['a', 'b', 'c']] }))
.then(operate(
lists
.getByRank('list', 1)
.withContext(ctx => ctx.addListIndex(0))
.andReturn(lists.returnType.VALUE)
))
.then(assertResultEql({ list: 2 }))
.then(cleanup)
})
})
})
})
describe('lists.getByRankRange', function () {
context('returnType=VALUE', function () {
it('fetches the item with the specified list rank and returns the value', function () {
return initState()
.then(createRecord({ list: [3, 1, 2, 5, 4] }))
.then(operate(lists.getByRankRange('list', 1, 3).andReturn(lists.returnType.VALUE)))
.then(assertResultSatisfy(result => expect(result.list.sort()).to.eql([2, 3, 4])))
.then(cleanup)
})
context('with nested list context', function () {
helper.skipUnlessVersion('>= 4.6.0', this)
it('fetches the item with the specified list rank and returns the value', function () {
return initState()
.th