chore: reorganize test files into domain-specific subdirectories under tests/
Split the monolithic test directory into three tiers (unit, api, e2e) with a path-mirroring directory structure. Added corresponding Makefile targets (test-unit, test-api, test-e2e) and updated all documentation references (CLAUDE.md, README.md, testing-cicd.html, testing-framework.html, testing-make.html) to reflect the new layout and naming conventions.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import time
|
||||
import requests
|
||||
from tests.conftest import BASE_URL
|
||||
def _session_validation():
|
||||
s = requests.Session()
|
||||
name = f"val_{int(time.time() * 1000)}"
|
||||
s.post(
|
||||
f"{BASE_URL}/auth/signup",
|
||||
data={
|
||||
"username": name,
|
||||
"email": f"{name}@t.dev",
|
||||
"password": "secret123",
|
||||
"confirm_password": "secret123",
|
||||
},
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s
|
||||
from datetime import datetime, timezone
|
||||
from devplacepy.database import get_table
|
||||
from devplacepy.utils import generate_uid
|
||||
_counter_votes = [0]
|
||||
AJAX_votes = {"X-Requested-With": "fetch"}
|
||||
def _session_votes():
|
||||
_counter_votes[0] += 1
|
||||
name = f"vot{int(time.time() * 1000)}{_counter_votes[0]}"
|
||||
s = requests.Session()
|
||||
s.post(
|
||||
f"{BASE_URL}/auth/signup",
|
||||
data={
|
||||
"username": name,
|
||||
"email": f"{name}@t.dev",
|
||||
"password": "secret123",
|
||||
"confirm_password": "secret123",
|
||||
},
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
def _uid_votes(username):
|
||||
return get_table("users").find_one(username=username)["uid"]
|
||||
def _make_post_votes(owner_uid):
|
||||
uid = generate_uid()
|
||||
get_table("posts").insert(
|
||||
{
|
||||
"uid": uid,
|
||||
"user_uid": owner_uid,
|
||||
"slug": f"{uid[:8]}-vote-post",
|
||||
"title": None,
|
||||
"content": "vote target content",
|
||||
"topic": "random",
|
||||
"project_uid": None,
|
||||
"image": None,
|
||||
"stars": 0,
|
||||
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||
}
|
||||
)
|
||||
return uid
|
||||
|
||||
|
||||
def test_vote_bad_value_is_handled_not_500(app_server):
|
||||
s = _session_validation()
|
||||
r = s.post(
|
||||
f"{BASE_URL}/votes/post/nonexistent",
|
||||
data={"value": "abc"},
|
||||
allow_redirects=False,
|
||||
)
|
||||
assert r.status_code != 500
|
||||
assert r.status_code in (302, 303, 400)
|
||||
|
||||
|
||||
def test_upvote_returns_ajax_payload(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
s_b, _ = _session_votes()
|
||||
post_uid = _make_post_votes(_uid_votes(a_name))
|
||||
r = s_b.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
assert r.json() == {"net": 1, "up": 1, "down": 0, "value": 1}
|
||||
|
||||
|
||||
def test_repeated_vote_toggles_off(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
s_b, _ = _session_votes()
|
||||
post_uid = _make_post_votes(_uid_votes(a_name))
|
||||
s_b.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
r = s_b.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
assert r.json()["net"] == 0
|
||||
assert r.json()["value"] == 0
|
||||
|
||||
|
||||
def test_switch_upvote_to_downvote(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
s_b, _ = _session_votes()
|
||||
post_uid = _make_post_votes(_uid_votes(a_name))
|
||||
s_b.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
r = s_b.post(
|
||||
f"{BASE_URL}/votes/post/{post_uid}", data={"value": "-1"}, headers=AJAX_votes
|
||||
)
|
||||
assert r.json()["net"] == -1
|
||||
assert r.json()["value"] == -1
|
||||
|
||||
|
||||
def test_upvote_notifies_owner_and_awards_xp(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
s_b, _ = _session_votes()
|
||||
a_uid = _uid_votes(a_name)
|
||||
post_uid = _make_post_votes(a_uid)
|
||||
before = get_table("users").find_one(uid=a_uid).get("xp", 0) or 0
|
||||
s_b.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
assert get_table("notifications").count(user_uid=a_uid, type="vote") == 1
|
||||
after = get_table("users").find_one(uid=a_uid).get("xp", 0) or 0
|
||||
assert after - before == 5
|
||||
|
||||
|
||||
def test_self_vote_does_not_notify(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
a_uid = _uid_votes(a_name)
|
||||
post_uid = _make_post_votes(a_uid)
|
||||
before = get_table("notifications").count(user_uid=a_uid, type="vote")
|
||||
s_a.post(f"{BASE_URL}/votes/post/{post_uid}", data={"value": "1"}, headers=AJAX_votes)
|
||||
after = get_table("notifications").count(user_uid=a_uid, type="vote")
|
||||
assert after == before
|
||||
|
||||
|
||||
def test_non_ajax_vote_redirects_to_referer(app_server):
|
||||
s_a, a_name = _session_votes()
|
||||
s_b, _ = _session_votes()
|
||||
post_uid = _make_post_votes(_uid_votes(a_name))
|
||||
r = s_b.post(
|
||||
f"{BASE_URL}/votes/post/{post_uid}",
|
||||
data={"value": "1"},
|
||||
headers={"Referer": f"{BASE_URL}/feed"},
|
||||
allow_redirects=False,
|
||||
)
|
||||
assert r.status_code == 302
|
||||
assert r.headers["location"] == f"{BASE_URL}/feed"
|
||||
Reference in New Issue
Block a user