fix(images): say plainly why Windows offers no Recycle Bin (#500)

Maintainer ruling (a): Windows never uses the Recycle Bin, because it deletes
files larger than the bin's capacity permanently and IFileOperation's
PreDeleteItem guard cannot detect it (native probe runs 40951037575,
40953347904). Each file in the Delete review now reads "Moving files to the
Recycle Bin isn't available on Windows: files larger than the bin's capacity
would be deleted permanently." (code windows-recycle) instead of "This drive has
no trash.", and the summary line drops its "yet": "Moving to the trash is not
available for these files; each one lists why."
This commit is contained in:
Vito Sansevero
2026-10-29 14:45:25 -07:00
parent d02a911748
commit ea3c3d4dce
7 changed files with 56 additions and 11 deletions
+2
View File
@@ -65,6 +65,8 @@ test('sentences follow the mode and the backup policy', () => {
assert.deepEqual(deleteSentences(plan(), 'trash'), [SENTENCES.trash, USE_LIBRARY_ONLY]);
const nowhere = plan({ trash: { count: 0 }, trash_refused: { count: 3, rows: [] } });
assert.deepEqual(deleteSentences(nowhere, 'trash'), [SENTENCES.trash, NO_TRASH_HERE, USE_LIBRARY_ONLY]);
// #500: no "yet" (Windows is unavailable by design); each file's reason says why.
assert.equal(NO_TRASH_HERE, 'Moving to the trash is not available for these files; each one lists why.');
assert.deepEqual(deleteSentences(plan({ trash_refused: { count: 0 } }), 'trash'), [SENTENCES.trash]);
});
+9 -1
View File
@@ -306,6 +306,13 @@ class TestFileCheck(ImageDeleteTestCase):
file_check(str(self.output_dir / "ok.png"), root, "ok.png")[0]
)
def test_windows_reason_says_why_plainly(self):
self.assertEqual(
image_delete.REASONS["windows-recycle"],
"Moving files to the Recycle Bin isn't available on Windows: files "
"larger than the bin's capacity would be deleted permanently.",
)
def test_reasons_cover_every_code(self):
from utils import trash
@@ -324,7 +331,8 @@ class TestNativeBackend(ImageDeleteTestCase):
native_trash = sys.platform not in ("win32", "darwin")
if not native_trash:
self.assertEqual(plan["trash"], {"count": 0})
self.assertEqual(plan["trash_refused"]["counts"], {"no-trash": 1})
code = "windows-recycle" if sys.platform == "win32" else "no-trash"
self.assertEqual(plan["trash_refused"]["counts"], {code: 1})
body = await self.post(APPLY, {"token": plan["token"], "mode": "library"}, 202)
job = self.api._jobs.get(body["job"]["id"])
await asyncio.wait_for(asyncio.shield(job.task), DISK_BOUND_SECONDS)
+14
View File
@@ -285,6 +285,20 @@ class UnavailableBackendTests(unittest.TestCase):
)
self.assertIsInstance(trash.default_backend("linux"), trash.FreedesktopBackend)
def test_windows_says_why_the_recycle_bin_is_not_used(self):
# #500 ruling (a): the Recycle Bin can delete permanently, so it is never used.
backend = trash.default_backend("win32")
self.assertEqual(
backend.available("C:/x/a.png", ["C:/x"]), trash.WINDOWS_RECYCLE
)
with self.assertRaises(trash.TrashRefused) as caught:
backend.move("C:/x/a.png", ["C:/x"])
self.assertEqual(caught.exception.code, trash.WINDOWS_RECYCLE)
self.assertIn(trash.WINDOWS_RECYCLE, trash.CODES)
self.assertEqual(
trash.default_backend("darwin").available("/x", ["/"]), trash.NO_TRASH
)
def test_module_functions_use_the_replaceable_backend(self):
fake = mock.Mock()
fake.available.return_value = "network"