06/16 Phase 6

This commit is contained in:
2026-06-16 13:29:32 -04:00
parent 2e9025fe55
commit 479afde2fa
16 changed files with 527 additions and 8 deletions
+129 -4
View File
@@ -489,6 +489,12 @@ def _phase6(app):
from app.services import admin_users as usvc
from app.services import admin_dashboard as dash
from app.services.settings import get_setting, set_setting
from app.models.report import Report
from app.models.category import Category
from app.models.enums import ListingStatus, ReportReason
from app.services import listings as lsvc
from app.services import reports as rsvc
from app.services import moderation as msvc
with app.app_context():
admin = User(email="admin@example.com", display_name="Admin",
@@ -547,6 +553,95 @@ def _phase6(app):
usvc.set_status(target, UserStatus.active, actor=admin)
db.session.commit()
# --- keyword blocklist: auto-flag on create ---
for_sale = Category.query.filter_by(slug="for-sale").first()
set_setting("keyword_blocklist", ["viagra"])
db.session.commit()
spammy = lsvc.create_listing(target, for_sale, title="Cheap meds",
body="Buy real viagra online cheap",
lang="en", price_cents=500, zip_code="92683",
raw_attributes={"condition": "new"})
assert spammy.status == ListingStatus.flagged
print("keyword blocklist auto-flags on create: ok")
# --- keyword blocklist: auto-flag on update ---
clean = lsvc.create_listing(target, for_sale, title="Clean Listing",
body="A perfectly normal item for sale.",
lang="en", price_cents=500, zip_code="92683",
raw_attributes={"condition": "new"})
assert clean.status == ListingStatus.active
lsvc.update_listing(clean, for_sale, title="Clean Listing",
body="Now selling viagra too", lang="en",
price_cents=500, zip_code="92683",
raw_attributes={"condition": "new"})
assert clean.status == ListingStatus.flagged
print("keyword blocklist auto-flags on update: ok")
set_setting("keyword_blocklist", [])
db.session.commit()
# --- reports: self-report blocked ---
report_target = lsvc.create_listing(target, for_sale, title="Reportable Item",
body="Nothing wrong with this one.",
lang="en", price_cents=1000, zip_code="92683",
raw_attributes={"condition": "new"})
try:
rsvc.create_report(report_target, target, ReportReason.spam)
assert False, "expected self-report to be blocked"
except rsvc.ReportError as e:
assert "own listing" in str(e)
print("self-report blocked: ok")
# --- reports: duplicate report blocked ---
buyer = User.query.filter_by(email="buyer@example.com").first()
rsvc.create_report(report_target, buyer, ReportReason.spam, note="looks fake")
try:
rsvc.create_report(report_target, buyer, ReportReason.scam)
assert False, "expected duplicate report to be blocked"
except rsvc.ReportError as e:
assert "already reported" in str(e)
print("duplicate report blocked: ok")
# --- reports: threshold auto-flags ---
set_setting("flag_threshold", 2)
db.session.commit()
assert report_target.flag_count == 1
assert report_target.status == ListingStatus.active
rsvc.create_report(report_target, admin, ReportReason.scam)
assert report_target.flag_count == 2
assert report_target.status == ListingStatus.flagged
print(f"report threshold auto-flag: ok (flag_count={report_target.flag_count})")
# --- moderation actions + audit log ---
msvc.approve(report_target, actor=admin)
db.session.commit()
assert report_target.status == ListingStatus.active and report_target.flag_count == 0
assert AuditLog.query.filter_by(action="listing.approved",
target_id=report_target.id).count() == 1
print("moderation approve + audit log: ok")
msvc.hide(spammy, actor=admin)
db.session.commit()
assert spammy.status == ListingStatus.flagged
assert AuditLog.query.filter_by(action="listing.hidden",
target_id=spammy.id).count() == 1
print("moderation hide + audit log: ok")
msvc.remove(clean, actor=admin)
db.session.commit()
assert clean.status == ListingStatus.removed
assert AuditLog.query.filter_by(action="listing.removed",
target_id=clean.id).count() == 1
print("moderation remove + audit log: ok")
# lower threshold to 1 so a single HTTP report flips status for the next check
set_setting("flag_threshold", 1)
http_listing = lsvc.create_listing(target, for_sale, title="HTTP Report Test",
body="Totally fine listing.", lang="en",
price_cents=750, zip_code="92683",
raw_attributes={"condition": "new"})
http_listing_id = http_listing.id
db.session.commit()
# --- route checks ---
c = app.test_client(); B = "https://localhost"
@@ -562,15 +657,17 @@ def _phase6(app):
# non-admin gets 403
login("t@example.com", "NewPass456")
assert c.get("/admin", base_url=B).status_code == 403
print("non-admin /admin -> 403: ok")
for path in ("/admin", "/admin/listings", "/admin/reports"):
assert c.get(path, base_url=B).status_code == 403
print("non-admin /admin* -> 403: ok")
c.get("/auth/logout", base_url=B)
# admin gets 200 on dashboard/users/user_detail
# admin gets 200 on dashboard/users/user_detail/listings/reports
login("admin@example.com", "AdminPass123")
with app.app_context():
target_id = User.query.filter_by(email="t@example.com").first().id
for path in ("/admin", "/admin/users", f"/admin/users/{target_id}"):
for path in ("/admin", "/admin/users", f"/admin/users/{target_id}",
"/admin/listings", "/admin/reports"):
code = c.get(path, base_url=B).status_code
assert code == 200, f"{path} -> {code}"
print(f"{code} {path}")
@@ -590,6 +687,34 @@ def _phase6(app):
assert "suspended" in r.get_data(as_text=True).lower()
print("banned user login rejected: ok")
# buyer reports a listing via the real HTTP route
c.get("/auth/logout", base_url=B)
login("buyer@example.com", "BuyerPass123")
r = c.get(f"/listings/{http_listing_id}", base_url=B)
tok = csrf(r.get_data(as_text=True))
c.post(f"/listings/{http_listing_id}/report", base_url=B,
data={"csrf_token": tok, "reason": "spam", "note": "test report"},
headers={"Referer": B + f"/listings/{http_listing_id}"}, follow_redirects=True)
with app.app_context():
rep = Report.query.filter_by(listing_id=http_listing_id).first()
assert rep is not None and rep.reason == ReportReason.spam
report_id = rep.id
assert db.session.get(Listing, http_listing_id).status == ListingStatus.flagged
print("user-facing report route: ok")
# admin sees it in the open reports queue, dismisses it, it disappears
c.get("/auth/logout", base_url=B)
login("admin@example.com", "AdminPass123")
r = c.get("/admin/reports", base_url=B)
assert "HTTP Report Test" in r.get_data(as_text=True)
tok = csrf(r.get_data(as_text=True))
c.post(f"/admin/reports/{report_id}/dismiss", base_url=B,
data={"csrf_token": tok},
headers={"Referer": B + "/admin/reports"}, follow_redirects=True)
r2 = c.get("/admin/reports", base_url=B)
assert "HTTP Report Test" not in r2.get_data(as_text=True)
print("report dismiss hides from open queue: ok")
# restore target to active for cleanliness (not strictly needed, smoke ends here)
with app.app_context():
u = User.query.filter_by(email="t@example.com").first()