Harden batch-wide recovery identity claims

Scope remote code ownership to normalized hoster and account identities while retaining title-only recovery serialization and canonical Unicode title matching.

Mark post-upload ambiguity and duplicate identities as uncertain so retries, account fallback, and later same-title jobs fail closed instead of reporting unsafe success.

Acquire recovery title leases before hoster and global semaphores, revalidate failed-account overrides before upload, and clear claim state at batch boundaries.

Add deterministic concurrent coverage for same-code rejection, distinct-code parallel success, uncertainty propagation, semaphore fairness, account isolation, Unicode equivalence, and registry lifetime.
This commit is contained in:
Sucukdeluxe
2026-08-13 23:09:03 +02:00
parent dd14381e43
commit c78160a521
7 changed files with 640 additions and 141 deletions
+43
View File
@@ -187,3 +187,46 @@ test('Vidmoly concurrent same-name recovery accepts distinct remote codes', asyn
assert.deepEqual(results.map(result => result.file_code), ['VIDFIRST0001', 'VIDSECOND001']);
});
for (const scenario of [
{
label: 'VOE',
hoster: 'voe.sx',
Uploader: VoeUploader,
sharedCode: 'VOE_UNCERTAIN',
lateCode: 'VOE_LATE_CODE',
build(uploader, code) {
return uploader._buildUrls(code);
}
},
{
label: 'Vidmoly',
hoster: 'vidmoly.me',
Uploader: VidmolyUploader,
sharedCode: 'VIDUNCERTAIN',
lateCode: 'VIDLATECODE1',
build(uploader, code) {
return uploader._buildUrlsFromCode(code);
}
}
]) {
test(`${scenario.label} marks a duplicate direct identity uncertain and blocks a later title match`, async () => {
const registry = createRecoveryClaimRegistry();
const firstClaim = registry.forUpload(scenario.hoster, 'ACCOUNT', 'First Episode.mkv');
const uncertainClaim = registry.forUpload(scenario.hoster, 'ACCOUNT', 'Second Episode.mkv');
const first = new scenario.Uploader(firstClaim);
const uncertain = new scenario.Uploader(uncertainClaim);
scenario.build(first, scenario.sharedCode);
assert.throws(
() => scenario.build(uncertain, scenario.sharedCode),
err => err.remoteIdentityClaimed === true && err.remoteCommitUncertain === true
);
const laterClaim = registry.forUpload(scenario.hoster, 'ACCOUNT', 'Second Episode.mp4');
await assert.rejects(
() => laterClaim.runExclusive(async () => scenario.build(new scenario.Uploader(laterClaim), scenario.lateCode)),
err => err.remoteCommitUncertain === true
);
});
}