493 lines
18 KiB
Python
493 lines
18 KiB
Python
import pytest
|
|
|
|
from app import db
|
|
from app.models import (Board, Card, CardLabel, CheckItem, Checklist, Comment,
|
|
Label, List)
|
|
|
|
|
|
@pytest.mark.integration
|
|
class TestBoardRoutes:
|
|
"""Test Board API routes"""
|
|
|
|
def test_get_boards_success(self, client, db_session, regular_user, auth_headers):
|
|
"""Test getting all boards for current user"""
|
|
# Create a board for the user
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.commit()
|
|
|
|
response = client.get("/api/boards", headers=auth_headers)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert len(data) == 1
|
|
assert data[0]["name"] == "Test Board"
|
|
|
|
def test_get_boards_unauthorized(self, client, db_session):
|
|
"""Test getting boards without authentication"""
|
|
response = client.get("/api/boards")
|
|
|
|
assert response.status_code == 401
|
|
|
|
def test_get_board_success(self, client, db_session, regular_user, auth_headers):
|
|
"""Test getting a single board with details"""
|
|
# Create a board with lists and cards
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card = Card(name="Test Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
db_session.add(card)
|
|
db_session.commit()
|
|
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data["id"] == board.id
|
|
assert data["name"] == "Test Board"
|
|
assert len(data["lists"]) == 1
|
|
assert data["lists"][0]["name"] == "To Do"
|
|
assert len(data["lists"][0]["cards"]) == 1
|
|
|
|
def test_get_board_not_found(self, client, db_session, auth_headers):
|
|
"""Test getting a non-existent board"""
|
|
response = client.get("/api/boards/99999", headers=auth_headers)
|
|
|
|
assert response.status_code == 404
|
|
|
|
def test_get_board_access_denied(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test getting another user's board"""
|
|
# Create a board for user 1
|
|
board = Board(name="User 1 Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.commit()
|
|
|
|
# Try to access with user 2's token (assuming auth_headers is for user 1)
|
|
# This test assumes auth_headers is for a different user
|
|
# In real scenario, you'd need another user fixture
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
|
|
# Should succeed since we're using same user's token
|
|
assert response.status_code == 200
|
|
|
|
def test_create_board_success(self, client, db_session, auth_headers):
|
|
"""Test creating a new board"""
|
|
response = client.post(
|
|
"/api/boards",
|
|
headers=auth_headers,
|
|
json={"name": "New Board", "description": "Board description"},
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
data = response.get_json()
|
|
assert data["name"] == "New Board"
|
|
assert data["description"] == "Board description"
|
|
assert "id" in data
|
|
|
|
def test_create_board_missing_name(self, client, db_session, auth_headers):
|
|
"""Test creating a board without name"""
|
|
response = client.post(
|
|
"/api/boards", headers=auth_headers, json={"description": "Test"}
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert "validation_error" in data
|
|
|
|
def test_create_board_unauthorized(self, client, db_session):
|
|
"""Test creating a board without authentication"""
|
|
response = client.post("/api/boards", json={"name": "New Board"})
|
|
|
|
assert response.status_code == 401
|
|
|
|
def test_update_board_success(self, client, db_session, regular_user, auth_headers):
|
|
"""Test updating a board"""
|
|
board = Board(name="Original Name", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.commit()
|
|
|
|
response = client.put(
|
|
f"/api/boards/{board.id}",
|
|
headers=auth_headers,
|
|
json={"name": "Updated Name", "description": "New description"},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data["name"] == "Updated Name"
|
|
assert data["description"] == "New description"
|
|
|
|
def test_update_board_not_found(self, client, db_session, auth_headers):
|
|
"""Test updating a non-existent board"""
|
|
response = client.put(
|
|
"/api/boards/99999",
|
|
headers=auth_headers,
|
|
json={"name": "Updated"},
|
|
)
|
|
|
|
assert response.status_code == 404
|
|
|
|
def test_delete_board_success(self, client, db_session, regular_user, auth_headers):
|
|
"""Test deleting a board"""
|
|
board = Board(name="To Delete", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.commit()
|
|
|
|
response = client.delete(f"/api/boards/{board.id}", headers=auth_headers)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert "message" in data
|
|
|
|
# Verify board is deleted
|
|
deleted_board = db.session.get(Board, board.id)
|
|
assert deleted_board is not None
|
|
assert deleted_board.status == "deleted"
|
|
|
|
def test_delete_board_not_found(self, client, db_session, auth_headers):
|
|
"""Test deleting a non-existent board"""
|
|
response = client.delete("/api/boards/99999", headers=auth_headers)
|
|
|
|
assert response.status_code == 404
|
|
|
|
def test_delete_board_unauthorized(self, client, db_session, regular_user):
|
|
"""Test deleting a board without authentication"""
|
|
board = Board(name="Test", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.commit()
|
|
|
|
response = client.delete(f"/api/boards/{board.id}")
|
|
|
|
assert response.status_code == 401
|
|
|
|
def test_get_board_filters_deleted_cards(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted cards are filtered out from board response"""
|
|
# Create board with list and 2 cards
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card1 = Card(name="Active Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
card2 = Card(name="Deleted Card", board_id=board.id, list_id=lst.id, pos=1)
|
|
db_session.add_all([card1, card2])
|
|
db_session.commit()
|
|
|
|
# Soft delete card2
|
|
card2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 card (the active one)
|
|
assert len(data["lists"][0]["cards"]) == 1
|
|
assert data["lists"][0]["cards"][0]["name"] == "Active Card"
|
|
|
|
def test_get_board_filters_deleted_lists(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted lists are filtered out from board response"""
|
|
# Create board with 2 lists
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst1 = List(name="To Do", board_id=board.id, pos=0)
|
|
lst2 = List(name="Deleted List", board_id=board.id, pos=1)
|
|
db_session.add_all([lst1, lst2])
|
|
db_session.commit()
|
|
|
|
# Soft delete lst2
|
|
lst2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 list (the active one)
|
|
assert len(data["lists"]) == 1
|
|
assert data["lists"][0]["name"] == "To Do"
|
|
|
|
def test_get_board_filters_deleted_comments(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted comments are filtered out from card response"""
|
|
# Create board, list, card and 2 comments
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card = Card(name="Test Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
db_session.add(card)
|
|
db_session.flush()
|
|
|
|
comment1 = Comment(
|
|
text="Active comment", card_id=card.id, user_id=regular_user.id
|
|
)
|
|
comment2 = Comment(
|
|
text="Deleted comment", card_id=card.id, user_id=regular_user.id
|
|
)
|
|
db_session.add_all([comment1, comment2])
|
|
db_session.commit()
|
|
|
|
# Soft delete comment2
|
|
comment2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 comment (the active one)
|
|
assert len(data["lists"][0]["cards"][0]["comments"]) == 1
|
|
assert data["lists"][0]["cards"][0]["comments"][0]["text"] == "Active comment"
|
|
|
|
def test_get_board_filters_deleted_checklists(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted checklists are filtered out from card response"""
|
|
# Create board, list, card and 2 checklists
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card = Card(name="Test Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
db_session.add(card)
|
|
db_session.flush()
|
|
|
|
checklist1 = Checklist(
|
|
name="Active Checklist", board_id=board.id, card_id=card.id, pos=0
|
|
)
|
|
checklist2 = Checklist(
|
|
name="Deleted Checklist", board_id=board.id, card_id=card.id, pos=1
|
|
)
|
|
db_session.add_all([checklist1, checklist2])
|
|
db_session.commit()
|
|
|
|
# Soft delete checklist2
|
|
checklist2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 checklist (the active one)
|
|
assert len(data["lists"][0]["cards"][0]["checklists"]) == 1
|
|
assert (
|
|
data["lists"][0]["cards"][0]["checklists"][0]["name"] == "Active Checklist"
|
|
)
|
|
|
|
def test_get_board_filters_deleted_check_items(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted check items are
|
|
filtered out from checklist response"""
|
|
# Create board, list, card, checklist and 2 check items
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card = Card(name="Test Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
db_session.add(card)
|
|
db_session.flush()
|
|
|
|
checklist = Checklist(name="Tasks", board_id=board.id, card_id=card.id, pos=0)
|
|
db_session.add(checklist)
|
|
db_session.flush()
|
|
|
|
item1 = CheckItem(name="Active Task", checklist_id=checklist.id, pos=0)
|
|
item2 = CheckItem(name="Deleted Task", checklist_id=checklist.id, pos=1)
|
|
db_session.add_all([item1, item2])
|
|
db_session.commit()
|
|
|
|
# Soft delete item2
|
|
item2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 check item (the active one)
|
|
assert len(data["lists"][0]["cards"][0]["checklists"][0]["items"]) == 1
|
|
assert (
|
|
data["lists"][0]["cards"][0]["checklists"][0]["items"][0]["name"]
|
|
== "Active Task"
|
|
)
|
|
|
|
def test_get_board_filters_deleted_card_labels(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test that soft-deleted card-label associations are filtered out"""
|
|
# Create board, list, card, label and 2 card-label associations
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
lst = List(name="To Do", board_id=board.id, pos=0)
|
|
db_session.add(lst)
|
|
db_session.flush()
|
|
|
|
card = Card(name="Test Card", board_id=board.id, list_id=lst.id, pos=0)
|
|
db_session.add(card)
|
|
db_session.flush()
|
|
|
|
label1 = Label(name="Urgent", color="red", board_id=board.id)
|
|
label2 = Label(name="Important", color="yellow", board_id=board.id)
|
|
db_session.add_all([label1, label2])
|
|
db_session.flush()
|
|
|
|
card_label1 = CardLabel(card_id=card.id, label_id=label1.id)
|
|
card_label2 = CardLabel(card_id=card.id, label_id=label2.id)
|
|
db_session.add_all([card_label1, card_label2])
|
|
db_session.commit()
|
|
|
|
# Soft delete card_label2
|
|
card_label2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
# Should only have 1 label (the active one)
|
|
assert len(data["lists"][0]["cards"][0]["labels"]) == 1
|
|
assert data["lists"][0]["cards"][0]["labels"][0]["name"] == "Urgent"
|
|
|
|
def test_get_board_comprehensive_soft_delete_filtering(
|
|
self, client, db_session, regular_user, auth_headers
|
|
):
|
|
"""Test comprehensive soft delete filtering across all nested resources"""
|
|
# Create board with all types of nested resources, some deleted
|
|
board = Board(name="Test Board", user_id=regular_user.id)
|
|
db_session.add(board)
|
|
db_session.flush()
|
|
|
|
# Active list
|
|
lst1 = List(name="Active List", board_id=board.id, pos=0)
|
|
db_session.add(lst1)
|
|
db_session.flush()
|
|
|
|
card1 = Card(name="Active Card 1", board_id=board.id, list_id=lst1.id, pos=0)
|
|
card2 = Card(name="Deleted Card", board_id=board.id, list_id=lst1.id, pos=1)
|
|
db_session.add_all([card1, card2])
|
|
db_session.flush()
|
|
|
|
# Active comment
|
|
comment1 = Comment(
|
|
text="Active comment", card_id=card1.id, user_id=regular_user.id
|
|
)
|
|
# Deleted comment
|
|
comment2 = Comment(
|
|
text="Deleted comment", card_id=card1.id, user_id=regular_user.id
|
|
)
|
|
db_session.add_all([comment1, comment2])
|
|
db_session.flush()
|
|
|
|
# Active checklist
|
|
checklist1 = Checklist(
|
|
name="Active Checklist", board_id=board.id, card_id=card1.id, pos=0
|
|
)
|
|
# Deleted checklist
|
|
checklist2 = Checklist(
|
|
name="Deleted Checklist", board_id=board.id, card_id=card1.id, pos=1
|
|
)
|
|
db_session.add_all([checklist1, checklist2])
|
|
db_session.flush()
|
|
|
|
# Active check item
|
|
item1 = CheckItem(name="Active Task", checklist_id=checklist1.id, pos=0)
|
|
# Deleted check item
|
|
item2 = CheckItem(name="Deleted Task", checklist_id=checklist1.id, pos=1)
|
|
db_session.add_all([item1, item2])
|
|
db_session.flush()
|
|
|
|
# Labels
|
|
label1 = Label(name="Active Label", color="red", board_id=board.id)
|
|
label2 = Label(name="Deleted Label", color="yellow", board_id=board.id)
|
|
db_session.add_all([label1, label2])
|
|
db_session.flush()
|
|
|
|
# Active card-label
|
|
card_label1 = CardLabel(card_id=card1.id, label_id=label1.id)
|
|
# Deleted card-label
|
|
card_label2 = CardLabel(card_id=card1.id, label_id=label2.id)
|
|
db_session.add_all([card_label1, card_label2])
|
|
db_session.commit()
|
|
|
|
# Soft delete some resources
|
|
card2.soft_delete()
|
|
comment2.soft_delete()
|
|
checklist2.soft_delete()
|
|
item2.soft_delete()
|
|
card_label2.soft_delete()
|
|
db_session.commit()
|
|
|
|
# Get board
|
|
response = client.get(f"/api/boards/{board.id}", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
data = response.get_json()
|
|
|
|
# Verify lists: should have 1 list (we didn't delete the list)
|
|
assert len(data["lists"]) == 1
|
|
assert data["lists"][0]["name"] == "Active List"
|
|
|
|
# Verify cards: should have 1 card (card2 is deleted)
|
|
assert len(data["lists"][0]["cards"]) == 1
|
|
assert data["lists"][0]["cards"][0]["name"] == "Active Card 1"
|
|
|
|
# Verify comments: should have 1 comment (comment2 is deleted)
|
|
assert len(data["lists"][0]["cards"][0]["comments"]) == 1
|
|
assert data["lists"][0]["cards"][0]["comments"][0]["text"] == "Active comment"
|
|
|
|
# Verify checklists: should have 1 checklist (checklist2 is deleted)
|
|
assert len(data["lists"][0]["cards"][0]["checklists"]) == 1
|
|
assert (
|
|
data["lists"][0]["cards"][0]["checklists"][0]["name"] == "Active Checklist"
|
|
)
|
|
|
|
# Verify check items: should have 1 item (item2 is deleted)
|
|
assert len(data["lists"][0]["cards"][0]["checklists"][0]["items"]) == 1
|
|
assert (
|
|
data["lists"][0]["cards"][0]["checklists"][0]["items"][0]["name"]
|
|
== "Active Task"
|
|
)
|
|
|
|
# Verify labels: should have 1 label (card_label2 is deleted)
|
|
assert len(data["lists"][0]["cards"][0]["labels"]) == 1
|
|
assert data["lists"][0]["cards"][0]["labels"][0]["name"] == "Active Label"
|