@@ -2,6 +2,8 @@ import {Model} from 'json-joy/lib/json-crdt';
22import { buildE2eClient } from '../../../common/testing/buildE2eClient' ;
33import { createCaller } from '../../../__demos__/json-crdt-server/routes' ;
44import { DemoServerRemoteHistory } from '../DemoServerRemoteHistory' ;
5+ import { SESSION } from 'json-joy/lib/json-crdt-patch/constants' ;
6+ import { Value } from 'json-joy/lib/json-type-value/Value' ;
57
68const setup = ( ) => {
79 const { caller, router} = createCaller ( ) ;
@@ -20,7 +22,7 @@ let cnt = 0;
2022const genId = ( ) => Math . random ( ) . toString ( 36 ) . slice ( 2 ) + '-' + Date . now ( ) . toString ( 36 ) + '-' + cnt ++ ;
2123
2224describe ( '.create()' , ( ) => {
23- test . skip ( 'can create a block with a simple patch' , async ( ) => {
25+ test ( 'can create a block with a simple patch' , async ( ) => {
2426 const { remote, caller} = await setup ( ) ;
2527 const model = Model . withLogicalClock ( ) ;
2628 model . api . root ( { foo : 'bar' } ) ;
@@ -29,8 +31,166 @@ describe('.create()', () => {
2931 const id = genId ( ) ;
3032 await remote . create ( id , [ { blob} ] ) ;
3133 const { data} = await caller . call ( 'block.get' , { id} , { } ) ;
32- // console.log(data.patches);
3334 const model2 = Model . fromBinary ( data . block . snapshot . blob ) ;
3435 expect ( model2 . view ( ) ) . toEqual ( { foo : 'bar' } ) ;
3536 } ) ;
37+
38+ test ( 'can create with empty model' , async ( ) => {
39+ const { remote, caller} = await setup ( ) ;
40+ const id = genId ( ) ;
41+ await remote . create ( id , [ ] ) ;
42+ const { data} = await caller . call ( 'block.get' , { id} , { } ) ;
43+ const model2 = Model . fromBinary ( data . block . snapshot . blob ) ;
44+ expect ( model2 . view ( ) ) . toBe ( undefined ) ;
45+ } ) ;
46+
47+ test ( 'empty model uses global session ID' , async ( ) => {
48+ const { remote, caller} = await setup ( ) ;
49+ const id = genId ( ) ;
50+ await remote . create ( id , [ ] ) ;
51+ const { data} = await caller . call ( 'block.get' , { id} , { } ) ;
52+ const model2 = Model . fromBinary ( data . block . snapshot . blob ) ;
53+ expect ( model2 . clock . sid ) . toBe ( SESSION . GLOBAL ) ;
54+ } ) ;
55+ } ) ;
56+
57+ describe ( '.read()' , ( ) => {
58+ test ( 'can read a block with a simple patch' , async ( ) => {
59+ const { remote} = await setup ( ) ;
60+ const model = Model . withLogicalClock ( ) ;
61+ model . api . root ( { score : 42 } ) ;
62+ const patch = model . api . flush ( ) ;
63+ const blob = patch . toBinary ( ) ;
64+ const id = genId ( ) ;
65+ await remote . create ( id , [ { blob} ] ) ;
66+ const read = await remote . read ( id ) ;
67+ expect ( read ) . toMatchObject ( {
68+ block : {
69+ id,
70+ snapshot : {
71+ blob : expect . any ( Uint8Array ) ,
72+ cur : 0 ,
73+ ts : expect . any ( Number ) ,
74+ } ,
75+ tip : [ ] ,
76+ } ,
77+ } ) ;
78+ const model2 = Model . fromBinary ( read . block . snapshot . blob ) ;
79+ expect ( model2 . view ( ) ) . toEqual ( { score : 42 } ) ;
80+ } ) ;
81+
82+ test ( 'throws NOT_FOUND error on missing block' , async ( ) => {
83+ const { remote} = await setup ( ) ;
84+ const id = genId ( ) ;
85+ try {
86+ const read = await remote . read ( id ) ;
87+ throw new Error ( 'not this error' ) ;
88+ } catch ( error ) {
89+ expect ( error ) . toMatchObject ( {
90+ message : 'NOT_FOUND' ,
91+ } ) ;
92+ }
93+ } ) ;
94+ } ) ;
95+
96+ describe ( '.update()' , ( ) => {
97+ test ( 'can apply changes to an empty document' , async ( ) => {
98+ const { remote} = await setup ( ) ;
99+ const id = genId ( ) ;
100+ await remote . create ( id , [ ] ) ;
101+ const read1 = await remote . read ( id ) ;
102+ const model1 = Model . fromBinary ( read1 . block . snapshot . blob ) ;
103+ expect ( model1 . view ( ) ) . toBe ( undefined ) ;
104+ const model = Model . withLogicalClock ( ) ;
105+ model . api . root ( { score : 42 } ) ;
106+ const patch = model . api . flush ( ) ;
107+ const blob = patch . toBinary ( ) ;
108+ const update = await remote . update ( id , [ { blob} ] ) ;
109+ expect ( update ) . toMatchObject ( {
110+ patches : [
111+ {
112+ ts : expect . any ( Number ) ,
113+ } ,
114+ ] ,
115+ } ) ;
116+ const read2 = await remote . read ( id ) ;
117+ const model2 = Model . fromBinary ( read2 . block . snapshot . blob ) ;
118+ expect ( model2 . view ( ) ) . toEqual ( { score : 42 } ) ;
119+ } ) ;
120+ } ) ;
121+
122+ describe ( '.scanFwd()' , ( ) => {
123+ test ( 'can scan patches forward' , async ( ) => {
124+ const { remote} = await setup ( ) ;
125+ const id = genId ( ) ;
126+ const model1 = Model . withLogicalClock ( ) ;
127+ model1 . api . root ( { score : 42 } ) ;
128+ const patch1 = model1 . api . flush ( ) ;
129+ const blob = patch1 . toBinary ( ) ;
130+ await remote . create ( id , [ { blob} ] ) ;
131+ const read1 = await remote . read ( id ) ;
132+ model1 . api . obj ( [ ] ) . set ( {
133+ foo : 'bar' ,
134+ } ) ;
135+ const patch2 = model1 . api . flush ( ) ;
136+ const blob2 = patch2 . toBinary ( ) ;
137+ await remote . update ( id , [ { blob : blob2 } ] ) ;
138+ const scan1 = await remote . scanFwd ( id , read1 . block . snapshot . cur ) ;
139+ expect ( scan1 ) . toMatchObject ( {
140+ patches : [
141+ {
142+ blob : expect . any ( Uint8Array ) ,
143+ ts : expect . any ( Number ) ,
144+ } ,
145+ ] ,
146+ } ) ;
147+ expect ( scan1 . patches [ 0 ] . blob ) . toEqual ( blob2 ) ;
148+ } ) ;
149+ } ) ;
150+
151+ describe ( '.scanBwd()' , ( ) => {
152+ test ( 'can scan patches backward' , async ( ) => {
153+ const { remote} = await setup ( ) ;
154+ const id = genId ( ) ;
155+ const model1 = Model . withLogicalClock ( ) ;
156+ model1 . api . root ( { score : 42 } ) ;
157+ const patch1 = model1 . api . flush ( ) ;
158+ const blob1 = patch1 . toBinary ( ) ;
159+ await remote . create ( id , [ { blob : blob1 } ] ) ;
160+ const read1 = await remote . read ( id ) ;
161+ model1 . api . obj ( [ ] ) . set ( {
162+ foo : 'bar' ,
163+ } ) ;
164+ const patch2 = model1 . api . flush ( ) ;
165+ const blob2 = patch2 . toBinary ( ) ;
166+ await remote . update ( id , [ { blob : blob2 } ] ) ;
167+ const read2 = await remote . read ( id ) ;
168+ const scan1 = await remote . scanBwd ( id , read2 . block . snapshot . cur ) ;
169+ expect ( scan1 . patches . length ) . toBe ( 1 ) ;
170+ expect ( scan1 ) . toMatchObject ( {
171+ patches : [
172+ {
173+ blob : expect . any ( Uint8Array ) ,
174+ ts : expect . any ( Number ) ,
175+ } ,
176+ ] ,
177+ } ) ;
178+ expect ( scan1 . patches [ 0 ] . blob ) . toEqual ( blob1 ) ;
179+ } ) ;
180+ } ) ;
181+
182+ describe ( '.delete()' , ( ) => {
183+ test ( 'can delete an existing block' , async ( ) => {
184+ const { remote, caller} = await setup ( ) ;
185+ const id = genId ( ) ;
186+ await remote . create ( id , [ ] ) ;
187+ const get1 = await caller . call ( 'block.get' , { id} , { } ) ;
188+ await remote . delete ( id ) ;
189+ try {
190+ const get2 = await caller . call ( 'block.get' , { id} , { } ) ;
191+ throw new Error ( 'not this error' ) ;
192+ } catch ( err ) {
193+ expect ( ( err as Value < any > ) . data . message ) . toBe ( 'NOT_FOUND' ) ;
194+ }
195+ } ) ;
36196} ) ;
0 commit comments