test(trash): classify the Needs review link route and prove it refuses a trashed file

POST /review/items/{id}/link needs a capture of the item's bytes. A capture is refused for a path in a trash folder, and a capture taken earlier stops working when its file moves to a trash folder or its folder becomes a link to one, so no code change is needed. The decision route takes the same path, so it moves to the same audit group.
This commit is contained in:
Vito Sansevero
2026-10-30 16:54:50 -07:00
parent cdaf4122de
commit 4a25db545c
2 changed files with 55 additions and 1 deletions
+53
View File
@@ -6,6 +6,8 @@ records provenance that cannot pose as what the file says.
"""
import json
import os
import unittest
try:
from tests.test_api_review import ITEMS_URL, ReviewRoutesTestCase
@@ -339,3 +341,54 @@ def detail_version(case, item):
return conn.execute(
"SELECT resolver_version FROM media_review_items WHERE id=?", (item["id"],)
).fetchone()[0]
class TestLinkNeverActsOnATrashedFile(LinkTestCase):
"""#543's gate: a link needs a capture, and neither way to one reaches a trash."""
TRASH = ".Trash-1000"
async def _captured_in_sub(self):
path = self._ambiguous("sub/two.png")
self._record(path)
item, detail, observed = await self._open()
positive_id, pair_id = self._pair()
return path, item, self._link_body(detail, observed, positive_id, pair_id)
async def _assert_refused(self, item, body):
before = self._snapshot()
status, data = await self._link(item["id"], body)
self.assertGreaterEqual(status, 400, data)
self.assertFalse(data.get("success"), data)
self.assertEqual(self._snapshot(), before)
async def test_a_file_moved_to_a_trash_folder_after_its_capture_is_refused(self):
path, item, body = await self._captured_in_sub()
trash = self.output_dir / self.TRASH / "files"
trash.mkdir(parents=True)
os.rename(path, trash / path.name)
await self._assert_refused(item, body)
async def test_a_folder_swapped_for_a_link_into_a_trash_folder_is_refused(self):
path, item, body = await self._captured_in_sub()
trash = self.output_dir / self.TRASH / "files"
trash.parent.mkdir(parents=True)
os.rename(path.parent, trash)
try:
os.symlink(trash, path.parent, target_is_directory=True)
except OSError as exc:
raise unittest.SkipTest(f"no symlinks here: {exc}")
self.assertTrue(path.exists())
await self._assert_refused(item, body)
async def test_an_item_in_a_trash_folder_cannot_get_a_capture(self):
path = self._ambiguous(f"{self.TRASH}/files/two.png")
self._record(path)
item = (await self._list())["items"][0]
status, data = await self._json(
"POST",
"/prompt_manager/image-annotations/observe",
json={"target": item["target"]},
)
self.assertEqual(status, 404, data)
self.assertFalse(data.get("capture"), data)
+2 -1
View File
@@ -53,6 +53,8 @@ ROUTE_AUDIT = {
"GET /prompt_manager/collections/membership",
"PUT /prompt_manager/images/category",
"DELETE /prompt_manager/images/category",
"POST /prompt_manager/review/items/{item_id}/decision",
"POST /prompt_manager/review/items/{item_id}/link",
),
"walker: utils/media_folders (listed files only)": (
"GET /prompt_manager/images/output",
@@ -94,7 +96,6 @@ ROUTE_AUDIT = {
"GET /prompt_manager/history/runs/{run_id}",
"GET /prompt_manager/review/items",
"GET /prompt_manager/review/items/{item_id}",
"POST /prompt_manager/review/items/{item_id}/decision",
"POST /prompt_manager/maintenance/preview",
"POST /prompt_manager/maintenance/apply",
),