feat: implement Instagram clone SocialPhoto API
- 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
This commit is contained in:
292
tests/test_posts.py
Normal file
292
tests/test_posts.py
Normal file
@@ -0,0 +1,292 @@
|
||||
"""Tests for post routes."""
|
||||
import io
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_create_post_success(client: TestClient, two_users: dict):
|
||||
"""Test successful post creation."""
|
||||
token = two_users["user1_token"]
|
||||
|
||||
# Create a fake image
|
||||
fake_image = io.BytesIO(b"fake image content")
|
||||
fake_image.name = "test.jpg"
|
||||
|
||||
response = client.post(
|
||||
"/posts",
|
||||
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
||||
data={"caption": "My first post!"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["caption"] == "My first post!"
|
||||
assert data["user_id"] is not None
|
||||
assert data["likes_count"] == 0
|
||||
assert data["dislikes_count"] == 0
|
||||
assert data["comments_count"] == 0
|
||||
|
||||
|
||||
def test_create_post_unauthorized(client: TestClient):
|
||||
"""Test creating post without authentication."""
|
||||
fake_image = io.BytesIO(b"fake image content")
|
||||
fake_image.name = "test.jpg"
|
||||
|
||||
response = client.post(
|
||||
"/posts",
|
||||
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
||||
data={"caption": "My first post!"},
|
||||
)
|
||||
|
||||
assert response.status_code == 401 # No credentials
|
||||
|
||||
|
||||
def test_create_post_invalid_file_type(client: TestClient, two_users: dict):
|
||||
"""Test creating post with invalid file type."""
|
||||
token = two_users["user1_token"]
|
||||
|
||||
fake_file = io.BytesIO(b"fake content")
|
||||
fake_file.name = "test.txt"
|
||||
|
||||
response = client.post(
|
||||
"/posts",
|
||||
files={"image": ("test.txt", fake_file, "text/plain")},
|
||||
data={"caption": "Invalid file"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "File type not allowed" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_get_posts_empty(client: TestClient):
|
||||
"""Test getting posts when database is empty."""
|
||||
response = client.get("/posts")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == []
|
||||
|
||||
|
||||
def test_get_posts(client: TestClient, two_users: dict):
|
||||
"""Test getting posts."""
|
||||
token = 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": "Test post"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
|
||||
response = client.get("/posts")
|
||||
assert response.status_code == 200
|
||||
posts = response.json()
|
||||
assert len(posts) == 1
|
||||
assert posts[0]["caption"] == "Test post"
|
||||
|
||||
|
||||
def test_get_single_post(client: TestClient, two_users: dict):
|
||||
"""Test getting a single post."""
|
||||
token = two_users["user1_token"]
|
||||
|
||||
# Create 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": "Single post test"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["caption"] == "Single post test"
|
||||
|
||||
|
||||
def test_get_nonexistent_post(client: TestClient):
|
||||
"""Test getting a post that doesn't exist."""
|
||||
response = client.get("/posts/9999")
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_delete_post_by_owner(client: TestClient, two_users: dict):
|
||||
"""Test deleting own post."""
|
||||
token = two_users["user1_token"]
|
||||
|
||||
# Create 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": "To be deleted"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
# Delete the post
|
||||
response = client.delete(
|
||||
f"/posts/{post_id}",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert response.status_code == 204
|
||||
|
||||
# Verify it's deleted
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_delete_post_by_non_owner(client: TestClient, two_users: dict):
|
||||
"""Test trying to delete someone else's post."""
|
||||
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"
|
||||
create_response = client.post(
|
||||
"/posts",
|
||||
files={"image": ("test.jpg", fake_image, "image/jpeg")},
|
||||
data={"caption": "User 1's post"},
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
# User 2 tries to delete it
|
||||
response = client.delete(
|
||||
f"/posts/{post_id}",
|
||||
headers={"Authorization": f"Bearer {token2}"},
|
||||
)
|
||||
assert response.status_code == 403
|
||||
assert "You can only delete your own posts" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_like_post(client: TestClient, two_users: dict):
|
||||
"""Test liking 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": "Like test"},
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
# Like the post
|
||||
response = client.post(
|
||||
f"/posts/{post_id}/like",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
assert response.json()["message"] == "Post liked"
|
||||
|
||||
# Verify like count
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
assert response.json()["likes_count"] == 1
|
||||
|
||||
|
||||
def test_unlike_post(client: TestClient, two_users: dict):
|
||||
"""Test removing a like from a post."""
|
||||
token1 = two_users["user1_token"]
|
||||
|
||||
# User 1 creates a post and likes it
|
||||
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": "Unlike test"},
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
client.post(
|
||||
f"/posts/{post_id}/like",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
|
||||
# Unlike the post
|
||||
response = client.delete(
|
||||
f"/posts/{post_id}/like",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["message"] == "Like removed"
|
||||
|
||||
# Verify like count
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
assert response.json()["likes_count"] == 0
|
||||
|
||||
|
||||
def test_dislike_post(client: TestClient, two_users: dict):
|
||||
"""Test disliking 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": "Dislike test"},
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
# Dislike the post
|
||||
response = client.post(
|
||||
f"/posts/{post_id}/dislike",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
assert response.json()["message"] == "Post disliked"
|
||||
|
||||
# Verify dislike count
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
assert response.json()["dislikes_count"] == 1
|
||||
|
||||
|
||||
def test_like_and_dislike_mutually_exclusive(client: TestClient, two_users: dict):
|
||||
"""Test that like and dislike are mutually exclusive."""
|
||||
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": "Mutual exclusion test"},
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
post_id = create_response.json()["id"]
|
||||
|
||||
# Like the post
|
||||
client.post(
|
||||
f"/posts/{post_id}/like",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
|
||||
# Dislike the post (should replace like)
|
||||
response = client.post(
|
||||
f"/posts/{post_id}/dislike",
|
||||
headers={"Authorization": f"Bearer {token1}"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
# Verify: no likes, 1 dislike
|
||||
response = client.get(f"/posts/{post_id}")
|
||||
data = response.json()
|
||||
assert data["likes_count"] == 0
|
||||
assert data["dislikes_count"] == 1
|
||||
Reference in New Issue
Block a user