UNPKG

@lightbend/akkaserverless-javascript-sdk

Version:
259 lines (232 loc) 9.05 kB
/* * Copyright 2021 Lightbend 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. */ const should = require('chai').should(); const ReplicatedData = require('../../src/replicated-data'); const ReplicatedRegisterMap = ReplicatedData.ReplicatedRegisterMap; const Clocks = ReplicatedData.Clocks; const path = require('path'); const protobuf = require('protobufjs'); const protobufHelper = require('../../src/protobuf-helper'); const AnySupport = require('../../src/protobuf-any'); const ReplicatedEntityDelta = protobufHelper.moduleRoot.akkaserverless.component.replicatedentity .ReplicatedEntityDelta; const root = new protobuf.Root(); root.loadSync(path.join(__dirname, '..', 'example.proto')); root.resolveAll(); const Example = root.lookupType('com.example.Example'); const anySupport = new AnySupport(root); function roundTripDelta(delta) { return ReplicatedEntityDelta.decode( ReplicatedEntityDelta.encode(delta).finish(), ); } function toAny(value) { return AnySupport.serialize(value, true, true); } function fromAnys(values) { return values.map((any) => anySupport.deserialize(any)); } function deltaEntries(entries) { return entries.map((entry) => { return { key: anySupport.deserialize(entry.key), delta: anySupport.deserialize(entry.delta.value), }; }); } function deltaEntriesWithClocks(entries) { return entries.map((entry) => { return { key: anySupport.deserialize(entry.key), value: anySupport.deserialize(entry.delta.value), clock: entry.delta.clock, customClockValue: entry.delta.customClockValue.toNumber(), }; }); } function registerDelta(key, value) { return { key: toAny(key), delta: { value: toAny(value) } }; } describe('ReplicatedRegisterMap', () => { it('should have no elements when instantiated', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.size.should.equal(0); should.equal(registerMap.getAndResetDelta(), null); }); it('should reflect an initial delta', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.applyDelta( roundTripDelta({ replicatedRegisterMap: { updated: [ registerDelta('one', 1), registerDelta('two', 'foo'), registerDelta('three', Example.create({ field1: 'bar' })), ], }, }), anySupport, ); Array.from(registerMap.keys()).should.have.members(['one', 'two', 'three']); registerMap.get('one').should.equal(1); registerMap.get('two').should.equal('foo'); registerMap.get('three').field1.should.equal('bar'); should.equal(registerMap.getAndResetDelta(), null); }); it('should generate an added delta', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.has('one').should.be.true; registerMap.size.should.equal(1); const delta = roundTripDelta(registerMap.getAndResetDelta()); deltaEntries(delta.replicatedRegisterMap.updated).should.have.deep.members([ { key: 'one', delta: 1 }, ]); delta.replicatedRegisterMap.removed.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); it('should generate a removed delta', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.set('two', 'foo'); registerMap.getAndResetDelta(); registerMap.delete('one'); registerMap.size.should.equal(1); registerMap.has('one').should.be.false; registerMap.has('two').should.be.true; const delta = roundTripDelta(registerMap.getAndResetDelta()); fromAnys(delta.replicatedRegisterMap.removed).should.have.members(['one']); delta.replicatedRegisterMap.updated.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); it('should generate an updated delta', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 42); registerMap.getAndResetDelta(); registerMap.set('one', 'forty-two'); const delta = roundTripDelta(registerMap.getAndResetDelta()); deltaEntries(delta.replicatedRegisterMap.updated).should.have.deep.members([ { key: 'one', delta: 'forty-two' }, ]); delta.replicatedRegisterMap.removed.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); it('should generate a cleared delta', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.set('two', 2); registerMap.getAndResetDelta(); registerMap.clear(); registerMap.size.should.equal(0); const delta = roundTripDelta(registerMap.getAndResetDelta()); delta.replicatedRegisterMap.cleared.should.be.true; delta.replicatedRegisterMap.updated.should.be.empty; delta.replicatedRegisterMap.removed.should.be.empty; }); it('should generate a delta with clocks', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set(1, 'foo'); registerMap.set(2, 'bar', Clocks.REVERSE); registerMap.set(3, 'baz', Clocks.CUSTOM, 42); const delta = roundTripDelta(registerMap.getAndResetDelta()); deltaEntriesWithClocks( delta.replicatedRegisterMap.updated, ).should.have.deep.members([ { key: 1, value: 'foo', clock: Clocks.DEFAULT, customClockValue: 0 }, { key: 2, value: 'bar', clock: Clocks.REVERSE, customClockValue: 0 }, { key: 3, value: 'baz', clock: Clocks.CUSTOM, customClockValue: 42 }, ]); delta.replicatedRegisterMap.removed.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); it('should reflect a delta with added entries', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.getAndResetDelta(); registerMap.applyDelta( roundTripDelta({ replicatedRegisterMap: { updated: [registerDelta('two', 2)], }, }), anySupport, ); registerMap.size.should.equal(2); Array.from(registerMap.keys()).should.have.members(['one', 'two']); registerMap.get('two').should.equal(2); should.equal(registerMap.getAndResetDelta(), null); }); it('should reflect a delta with removed entries', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.set('two', 2); registerMap.getAndResetDelta(); registerMap.applyDelta( roundTripDelta({ replicatedRegisterMap: { removed: [toAny('two')], }, }), anySupport, ); registerMap.size.should.equal(1); Array.from(registerMap.keys()).should.have.members(['one']); should.equal(registerMap.getAndResetDelta(), null); }); it('should reflect a delta with cleared entries', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set('one', 1); registerMap.set('two', 2); registerMap.getAndResetDelta(); registerMap.applyDelta( roundTripDelta({ replicatedRegisterMap: { cleared: true, }, }), anySupport, ); registerMap.size.should.equal(0); should.equal(registerMap.getAndResetDelta(), null); }); it('should support protobuf messages for keys', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set(Example.create({ field1: 'one' }), 1); registerMap.set(Example.create({ field1: 'two' }), 2); registerMap.getAndResetDelta(); registerMap.delete(Example.create({ field1: 'one' })); registerMap.size.should.equal(1); const delta = roundTripDelta(registerMap.getAndResetDelta()); delta.replicatedRegisterMap.removed.should.have.lengthOf(1); fromAnys(delta.replicatedRegisterMap.removed)[0].field1.should.equal('one'); delta.replicatedRegisterMap.updated.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); it('should support json objects for keys', () => { const registerMap = new ReplicatedRegisterMap(); registerMap.set({ foo: 'one' }, 1); registerMap.set({ foo: 'two' }, 2); registerMap.getAndResetDelta(); registerMap.delete({ foo: 'one' }); registerMap.size.should.equal(1); const delta = roundTripDelta(registerMap.getAndResetDelta()); delta.replicatedRegisterMap.removed.should.have.lengthOf(1); fromAnys(delta.replicatedRegisterMap.removed)[0].foo.should.equal('one'); delta.replicatedRegisterMap.updated.should.be.empty; delta.replicatedRegisterMap.cleared.should.be.false; }); });