fix: preserve Drafts when cleanup fails (Closes #625)
This commit is contained in:
1 parent
89d153d6a5
commit
285fdff869
2 files changed
+162
-10
No files matched your search
@@ -104,6 +104,131 @@ const attachmentStore = {{
|
||||
}
|
||||
|
||||
|
||||
def test_failed_replacement_attachment_cleanup_restores_original_draft_and_removes_staged_blob():
|
||||
script = f"""
|
||||
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
|
||||
const values = new Map();
|
||||
const blobs = new Map();
|
||||
const storage = {{getItem:k=>values.get(k)||null,setItem:(k,v)=>values.set(k,v)}};
|
||||
const attachmentStore = {{
|
||||
put: async (id, attachment) => blobs.set(id, attachment),
|
||||
get: async id => blobs.get(id) || null,
|
||||
delete: async id => {{
|
||||
if (id === '1') throw new Error('IndexedDB delete failed');
|
||||
blobs.delete(id);
|
||||
}},
|
||||
}};
|
||||
(async () => {{
|
||||
let id = 0;
|
||||
const captures = createUnfiledCaptures({{
|
||||
storage, attachmentStore, maxItems:1, getCaptureLogin:()=>'timmy', getCurrentLogin:()=>'timmy',
|
||||
createId:()=>String(++id), now:()=>id,
|
||||
}});
|
||||
const image = name => ({{filename:name,contentType:'image/png',blob:new Blob([name],{{type:'image/png'}})}});
|
||||
const original = await captures.save({{title:'Original',body:'keep me',attachment:image('original.png')}});
|
||||
let error = '';
|
||||
try {{ await captures.replaceOldest({{title:'Replacement',body:'new',attachment:image('new.png')}}, original.id); }}
|
||||
catch (caught) {{ error = caught.message; }}
|
||||
const resumed = await captures.resume(original.id, 'timmy');
|
||||
process.stdout.write(JSON.stringify({{
|
||||
error, titles:captures.list().map(item=>item.title), blobs:[...blobs.keys()],
|
||||
resumedTitle:resumed.title, resumedFilename:resumed.attachment.filename,
|
||||
}}));
|
||||
}})().catch(error => {{ console.error(error); process.exit(1); }});
|
||||
"""
|
||||
output = run_node(script)
|
||||
|
||||
assert output == {
|
||||
"error": "IndexedDB delete failed",
|
||||
"titles": ["Original"],
|
||||
"blobs": ["1"],
|
||||
"resumedTitle": "Original",
|
||||
"resumedFilename": "original.png",
|
||||
}
|
||||
|
||||
|
||||
def test_failed_discard_attachment_cleanup_leaves_draft_visible_and_resumable():
|
||||
script = f"""
|
||||
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
|
||||
const values = new Map();
|
||||
const blobs = new Map();
|
||||
const storage = {{getItem:k=>values.get(k)||null,setItem:(k,v)=>values.set(k,v)}};
|
||||
const attachmentStore = {{
|
||||
put: async (id, attachment) => blobs.set(id, attachment),
|
||||
get: async id => blobs.get(id) || null,
|
||||
delete: async () => {{ throw new Error('IndexedDB delete failed'); }},
|
||||
}};
|
||||
(async () => {{
|
||||
const captures = createUnfiledCaptures({{
|
||||
storage, attachmentStore, getCaptureLogin:()=>'timmy', getCurrentLogin:()=>'timmy',
|
||||
createId:()=>'capture-1', now:()=>42,
|
||||
}});
|
||||
await captures.save({{title:'Keep this',body:'Important',attachment:{{
|
||||
filename:'proof.png',contentType:'image/png',blob:new Blob(['proof'],{{type:'image/png'}}),
|
||||
}}}});
|
||||
let error = '';
|
||||
try {{ await captures.discard('capture-1'); }} catch (caught) {{ error = caught.message; }}
|
||||
const resumed = await captures.resume('capture-1', 'timmy');
|
||||
process.stdout.write(JSON.stringify({{
|
||||
error, titles:captures.list().map(item=>item.title), resumedTitle:resumed.title,
|
||||
resumedFilename:resumed.attachment.filename,
|
||||
}}));
|
||||
}})().catch(error => {{ console.error(error); process.exit(1); }});
|
||||
"""
|
||||
output = run_node(script)
|
||||
|
||||
assert output == {
|
||||
"error": "IndexedDB delete failed",
|
||||
"titles": ["Keep this"],
|
||||
"resumedTitle": "Keep this",
|
||||
"resumedFilename": "proof.png",
|
||||
}
|
||||
|
||||
|
||||
def test_discard_metadata_write_failure_restores_attachment_and_draft():
|
||||
script = f"""
|
||||
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
|
||||
const values = new Map();
|
||||
const blobs = new Map();
|
||||
let failWrites = false;
|
||||
const storage = {{
|
||||
getItem:k=>values.get(k)||null,
|
||||
setItem:(k,v)=>{{ if (failWrites) throw new Error('Storage write failed'); values.set(k,v); }},
|
||||
}};
|
||||
const attachmentStore = {{
|
||||
put: async (id, attachment) => blobs.set(id, attachment),
|
||||
get: async id => blobs.get(id) || null,
|
||||
delete: async id => blobs.delete(id),
|
||||
}};
|
||||
(async () => {{
|
||||
const captures = createUnfiledCaptures({{
|
||||
storage, attachmentStore, getCaptureLogin:()=>'timmy', getCurrentLogin:()=>'timmy',
|
||||
createId:()=>'capture-1', now:()=>42,
|
||||
}});
|
||||
await captures.save({{title:'Keep this',body:'Important',attachment:{{
|
||||
filename:'proof.png',contentType:'image/png',blob:new Blob(['proof'],{{type:'image/png'}}),
|
||||
}}}});
|
||||
failWrites = true;
|
||||
let error = '';
|
||||
try {{ await captures.discard('capture-1'); }} catch (caught) {{ error = caught.message; }}
|
||||
failWrites = false;
|
||||
const resumed = await captures.resume('capture-1', 'timmy');
|
||||
process.stdout.write(JSON.stringify({{
|
||||
error, titles:captures.list().map(item=>item.title), blobIds:[...blobs.keys()],
|
||||
resumedFilename:resumed.attachment.filename,
|
||||
}}));
|
||||
}})().catch(error => {{ console.error(error); process.exit(1); }});
|
||||
"""
|
||||
output = run_node(script)
|
||||
|
||||
assert output == {
|
||||
"error": "Storage write failed",
|
||||
"titles": ["Keep this"],
|
||||
"blobIds": ["capture-1"],
|
||||
"resumedFilename": "proof.png",
|
||||
}
|
||||
|
||||
|
||||
def test_unfiled_capture_resume_requires_matching_confirmed_account_and_removes_only_selected_note():
|
||||
script = f"""
|
||||
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
|
||||
|
||||
Reference in new issue
Block a user