fix: 完善商品—模特库-视频项目-自由创作等删除先进垃圾桶及可恢复等流程
This commit is contained in:
@@ -230,6 +230,14 @@ class FreeVideoAssetDeletionTests(TestCase):
|
||||
preview_url="http://tos.example/video.mp4",
|
||||
is_primary=True,
|
||||
)
|
||||
AssetFile.objects.create(
|
||||
asset=self.asset,
|
||||
object_key="free-video-poster.jpg",
|
||||
bucket="b",
|
||||
content_type="image/jpeg",
|
||||
preview_url="http://tos.example/free-video-poster.jpg",
|
||||
is_primary=False,
|
||||
)
|
||||
|
||||
def test_deleted_persisted_asset_does_not_fall_back_to_raw_video_url(self):
|
||||
from apps.ai.free_video import serialize_free_video_task
|
||||
@@ -240,7 +248,76 @@ class FreeVideoAssetDeletionTests(TestCase):
|
||||
task = AITask.objects.prefetch_related("generated_assets", "generated_assets__files").get(id=self.task.id)
|
||||
data = serialize_free_video_task(task)
|
||||
self.assertEqual(data["video_url"], "")
|
||||
self.assertEqual(data["thumbnail_url"], "")
|
||||
self.assertEqual(data["fallback_note"], "fallback")
|
||||
trash_data = serialize_free_video_task(task, include_deleted_assets=True)
|
||||
self.assertEqual(trash_data["video_url"], "http://tos.example/video.mp4")
|
||||
self.assertEqual(trash_data["thumbnail_url"], "http://tos.example/free-video-poster.jpg")
|
||||
|
||||
def test_direct_asset_delete_stays_in_asset_trash(self):
|
||||
client = APIClient()
|
||||
client.force_authenticate(self.user)
|
||||
|
||||
delete = client.delete(f"/api/assets/{self.asset.id}/")
|
||||
self.assertEqual(delete.status_code, 204)
|
||||
self.task.refresh_from_db()
|
||||
self.asset.refresh_from_db()
|
||||
self.assertFalse(self.task.is_deleted)
|
||||
self.assertTrue(self.asset.is_deleted)
|
||||
|
||||
trash_ids = {a["id"] for a in client.get("/api/assets/trash/").json()["results"]}
|
||||
self.assertIn(str(self.asset.id), trash_ids)
|
||||
self.assertEqual(client.get("/api/ai/free-video/").json()["total"], 0)
|
||||
|
||||
restore = client.post(f"/api/assets/{self.asset.id}/restore/")
|
||||
self.assertEqual(restore.status_code, 200)
|
||||
live_ids = {t["id"] for t in client.get("/api/ai/free-video/").json()["results"]}
|
||||
self.assertIn(str(self.task.id), live_ids)
|
||||
|
||||
def test_trash_restore_and_purge_free_video_keeps_record(self):
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(self.user)
|
||||
|
||||
delete = self.client.delete(f"/api/ai/free-video/{self.task.id}/")
|
||||
self.assertEqual(delete.status_code, 204)
|
||||
self.task.refresh_from_db()
|
||||
self.asset.refresh_from_db()
|
||||
self.assertTrue(self.task.is_deleted)
|
||||
self.assertTrue(self.asset.is_deleted)
|
||||
self.assertIsNone(self.task.purged_at)
|
||||
self.assertEqual(self.client.get("/api/ai/free-video/").json()["total"], 0)
|
||||
trash_items = self.client.get("/api/ai/free-video/trash/").json()["results"]
|
||||
trash_ids = {t["id"] for t in trash_items}
|
||||
self.assertIn(str(self.task.id), trash_ids)
|
||||
asset_trash_ids = {a["id"] for a in self.client.get("/api/assets/trash/").json()["results"]}
|
||||
self.assertNotIn(str(self.asset.id), asset_trash_ids)
|
||||
trash_item = next(t for t in trash_items if t["id"] == str(self.task.id))
|
||||
self.assertEqual(trash_item["thumbnail_url"], "http://tos.example/free-video-poster.jpg")
|
||||
|
||||
restore = self.client.post(f"/api/ai/free-video/{self.task.id}/restore/")
|
||||
self.assertEqual(restore.status_code, 200)
|
||||
self.task.refresh_from_db()
|
||||
self.asset.refresh_from_db()
|
||||
self.assertFalse(self.task.is_deleted)
|
||||
self.assertFalse(self.asset.is_deleted)
|
||||
self.assertIsNone(self.task.purged_at)
|
||||
live_ids = {t["id"] for t in self.client.get("/api/ai/free-video/").json()["results"]}
|
||||
self.assertIn(str(self.task.id), live_ids)
|
||||
|
||||
self.client.delete(f"/api/ai/free-video/{self.task.id}/")
|
||||
purge = self.client.delete(f"/api/ai/free-video/{self.task.id}/purge/")
|
||||
self.assertEqual(purge.status_code, 204)
|
||||
self.task.refresh_from_db()
|
||||
self.asset.refresh_from_db()
|
||||
self.assertTrue(self.task.is_deleted)
|
||||
self.assertTrue(self.asset.is_deleted)
|
||||
self.assertIsNotNone(self.task.purged_at)
|
||||
self.assertIsNotNone(self.asset.purged_at)
|
||||
self.assertTrue(AITask.objects.filter(id=self.task.id).exists())
|
||||
self.assertTrue(Asset.objects.filter(id=self.asset.id).exists())
|
||||
self.assertEqual(self.asset.files.count(), 2)
|
||||
trash_ids = {t["id"] for t in self.client.get("/api/ai/free-video/trash/").json()["results"]}
|
||||
self.assertNotIn(str(self.task.id), trash_ids)
|
||||
|
||||
|
||||
class StandaloneImageReferenceTests(TestCase):
|
||||
@@ -378,6 +455,138 @@ class ImageConversationTests(TestCase):
|
||||
self.assertEqual(self.client.get("/api/ai/image-conversations/?mode=image").json()["results"], [])
|
||||
self.assertTrue(ImageConversation.objects.get(id=conv_id).is_deleted)
|
||||
|
||||
def test_trash_restore_and_purge_conversation_keeps_record(self):
|
||||
conv = ImageConversation.objects.create(team=self.team, created_by=self.user, title="待删", mode=ImageConversation.Mode.IMAGE)
|
||||
provider = ModelProvider.objects.create(name="conv-trash-provider", display_name="Conv Trash Provider")
|
||||
model = ModelConfig.objects.create(
|
||||
provider=provider,
|
||||
name="conv-trash-model",
|
||||
display_name="Conv Trash Model",
|
||||
capability=ModelConfig.Capability.IMAGE,
|
||||
)
|
||||
task = AITask.objects.create(
|
||||
team=self.team,
|
||||
created_by=self.user,
|
||||
conversation=conv,
|
||||
task_type=AITask.Type.PRODUCT_IMAGE,
|
||||
status=AITask.Status.SUCCEEDED,
|
||||
model_config=model,
|
||||
idempotency_key="conv-trash-task",
|
||||
)
|
||||
asset = Asset.objects.create(
|
||||
team=self.team,
|
||||
created_by=self.user,
|
||||
name="会话生成图",
|
||||
asset_type=Asset.Type.IMAGE,
|
||||
source=Asset.Source.AI_GENERATED,
|
||||
category=Asset.Category.FREE_CREATE,
|
||||
origin_task=task,
|
||||
)
|
||||
asset_file = AssetFile.objects.create(
|
||||
asset=asset,
|
||||
object_key="free-create/image.png",
|
||||
bucket="test",
|
||||
content_type="image/png",
|
||||
is_primary=True,
|
||||
)
|
||||
|
||||
delete = self.client.delete(f"/api/ai/image-conversations/{conv.id}/")
|
||||
self.assertIn(delete.status_code, (204, 200))
|
||||
conv.refresh_from_db()
|
||||
task.refresh_from_db()
|
||||
asset.refresh_from_db()
|
||||
self.assertTrue(conv.is_deleted)
|
||||
self.assertTrue(task.is_deleted)
|
||||
self.assertTrue(asset.is_deleted)
|
||||
self.assertIsNone(conv.purged_at)
|
||||
trash_ids = {c["id"] for c in self.client.get("/api/ai/image-conversations/trash/?mode=image").json()["results"]}
|
||||
self.assertIn(str(conv.id), trash_ids)
|
||||
asset_trash_ids = {a["id"] for a in self.client.get("/api/assets/trash/").json()["results"]}
|
||||
self.assertNotIn(str(asset.id), asset_trash_ids)
|
||||
|
||||
restore = self.client.post(f"/api/ai/image-conversations/{conv.id}/restore/")
|
||||
self.assertEqual(restore.status_code, 200)
|
||||
conv.refresh_from_db()
|
||||
task.refresh_from_db()
|
||||
asset.refresh_from_db()
|
||||
self.assertFalse(conv.is_deleted)
|
||||
self.assertFalse(task.is_deleted)
|
||||
self.assertFalse(asset.is_deleted)
|
||||
self.assertIsNone(conv.purged_at)
|
||||
live_ids = {c["id"] for c in self.client.get("/api/ai/image-conversations/?mode=image").json()["results"]}
|
||||
self.assertIn(str(conv.id), live_ids)
|
||||
|
||||
self.client.delete(f"/api/ai/image-conversations/{conv.id}/")
|
||||
purge = self.client.delete(f"/api/ai/image-conversations/{conv.id}/purge/")
|
||||
self.assertEqual(purge.status_code, 204)
|
||||
conv.refresh_from_db()
|
||||
task.refresh_from_db()
|
||||
asset.refresh_from_db()
|
||||
self.assertTrue(conv.is_deleted)
|
||||
self.assertTrue(task.is_deleted)
|
||||
self.assertTrue(asset.is_deleted)
|
||||
self.assertIsNotNone(conv.purged_at)
|
||||
self.assertIsNotNone(task.purged_at)
|
||||
self.assertIsNotNone(asset.purged_at)
|
||||
self.assertTrue(ImageConversation.objects.filter(id=conv.id).exists())
|
||||
self.assertTrue(AITask.objects.filter(id=task.id).exists())
|
||||
self.assertTrue(Asset.objects.filter(id=asset.id).exists())
|
||||
self.assertTrue(AssetFile.objects.filter(id=asset_file.id).exists())
|
||||
trash_ids = {c["id"] for c in self.client.get("/api/ai/image-conversations/trash/?mode=image").json()["results"]}
|
||||
self.assertNotIn(str(conv.id), trash_ids)
|
||||
|
||||
def test_direct_asset_delete_hides_image_but_keeps_conversation(self):
|
||||
conv = ImageConversation.objects.create(
|
||||
team=self.team,
|
||||
created_by=self.user,
|
||||
title="资产独立删除",
|
||||
mode=ImageConversation.Mode.IMAGE,
|
||||
)
|
||||
provider = ModelProvider.objects.create(name="asset-delete-provider", display_name="Asset Delete Provider")
|
||||
model = ModelConfig.objects.create(
|
||||
provider=provider,
|
||||
name="asset-delete-model",
|
||||
display_name="Asset Delete Model",
|
||||
capability=ModelConfig.Capability.IMAGE,
|
||||
)
|
||||
task = AITask.objects.create(
|
||||
team=self.team,
|
||||
created_by=self.user,
|
||||
conversation=conv,
|
||||
task_type=AITask.Type.PRODUCT_IMAGE,
|
||||
status=AITask.Status.SUCCEEDED,
|
||||
model_config=model,
|
||||
idempotency_key="direct-asset-delete-image",
|
||||
)
|
||||
asset = Asset.objects.create(
|
||||
team=self.team,
|
||||
created_by=self.user,
|
||||
name="自由创作图片",
|
||||
asset_type=Asset.Type.IMAGE,
|
||||
source=Asset.Source.AI_GENERATED,
|
||||
category=Asset.Category.FREE_CREATE,
|
||||
origin_task=task,
|
||||
)
|
||||
|
||||
delete = self.client.delete(f"/api/assets/{asset.id}/")
|
||||
self.assertEqual(delete.status_code, 204)
|
||||
conv.refresh_from_db()
|
||||
task.refresh_from_db()
|
||||
self.assertFalse(conv.is_deleted)
|
||||
self.assertFalse(task.is_deleted)
|
||||
|
||||
conversation_ids = {
|
||||
c["id"] for c in self.client.get("/api/ai/image-conversations/?mode=image").json()["results"]
|
||||
}
|
||||
self.assertIn(str(conv.id), conversation_ids)
|
||||
deleted_tasks = self.client.get(f"/api/ai/image-conversations/{conv.id}/tasks/").json()["tasks"]
|
||||
self.assertEqual(deleted_tasks[0]["assets"], [])
|
||||
|
||||
restore = self.client.post(f"/api/assets/{asset.id}/restore/")
|
||||
self.assertEqual(restore.status_code, 200)
|
||||
restored_tasks = self.client.get(f"/api/ai/image-conversations/{conv.id}/tasks/").json()["tasks"]
|
||||
self.assertEqual([a["id"] for a in restored_tasks[0]["assets"]], [str(asset.id)])
|
||||
|
||||
def test_team_isolation(self):
|
||||
other = User.objects.create_user(username="other", password="pass")
|
||||
other_team = Team.objects.create(name="Other", owner=other)
|
||||
|
||||
Reference in New Issue
Block a user