import pytest from app import db from app.models import Board, Card, List @pytest.mark.integration class TestCardRoutes: """Test Card API routes""" def test_create_card_success(self, client, db_session, regular_user, auth_headers): """Test creating a new card in a list""" 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.commit() response = client.post( f"/api/lists/{lst.id}/cards", headers=auth_headers, json={"name": "New Card", "description": "Card description", "pos": 0}, ) assert response.status_code == 201 data = response.get_json() assert data["name"] == "New Card" assert data["description"] == "Card description" assert data["list_id"] == lst.id assert data["board_id"] == board.id def test_create_card_missing_name( self, client, db_session, regular_user, auth_headers ): """Test creating a card without name""" 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.commit() response = client.post( f"/api/lists/{lst.id}/cards", headers=auth_headers, json={"description": "Test"}, ) assert response.status_code == 400 def test_create_card_unauthorized(self, client, db_session, regular_user): """Test creating a card without authentication""" 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.commit() response = client.post( f"/api/lists/{lst.id}/cards", json={"name": "New Card"}, ) assert response.status_code == 401 def test_get_card_success(self, client, db_session, regular_user, auth_headers): """Test getting a single card with full details""" 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/cards/{card.id}", headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert data["id"] == card.id assert data["name"] == "Test Card" assert "labels" in data assert "checklists" in data assert "comments" in data def test_get_card_not_found(self, client, db_session, auth_headers): """Test getting a non-existent card""" response = client.get("/api/cards/99999", headers=auth_headers) assert response.status_code == 404 def test_update_card_success(self, client, db_session, regular_user, auth_headers): """Test updating a card""" 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) db_session.add(lst1) db_session.flush() lst2 = List(name="Done", board_id=board.id, pos=1) db_session.add(lst2) db_session.flush() card = Card(name="Original", board_id=board.id, list_id=lst1.id, pos=0) db_session.add(card) db_session.commit() response = client.put( f"/api/cards/{card.id}", headers=auth_headers, json={"name": "Updated Name", "list_id": lst2.id, "pos": 1}, ) assert response.status_code == 200 data = response.get_json() assert data["name"] == "Updated Name" assert data["list_id"] == lst2.id assert data["pos"] == 1 def test_update_card_not_found(self, client, db_session, auth_headers): """Test updating a non-existent card""" response = client.put( "/api/cards/99999", headers=auth_headers, json={"name": "Updated"}, ) assert response.status_code == 404 def test_delete_card_success(self, client, db_session, regular_user, auth_headers): """Test deleting a card""" 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="To Delete", board_id=board.id, list_id=lst.id, pos=0) db_session.add(card) db_session.commit() response = client.delete(f"/api/cards/{card.id}", headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert "message" in data # Verify card is deleted deleted_card = db.session.get(Card, card.id) assert deleted_card is None def test_delete_card_not_found(self, client, db_session, auth_headers): """Test deleting a non-existent card""" response = client.delete("/api/cards/99999", headers=auth_headers) assert response.status_code == 404 def test_update_card_position_within_same_list(self, client, db_session, regular_user, auth_headers): """Test updating card position within the same list reorders other 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() # Create 3 cards in sequential positions card1 = Card(name="Card 1", board_id=board.id, list_id=lst.id, pos=0) card2 = Card(name="Card 2", board_id=board.id, list_id=lst.id, pos=1) card3 = Card(name="Card 3", board_id=board.id, list_id=lst.id, pos=2) db_session.add(card1) db_session.add(card2) db_session.add(card3) db_session.commit() # Move card3 from position 2 to position 0 (top) response = client.put( f"/api/cards/{card3.id}", headers=auth_headers, json={"name": "Card 3", "pos": 0}, ) assert response.status_code == 200 # Verify all cards have unique, sequential positions updated_cards = Card.query.filter_by(list_id=lst.id).order_by(Card.pos).all() assert len(updated_cards) == 3 assert updated_cards[0].id == card3.id assert updated_cards[0].pos == 0.0 assert updated_cards[1].id == card1.id assert updated_cards[1].pos == 1.0 assert updated_cards[2].id == card2.id assert updated_cards[2].pos == 2.0 def test_update_card_position_between_lists(self, client, db_session, regular_user, auth_headers): """Test moving card between lists reorders both lists""" board = Board(name="Test Board", user_id=regular_user.id) db_session.add(board) db_session.flush() list1 = List(name="To Do", board_id=board.id, pos=0) list2 = List(name="Done", board_id=board.id, pos=1) db_session.add(list1) db_session.add(list2) db_session.flush() # Create cards in both lists card1 = Card(name="Card 1", board_id=board.id, list_id=list1.id, pos=0) card2 = Card(name="Card 2", board_id=board.id, list_id=list1.id, pos=1) card3 = Card(name="Card 3", board_id=board.id, list_id=list2.id, pos=0) db_session.add(card1) db_session.add(card2) db_session.add(card3) db_session.commit() # Move card1 from list1 to list2 at position 0 response = client.put( f"/api/cards/{card1.id}", headers=auth_headers, json={"name": "Card 1", "list_id": list2.id, "pos": 0}, ) assert response.status_code == 200 # Verify list1 now has only card2 at position 0 list1_cards = Card.query.filter_by(list_id=list1.id).order_by(Card.pos).all() assert len(list1_cards) == 1 assert list1_cards[0].id == card2.id assert list1_cards[0].pos == 0.0 # Verify list2 now has card1 at position 0 and card3 at position 1 list2_cards = Card.query.filter_by(list_id=list2.id).order_by(Card.pos).all() assert len(list2_cards) == 2 assert list2_cards[0].id == card1.id assert list2_cards[0].pos == 0.0 assert list2_cards[1].id == card3.id assert list2_cards[1].pos == 1.0 def test_update_card_position_no_change(self, client, db_session, regular_user, auth_headers): """Test updating card with same position doesn't reorder others""" 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="Card 1", board_id=board.id, list_id=lst.id, pos=0) card2 = Card(name="Card 2", board_id=board.id, list_id=lst.id, pos=1) db_session.add(card1) db_session.add(card2) db_session.commit() original_pos1 = card1.pos original_pos2 = card2.pos # Update card2 but keep same position response = client.put( f"/api/cards/{card2.id}", headers=auth_headers, json={"name": "Updated Card 2", "pos": original_pos2}, ) assert response.status_code == 200 # Verify positions unchanged updated_card1 = db.session.get(Card, card1.id) updated_card2 = db.session.get(Card, card2.id) assert updated_card1.pos == original_pos1 assert updated_card2.pos == original_pos2 def test_create_card_with_position(self, client, db_session, regular_user, auth_headers): """Test creating card with specific position reorders existing 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() # Create existing cards card1 = Card(name="Card 1", board_id=board.id, list_id=lst.id, pos=0) card2 = Card(name="Card 2", board_id=board.id, list_id=lst.id, pos=1) db_session.add(card1) db_session.add(card2) db_session.commit() # Create new card at position 0 (should push others down) response = client.post( f"/api/lists/{lst.id}/cards", headers=auth_headers, json={"name": "New Card", "pos": 0}, ) assert response.status_code == 201 # Note: create_card endpoint doesn't use CardPositionService yet # This test documents current behavior - positions may not be unique after creation # The reordering happens when cards are moved, not when created all_cards = Card.query.filter_by(list_id=lst.id).order_by(Card.pos).all() assert len(all_cards) == 3