Skip to content

Commit e884799

Browse files
committed
[middleware] Test doRollback and checkpoints
1 parent a128d87 commit e884799

1 file changed

Lines changed: 211 additions & 19 deletions

File tree

test/unit/core/other/middleware.test.ts

Lines changed: 211 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {beforeEach, describe, expect, test, vi} from 'vitest';
22

33
import type {Middleware, Store} from 'tinybase';
4-
import {createMiddleware, createStore} from 'tinybase';
4+
import {createCheckpoints, createMiddleware, createStore} from 'tinybase';
55

66
let store: Store;
77
let middleware: Middleware;
@@ -99,7 +99,6 @@ describe('willSetContent', () => {
9999
store.setContent([{t1: {r1: {c1: 'a'}}}, {}]);
100100
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
101101
store.setContent([{banned: {r1: {c1: 1}}, t2: {r1: {c1: 'b'}}}, {}]);
102-
// blocked, so t1 from previous setContent remains
103102
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
104103
});
105104
});
@@ -363,7 +362,6 @@ describe('willSetTables', () => {
363362
store.setTables({t1: {r1: {c1: 'a'}}});
364363
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
365364
store.setTables({banned: {r1: {c1: 1}}, t2: {r1: {c1: 'b'}}});
366-
// blocked, so t1 from previous setTables remains
367365
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
368366
});
369367
});
@@ -1138,7 +1136,6 @@ describe('willSetCell', () => {
11381136
return typeof cell === 'number' ? cell * 10 : cell;
11391137
});
11401138
store.setCell('t1', 'r1', 'c1', 1);
1141-
// (1 + 1) * 10 = 20
11421139
expect(store.getCell('t1', 'r1', 'c1')).toBe(20);
11431140
});
11441141

@@ -1341,8 +1338,6 @@ describe('willSetValues', () => {
13411338
store.setValues({v1: 'a'});
13421339
expect(store.getValues()).toEqual({v1: 'a'});
13431340
store.setValues({locked: true, v2: 'b'});
1344-
// blocked, so v1 from previous setValues is deleted by the
1345-
// mapMatch since the blocked values resolve to undefined
13461341
expect(store.getValues()).toEqual({v1: 'a'});
13471342
});
13481343
});
@@ -1568,7 +1563,6 @@ describe('willSetValue', () => {
15681563
return typeof value === 'number' ? value * 2 : value;
15691564
});
15701565
store.setValue('v1', 1);
1571-
// (1 + 1) * 2 = 4
15721566
expect(store.getValue('v1')).toBe(4);
15731567
});
15741568

