docs(tests): explain migration retries and explicit conflict decisions
This commit is contained in:
@@ -63,6 +63,7 @@ test('keep-local freezes a new conditional intent/key, retains the old rejection
|
||||
const f=fixture(),engine=await f.open();title(engine,'local');await engine.settle();f.remote({title:'remote',text:'untouched remote'});
|
||||
const before=engine.getSnapshot(),old=[...before.runtime.ledger.requests.values()][0],session=await engine.prepareRecovery(),plan=session.plan(choices(engine));
|
||||
assert.equal(engine.getSnapshot(),before);assert.equal(plan.document.elements.get('a').data.title,'local');assert.equal(plan.document.elements.get('a').data.text,'untouched remote');
|
||||
// keep-local 是基于远程 before 值重新表达意图,不是重发旧请求;旧冻结 body 与 not-applied 证据应继续留存。
|
||||
const replacement=plan.operations.get(plan.operationIds[0]);assert.notEqual(replacement.id,old.operationIds[0]);assert.equal(replacement.changes[0].before,'remote');
|
||||
assert.equal(plan.ledger.requests.get(old.id).body,old.body);assert.equal(plan.ledger.operations.get(old.operationIds[0]).resolution,'superseded');
|
||||
f.reject=false;assert.equal((await session.resolve(plan)).status,'applied');await engine.settle();
|
||||
@@ -122,6 +123,7 @@ test('recovery publication failure keeps one plan and blocks sends until the exa
|
||||
const f=fixture(),engine=await f.open();title(engine,'local');await engine.settle();f.remote({title:'remote'});const session=await engine.prepareRecovery(),plan=session.plan(choices(engine));
|
||||
f.reject=false;f.broken=true;await assert.rejects(session.resolve(plan),/quota/);const revision=engine.getSnapshot().rootRevision;assert.equal(f.calls.length,1);
|
||||
await assert.rejects(session.apply(),/recovery_already_committed/);await assert.rejects(session.resolve({...plan}),/invalid_recovery_plan/);
|
||||
// 使用原 plan 对象重试保存,避免同一恢复决策被重复发布;持久屏障解除后才允许替代请求发送。
|
||||
f.broken=false;await session.resolve(plan);await engine.settle();assert.equal(f.calls.length,2);assert.ok(engine.getDurability().durableRootRevision>revision);assert.equal(engine.getSnapshot().runtime.recoveries.size,1);await engine.close();
|
||||
});
|
||||
|
||||
@@ -184,7 +186,7 @@ test('discard-only resolution rechecks current identity and a cancelled read can
|
||||
const f=fixture();let allowed=true,release,signal;const engine=await f.open();title(engine,'local');await engine.settle();
|
||||
const session=await engine.prepareRecovery(),plan=session.plan(choices(engine,'discard-local'));await session.cancel();
|
||||
const actualRead=f.port.read;f.port.read=async input=>{signal=input;await new Promise(resolve=>{release=resolve;});return actualRead(input);};const preparing=engine.prepareRecovery();await tick();await engine.cancelRecovery();assert.equal(signal.aborted,true);release();await assert.rejects(preparing,/recovery_session_closed/);assert.equal(engine.editor.can({type:'element.data',target,patch:{title:'more'}}).enabled,true);await engine.close();
|
||||
// The policy is evaluated again even when all chosen operations will be discarded.
|
||||
// 全部 discard 仍会改变本地历史/账本,提交时须再次检查策略;不能因没有新文档请求就跳过当前身份检查。
|
||||
const other=fixture(),guarded=await openProjectEngine({scope:{accountId:'account',workspaceId:'workspace',projectId:'project'},registry,document:original,versions:new Map([['canvas',1]]),storage:other.storage,persistence:other.port,policy:()=>allowed?{enabled:true}:{enabled:false,code:'identity_changed',targets:[]}});
|
||||
title(guarded,'local');await guarded.settle();const review=await guarded.prepareRecovery(),discard=review.plan(choices(guarded,'discard-local'));allowed=false;assert.equal((await review.resolve(discard)).code,'identity_changed');assert.equal(guarded.getSnapshot().runtime.recoveries.size,0);await review.cancel();await guarded.close();
|
||||
});
|
||||
@@ -200,6 +202,7 @@ test('later locally elided history survives keep/discard of an earlier rejected
|
||||
const localIds=[...created.operationIds,...removed.operationIds];assert.equal(f.calls.length,1);for(const id of localIds)assert.equal(engine.getSnapshot().runtime.ledger.operations.get(id).resolution,'elided');
|
||||
f.remote({title:'remote'});const session=await engine.prepareRecovery(),plan=session.plan(choices(engine,keep?'keep-local':'discard-local'));assert.equal(plan.document.tombstones.get(temporary.id),1);f.reject=false;await session.resolve(plan);await engine.settle();
|
||||
await engine.close();engine=await f.open();assert.equal(engine.getSnapshot().document.tombstones.get(temporary.id),1);assert.equal(engine.getSnapshot().runtime.acknowledged.tombstones.has(temporary.id),false);assert.equal(engine.getSnapshot().history.undo.length,keep?3:2);
|
||||
// localIds 已被本地抵消但仍有撤销历史;逐请求检查它们未被重新塞入网络同步,同时重开后墓碑仍有效。
|
||||
for(const request of engine.getSnapshot().runtime.ledger.requests.values())assert.equal(request.operationIds.some(id=>localIds.includes(id)),false);
|
||||
assert.equal(engine.editor.history.undo().status,'applied');await engine.settle();assert.equal(engine.getSnapshot().document.elements.get(temporary.id).incarnation,2);
|
||||
assert.equal(engine.editor.history.redo().status,'applied');await engine.settle();assert.equal(engine.getSnapshot().document.elements.has(temporary.id),false);assert.equal(engine.getSnapshot().document.elements.get('a').data.title,keep?'pending':'remote');
|
||||
|
||||
Reference in New Issue
Block a user