All checks were successful
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 4s
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 11s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 8s
CI/CD Pipeline / VM Test - performance (push) Successful in 8s
CI/CD Pipeline / Nix Flake Check (push) Successful in 38s
CI/CD Pipeline / CI Summary (push) Successful in 0s
CI/CD Pipeline / Frontend Linting (push) Successful in 17s
222 lines
6.0 KiB
Python
222 lines
6.0 KiB
Python
"""Integration tests for image deletion endpoints."""
|
|
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
from httpx import AsyncClient
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database.models.board import Board
|
|
from app.database.models.board_image import BoardImage
|
|
from app.database.models.image import Image
|
|
from app.database.models.user import User
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_image_from_board(client: AsyncClient, test_user: User, db: AsyncSession):
|
|
"""Test removing image from board (not deleting)."""
|
|
# Create board and image
|
|
board = Board(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
title="Test Board",
|
|
viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0},
|
|
)
|
|
db.add(board)
|
|
|
|
image = Image(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
filename="test.jpg",
|
|
storage_path=f"{test_user.id}/test.jpg",
|
|
file_size=1024,
|
|
mime_type="image/jpeg",
|
|
width=800,
|
|
height=600,
|
|
metadata={"format": "jpeg", "checksum": "abc123"},
|
|
reference_count=1,
|
|
)
|
|
db.add(image)
|
|
|
|
board_image = BoardImage(
|
|
id=uuid4(),
|
|
board_id=board.id,
|
|
image_id=image.id,
|
|
position={"x": 100, "y": 100},
|
|
transformations={
|
|
"scale": 1.0,
|
|
"rotation": 0,
|
|
"opacity": 1.0,
|
|
"flipped_h": False,
|
|
"flipped_v": False,
|
|
"greyscale": False,
|
|
},
|
|
z_order=0,
|
|
)
|
|
db.add(board_image)
|
|
await db.commit()
|
|
|
|
# Remove from board
|
|
response = await client.delete(f"/api/images/boards/{board.id}/images/{image.id}")
|
|
|
|
assert response.status_code == 204
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_image_not_on_board(client: AsyncClient, test_user: User, db: AsyncSession):
|
|
"""Test removing image that's not on the board."""
|
|
board = Board(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
title="Test Board",
|
|
viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0},
|
|
)
|
|
db.add(board)
|
|
|
|
image = Image(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
filename="test.jpg",
|
|
storage_path=f"{test_user.id}/test.jpg",
|
|
file_size=1024,
|
|
mime_type="image/jpeg",
|
|
width=800,
|
|
height=600,
|
|
metadata={"format": "jpeg", "checksum": "abc123"},
|
|
)
|
|
db.add(image)
|
|
await db.commit()
|
|
|
|
# Try to remove (image not on board)
|
|
response = await client.delete(f"/api/images/boards/{board.id}/images/{image.id}")
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_image_unauthorized(client: AsyncClient, test_user: User, db: AsyncSession):
|
|
"""Test removing image from board not owned by user."""
|
|
# Create another user
|
|
other_user = User(id=uuid4(), email="other@example.com", password_hash="hashed")
|
|
db.add(other_user)
|
|
|
|
# Create board owned by other user
|
|
board = Board(
|
|
id=uuid4(),
|
|
user_id=other_user.id,
|
|
title="Other Board",
|
|
viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0},
|
|
)
|
|
db.add(board)
|
|
|
|
image = Image(
|
|
id=uuid4(),
|
|
user_id=other_user.id,
|
|
filename="test.jpg",
|
|
storage_path=f"{other_user.id}/test.jpg",
|
|
file_size=1024,
|
|
mime_type="image/jpeg",
|
|
width=800,
|
|
height=600,
|
|
metadata={"format": "jpeg", "checksum": "abc123"},
|
|
)
|
|
db.add(image)
|
|
|
|
board_image = BoardImage(
|
|
id=uuid4(),
|
|
board_id=board.id,
|
|
image_id=image.id,
|
|
position={"x": 100, "y": 100},
|
|
transformations={
|
|
"scale": 1.0,
|
|
"rotation": 0,
|
|
"opacity": 1.0,
|
|
"flipped_h": False,
|
|
"flipped_v": False,
|
|
"greyscale": False,
|
|
},
|
|
z_order=0,
|
|
)
|
|
db.add(board_image)
|
|
await db.commit()
|
|
|
|
# Try to remove as current user
|
|
response = await client.delete(f"/api/images/boards/{board.id}/images/{image.id}")
|
|
|
|
assert response.status_code == 403
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_permanent_delete_image(client: AsyncClient, test_user: User, db: AsyncSession):
|
|
"""Test permanently deleting image from library."""
|
|
image = Image(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
filename="test.jpg",
|
|
storage_path=f"{test_user.id}/test.jpg",
|
|
file_size=1024,
|
|
mime_type="image/jpeg",
|
|
width=800,
|
|
height=600,
|
|
metadata={"format": "jpeg", "checksum": "abc123"},
|
|
reference_count=0, # Not used on any boards
|
|
)
|
|
db.add(image)
|
|
await db.commit()
|
|
|
|
# Delete permanently
|
|
response = await client.delete(f"/api/images/{image.id}")
|
|
|
|
assert response.status_code == 204
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_cannot_delete_image_in_use(client: AsyncClient, test_user: User, db: AsyncSession):
|
|
"""Test that images in use cannot be permanently deleted."""
|
|
board = Board(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
title="Test Board",
|
|
viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0},
|
|
)
|
|
db.add(board)
|
|
|
|
image = Image(
|
|
id=uuid4(),
|
|
user_id=test_user.id,
|
|
filename="test.jpg",
|
|
storage_path=f"{test_user.id}/test.jpg",
|
|
file_size=1024,
|
|
mime_type="image/jpeg",
|
|
width=800,
|
|
height=600,
|
|
metadata={"format": "jpeg", "checksum": "abc123"},
|
|
reference_count=1, # Used on a board
|
|
)
|
|
db.add(image)
|
|
|
|
board_image = BoardImage(
|
|
id=uuid4(),
|
|
board_id=board.id,
|
|
image_id=image.id,
|
|
position={"x": 100, "y": 100},
|
|
transformations={
|
|
"scale": 1.0,
|
|
"rotation": 0,
|
|
"opacity": 1.0,
|
|
"flipped_h": False,
|
|
"flipped_v": False,
|
|
"greyscale": False,
|
|
},
|
|
z_order=0,
|
|
)
|
|
db.add(board_image)
|
|
await db.commit()
|
|
|
|
# Try to delete
|
|
response = await client.delete(f"/api/images/{image.id}")
|
|
|
|
assert response.status_code == 400
|
|
assert "still used" in response.json()["detail"].lower()
|
|
|