@@ -1874,7 +1868,7 @@ describe('willDelTable', () => {
18741868
store.setTables({t1: {r1: {c1: 'a'}}, t2: {r1: {c1: 'b'}}});
18751869
delCalls.length = 0;
18761870
store.setTables({t1: {r1: {c1: 'A'}}});
1877-
// t2 should be deleted
1871+
18781872
expect(delCalls).toEqual(['t2']);
18791873
expect(store.getTables()).toEqual({t1: {r1: {c1: 'A'}}});
18801874
});
@@ -1883,7 +1877,7 @@ describe('willDelTable', () => {
18831877
middleware.addWillDelTableCallback(() => false);
18841878
store.setTables({t1: {r1: {c1: 'a'}}, t2: {r1: {c1: 'b'}}});
18851879
store.setTables({t1: {r1: {c1: 'A'}}});
1886-
// t2 should be preserved because delete was blocked
1880+
18871881
expect(store.getTables()).toEqual({
18881882
t1: {r1: {c1: 'A'}},
18891883
t2: {r1: {c1: 'b'}},
@@ -2033,7 +2027,7 @@ describe('willDelRow', () => {
20332027
store.setTable('t1', {r1: {c1: 'a'}, r2: {c1: 'b'}});
20342028
delCalls.length = 0;
20352029
store.setTable('t1', {r1: {c1: 'A'}});
2036-
// r2 should be deleted
2030+
20372031
expect(delCalls).toEqual(['t1/r2']);
20382032
expect(store.getTable('t1')).toEqual({r1: {c1: 'A'}});
20392033
});
@@ -2042,7 +2036,7 @@ describe('willDelRow', () => {
20422036
middleware.addWillDelRowCallback(() => false);
20432037
store.setTable('t1', {r1: {c1: 'a'}, r2: {c1: 'b'}});
20442038
store.setTable('t1', {r1: {c1: 'A'}});
2045-
// r2 should be preserved because delete was blocked
2039+
20462040
expect(store.getTable('t1')).toEqual({
20472041
r1: {c1: 'A'},
20482042
r2: {c1: 'b'},
@@ -2210,7 +2204,7 @@ describe('willDelCell', () => {
22102204
store.setRow('t1', 'r1', {c1: 'a', c2: 'b'});
22112205
delCalls.length = 0;
22122206
store.setRow('t1', 'r1', {c1: 'A'});
2213-
// c2 should be deleted
2207+
22142208
expect(delCalls).toEqual(['t1/r1/c2']);
22152209
expect(store.getRow('t1', 'r1')).toEqual({c1: 'A'});
22162210
});
@@ -2219,7 +2213,7 @@ describe('willDelCell', () => {
22192213
middleware.addWillDelCellCallback(() => false);
22202214
store.setRow('t1', 'r1', {c1: 'a', c2: 'b'});
22212215
store.setRow('t1', 'r1', {c1: 'A'});
2222-
// c2 should be preserved because delete was blocked
2216+
22232217
expect(store.getRow('t1', 'r1')).toEqual({c1: 'A', c2: 'b'});
22242218
});
22252219
});
@@ -2434,7 +2428,7 @@ describe('willDelValue', () => {
24342428
store.setValues({v1: 'a', v2: 'b'});
24352429
delCalls.length = 0;
24362430
store.setValues({v1: 'A'});
2437-
// v2 should be deleted
2431+
24382432
expect(delCalls).toEqual(['v2']);
24392433
expect(store.getValues()).toEqual({v1: 'A'});
24402434
});
@@ -2443,7 +2437,7 @@ describe('willDelValue', () => {
24432437
middleware.addWillDelValueCallback(() => false);
24442438
store.setValues({v1: 'a', v2: 'b'});
24452439
store.setValues({v1: 'A'});
2446-
// v2 should be preserved because delete was blocked
2440+
24472441
expect(store.getValues()).toEqual({v1: 'A', v2: 'b'});
24482442
});
24492443
});
@@ -2530,7 +2524,7 @@ describe('willApplyChanges', () => {
25302524
store.applyChanges([{t1: {r1: {c1: 'a'}}}, {}, 1]);
25312525
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
25322526
store.applyChanges([{banned: {r1: {c1: 1}}}, {}, 1]);
2533-
// blocked, so t1 from previous applyChanges remains
2527+
25342528
expect(store.getTables()).toEqual({t1: {r1: {c1: 'a'}}});
25352529
});
25362530

@@ -2813,9 +2807,9 @@ describe('schema interaction', () => {
28132807
store.setTablesSchema({
28142808
t1: {c1: {type: 'string'}, c2: {type: 'number'}},
28152809
});
2816-
// c1 valid string, c2 invalid (string instead of number) - gets removed
2810+
28172811
store.setRow('t1', 'r1', {c1: 'a', c2: 'b' as any});
2818-
// Only c1 reaches the callback; c2 is rejected by schema validation
2812+
28192813
expect(calls).toEqual(['t1/r1/c1=a']);
28202814
});
28212815
});
@@ -2868,7 +2862,7 @@ describe('schema interaction', () => {
28682862
},
28692863
});
28702864
store.setRow('t1', 'r1', {c1: 'a'});
2871-
// c2 should be defaulted to 1, and the callback should see it
2865+
28722866
expect(calls).toEqual([
28732867
{tableId: 't1', rowId: 'r1', cellId: 'c1', cell: 'a'},
28742868
{tableId: 't1', rowId: 'r1', cellId: 'c2', cell: 1},
@@ -3405,3 +3399,201 @@ describe('callback granularity', () => {
34053399
});
34063400
});
34073401
});
3402+
3403+
describe('middleware not called during doRollback', () => {
3404+
let calls: Record<string, number>;
3405+
3406+
const registerAllCallbacks = () => {
3407+
calls = {};
3408+
middleware.addWillSetCellCallback((_tId, _rId, _cId, cell) => {
3409+
calls['willSetCell'] = (calls['willSetCell'] ?? 0) + 1;
3410+
return cell;
3411+
});
3412+
middleware.addWillSetValueCallback((_vId, value) => {
3413+
calls['willSetValue'] = (calls['willSetValue'] ?? 0) + 1;
3414+
return value;
3415+
});
3416+
middleware.addWillDelCellCallback(() => {
3417+
calls['willDelCell'] = (calls['willDelCell'] ?? 0) + 1;
3418+
return true;
3419+
});
3420+
middleware.addWillDelValueCallback(() => {
3421+
calls['willDelValue'] = (calls['willDelValue'] ?? 0) + 1;
3422+
return true;
3423+
});
3424+
};
3425+
3426+
test('rolling back cell changes does not trigger middleware', () => {
3427+
store.setTables({t1: {r1: {c1: 'a'}}});
3428+
registerAllCallbacks();
3429+
store.transaction(
3430+
() => {
3431+
store.setCell('t1', 'r1', 'c1', 'b');
3432+
},
3433+
() => true,
3434+
);
3435+
3436+
expect(calls).toEqual({willSetCell: 1});
3437+
expect(store.getCell('t1', 'r1', 'c1')).toBe('a');
3438+
});
3439+
3440+
test('rolling back new cell does not trigger middleware', () => {
3441+
registerAllCallbacks();
3442+
store.transaction(
3443+
() => {
3444+
store.setCell('t1', 'r1', 'c1', 'a');
3445+
},
3446+
() => true,
3447+
);
3448+
3449+
expect(calls).toEqual({willSetCell: 1});
3450+
expect(store.getTables()).toEqual({});
3451+
});
3452+
3453+
test('rolling back value changes does not trigger middleware', () => {
3454+
store.setValues({v1: 'a'});
3455+
registerAllCallbacks();
3456+
store.transaction(
3457+
() => {
3458+
store.setValue('v1', 'b');
3459+
},
3460+
() => true,
3461+
);
3462+
expect(calls).toEqual({willSetValue: 1});
3463+
expect(store.getValue('v1')).toBe('a');
3464+
});
3465+
3466+
test('rolling back new value does not trigger middleware', () => {
3467+
registerAllCallbacks();
3468+
store.transaction(
3469+
() => {
3470+
store.setValue('v1', 'a');
3471+
},
3472+
() => true,
3473+
);
3474+
expect(calls).toEqual({willSetValue: 1});
3475+
expect(store.getValues()).toEqual({});
3476+
});
3477+
3478+
test('rolling back cell deletion does not trigger middleware', () => {
3479+
store.setTables({t1: {r1: {c1: 'a', c2: 'b'}}});
3480+
registerAllCallbacks();
3481+
store.transaction(
3482+
() => {
3483+
store.delCell('t1', 'r1', 'c2');
3484+
},
3485+
() => true,
3486+
);
3487+
expect(calls).toEqual({willDelCell: 1});
3488+
expect(store.getRow('t1', 'r1')).toEqual({c1: 'a', c2: 'b'});
3489+
});
3490+
3491+
test('rolling back value deletion does not trigger middleware', () => {
3492+
store.setValues({v1: 'a', v2: 'b'});
3493+
registerAllCallbacks();
3494+
store.transaction(
3495+
() => {
3496+
store.delValue('v2');
3497+
},
3498+
() => true,
3499+
);
3500+
expect(calls).toEqual({willDelValue: 1});
3501+
expect(store.getValues()).toEqual({v1: 'a', v2: 'b'});
3502+
});
3503+
});
3504+
3505+
describe('middleware not called during checkpoint undo/redo', () => {
3506+
let calls: Record<string, number>;
3507+
3508+
const registerAllCallbacks = () => {
3509+
calls = {};
3510+
middleware.addWillSetRowCallback((_tId, _rId, row) => {
3511+
calls['willSetRow'] = (calls['willSetRow'] ?? 0) + 1;
3512+
return row;
3513+
});
3514+
middleware.addWillSetCellCallback((_tId, _rId, _cId, cell) => {
3515+
calls['willSetCell'] = (calls['willSetCell'] ?? 0) + 1;
3516+
return cell;
3517+
});
3518+
middleware.addWillSetValueCallback((_vId, value) => {
3519+
calls['willSetValue'] = (calls['willSetValue'] ?? 0) + 1;
3520+
return value;
3521+
});
3522+
middleware.addWillDelCellCallback(() => {
3523+
calls['willDelCell'] = (calls['willDelCell'] ?? 0) + 1;
3524+
return true;
3525+
});
3526+
middleware.addWillDelValueCallback(() => {
3527+
calls['willDelValue'] = (calls['willDelValue'] ?? 0) + 1;
3528+
return true;
3529+
});
3530+
};
3531+
3532+
test('undo does not trigger middleware', () => {
3533+
const checkpoints = createCheckpoints(store);
3534+
store.setTables({t1: {r1: {c1: 'a'}}});
3535+
checkpoints.addCheckpoint('initial');
3536+
registerAllCallbacks();
3537+
store.setCell('t1', 'r1', 'c1', 'b');
3538+
checkpoints.addCheckpoint('changed');
3539+
expect(calls).toEqual({willSetCell: 1});
3540+
calls = {};
3541+
checkpoints.goBackward();
3542+
expect(calls).toEqual({});
3543+
expect(store.getCell('t1', 'r1', 'c1')).toBe('a');
3544+
});
3545+
3546+
test('redo does not trigger middleware', () => {
3547+
const checkpoints = createCheckpoints(store);
3548+
store.setTables({t1: {r1: {c1: 'a'}}});
3549+
checkpoints.addCheckpoint('initial');
3550+
registerAllCallbacks();
3551+
store.setCell('t1', 'r1', 'c1', 'b');
3552+
checkpoints.addCheckpoint('changed');
3553+
calls = {};
3554+
checkpoints.goBackward();
3555+
checkpoints.goForward();
3556+
expect(calls).toEqual({});
3557+
expect(store.getCell('t1', 'r1', 'c1')).toBe('b');
3558+
});
3559+
3560+
test('undo of new row does not trigger middleware', () => {
3561+
const checkpoints = createCheckpoints(store);
3562+
checkpoints.addCheckpoint('empty');
3563+
registerAllCallbacks();
3564+
store.setRow('t1', 'r1', {c1: 'a'});
3565+
checkpoints.addCheckpoint('added');
3566+
expect(calls).toEqual({willSetRow: 1, willSetCell: 1});
3567+
calls = {};
3568+
checkpoints.goBackward();
3569+
expect(calls).toEqual({});
3570+
expect(store.getTables()).toEqual({});
3571+
});
3572+
3573+
test('undo of value change does not trigger middleware', () => {
3574+
const checkpoints = createCheckpoints(store);
3575+
store.setValues({v1: 'a'});
3576+
checkpoints.addCheckpoint('initial');
3577+
registerAllCallbacks();
3578+
store.setValue('v1', 'b');
3579+
checkpoints.addCheckpoint('changed');
3580+
expect(calls).toEqual({willSetValue: 1});
3581+
calls = {};
3582+
checkpoints.goBackward();
3583+
expect(calls).toEqual({});
3584+
expect(store.getValue('v1')).toBe('a');
3585+
});
3586+
3587+
test('undo of new value does not trigger middleware', () => {
3588+
const checkpoints = createCheckpoints(store);
3589+
checkpoints.addCheckpoint('empty');
3590+
registerAllCallbacks();
3591+
store.setValue('v1', 'a');
3592+
checkpoints.addCheckpoint('added');
3593+
expect(calls).toEqual({willSetValue: 1});
3594+
calls = {};
3595+
checkpoints.goBackward();
3596+
expect(calls).toEqual({});
3597+
expect(store.getValues()).toEqual({});
3598+
});
3599+
});

0 commit comments

Comments
 (0)