This commit is contained in:
Danilo Reyes
2025-11-02 01:01:38 -06:00
parent b0e22af242
commit 48020b6f42
8 changed files with 2473 additions and 17 deletions

View File

@@ -0,0 +1,442 @@
"""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