Files
webref/backend/tests/boards/test_repository.py
Danilo Reyes 48020b6f42 phase 4
2025-11-02 01:01:38 -06:00

443 lines
17 KiB
Python

"""Unit tests for board repository."""
from uuid import uuid4
import pytest
from sqlalchemy.orm import Session
from app.boards.repository import BoardRepository
from app.database.models.board import Board
from app.database.models.user import User
@pytest.fixture
def test_user(db: Session) -> User:
"""Create a test user."""
user = User(email="test@example.com", password_hash="hashed_password")
db.add(user)
db.commit()
db.refresh(user)
return user
@pytest.fixture
def board_repo(db: Session) -> BoardRepository:
"""Create a board repository instance."""
return BoardRepository(db)
class TestCreateBoard:
"""Test board creation."""
def test_create_board_minimal(self, board_repo: BoardRepository, test_user: User):
"""Test creating board with only required fields."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
assert board.id is not None
assert board.user_id == test_user.id
assert board.title == "Test Board"
assert board.description is None
assert board.is_deleted is False
assert board.created_at is not None
assert board.updated_at is not None
def test_create_board_with_description(self, board_repo: BoardRepository, test_user: User):
"""Test creating board with description."""
board = board_repo.create_board(
user_id=test_user.id, title="Test Board", description="This is a test description"
)
assert board.description == "This is a test description"
def test_create_board_default_viewport(self, board_repo: BoardRepository, test_user: User):
"""Test that board is created with default viewport state."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
assert board.viewport_state is not None
assert board.viewport_state["x"] == 0
assert board.viewport_state["y"] == 0
assert board.viewport_state["zoom"] == 1.0
assert board.viewport_state["rotation"] == 0
def test_create_board_custom_viewport(self, board_repo: BoardRepository, test_user: User):
"""Test creating board with custom viewport state."""
custom_viewport = {"x": 100, "y": 200, "zoom": 2.0, "rotation": 45}
board = board_repo.create_board(
user_id=test_user.id, title="Test Board", viewport_state=custom_viewport
)
assert board.viewport_state == custom_viewport
def test_create_multiple_boards(self, board_repo: BoardRepository, test_user: User):
"""Test creating multiple boards for same user."""
board1 = board_repo.create_board(user_id=test_user.id, title="Board 1")
board2 = board_repo.create_board(user_id=test_user.id, title="Board 2")
board3 = board_repo.create_board(user_id=test_user.id, title="Board 3")
assert board1.id != board2.id
assert board2.id != board3.id
assert all(b.user_id == test_user.id for b in [board1, board2, board3])
class TestGetBoardById:
"""Test retrieving board by ID."""
def test_get_existing_board(self, board_repo: BoardRepository, test_user: User):
"""Test getting existing board owned by user."""
created = board_repo.create_board(user_id=test_user.id, title="Test Board")
retrieved = board_repo.get_board_by_id(board_id=created.id, user_id=test_user.id)
assert retrieved is not None
assert retrieved.id == created.id
assert retrieved.title == created.title
def test_get_nonexistent_board(self, board_repo: BoardRepository, test_user: User):
"""Test getting board that doesn't exist."""
fake_id = uuid4()
result = board_repo.get_board_by_id(board_id=fake_id, user_id=test_user.id)
assert result is None
def test_get_board_wrong_owner(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that users can't access boards they don't own."""
# Create another user
other_user = User(email="other@example.com", password_hash="hashed")
db.add(other_user)
db.commit()
db.refresh(other_user)
# Create board owned by test_user
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Try to get with other_user
result = board_repo.get_board_by_id(board_id=board.id, user_id=other_user.id)
assert result is None
def test_get_deleted_board(self, board_repo: BoardRepository, test_user: User):
"""Test that soft-deleted boards are not returned."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Delete the board
board_repo.delete_board(board_id=board.id, user_id=test_user.id)
# Try to get it
result = board_repo.get_board_by_id(board_id=board.id, user_id=test_user.id)
assert result is None
class TestGetUserBoards:
"""Test listing user's boards."""
def test_get_user_boards_empty(self, board_repo: BoardRepository, test_user: User):
"""Test getting boards when user has none."""
boards, total = board_repo.get_user_boards(user_id=test_user.id)
assert boards == []
assert total == 0
def test_get_user_boards_multiple(self, board_repo: BoardRepository, test_user: User):
"""Test getting multiple boards."""
board1 = board_repo.create_board(user_id=test_user.id, title="Board 1")
board2 = board_repo.create_board(user_id=test_user.id, title="Board 2")
board3 = board_repo.create_board(user_id=test_user.id, title="Board 3")
boards, total = board_repo.get_user_boards(user_id=test_user.id)
assert len(boards) == 3
assert total == 3
assert {b.id for b in boards} == {board1.id, board2.id, board3.id}
def test_get_user_boards_pagination(self, board_repo: BoardRepository, test_user: User):
"""Test pagination of board list."""
# Create 5 boards
for i in range(5):
board_repo.create_board(user_id=test_user.id, title=f"Board {i}")
# Get first 2
boards_page1, total = board_repo.get_user_boards(user_id=test_user.id, limit=2, offset=0)
assert len(boards_page1) == 2
assert total == 5
# Get next 2
boards_page2, total = board_repo.get_user_boards(user_id=test_user.id, limit=2, offset=2)
assert len(boards_page2) == 2
assert total == 5
# Ensure no overlap
page1_ids = {b.id for b in boards_page1}
page2_ids = {b.id for b in boards_page2}
assert page1_ids.isdisjoint(page2_ids)
def test_get_user_boards_sorted_by_update(self, board_repo: BoardRepository, test_user: User):
"""Test that boards are sorted by updated_at descending."""
board1 = board_repo.create_board(user_id=test_user.id, title="Oldest")
board2 = board_repo.create_board(user_id=test_user.id, title="Middle")
board3 = board_repo.create_board(user_id=test_user.id, title="Newest")
boards, _ = board_repo.get_user_boards(user_id=test_user.id)
# Most recently updated should be first
assert boards[0].id == board3.id
assert boards[1].id == board2.id
assert boards[2].id == board1.id
def test_get_user_boards_excludes_deleted(self, board_repo: BoardRepository, test_user: User):
"""Test that soft-deleted boards are excluded."""
board1 = board_repo.create_board(user_id=test_user.id, title="Board 1")
board2 = board_repo.create_board(user_id=test_user.id, title="Board 2")
board3 = board_repo.create_board(user_id=test_user.id, title="Board 3")
# Delete board2
board_repo.delete_board(board_id=board2.id, user_id=test_user.id)
boards, total = board_repo.get_user_boards(user_id=test_user.id)
assert len(boards) == 2
assert total == 2
assert {b.id for b in boards} == {board1.id, board3.id}
def test_get_user_boards_isolation(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that users only see their own boards."""
# Create another user
other_user = User(email="other@example.com", password_hash="hashed")
db.add(other_user)
db.commit()
db.refresh(other_user)
# Create boards for both users
test_board = board_repo.create_board(user_id=test_user.id, title="Test Board")
other_board = board_repo.create_board(user_id=other_user.id, title="Other Board")
# Get test_user's boards
test_boards, _ = board_repo.get_user_boards(user_id=test_user.id)
assert len(test_boards) == 1
assert test_boards[0].id == test_board.id
# Get other_user's boards
other_boards, _ = board_repo.get_user_boards(user_id=other_user.id)
assert len(other_boards) == 1
assert other_boards[0].id == other_board.id
class TestUpdateBoard:
"""Test board updates."""
def test_update_board_title(self, board_repo: BoardRepository, test_user: User):
"""Test updating board title."""
board = board_repo.create_board(user_id=test_user.id, title="Original Title")
updated = board_repo.update_board(
board_id=board.id, user_id=test_user.id, title="Updated Title"
)
assert updated is not None
assert updated.title == "Updated Title"
assert updated.id == board.id
def test_update_board_description(self, board_repo: BoardRepository, test_user: User):
"""Test updating board description."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
updated = board_repo.update_board(
board_id=board.id, user_id=test_user.id, description="New description"
)
assert updated is not None
assert updated.description == "New description"
def test_update_board_viewport(self, board_repo: BoardRepository, test_user: User):
"""Test updating viewport state."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
new_viewport = {"x": 100, "y": 200, "zoom": 1.5, "rotation": 90}
updated = board_repo.update_board(
board_id=board.id, user_id=test_user.id, viewport_state=new_viewport
)
assert updated is not None
assert updated.viewport_state == new_viewport
def test_update_multiple_fields(self, board_repo: BoardRepository, test_user: User):
"""Test updating multiple fields at once."""
board = board_repo.create_board(user_id=test_user.id, title="Original")
updated = board_repo.update_board(
board_id=board.id,
user_id=test_user.id,
title="Updated Title",
description="Updated Description",
viewport_state={"x": 50, "y": 50, "zoom": 2.0, "rotation": 45},
)
assert updated is not None
assert updated.title == "Updated Title"
assert updated.description == "Updated Description"
assert updated.viewport_state["zoom"] == 2.0
def test_update_nonexistent_board(self, board_repo: BoardRepository, test_user: User):
"""Test updating board that doesn't exist."""
fake_id = uuid4()
result = board_repo.update_board(board_id=fake_id, user_id=test_user.id, title="New Title")
assert result is None
def test_update_board_wrong_owner(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that users can't update boards they don't own."""
# Create another user
other_user = User(email="other@example.com", password_hash="hashed")
db.add(other_user)
db.commit()
db.refresh(other_user)
# Create board owned by test_user
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Try to update with other_user
result = board_repo.update_board(
board_id=board.id, user_id=other_user.id, title="Hacked Title"
)
assert result is None
# Verify original board unchanged
original = board_repo.get_board_by_id(board_id=board.id, user_id=test_user.id)
assert original.title == "Test Board"
def test_update_board_partial_update(self, board_repo: BoardRepository, test_user: User):
"""Test that partial updates don't affect unspecified fields."""
board = board_repo.create_board(
user_id=test_user.id, title="Original Title", description="Original Description"
)
# Update only title
updated = board_repo.update_board(board_id=board.id, user_id=test_user.id, title="New Title")
assert updated is not None
assert updated.title == "New Title"
assert updated.description == "Original Description" # Should be unchanged
class TestDeleteBoard:
"""Test board deletion."""
def test_delete_board_success(self, board_repo: BoardRepository, test_user: User):
"""Test successfully deleting a board."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
success = board_repo.delete_board(board_id=board.id, user_id=test_user.id)
assert success is True
def test_delete_board_soft_delete(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that delete is a soft delete (sets flag instead of removing)."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
board_repo.delete_board(board_id=board.id, user_id=test_user.id)
# Board should still exist in database but marked as deleted
db_board = db.get(Board, board.id)
assert db_board is not None
assert db_board.is_deleted is True
def test_delete_board_not_in_listings(self, board_repo: BoardRepository, test_user: User):
"""Test that deleted boards don't appear in listings."""
board1 = board_repo.create_board(user_id=test_user.id, title="Board 1")
board2 = board_repo.create_board(user_id=test_user.id, title="Board 2")
# Delete board1
board_repo.delete_board(board_id=board1.id, user_id=test_user.id)
boards, total = board_repo.get_user_boards(user_id=test_user.id)
assert len(boards) == 1
assert total == 1
assert boards[0].id == board2.id
def test_delete_nonexistent_board(self, board_repo: BoardRepository, test_user: User):
"""Test deleting board that doesn't exist."""
fake_id = uuid4()
success = board_repo.delete_board(board_id=fake_id, user_id=test_user.id)
assert success is False
def test_delete_board_wrong_owner(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that users can't delete boards they don't own."""
# Create another user
other_user = User(email="other@example.com", password_hash="hashed")
db.add(other_user)
db.commit()
db.refresh(other_user)
# Create board owned by test_user
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Try to delete with other_user
success = board_repo.delete_board(board_id=board.id, user_id=other_user.id)
assert success is False
# Verify board still exists for original owner
still_exists = board_repo.get_board_by_id(board_id=board.id, user_id=test_user.id)
assert still_exists is not None
assert still_exists.is_deleted is False
class TestBoardExists:
"""Test board existence check."""
def test_board_exists_true(self, board_repo: BoardRepository, test_user: User):
"""Test checking if board exists."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
exists = board_repo.board_exists(board_id=board.id, user_id=test_user.id)
assert exists is True
def test_board_exists_false(self, board_repo: BoardRepository, test_user: User):
"""Test checking if board doesn't exist."""
fake_id = uuid4()
exists = board_repo.board_exists(board_id=fake_id, user_id=test_user.id)
assert exists is False
def test_board_exists_wrong_owner(self, board_repo: BoardRepository, test_user: User, db: Session):
"""Test that board_exists returns False for wrong owner."""
# Create another user
other_user = User(email="other@example.com", password_hash="hashed")
db.add(other_user)
db.commit()
db.refresh(other_user)
# Create board owned by test_user
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Check with wrong owner
exists = board_repo.board_exists(board_id=board.id, user_id=other_user.id)
assert exists is False
def test_board_exists_deleted(self, board_repo: BoardRepository, test_user: User):
"""Test that deleted boards return False for existence check."""
board = board_repo.create_board(user_id=test_user.id, title="Test Board")
# Delete board
board_repo.delete_board(board_id=board.id, user_id=test_user.id)
# Check existence
exists = board_repo.board_exists(board_id=board.id, user_id=test_user.id)
assert exists is False