- FastAPI backend with SQLite database - JWT authentication (register, login) - User profiles with follow/unfollow - Posts with image upload and likes/dislikes - Comments with likes - Global and personalized feed - Comprehensive pytest test suite (37 tests) TASK-ID: 758f4029-702
359 lines
11 KiB
Python
359 lines
11 KiB
Python
"""Tests for social features: users, follows, comments."""
|
|
import io
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
def test_get_user_profile(client: TestClient, two_users: dict):
|
|
"""Test getting a user profile."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# Get user ID from token (simplified - in real app would decode)
|
|
# Let's create a post first to get the user
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Test post"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
# For now, just test that user endpoint exists and works
|
|
# The actual user ID would need to be extracted from the token
|
|
response = client.get("/users/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["username"] == "user1"
|
|
|
|
|
|
def test_get_user_stats(client: TestClient, two_users: dict):
|
|
"""Test getting user statistics."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# Create a post
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Stats test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
# User 2 follows User 1
|
|
client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
|
|
response = client.get("/users/1/stats")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["posts_count"] == 1
|
|
assert data["followers_count"] == 1
|
|
assert data["following_count"] == 0
|
|
|
|
|
|
def test_follow_user(client: TestClient, two_users: dict):
|
|
"""Test following a user."""
|
|
# User 2 follows User 1
|
|
response = client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
assert response.status_code == 201
|
|
assert response.json()["message"] == "Successfully followed user"
|
|
|
|
|
|
def test_follow_self(client: TestClient, two_users: dict):
|
|
"""Test that you cannot follow yourself."""
|
|
response = client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user1_token']}"},
|
|
)
|
|
assert response.status_code == 400
|
|
assert "You cannot follow yourself" in response.json()["detail"]
|
|
|
|
|
|
def test_follow_already_following(client: TestClient, two_users: dict):
|
|
"""Test following someone you already follow."""
|
|
# User 2 follows User 1
|
|
client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
|
|
# Try to follow again
|
|
response = client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
assert response.status_code == 400
|
|
assert "You already follow this user" in response.json()["detail"]
|
|
|
|
|
|
def test_unfollow_user(client: TestClient, two_users: dict):
|
|
"""Test unfollowing a user."""
|
|
token2 = two_users["user2_token"]
|
|
|
|
# User 2 follows User 1
|
|
client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {token2}"},
|
|
)
|
|
|
|
# User 2 unfollows User 1
|
|
response = client.delete(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {token2}"},
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.json()["message"] == "Successfully unfollowed user"
|
|
|
|
|
|
def test_unfollow_not_following(client: TestClient, two_users: dict):
|
|
"""Test unfollowing someone you don't follow."""
|
|
response = client.delete(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
assert response.status_code == 404
|
|
assert "You are not following this user" in response.json()["detail"]
|
|
|
|
|
|
def test_get_user_posts(client: TestClient, two_users: dict):
|
|
"""Test getting posts by a specific user."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# Create two posts as User 1
|
|
for i in range(2):
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": f"User 1 post {i}"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
response = client.get("/users/1/posts")
|
|
assert response.status_code == 200
|
|
posts = response.json()
|
|
assert len(posts) == 2
|
|
|
|
|
|
def test_create_comment(client: TestClient, two_users: dict):
|
|
"""Test creating a comment."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# User 1 creates a post
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
create_response = client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Comment test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
post_id = create_response.json()["id"]
|
|
|
|
# User 2 comments on the post
|
|
response = client.post(
|
|
f"/posts/{post_id}/comments",
|
|
json={"content": "Great post!"},
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
assert response.status_code == 201
|
|
data = response.json()
|
|
assert data["content"] == "Great post!"
|
|
assert data["username"] == "user2"
|
|
|
|
|
|
def test_get_post_comments(client: TestClient, two_users: dict):
|
|
"""Test getting comments for a post."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# User 1 creates a post
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
create_response = client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Comments test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
post_id = create_response.json()["id"]
|
|
|
|
# Add a comment
|
|
client.post(
|
|
f"/posts/{post_id}/comments",
|
|
json={"content": "First comment!"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
response = client.get(f"/posts/{post_id}/comments")
|
|
assert response.status_code == 200
|
|
comments = response.json()
|
|
assert len(comments) == 1
|
|
assert comments[0]["content"] == "First comment!"
|
|
|
|
|
|
def test_delete_comment_by_owner(client: TestClient, two_users: dict):
|
|
"""Test deleting own comment."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# User 1 creates a post and comments
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
create_response = client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Delete comment test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
post_id = create_response.json()["id"]
|
|
|
|
comment_response = client.post(
|
|
f"/posts/{post_id}/comments",
|
|
json={"content": "To be deleted"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
comment_id = comment_response.json()["id"]
|
|
|
|
# Delete the comment
|
|
response = client.delete(
|
|
f"/comments/{comment_id}",
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
assert response.status_code == 204
|
|
|
|
|
|
def test_delete_comment_by_non_owner(client: TestClient, two_users: dict):
|
|
"""Test trying to delete someone else's comment."""
|
|
token1 = two_users["user1_token"]
|
|
token2 = two_users["user2_token"]
|
|
|
|
# User 1 creates a post and comments
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
create_response = client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Unauthorized delete test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
post_id = create_response.json()["id"]
|
|
|
|
comment_response = client.post(
|
|
f"/posts/{post_id}/comments",
|
|
json={"content": "User 1's comment"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
comment_id = comment_response.json()["id"]
|
|
|
|
# User 2 tries to delete it
|
|
response = client.delete(
|
|
f"/comments/{comment_id}",
|
|
headers={"Authorization": f"Bearer {token2}"},
|
|
)
|
|
assert response.status_code == 403
|
|
assert "You can only delete your own comments" in response.json()["detail"]
|
|
|
|
|
|
def test_like_comment(client: TestClient, two_users: dict):
|
|
"""Test liking a comment."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# User 1 creates a post and comments
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
create_response = client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Comment like test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
post_id = create_response.json()["id"]
|
|
|
|
comment_response = client.post(
|
|
f"/posts/{post_id}/comments",
|
|
json={"content": "Comment to like"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
comment_id = comment_response.json()["id"]
|
|
|
|
# Like the comment
|
|
response = client.post(
|
|
f"/comments/{comment_id}/like",
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
assert response.status_code == 201
|
|
assert response.json()["message"] == "Comment liked"
|
|
|
|
|
|
def test_feed_global(client: TestClient, two_users: dict):
|
|
"""Test global feed."""
|
|
token1 = two_users["user1_token"]
|
|
|
|
# User 1 creates a post
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Global feed test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
response = client.get("/feed/global")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] >= 1
|
|
assert len(data["posts"]) >= 1
|
|
|
|
|
|
def test_feed_followed(client: TestClient, two_users: dict):
|
|
"""Test followed users feed."""
|
|
token1 = two_users["user1_token"]
|
|
token2 = two_users["user2_token"]
|
|
|
|
# User 1 creates a post
|
|
fake_image = io.BytesIO(b"fake image content")
|
|
fake_image.name = "test.jpg"
|
|
client.post(
|
|
"/posts",
|
|
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
|
data={"caption": "Followed feed test"},
|
|
headers={"Authorization": f"Bearer {token1}"},
|
|
)
|
|
|
|
# User 2 follows User 1
|
|
client.post(
|
|
"/users/1/follow",
|
|
headers={"Authorization": f"Bearer {token2}"},
|
|
)
|
|
|
|
# Get followed feed for User 2
|
|
response = client.get(
|
|
"/feed",
|
|
headers={"Authorization": f"Bearer {token2}"},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] >= 1
|
|
assert len(data["posts"]) >= 1
|
|
|
|
|
|
def test_feed_followed_no_follows(client: TestClient, two_users: dict):
|
|
"""Test followed feed when not following anyone."""
|
|
response = client.get(
|
|
"/feed",
|
|
headers={"Authorization": f"Bearer {two_users['user2_token']}"},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] == 0
|
|
assert len(data["posts"]) == 0
|