Complete bounded browser and API product behavior
This commit is contained in:
parent
619c2911d2
commit
ea55108f44
1 changed files with 2117 additions and 399 deletions
96
web/test/mutations.test.mjs
Normal file
96
web/test/mutations.test.mjs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
captureOperation,
|
||||
deleteWarning,
|
||||
decodePendingDelete,
|
||||
encodePendingDelete,
|
||||
mutationFollowup,
|
||||
readPendingDelete,
|
||||
sharedUploadWarning,
|
||||
unfinishedDeleteItems
|
||||
} from '../src/mutations.ts';
|
||||
|
||||
test('pending mutations replay while terminal failures restart', () => {
|
||||
assert.equal(mutationFollowup('succeeded'), 'complete');
|
||||
assert.equal(mutationFollowup('pending'), 'replay');
|
||||
assert.equal(mutationFollowup('failed'), 'restart');
|
||||
assert.equal(mutationFollowup('rejected'), 'restart');
|
||||
assert.equal(mutationFollowup('conflict'), 'restart');
|
||||
});
|
||||
|
||||
test('partial delete recovery preserves only unfinished identities', () => {
|
||||
const ids = ['new-failed', 'new-conflict'];
|
||||
const pending = unfinishedDeleteItems(
|
||||
[
|
||||
{
|
||||
path: 'done',
|
||||
operation_id: 'done-id',
|
||||
result: 'succeeded',
|
||||
error: null
|
||||
},
|
||||
{
|
||||
path: 'waiting',
|
||||
operation_id: 'waiting-id',
|
||||
result: 'pending',
|
||||
error: null
|
||||
},
|
||||
{
|
||||
path: 'failed',
|
||||
operation_id: 'failed-id',
|
||||
result: 'failed',
|
||||
error: null
|
||||
},
|
||||
{
|
||||
path: 'conflict',
|
||||
operation_id: 'conflict-id',
|
||||
result: 'conflict',
|
||||
error: null
|
||||
},
|
||||
{
|
||||
path: 'uncertain',
|
||||
operation_id: 'uncertain-id',
|
||||
result: null,
|
||||
error: 'transport interrupted'
|
||||
}
|
||||
],
|
||||
() => ids.shift()
|
||||
);
|
||||
assert.deepEqual(pending, [
|
||||
{ path: 'waiting', operation_id: 'waiting-id' },
|
||||
{ path: 'failed', operation_id: 'new-failed' },
|
||||
{ path: 'conflict', operation_id: 'new-conflict' },
|
||||
{ path: 'uncertain', operation_id: 'uncertain-id' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('persisted delete batches survive reload with the same captured root', () => {
|
||||
const items = [{ path: 'folder/file', operation_id: 'operation-1' }];
|
||||
const encoded = encodePendingDelete('shared', 'folder', items);
|
||||
assert.deepEqual(readPendingDelete(encoded), {
|
||||
root: 'shared',
|
||||
prefix: 'folder',
|
||||
items
|
||||
});
|
||||
assert.deepEqual(decodePendingDelete(encoded, 'shared', 'folder'), items);
|
||||
assert.deepEqual(decodePendingDelete(encoded, 'personal', 'folder'), []);
|
||||
assert.deepEqual(decodePendingDelete('{"items":[null]}', 'shared', 'folder'), []);
|
||||
});
|
||||
|
||||
test('logical mutations capture their root before navigation changes', () => {
|
||||
let root = 'personal';
|
||||
const captured = captureOperation(root, 'folder/file', 'operation-1');
|
||||
root = 'shared';
|
||||
assert.deepEqual(captured, {
|
||||
root: 'personal',
|
||||
path: 'folder/file',
|
||||
operationId: 'operation-1'
|
||||
});
|
||||
});
|
||||
|
||||
test('shared replacement confirmation is explicit', () => {
|
||||
assert.equal(sharedUploadWarning('personal', 'file'), null);
|
||||
assert.match(sharedUploadWarning('shared', 'file'), /replace a file for every user/);
|
||||
assert.match(deleteWarning('shared', 'folder', true), /for every user/);
|
||||
assert.doesNotMatch(deleteWarning('personal', 'file', false), /every user/);
|
||||
});
|
||||
Loading…
Reference in a new issue