SurfSmart/test/tests_backend/test_projects.py
2025-06-09 17:53:19 +08:00

209 lines
9.9 KiB
Python

import json
import uuid
import pytest
from backend.app import create_app
@pytest.fixture
def client():
app = create_app()
app.config["TESTING"] = True
with app.test_client() as client:
yield client
@pytest.fixture
def auth_headers(client):
"""
Registers a test user with unique username and returns auth headers.
"""
unique_suffix = str(uuid.uuid4())[:8]
username = f"projtest_{unique_suffix}"
email = f"{username}@example.com"
payload = {
"username": username,
"email": email,
"password": "Password123"
}
response = client.post("/api/register", data=json.dumps(payload), content_type="application/json")
assert response.status_code == 201, f"Registration failed: {response.data.decode()}"
data = json.loads(response.data)
token = data["token"]
return {"Authorization": f"Bearer {token}"}
def test_create_project(client, auth_headers):
payload = {
"name": "Test Project",
"topic": "Testing",
"description": "A project for testing purposes."
}
response = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert response.status_code == 201, f"Create project failed: {response.data.decode()}"
data = json.loads(response.data)
assert "project_id" in data
assert "passkey" in data
def test_get_projects(client, auth_headers):
# Create two projects.
payload1 = {"name": "Test Project One", "description": "First test project."}
payload2 = {"name": "Test Project Two", "description": "Second test project."}
response1 = client.post("/api/projects", data=json.dumps(payload1),
content_type="application/json", headers=auth_headers)
response2 = client.post("/api/projects", data=json.dumps(payload2),
content_type="application/json", headers=auth_headers)
assert response1.status_code == 201, f"Project one creation failed: {response1.data.decode()}"
assert response2.status_code == 201, f"Project two creation failed: {response2.data.decode()}"
response = client.get("/api/projects", headers=auth_headers)
assert response.status_code == 200, f"Get projects failed: {response.data.decode()}"
data = json.loads(response.data)
assert "projects" in data
assert isinstance(data["projects"], list)
assert len(data["projects"]) >= 2
def test_get_project_detail(client, auth_headers):
payload = {"name": "Detail Project1111", "description": "A project for detail testing."}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Creation failed: {create_resp.data.decode()}"
project_id = json.loads(create_resp.data)["project_id"]
response = client.get(f"/api/projects/{project_id}", headers=auth_headers)
assert response.status_code == 200, f"Get detail failed: {response.data.decode()}"
data = json.loads(response.data)
assert data.get("name") == "Detail Project1111"
assert "updatedAt" in data
def test_update_project(client, auth_headers):
payload = {"name": "Update Project", "description": "Initial description."}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Creation failed: {create_resp.data.decode()}"
project_id = json.loads(create_resp.data)["project_id"]
update_payload = {"description": "Updated description.", "topic": "Updated Topic"}
update_resp = client.put(f"/api/projects/{project_id}", data=json.dumps(update_payload),
content_type="application/json", headers=auth_headers)
assert update_resp.status_code == 200, f"Update failed: {update_resp.data.decode()}"
detail_resp = client.get(f"/api/projects/{project_id}", headers=auth_headers)
detail_data = json.loads(detail_resp.data)
assert detail_data.get("description") == "Updated description."
assert detail_data.get("topic") == "Updated Topic"
def test_delete_project(client, auth_headers):
payload = {"name": "Delete Project", "description": "Project to be deleted."}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Creation failed: {create_resp.data.decode()}"
project_id = json.loads(create_resp.data)["project_id"]
delete_resp = client.delete(f"/api/projects/{project_id}", headers=auth_headers)
assert delete_resp.status_code == 200, f"Deletion failed: {delete_resp.data.decode()}"
detail_resp = client.get(f"/api/projects/{project_id}", headers=auth_headers)
assert detail_resp.status_code == 404, "Deleted project still accessible"
def test_get_project_summaries(client, auth_headers):
# Create a new project to ensure at least one exists.
payload = {"name": "Summary Project", "description": "Project for summary test."}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Project creation failed: {create_resp.data.decode()}"
response = client.get("/api/projects/summary", headers=auth_headers)
assert response.status_code == 200, f"Summary fetch failed: {response.data.decode()}"
data = json.loads(response.data)
assert "projects" in data
# Each project summary should include project_id, name, and updatedAt.
for proj in data["projects"]:
assert "project_id" in proj
assert "name" in proj
assert "updatedAt" in proj
# ------------------------------------------------------------------------------
# New Tests
# ------------------------------------------------------------------------------
def test_get_project_info(client, auth_headers):
"""
Tests the /api/projects/<project_id>/info endpoint,
which returns name, topic, description, keywords, summary.
"""
payload = {
"name": "Info Project",
"topic": "InfoTopic",
"description": "Project with an info endpoint."
}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Project creation failed: {create_resp.data.decode()}"
project_id = json.loads(create_resp.data)["project_id"]
info_resp = client.get(f"/api/projects/{project_id}/info", headers=auth_headers)
assert info_resp.status_code == 200, f"Get project info failed: {info_resp.data.decode()}"
info_data = json.loads(info_resp.data)
assert info_data.get("name") == "Info Project"
assert info_data.get("topic") == "InfoTopic"
assert info_data.get("description") == "Project with an info endpoint."
assert isinstance(info_data.get("keywords", []), list)
assert "summary" in info_data
def test_recalc_project_keywords(client, auth_headers):
"""
Tests the /api/projects/<project_id>/recalc_keywords endpoint.
It should gather all URL keywords, sum them, sort by top 20, and update the project.
"""
# 1) Create a project
payload = {"name": "Keyword Recalc Project", "description": "Test for recalc keywords."}
create_resp = client.post("/api/projects", data=json.dumps(payload),
content_type="application/json", headers=auth_headers)
assert create_resp.status_code == 201, f"Project creation failed: {create_resp.data.decode()}"
project_id = json.loads(create_resp.data)["project_id"]
# 2) Create multiple URLs with keywords
url1_keywords = [{"word": "alpha", "percentage": 50}, {"word": "beta", "percentage": 10}]
url2_keywords = [{"word": "alpha", "percentage": 20}, {"word": "gamma", "percentage": 15}]
url_create_payload1 = {"url": "https://url1.com", "keywords": url1_keywords}
url_create_payload2 = {"url": "https://url2.com", "keywords": url2_keywords}
resp1 = client.post(f"/api/projects/{project_id}/urls", data=json.dumps(url_create_payload1),
content_type="application/json", headers=auth_headers)
resp2 = client.post(f"/api/projects/{project_id}/urls", data=json.dumps(url_create_payload2),
content_type="application/json", headers=auth_headers)
assert resp1.status_code == 201, f"URL1 creation failed: {resp1.data.decode()}"
assert resp2.status_code == 201, f"URL2 creation failed: {resp2.data.decode()}"
# 3) Recalc keywords
recalc_resp = client.put(f"/api/projects/{project_id}/recalc_keywords", headers=auth_headers)
assert recalc_resp.status_code == 200, f"Keyword recalc failed: {recalc_resp.data.decode()}"
recalc_data = json.loads(recalc_resp.data)
assert "keywords" in recalc_data
top_keywords = recalc_data["keywords"]
# Check that alpha is presumably 70, gamma=15, beta=10 => alpha, gamma, beta
assert len(top_keywords) <= 20
# alpha => 70 (50 + 20)
alpha_kw = next((k for k in top_keywords if k["word"] == "alpha"), None)
assert alpha_kw is not None, "Expected alpha in combined keywords"
assert alpha_kw["percentage"] == 70.0
# 4) Fetch the project detail or info to confirm stored result
info_resp = client.get(f"/api/projects/{project_id}/info", headers=auth_headers)
assert info_resp.status_code == 200, f"Get project info after recalc failed: {info_resp.data.decode()}"
info_data = json.loads(info_resp.data)
# The project now has 'keywords' -> top 20
project_keywords = info_data.get("keywords", [])
alpha_in_project = next((k for k in project_keywords if k["word"] == "alpha"), None)
assert alpha_in_project is not None, "Project keywords missing alpha"
assert alpha_in_project["percentage"] == 70.0