UNPKG

aerospike

Version:
467 lines (407 loc) 18.4 kB
// ***************************************************************************** // Copyright 2019-2023 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 */ import Aerospike, { Client } from 'aerospike'; import { expect } from 'chai'; import * as helper from './test_helper'; const status_codes = Aerospike.status const bits = Aerospike.bitwise const { assertError, assertRecordEql, assertResultEql, cleanup, createRecord, expectError, initState, operate } = require('./util/statefulAsyncTest') describe('client.operate() - Bitwise operations', function () { helper.skipUnlessSupportsFeature(Aerospike.features.BLOB_BITS, this) describe('bitwise.resize', function () { it('grows the bytes value', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02]) })) .then(operate(bits.resize('bits', 4))) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02, 0x00, 0x00]) })) .then(cleanup()) }) it('shrinks the bytes value', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(operate(bits.resize('bits', 2))) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02]) })) .then(cleanup()) }) context('with resize flags', function () { context('with resize from front flag', function () { const resizeFlags = bits.resizeFlags.FROM_FRONT it('grows the value from the front', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02]) })) .then(operate(bits.resize('bits', 4, resizeFlags))) .then(assertRecordEql({ bits: Buffer.from([0x00, 0x00, 0x01, 0x02]) })) .then(cleanup()) }) }) context('with grow only flag', function () { const resizeFlags = bits.resizeFlags.GROW_ONLY it('returns an error when trying to shrink the value', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(expectError()) .then(operate(bits.resize('bits', 2, resizeFlags))) .then(assertError(status_codes.ERR_REQUEST_INVALID)) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(cleanup()) }) }) context('with shrink only flag', function () { const resizeFlags = bits.resizeFlags.SHRINK_ONLY it('returns an error when trying to grow the value', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(expectError()) .then(operate(bits.resize('bits', 4, resizeFlags))) .then(assertError(status_codes.ERR_REQUEST_INVALID)) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(cleanup()) }) }) }) context('with bitwise policy', function () { context('with create-only write flag', function () { const policy = { writeFlags: bits.writeFlags.CREATE_ONLY } it('creates a new byte value bin and initializes it with zeros', function () { return initState() .then(createRecord({ foo: 'bar' })) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertRecordEql({ bits: Buffer.from([0x00, 0x00, 0x00, 0x00]), foo: 'bar' })) .then(cleanup()) }) it('returns an error if the bin already exists', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(expectError()) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertError(status_codes.ERR_BIN_EXISTS)) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(cleanup()) }) context('with no-fail write flag', function () { const policy = { writeFlags: bits.writeFlags.CREATE_ONLY | bits.writeFlags.NO_FAIL } it('does not update the bin', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x02, 0x03]) })) .then(cleanup()) }) }) }) context('with update-only write flag', function () { const policy = { writeFlags: bits.writeFlags.UPDATE_ONLY } it('updates an existing byte value', function () { return initState() .then(createRecord({ bits: Buffer.from([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]) })) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertRecordEql({ bits: Buffer.from([0x00, 0x01, 0x02, 0x03]) })) .then(cleanup()) }) it('returns an error if the bin does not exists', function () { return initState() .then(createRecord({ foo: 'bar' })) .then(expectError()) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertError(status_codes.ERR_BIN_NOT_FOUND)) .then(assertRecordEql({ foo: 'bar' })) .then(cleanup()) }) context('with no-fail write flag', function () { const policy = { writeFlags: bits.writeFlags.UPDATE_ONLY | bits.writeFlags.NO_FAIL } it('does not create the bin', function () { return initState() .then(createRecord({ foo: 'bar' })) .then(operate(bits.resize('bits', 4).withPolicy(policy))) .then(assertRecordEql({ foo: 'bar' })) .then(cleanup()) }) }) }) }) }) describe('bitwise.insert', function () { it('inserts value at the stated offset', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02]) })) .then(operate(bits.insert('bits', 1, Buffer.from([0x03, 0x04])))) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x03, 0x04, 0x02]) })) .then(cleanup()) }) }) describe('bitwise.remove', function () { it('removes number of bytes from the stated offset', function () { return initState() .then(createRecord({ bits: Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]) })) .then(operate(bits.remove('bits', 1, 3))) .then(assertRecordEql({ bits: Buffer.from([0x01, 0x05]) })) .then(cleanup()) }) }) describe('bitwise.set', function () { context('with value as Buffer', function () { it('sets value on bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.set('bits', 13, 3, Buffer.from([0b11100000])))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000111, 0b00000011, 0b00000100, 0b00000101]) })) .then(cleanup()) }) }) context('with value as Integer', function () { it('sets value on bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.set('bits', 1, 8, 127))) .then(assertRecordEql({ bits: Buffer.from([0b00111111, 0b11000010, 0b00000011, 0b0000100, 0b00000101]) })) .then(cleanup()) }) }) it('throws a TypeError if passed an unsupported value type', function () { expect(() => { bits.set('bin', 0, 0, 3.1416) }).to.throw(TypeError) }) context('with bitwise policy', function () { context('with no-fail flag', function () { const policy = { writeFlags: bits.writeFlags.UPDATE_ONLY | bits.writeFlags.NO_FAIL } it('sets value on bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000000]) })) .then(operate([ bits.set('bits', 4, 8, Buffer.from([0b10101010])).withPolicy(policy) ])) .then(assertRecordEql({ bits: Buffer.from([0b00000000]) })) .then(cleanup()) }) }) context('with partial flag', function () { const policy = { writeFlags: bits.writeFlags.UPDATE_ONLY | bits.writeFlags.NO_FAIL | bits.writeFlags.PARTIAL } it('sets value on bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000000]) })) .then(operate([ bits.set('bits', 4, 8, Buffer.from([0b10101010])).withPolicy(policy) ])) .then(assertRecordEql({ bits: Buffer.from([0b00001010]) })) .then(cleanup()) }) }) }) }) describe('bitwise.or', function () { it('performs bitwise "or" on value and bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.or('bits', 17, 6, Buffer.from([0b10101000])))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b01010111, 0b00000100, 0b00000101]) })) .then(cleanup()) }) }) describe('bitwise.xor', function () { it('performs bitwise "xor" on value and bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.xor('bits', 17, 6, Buffer.from([0b10101100])))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b01010101, 0b00000100, 0b00000101]) })) .then(cleanup()) }) }) describe('bitwise.and', function () { it('performs bitwise "and" on value and bitmap at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.and('bits', 23, 9, Buffer.from([0b00111100, 0b10000000])))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000010, 0b00000000, 0b00000101]) })) .then(cleanup()) }) }) describe('bitwise.not', function () { it('negates bitmap starting at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.not('bits', 25, 6))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b01111010, 0b00000101]) })) .then(cleanup()) }) }) describe('bitwise.lshift', function () { it('shifts left bitmap start at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.lshift('bits', 32, 8, 3))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00101000]) })) .then(cleanup()) }) }) describe('bitwise.rshift', function () { it('shifts right bitmap start at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.rshift('bits', 0, 9, 1))) .then(assertRecordEql({ bits: Buffer.from([0b00000000, 0b11000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(cleanup()) }) }) describe('bitwise.add', function () { it('adds value to bitmap starting at bitOffset for bitSize', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.add('bits', 24, 16, 128, false))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b10000101]) })) .then(cleanup()) }) context('with overflow', function () { context('on overflow fail', function () { const FAIL = bits.overflow.FAIL it('returns an error if the addition overflows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b11111111]) })) .then(expectError()) .then(operate(bits.add('bits', 0, 8, 1, false).onOverflow(FAIL))) .then(assertError(status_codes.ERR_OP_NOT_APPLICABLE)) .then(assertRecordEql({ bits: Buffer.from([0b11111111]) })) .then(cleanup()) }) }) context('on overflow saturate', function () { const SATURATE = bits.overflow.SATURATE it('sets max value if the addition overlows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b11111100]) })) .then(operate(bits.add('bits', 0, 8, 100, false).onOverflow(SATURATE))) .then(assertRecordEql({ bits: Buffer.from([0b11111111]) })) .then(cleanup()) }) }) context('on overflow wrap', function () { const WRAP = bits.overflow.WRAP it('wraps the value if the addition overflows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b11111110]) })) .then(operate(bits.add('bits', 0, 8, 2, false).onOverflow(WRAP))) .then(assertRecordEql({ bits: Buffer.from([0b00000000]) })) .then(cleanup()) }) }) }) }) describe('bitwise.subtract', function () { it('subracts value from bitmap starting at bitOffset for bitSize', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.subtract('bits', 24, 16, 128, false))) .then(assertRecordEql({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000011, 0b10000101]) })) .then(cleanup()) }) context('with overflow', function () { context('on overflow fail', function () { const FAIL = bits.overflow.FAIL it('returns an error if the subtraction underflows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000100]) })) .then(expectError()) .then(operate(bits.subtract('bits', 0, 8, 10, false).onOverflow(FAIL))) .then(assertError(status_codes.ERR_OP_NOT_APPLICABLE)) .then(assertRecordEql({ bits: Buffer.from([0b00000100]) })) .then(cleanup()) }) }) context('on overflow saturate', function () { const SATURATE = bits.overflow.SATURATE it('sets min value if the subtraction underflows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000100]) })) .then(operate(bits.subtract('bits', 0, 8, 10, false).onOverflow(SATURATE))) .then(assertRecordEql({ bits: Buffer.from([0b00000000]) })) .then(cleanup()) }) }) context('on overflow wrap', function () { const WRAP = bits.overflow.WRAP it('wraps the value if the subtraction underflows', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000100]) })) .then(operate(bits.subtract('bits', 0, 8, 10, false).onOverflow(WRAP))) .then(assertRecordEql({ bits: Buffer.from([0b11111010]) })) .then(cleanup()) }) }) }) }) describe('bitwise.get', function () { it('returns bits from bitmap starting at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.get('bits', 9, 5))) .then(assertResultEql({ bits: Buffer.from([0b10000000]) })) .then(cleanup()) }) }) describe('bitwise.getInt', function () { it('returns integer from bitmap starting at offset for size', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.getInt('bits', 8, 16, false))) .then(assertResultEql({ bits: 16899 })) .then(cleanup()) }) }) describe('bitwise.lscan', function () { it('returns interger bit offset of the first specified value bit in bitmap', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.lscan('bits', 24, 8, true))) .then(assertResultEql({ bits: 5 })) .then(cleanup()) }) }) describe('bitwise.rscan', function () { it('returns interger bit offset of the last specified value bit in bitmap', function () { return initState() .then(createRecord({ bits: Buffer.from([0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101]) })) .then(operate(bits.rscan('bits', 32, 8, true))) .then(assertResultEql({ bits: 7 })) .then(cleanup()) }) }) })