303 lines
10 KiB
Python
303 lines
10 KiB
Python
"""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
|
|
|