mirror of
https://github.com/Start9Labs/patch-db.git
synced 2026-03-31 12:23:40 +00:00
move client from separate repo
This commit is contained in:
195
client/tests/seq.test.ts
Normal file
195
client/tests/seq.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { expect } from 'chai'
|
||||
import { PatchOp } from '../lib/patch-db'
|
||||
import { TestScheduler } from 'rxjs/testing'
|
||||
import { Result, SequenceStore } from '../lib/sequence-store'
|
||||
import { concatMap, map } from 'rxjs/operators'
|
||||
import { Store } from '../lib/store'
|
||||
import { RemoveOperation } from 'fast-json-patch'
|
||||
import 'chai-string'
|
||||
|
||||
type TestStore = { a: string, b: number[], c?: { [key: string]: number } }
|
||||
describe('sequence store', function () {
|
||||
let scheduler: TestScheduler
|
||||
beforeEach(() => {
|
||||
scheduler = new TestScheduler((actual, expected) => {
|
||||
// console.log('actual', JSON.stringify(actual))
|
||||
// console.log('expected', JSON.stringify(expected))
|
||||
expect(actual).eql(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('dumps', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'valueX', b: [0], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 5, value: finalStore, expireId: null }).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(5)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('ignores dump for id too low', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'valueX', b: [0], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 5, value: finalStore, expireId: null }).pipe(concatMap(() =>
|
||||
toTest.update$({ id: 4, value: initialStore, expireId: null }),
|
||||
)).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(5)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('revises', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value', b: [1, 2, 3], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 1, patch: [{ op: PatchOp.REPLACE, value: finalStore.c, path: '/c' }], expireId: null }).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(1)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('saves a revision when not next in line', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value', b: [1, 2, 3], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 2, patch: [{ op: PatchOp.REPLACE, value: finalStore.c, path: '/c' }], expireId: null }).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(initialStore)
|
||||
expect(toTest.sequence).eql(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('applies saved revisions when contiguous revisions become available', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value', b: [1, 2, 3, 4], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 2, patch: [{ op: PatchOp.REPLACE, value: finalStore.c, path: '/c' }], expireId: null }).pipe(concatMap(() =>
|
||||
toTest.update$({ id: 1, patch: [{ op: PatchOp.ADD, value: 4, path: '/b/-' }], expireId: null }),
|
||||
)).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(2)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('applies saved revisions when contiguous revisions become available part 2', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value2', b: [0], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
toTest.update$({ id: 2, patch: [{ op: PatchOp.REPLACE, value: finalStore.c, path: '/c' }], expireId: null }).pipe(concatMap(() =>
|
||||
toTest.update$({ id: 1, value: { a: 'value2', b: [0], c: { d: 1, e: 2, f: 3 } }, expireId: null }),
|
||||
)).subscribe(() => {
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(2)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('wipes out stashed patches when sequence is force updated', done => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value2', b: [0], c: { g: 10 } }
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
// patch gets stashed
|
||||
expect(toTest.viewRevisions().length).eql(0)
|
||||
|
||||
toTest.update$({ id: 2, patch: [{ op: PatchOp.REPLACE, value: finalStore.c, path: '/c' }], expireId: null }).pipe(
|
||||
map(res => expect(res).eql(Result.STASHED) && expect(toTest.viewRevisions().length).eql(1)),
|
||||
concatMap(() => toTest.update$({ id: 3, value: finalStore, expireId: null })),
|
||||
map(res => expect(res).eql(Result.DUMPED) && expect(toTest.viewRevisions().length).eql(0)),
|
||||
).subscribe(() => done())
|
||||
})
|
||||
|
||||
it('emits sequence + state on updates (revisions)', () => {
|
||||
scheduler.run( ({ expectObservable, cold }) => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value2', b: [0], c: { g: 10 } }
|
||||
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
const expectedStream = 'ab'
|
||||
|
||||
cold('-b').subscribe(() => {
|
||||
toTest.update$({ id: 3, value: finalStore, expireId: null }).subscribe()
|
||||
})
|
||||
|
||||
expectObservable(toTest.watch$().pipe(
|
||||
map(cache => ({ sequence: cache.sequence, contents: cache.data})),
|
||||
)).toBe(expectedStream, {
|
||||
a: { sequence: 0, contents: initialStore },
|
||||
b: { sequence: 3, contents: finalStore },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('emits sequence + state on updates (patch)', () => {
|
||||
scheduler.run( ({ expectObservable, cold }) => {
|
||||
const initialStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const finalStore: TestStore = { a: 'value', b: [1, 2, 3], c: { d: 1, e: 2, f: 3, g: 4 } }
|
||||
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
const expectedStream = 'ab'
|
||||
|
||||
cold('-b').subscribe(() => {
|
||||
toTest.update$({ id: 1, patch: [{ op: PatchOp.ADD, path: '/c/g', value: 4 }], expireId: null }).subscribe()
|
||||
})
|
||||
|
||||
expectObservable(toTest.watch$().pipe(
|
||||
map(cache => ({ sequence: cache.sequence, contents: cache.data })),
|
||||
)).toBe(expectedStream, {
|
||||
a: { sequence: 0, contents: initialStore },
|
||||
b: { sequence: 1, contents: finalStore },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('errors bubble out in results', done => {
|
||||
const initialStore : TestStore = { a: 'value' , b: [1, 2, 3], c: { d: 1, e: 2, f: 3 } }
|
||||
const intermediaryStore : TestStore = { a: 'value' , b: [1, 2, 3] }
|
||||
const finalStore : TestStore = { a: 'value' , b: [1, 2, 3] }
|
||||
|
||||
const store = new Store(initialStore)
|
||||
const toTest = new SequenceStore(store, 0)
|
||||
|
||||
const patch1 = {
|
||||
id: 1,
|
||||
patch: [{ op: PatchOp.REMOVE, path: '/c' } as RemoveOperation],
|
||||
expireId: null,
|
||||
}
|
||||
|
||||
const patch2 = {
|
||||
id: 2,
|
||||
patch: [{ op: PatchOp.ADD, value: 4, path: '/c/g' }],
|
||||
expireId: null,
|
||||
}
|
||||
|
||||
toTest.update$(patch1).pipe(
|
||||
map(res => expect(res).eql(Result.REVISED) && expect(toTest.store.peek).eql(intermediaryStore)),
|
||||
concatMap(() => toTest.update$(patch2)),
|
||||
).subscribe(res => {
|
||||
expect(res).eql(Result.ERROR)
|
||||
expect(toTest.store.peek).eql(finalStore)
|
||||
expect(toTest.sequence).eql(1)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user