"""Tests for board sharing endpoints.""" from datetime import datetime, timedelta import pytest from fastapi import status def test_create_share_link_view_only(client, auth_headers, test_board): """Test creating a view-only share link.""" response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["permission_level"] == "view-only" assert data["board_id"] == str(test_board.id) assert data["token"] is not None assert len(data["token"]) == 64 assert data["is_revoked"] == False # noqa: E712 assert data["access_count"] == 0 def test_create_share_link_view_comment(client, auth_headers, test_board): """Test creating a view-comment share link.""" response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-comment"}, headers=auth_headers, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["permission_level"] == "view-comment" def test_create_share_link_with_expiration(client, auth_headers, test_board): """Test creating a share link with expiration.""" expires_at = (datetime.utcnow() + timedelta(days=7)).isoformat() response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only", "expires_at": expires_at}, headers=auth_headers, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["expires_at"] is not None def test_create_share_link_invalid_permission(client, auth_headers, test_board): """Test creating share link with invalid permission level.""" response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "invalid-permission"}, headers=auth_headers, ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY def test_create_share_link_unauthorized(client, test_board): """Test creating share link without authentication.""" response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, ) assert response.status_code == status.HTTP_403_FORBIDDEN def test_create_share_link_not_owner(client, other_auth_headers, test_board): """Test creating share link for board user doesn't own.""" response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=other_auth_headers, ) assert response.status_code == status.HTTP_404_NOT_FOUND def test_list_share_links(client, auth_headers, test_board): """Test listing all share links for a board.""" # Create multiple share links client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-comment"}, headers=auth_headers, ) response = client.get( f"/api/boards/{test_board.id}/share-links", headers=auth_headers, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data) >= 2 assert all("token" in link for link in data) def test_list_share_links_unauthorized(client, test_board): """Test listing share links without authentication.""" response = client.get( f"/api/boards/{test_board.id}/share-links", ) assert response.status_code == status.HTTP_403_FORBIDDEN def test_revoke_share_link(client, auth_headers, test_board): """Test revoking a share link.""" # Create a share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) link_id = create_response.json()["id"] # Revoke it response = client.delete( f"/api/boards/{test_board.id}/share-links/{link_id}", headers=auth_headers, ) assert response.status_code == status.HTTP_204_NO_CONTENT # Verify it's revoked by listing list_response = client.get( f"/api/boards/{test_board.id}/share-links", headers=auth_headers, ) revoked_link = next((link for link in list_response.json() if link["id"] == link_id), None) assert revoked_link is not None assert revoked_link["is_revoked"] == True # noqa: E712 def test_revoke_share_link_not_found(client, auth_headers, test_board): """Test revoking non-existent share link.""" import uuid fake_id = uuid.uuid4() response = client.delete( f"/api/boards/{test_board.id}/share-links/{fake_id}", headers=auth_headers, ) assert response.status_code == status.HTTP_404_NOT_FOUND def test_access_shared_board(client, auth_headers, test_board): """Test accessing a board via share link.""" # Create share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) token = create_response.json()["token"] # Access shared board (no auth required) response = client.get(f"/api/shared/{token}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["id"] == str(test_board.id) assert data["title"] == test_board.title def test_access_shared_board_invalid_token(client): """Test accessing board with invalid token.""" response = client.get("/api/shared/invalid-token-12345") assert response.status_code == status.HTTP_403_FORBIDDEN def test_access_shared_board_revoked_token(client, auth_headers, test_board): """Test accessing board with revoked token.""" # Create and revoke share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) data = create_response.json() token = data["token"] link_id = data["id"] client.delete( f"/api/boards/{test_board.id}/share-links/{link_id}", headers=auth_headers, ) # Try to access with revoked token response = client.get(f"/api/shared/{token}") assert response.status_code == status.HTTP_403_FORBIDDEN def test_create_comment_on_shared_board(client, auth_headers, test_board): """Test creating a comment via share link with view-comment permission.""" # Create view-comment share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-comment"}, headers=auth_headers, ) token = create_response.json()["token"] # Create comment (no auth required, just token) comment_data = { "author_name": "Test Viewer", "content": "This is a test comment", "position": {"x": 100, "y": 200}, } response = client.post(f"/api/shared/{token}/comments", json=comment_data) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["author_name"] == "Test Viewer" assert data["content"] == "This is a test comment" assert data["position"]["x"] == 100 def test_create_comment_view_only_permission_denied(client, auth_headers, test_board): """Test creating comment with view-only permission fails.""" # Create view-only share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) token = create_response.json()["token"] # Try to create comment (should fail) comment_data = { "author_name": "Test Viewer", "content": "This should fail", } response = client.post(f"/api/shared/{token}/comments", json=comment_data) assert response.status_code == status.HTTP_403_FORBIDDEN def test_list_comments_on_shared_board(client, auth_headers, test_board): """Test listing comments via share link.""" # Create view-comment share link create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-comment"}, headers=auth_headers, ) token = create_response.json()["token"] # Create a comment client.post( f"/api/shared/{token}/comments", json={"author_name": "Viewer 1", "content": "Comment 1"}, ) # List comments response = client.get(f"/api/shared/{token}/comments") assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data) >= 1 assert data[0]["content"] == "Comment 1" def test_list_board_comments_as_owner(client, auth_headers, test_board): """Test board owner listing all comments.""" # Create share link and comment create_response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-comment"}, headers=auth_headers, ) token = create_response.json()["token"] client.post( f"/api/shared/{token}/comments", json={"author_name": "Viewer", "content": "Test comment"}, ) # Owner lists comments response = client.get( f"/api/boards/{test_board.id}/comments", headers=auth_headers, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data) >= 1 def test_token_uniqueness(client, auth_headers, test_board): """Test that generated tokens are unique.""" tokens = set() for _ in range(10): response = client.post( f"/api/boards/{test_board.id}/share-links", json={"permission_level": "view-only"}, headers=auth_headers, ) token = response.json()["token"] tokens.add(token) # All tokens should be unique assert len(tokens) == 10