Complete bounded browser and API product behavior

This commit is contained in:
ms2k 2026-07-19 01:12:29 +09:00
commit ea55108f44

View 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/);
});