599 lines
24 KiB
Python
599 lines
24 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from CTFd.cache import clear_ratings
|
|
from CTFd.models import Ratings
|
|
from CTFd.utils import set_config
|
|
from tests.helpers import (
|
|
create_ctfd,
|
|
destroy_ctfd,
|
|
gen_challenge,
|
|
gen_flag,
|
|
gen_rating,
|
|
gen_solve,
|
|
login_as_user,
|
|
register_user,
|
|
)
|
|
|
|
|
|
def test_ratings_public_config():
|
|
"""Test that users can see and leave ratings when configuration is set to public"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
# Create users and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user1_id = 2
|
|
register_user(app, name="user2", email="user2@example.com")
|
|
user2_id = 3
|
|
register_user(app, name="user3", email="user3@example.com")
|
|
user3_id = 4
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
challenge_id = challenge.id
|
|
gen_flag(app.db, challenge_id=challenge_id, content="flag{test}")
|
|
|
|
# Solve the challenge for both users (required to rate)
|
|
gen_solve(
|
|
app.db, user_id=user1_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
gen_solve(
|
|
app.db, user_id=user2_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
gen_solve(
|
|
app.db, user_id=user3_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
|
|
client1 = login_as_user(app, name="user1", password="password")
|
|
client2 = login_as_user(app, name="user2", password="password")
|
|
client3 = login_as_user(app, name="user3", password="password")
|
|
|
|
# Test that users can leave ratings (PUT request)
|
|
rating_data1 = {
|
|
"value": 1,
|
|
"review": "Great challenge! Really enjoyed solving it.",
|
|
}
|
|
r = client1.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data1)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Great challenge! Really enjoyed solving it."
|
|
assert data["data"]["challenge_id"] == challenge_id
|
|
|
|
# Second user can also rate
|
|
rating_data2 = {"value": 1, "review": "Excellent challenge!"}
|
|
r = client2.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data2)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Excellent challenge!"
|
|
|
|
# Second user can also rate
|
|
rating_data3 = {"value": -1, "review": "Hated it!"}
|
|
r = client3.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data3)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == -1
|
|
assert data["data"]["review"] == "Hated it!"
|
|
|
|
# Test that challenge detail includes rating info when public
|
|
r = client1.get(f"/api/v1/challenges/{challenge_id}")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert "ratings" in data["data"]
|
|
assert data["data"]["ratings"]["up"] == 2
|
|
assert data["data"]["ratings"]["down"] == 1
|
|
assert data["data"]["ratings"]["count"] == 3
|
|
assert "rating" in data["data"] # User's own rating
|
|
assert data["data"]["rating"]["value"] == 1
|
|
assert (
|
|
data["data"]["rating"]["review"]
|
|
== "Great challenge! Really enjoyed solving it."
|
|
)
|
|
assert len(Ratings.query.all()) == 3
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_ratings_private_config():
|
|
"""Test that users can only leave ratings but cannot see aggregated ratings when set to private"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "private")
|
|
|
|
# Create users and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user1_id = 2
|
|
register_user(app, name="user2", email="user2@example.com")
|
|
user2_id = 3
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
challenge_id = challenge.id
|
|
gen_flag(app.db, challenge_id=challenge_id, content="flag{test}")
|
|
|
|
# Solve the challenge for both users (required to rate)
|
|
gen_solve(
|
|
app.db, user_id=user1_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
gen_solve(
|
|
app.db, user_id=user2_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
|
|
client1 = login_as_user(app, name="user1", password="password")
|
|
client2 = login_as_user(app, name="user2", password="password")
|
|
admin_client = login_as_user(app, name="admin", password="password")
|
|
|
|
# Test that non-admin users cannot see aggregated ratings (GET request should fail)
|
|
r = client1.get(f"/api/v1/challenges/{challenge_id}/ratings", json=True)
|
|
assert r.status_code == 403 # Forbidden
|
|
|
|
# Test that users can still leave ratings (PUT request)
|
|
rating_data1 = {"value": 1, "review": "Good challenge with private rating"}
|
|
r = client1.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data1)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Good challenge with private rating"
|
|
|
|
# Second user can also rate
|
|
rating_data2 = {"value": 1, "review": "Excellent private challenge!"}
|
|
r = client2.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data2)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Excellent private challenge!"
|
|
|
|
# Test that challenge detail doesn't include aggregated ratings for regular users
|
|
r = client1.get(f"/api/v1/challenges/{challenge_id}")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["ratings"] is None # No aggregated ratings shown
|
|
assert "rating" in data["data"] # But user can see their own rating
|
|
assert data["data"]["rating"]["value"] == 1
|
|
assert data["data"]["rating"]["review"] == "Good challenge with private rating"
|
|
|
|
# Test that admin can still see aggregated ratings
|
|
r = admin_client.get(f"/api/v1/challenges/{challenge_id}/ratings")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["meta"]["summary"]["up"] == 2
|
|
assert data["meta"]["summary"]["down"] == 0
|
|
assert data["meta"]["summary"]["count"] == 2
|
|
|
|
assert len(Ratings.query.all()) == 2
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_ratings_disabled_config():
|
|
"""Test that users cannot see or leave ratings when ratings are disabled"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "disabled")
|
|
|
|
# Create users and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user1_id = 2
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
challenge_id = challenge.id
|
|
gen_flag(app.db, challenge_id=challenge_id, content="flag{test}")
|
|
|
|
# Solve the challenge (required to rate)
|
|
gen_solve(
|
|
app.db, user_id=user1_id, challenge_id=challenge_id, provided="flag{test}"
|
|
)
|
|
|
|
client1 = login_as_user(app, name="user1", password="password")
|
|
admin_client = login_as_user(app, name="admin", password="password")
|
|
|
|
# Test that users cannot see ratings (GET request should fail)
|
|
r = client1.get(f"/api/v1/challenges/{challenge_id}/ratings", json=True)
|
|
assert r.status_code == 403 # Forbidden
|
|
|
|
# Test that users cannot leave ratings (PUT request should fail)
|
|
rating_data = {"value": 2, "review": "Trying to rate when disabled"}
|
|
r = client1.put(f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data)
|
|
assert r.status_code == 403 # Forbidden
|
|
assert len(Ratings.query.all()) == 0
|
|
|
|
# Test that challenge detail doesn't include any rating info
|
|
r = client1.get(f"/api/v1/challenges/{challenge_id}")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["ratings"] is None
|
|
assert data["data"]["rating"] is None
|
|
|
|
# Test that even admin cannot leave ratings when disabled
|
|
r = admin_client.put(
|
|
f"/api/v1/challenges/{challenge_id}/ratings", json=rating_data
|
|
)
|
|
assert r.status_code == 403 # Forbidden
|
|
|
|
# But admin can still see the endpoint (though it will be empty)
|
|
r = admin_client.get(f"/api/v1/challenges/{challenge_id}/ratings", json=True)
|
|
data = r.get_json()
|
|
assert len(data["data"]) == 0
|
|
|
|
assert len(Ratings.query.all()) == 0
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_rating_requires_solve():
|
|
"""Test that users can only rate challenges they have solved"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
# Create a user and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user_id = 2
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
chal_id = challenge.id
|
|
gen_flag(app.db, challenge_id=chal_id, content="flag{test}")
|
|
|
|
client = login_as_user(app, name="user1", password="password")
|
|
|
|
# Try to rate without solving first
|
|
rating_data = {"value": 1, "review": "Great challenge!"}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 403
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert "You must solve this challenge before rating it" in data["errors"][""][0]
|
|
assert len(Ratings.query.all()) == 0
|
|
|
|
# Now solve the challenge
|
|
gen_solve(app.db, user_id=user_id, challenge_id=chal_id, provided="flag{test}")
|
|
|
|
# Now rating should work
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Great challenge!"
|
|
assert len(Ratings.query.all()) == 1
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_rating_validation():
|
|
"""Test rating input validation"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
# Create a user and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user_id = 2
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
chal_id = challenge.id
|
|
gen_flag(app.db, challenge_id=chal_id, content="flag{test}")
|
|
|
|
# Solve the challenge
|
|
gen_solve(app.db, user_id=user_id, challenge_id=chal_id, provided="flag{test}")
|
|
|
|
client = login_as_user(app, name="user1", password="password")
|
|
|
|
# Test missing rating value
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json={})
|
|
assert r.status_code == 400
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert "Rating value is required" in data["errors"]["value"][0]
|
|
|
|
# Test invalid rating value (not -1 or 1)
|
|
rating_data = {"value": 0}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 400
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert "Rating value must be either 1 or -1" in data["errors"]["value"][0]
|
|
|
|
# Test invalid rating value (too high)
|
|
rating_data = {"value": 2}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 400
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert "Rating value must be either 1 or -1" in data["errors"]["value"][0]
|
|
|
|
# Test invalid rating value (non-integer)
|
|
rating_data = {"value": "invalid"}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 400
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert "Rating value must be an integer" in data["errors"]["value"][0]
|
|
|
|
# Test review text too long
|
|
long_review = "x" * 2001 # Exceeds 2000 character limit
|
|
rating_data = {"value": 1, "review": long_review}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 400
|
|
data = r.get_json()
|
|
assert data["success"] is False
|
|
assert (
|
|
"Review text cannot exceed 2000 characters" in data["errors"]["review"][0]
|
|
)
|
|
assert len(Ratings.query.all()) == 0
|
|
|
|
# Test valid rating with review
|
|
rating_data = {
|
|
"value": -1,
|
|
"review": "This is a valid review within the character limit.",
|
|
}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=rating_data)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == -1
|
|
assert (
|
|
data["data"]["review"]
|
|
== "This is a valid review within the character limit."
|
|
)
|
|
assert len(Ratings.query.all()) == 1
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_rating_update():
|
|
"""Test that users can update their existing ratings"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
# Create a user and challenge
|
|
register_user(app, name="user1", email="user1@example.com")
|
|
user_id = 2
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
chal_id = challenge.id
|
|
gen_flag(app.db, challenge_id=challenge.id, content="flag{test}")
|
|
|
|
# Solve the challenge
|
|
gen_solve(app.db, user_id=user_id, challenge_id=chal_id, provided="flag{test}")
|
|
|
|
client = login_as_user(app, name="user1", password="password")
|
|
|
|
# Create initial rating
|
|
initial_rating = {"value": 1, "review": "Initial review"}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=initial_rating)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == 1
|
|
assert data["data"]["review"] == "Initial review"
|
|
initial_id = data["data"]["id"]
|
|
assert len(Ratings.query.all()) == 1
|
|
r = Ratings.query.get(1)
|
|
assert r.value == 1
|
|
assert r.review == "Initial review"
|
|
|
|
# Update the rating
|
|
updated_rating = {
|
|
"value": -1,
|
|
"review": "Updated review after thinking more about it",
|
|
}
|
|
r = client.put(f"/api/v1/challenges/{chal_id}/ratings", json=updated_rating)
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["data"]["value"] == -1
|
|
assert data["data"]["review"] == "Updated review after thinking more about it"
|
|
assert data["data"]["id"] == initial_id # Same rating record, just updated
|
|
assert len(Ratings.query.all()) == 1
|
|
|
|
r = Ratings.query.get(1)
|
|
assert r.value == -1
|
|
assert r.review == "Updated review after thinking more about it"
|
|
|
|
# Verify only one rating exists in database
|
|
ratings = Ratings.query.filter_by(user_id=user_id, challenge_id=chal_id).all()
|
|
assert len(ratings) == 1
|
|
assert ratings[0].value == -1
|
|
assert ratings[0].review == "Updated review after thinking more about it"
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_rating_without_authentication():
|
|
"""Test that unauthenticated users cannot rate challenges"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
gen_flag(app.db, challenge_id=challenge.id, content="flag{test}")
|
|
|
|
# Try to rate without being logged in
|
|
with app.test_client() as client:
|
|
rating_data = {"value": 4, "review": "Great challenge!"}
|
|
r = client.put(
|
|
f"/api/v1/challenges/{challenge.id}/ratings", json=rating_data
|
|
)
|
|
assert r.status_code == 403
|
|
assert len(Ratings.query.all()) == 0
|
|
|
|
destroy_ctfd(app)
|
|
|
|
|
|
def test_ratings_upvote_downvote_count():
|
|
"""Test that upvote and downvote calculations are correct with multiple users and ratings"""
|
|
app = create_ctfd()
|
|
with app.app_context():
|
|
set_config("challenge_ratings", "public")
|
|
|
|
# Define test data for upvote/downvote ratings
|
|
user_ratings = [
|
|
{
|
|
"name": "user1",
|
|
"email": "user1@example.com",
|
|
"value": 1, # upvote
|
|
"review": "Excellent challenge!",
|
|
},
|
|
{
|
|
"name": "user2",
|
|
"email": "user2@example.com",
|
|
"value": -1, # downvote
|
|
"review": "Not great",
|
|
},
|
|
{
|
|
"name": "user3",
|
|
"email": "user3@example.com",
|
|
"value": 1, # upvote
|
|
"review": "Pretty good!",
|
|
},
|
|
]
|
|
|
|
# Expected upvotes/downvotes after each rating submission
|
|
expected_counts = [
|
|
{"up": 1, "down": 0, "count": 1}, # After user1
|
|
{"up": 1, "down": 1, "count": 2}, # After user2
|
|
{"up": 2, "down": 1, "count": 3}, # After user3
|
|
]
|
|
|
|
# Create users and challenge
|
|
users = []
|
|
user_ids = []
|
|
user_id_start = 2
|
|
for rating_data in user_ratings:
|
|
user = register_user(
|
|
app, name=rating_data["name"], email=rating_data["email"]
|
|
)
|
|
users.append(user)
|
|
user_ids.append(user_id_start) # Store ID immediately
|
|
user_id_start += 1
|
|
|
|
# Create admin client for accessing ratings endpoint
|
|
admin_client = login_as_user(app, name="admin", password="password")
|
|
|
|
challenge = gen_challenge(app.db, name="Test Challenge", value=100)
|
|
challenge_id = challenge.id # Store ID immediately
|
|
gen_flag(app.db, challenge_id=challenge_id, content="flag{test}")
|
|
|
|
# Solve the challenge for all users (required to rate)
|
|
for user_id in user_ids:
|
|
gen_solve(
|
|
app.db,
|
|
user_id=user_id,
|
|
challenge_id=challenge_id,
|
|
provided="flag{test}",
|
|
)
|
|
|
|
# Initially no ratings - check admin can see empty ratings endpoint
|
|
r = admin_client.get(f"/api/v1/challenges/{challenge_id}/ratings")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["meta"]["summary"]["up"] == 0
|
|
assert data["meta"]["summary"]["down"] == 0
|
|
assert data["meta"]["summary"]["count"] == 0
|
|
|
|
# Submit ratings progressively and verify calculations using database creation
|
|
for i, rating_data in enumerate(user_ratings):
|
|
# Create rating directly in database using helper
|
|
gen_rating(
|
|
app.db,
|
|
user_id=user_ids[i],
|
|
challenge_id=challenge_id,
|
|
value=rating_data["value"],
|
|
review=rating_data["review"],
|
|
)
|
|
|
|
assert len(Ratings.query.all()) == i + 1
|
|
|
|
# Check admin ratings endpoint has correct upvote/downvote counts
|
|
r = admin_client.get(f"/api/v1/challenges/{challenge_id}/ratings")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
assert data["success"] is True
|
|
assert data["meta"]["summary"]["up"] == expected_counts[i]["up"]
|
|
assert data["meta"]["summary"]["down"] == expected_counts[i]["down"]
|
|
assert data["meta"]["summary"]["count"] == expected_counts[i]["count"]
|
|
|
|
# Verify each user can see their own correct rating in challenge detail
|
|
for rating_data in user_ratings:
|
|
user_client = login_as_user(
|
|
app, name=rating_data["name"], password="password"
|
|
)
|
|
r = user_client.get(f"/api/v1/challenges/{challenge_id}")
|
|
data = r.get_json()
|
|
assert data["data"]["rating"]["value"] == rating_data["value"]
|
|
assert data["data"]["rating"]["review"] == rating_data["review"]
|
|
|
|
# Test rating update - user 1 changes their rating from upvote (1) to downvote (-1)
|
|
# Update rating directly in database
|
|
first_rating = Ratings.query.filter_by(
|
|
user_id=user_ids[0], challenge_id=challenge_id
|
|
).first()
|
|
first_rating.value = -1 # Change from upvote to downvote
|
|
first_rating.review = "Actually, not so great after reconsidering"
|
|
app.db.session.commit()
|
|
clear_ratings()
|
|
|
|
assert len(Ratings.query.all()) == len(
|
|
user_ratings
|
|
) # Still same number of ratings
|
|
|
|
# Update the expected values list for verification
|
|
user_ratings[0]["value"] = -1
|
|
user_ratings[0]["review"] = "Actually, not so great after reconsidering"
|
|
|
|
# Expected counts after update: user1 changed from upvote to downvote
|
|
# So now we have: user1(-1), user2(-1), user3(1) = 1 upvote, 2 downvotes
|
|
expected_updated_counts = {"up": 1, "down": 2, "count": 3}
|
|
|
|
# Check counts after update using admin ratings endpoint
|
|
r = admin_client.get(f"/api/v1/challenges/{challenge_id}/ratings")
|
|
assert r.status_code == 200
|
|
data = r.get_json()
|
|
print(data)
|
|
assert data["success"] is True
|
|
assert data["meta"]["summary"]["up"] == expected_updated_counts["up"]
|
|
assert data["meta"]["summary"]["down"] == expected_updated_counts["down"]
|
|
assert data["meta"]["summary"]["count"] == expected_updated_counts["count"]
|
|
|
|
# Verify updated user sees their new rating in challenge detail
|
|
user1_client = login_as_user(app, name="user1", password="password")
|
|
r = user1_client.get(f"/api/v1/challenges/{challenge_id}")
|
|
data = r.get_json()
|
|
assert data["data"]["rating"]["value"] == -1
|
|
assert (
|
|
data["data"]["rating"]["review"]
|
|
== "Actually, not so great after reconsidering"
|
|
)
|
|
|
|
# Verify all other users still see their original ratings in challenge detail
|
|
for user in user_ratings:
|
|
user_client = login_as_user(
|
|
app, name=user["name"], password="password", raise_for_error=False
|
|
)
|
|
r = user_client.get(f"/api/v1/challenges/{challenge_id}")
|
|
data = r.get_json()
|
|
assert data["data"]["rating"]["value"] == user["value"]
|
|
assert data["data"]["rating"]["review"] == user["review"]
|
|
|
|
# Verify database consistency
|
|
all_ratings = Ratings.query.filter_by(challenge_id=challenge_id).all()
|
|
assert len(all_ratings) == len(user_ratings)
|
|
rating_values = [r.value for r in all_ratings]
|
|
expected_values = sorted([rating["value"] for rating in user_ratings])
|
|
assert sorted(rating_values) == expected_values
|
|
|
|
# Verify counts match database reality
|
|
upvotes = sum(1 for value in rating_values if value == 1)
|
|
downvotes = sum(1 for value in rating_values if value == -1)
|
|
assert upvotes == expected_updated_counts["up"]
|
|
assert downvotes == expected_updated_counts["down"]
|
|
|
|
destroy_ctfd(app)
|