forked from retoor/devplacepy
fix: correct "bugs" to "issues" in routing table and README references across multiple documentation files
This commit is contained in:
@@ -21,7 +21,7 @@ def _session_content_negotiation(password="secret123"):
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/bugs"]
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/issues"]
|
||||
|
||||
|
||||
def test_admin_pages_negotiate(seeded_db):
|
||||
|
||||
+39
-27
@@ -85,6 +85,15 @@ def _call(endpoint, headers):
|
||||
elif data:
|
||||
kwargs["data"] = data
|
||||
return requests.request(endpoint["method"], f"{BASE_URL}{path}", **kwargs)
|
||||
def _clear_probe_jobs():
|
||||
from devplacepy.database import refresh_snapshot
|
||||
|
||||
refresh_snapshot()
|
||||
jobs = get_table("jobs")
|
||||
for row in list(jobs.find()):
|
||||
jobs.delete(uid=row["uid"])
|
||||
|
||||
|
||||
def _redirects_to(response, target):
|
||||
return response.status_code in (
|
||||
302,
|
||||
@@ -110,38 +119,41 @@ def test_documented_minimal_role_matches_enforcement(seeded_db):
|
||||
assert len(endpoints) >= 50
|
||||
|
||||
failures = []
|
||||
for endpoint in endpoints:
|
||||
auth = endpoint["auth"]
|
||||
label = (
|
||||
f"{endpoint['method']} {endpoint['path']} ({endpoint['id']}, doc={auth})"
|
||||
)
|
||||
anon = _call(endpoint, JSON_auth_matrix)
|
||||
|
||||
if auth == "public":
|
||||
if not _is_allowed(anon):
|
||||
failures.append(
|
||||
f"{label}: public but anonymous was rejected ({anon.status_code})"
|
||||
)
|
||||
continue
|
||||
|
||||
if not _is_auth_rejected(anon):
|
||||
failures.append(
|
||||
f"{label}: requires {auth} but anonymous was NOT rejected ({anon.status_code})"
|
||||
try:
|
||||
for endpoint in endpoints:
|
||||
auth = endpoint["auth"]
|
||||
label = (
|
||||
f"{endpoint['method']} {endpoint['path']} ({endpoint['id']}, doc={auth})"
|
||||
)
|
||||
anon = _call(endpoint, JSON_auth_matrix)
|
||||
|
||||
if auth == "user" and endpoint["method"] == "GET":
|
||||
mem = _call(endpoint, member)
|
||||
if not _is_allowed(mem):
|
||||
if auth == "public":
|
||||
if not _is_allowed(anon):
|
||||
failures.append(
|
||||
f"{label}: public but anonymous was rejected ({anon.status_code})"
|
||||
)
|
||||
continue
|
||||
|
||||
if not _is_auth_rejected(anon):
|
||||
failures.append(
|
||||
f"{label}: documented user but a member was rejected ({mem.status_code})"
|
||||
f"{label}: requires {auth} but anonymous was NOT rejected ({anon.status_code})"
|
||||
)
|
||||
|
||||
if auth == "admin":
|
||||
mem = _call(endpoint, member)
|
||||
if not _is_role_rejected(mem):
|
||||
failures.append(
|
||||
f"{label}: documented admin but a non-admin member was NOT rejected ({mem.status_code})"
|
||||
)
|
||||
if auth == "user" and endpoint["method"] == "GET":
|
||||
mem = _call(endpoint, member)
|
||||
if not _is_allowed(mem):
|
||||
failures.append(
|
||||
f"{label}: documented user but a member was rejected ({mem.status_code})"
|
||||
)
|
||||
|
||||
if auth == "admin":
|
||||
mem = _call(endpoint, member)
|
||||
if not _is_role_rejected(mem):
|
||||
failures.append(
|
||||
f"{label}: documented admin but a non-admin member was NOT rejected ({mem.status_code})"
|
||||
)
|
||||
finally:
|
||||
_clear_probe_jobs()
|
||||
|
||||
assert not failures, "Auth enforcement does not match documentation:\n" + "\n".join(
|
||||
failures
|
||||
|
||||
@@ -21,7 +21,7 @@ def _session_content_negotiation(password="secret123"):
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/bugs"]
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/issues"]
|
||||
|
||||
|
||||
def test_public_pages_serve_json_and_html(app_server):
|
||||
|
||||
@@ -340,3 +340,23 @@ def test_x_robots_tag_header(app_server):
|
||||
def test_x_content_type_options_header(app_server):
|
||||
r = requests.get(f"{BASE_URL}/feed", allow_redirects=True)
|
||||
assert r.headers.get("X-Content-Type-Options") == "nosniff"
|
||||
|
||||
|
||||
def test_strict_transport_security_header(app_server):
|
||||
r = requests.get(f"{BASE_URL}/feed", allow_redirects=True)
|
||||
assert "includeSubDomains" in r.headers.get("Strict-Transport-Security", "")
|
||||
|
||||
|
||||
def test_referrer_policy_header(app_server):
|
||||
r = requests.get(f"{BASE_URL}/feed", allow_redirects=True)
|
||||
assert r.headers.get("Referrer-Policy") == "strict-origin-when-cross-origin"
|
||||
|
||||
|
||||
def test_x_frame_options_header(app_server):
|
||||
r = requests.get(f"{BASE_URL}/feed", allow_redirects=True)
|
||||
assert r.headers.get("X-Frame-Options") == "DENY"
|
||||
|
||||
|
||||
def test_x_frame_options_excluded_for_ingress_proxy(app_server):
|
||||
r = requests.get(f"{BASE_URL}/p/nonexistent-instance", allow_redirects=False)
|
||||
assert "X-Frame-Options" not in r.headers
|
||||
|
||||
@@ -124,13 +124,13 @@ from devplacepy.services.gitea import runtime, store
|
||||
from devplacepy.services.gitea.config import gitea_config
|
||||
from devplacepy.services.gitea.enhance import enhance_ticket
|
||||
from devplacepy.services.gitea.fake import FakeGiteaClient
|
||||
from devplacepy.services.gitea.service import BugTrackerService
|
||||
from devplacepy.services.gitea.service import IssueTrackerService
|
||||
from devplacepy.services.jobs import queue
|
||||
from devplacepy.services.jobs.bug_create_service import BugCreateService
|
||||
from devplacepy.services.jobs.issue_create_service import IssueCreateService
|
||||
from tests.conftest import run_async
|
||||
_counter_bugs_gitea = [0]
|
||||
_counter_issues_gitea = [0]
|
||||
@pytest.fixture(autouse=True)
|
||||
def _init_db_bugs_gitea():
|
||||
def _init_db_issues_gitea():
|
||||
init_db()
|
||||
yield
|
||||
@pytest.fixture
|
||||
@@ -141,20 +141,20 @@ def gitea_env():
|
||||
set_setting("gitea_owner", "retoor")
|
||||
set_setting("gitea_repo", "pydevplace")
|
||||
set_setting("gitea_token", "test-token")
|
||||
set_setting("bug_ai_enhance", "0")
|
||||
set_setting("issue_ai_enhance", "0")
|
||||
yield fake
|
||||
runtime.set_client(None)
|
||||
for key in ("gitea_base_url", "gitea_owner", "gitea_repo", "gitea_token"):
|
||||
set_setting(key, "")
|
||||
for table in ("bug_tickets", "bug_comment_authors"):
|
||||
for table in ("issue_tickets", "issue_comment_authors"):
|
||||
for row in list(get_table(table).find()):
|
||||
get_table(table).delete(uid=row["uid"])
|
||||
for row in list(get_table("jobs").find(kind="bug_create")):
|
||||
for row in list(get_table("jobs").find(kind="issue_create")):
|
||||
get_table("jobs").delete(uid=row["uid"])
|
||||
def _make_user_bugs_gitea():
|
||||
_counter_bugs_gitea[0] += 1
|
||||
uid = f"bugtest-user-{_counter_bugs_gitea[0]}"
|
||||
username = f"bugtester{_counter_bugs_gitea[0]}"
|
||||
def _make_user_issues_gitea():
|
||||
_counter_issues_gitea[0] += 1
|
||||
uid = f"issuetest-user-{_counter_issues_gitea[0]}"
|
||||
username = f"issuetester{_counter_issues_gitea[0]}"
|
||||
get_table("users").insert(
|
||||
{
|
||||
"uid": uid,
|
||||
@@ -170,17 +170,17 @@ def _unread(user_uid):
|
||||
return [
|
||||
n
|
||||
for n in get_table("notifications").find(user_uid=user_uid)
|
||||
if n.get("type") == "bug"
|
||||
if n.get("type") == "issue"
|
||||
]
|
||||
def _drive_jobs():
|
||||
async def run():
|
||||
svc = BugCreateService()
|
||||
svc = IssueCreateService()
|
||||
for _ in range(200):
|
||||
await svc.run_once()
|
||||
refresh_snapshot()
|
||||
pending = [
|
||||
r
|
||||
for r in get_table("jobs").find(kind="bug_create")
|
||||
for r in get_table("jobs").find(kind="issue_create")
|
||||
if r["status"] in ("pending", "running")
|
||||
]
|
||||
if not pending and not svc._inflight:
|
||||
@@ -189,7 +189,7 @@ def _drive_jobs():
|
||||
|
||||
run_async(run())
|
||||
def _role_user(role: str):
|
||||
uid, username = _make_user_bugs_gitea()
|
||||
uid, username = _make_user_issues_gitea()
|
||||
get_table("users").update({"uid": uid, "role": role}, ["uid"])
|
||||
return uid, username
|
||||
def _open_ticket(fake, role: str):
|
||||
@@ -214,14 +214,14 @@ def _open_ticket(fake, role: str):
|
||||
return number, client
|
||||
|
||||
|
||||
def test_bug_create_recorded(seeded_db):
|
||||
# Filing is async + Gitea-gated: POST /bugs/create only enqueues a job (returns
|
||||
# {uid, status_url}) and records the synchronous "bug.create.request" event; the
|
||||
# final "bug.create" fires from BugCreateService, which the tests never run
|
||||
def test_issue_create_recorded(seeded_db):
|
||||
# Filing is async + Gitea-gated: POST /issues/create only enqueues a job (returns
|
||||
# {uid, status_url}) and records the synchronous "issue.create.request" event; the
|
||||
# final "issue.create" fires from IssueCreateService, which the tests never run
|
||||
# (DEVPLACE_DISABLE_SERVICES=1). Configure Gitea so the enqueue path is taken
|
||||
# (no network call happens here) and assert the request event. Restore after.
|
||||
s, _ = _member()
|
||||
title = _unique("aubug")
|
||||
title = _unique("auissue")
|
||||
for key, value in {
|
||||
"gitea_base_url": "https://gitea.test",
|
||||
"gitea_owner": "o",
|
||||
@@ -231,9 +231,9 @@ def test_bug_create_recorded(seeded_db):
|
||||
set_setting(key, value)
|
||||
try:
|
||||
resp = s.post(
|
||||
f"{BASE_URL}/bugs/create",
|
||||
f"{BASE_URL}/issues/create",
|
||||
headers=JSON_audit_log,
|
||||
data={"title": title, "description": "audited bug description text"},
|
||||
data={"title": title, "description": "audited issue description text"},
|
||||
)
|
||||
assert resp.status_code == 200, resp.text[:300]
|
||||
assert resp.json().get("uid")
|
||||
@@ -241,7 +241,7 @@ def test_bug_create_recorded(seeded_db):
|
||||
assert (
|
||||
_find(
|
||||
admin,
|
||||
"bug.create.request",
|
||||
"issue.create.request",
|
||||
lambda e: title in (e.get("summary") or ""),
|
||||
)
|
||||
is not None
|
||||
@@ -251,24 +251,24 @@ def test_bug_create_recorded(seeded_db):
|
||||
set_setting(key, "")
|
||||
|
||||
|
||||
def test_bug_detail_renders_for_member(gitea_env):
|
||||
def test_issue_detail_renders_for_member(gitea_env):
|
||||
number, client = _open_ticket(gitea_env, "Member")
|
||||
html = client.get(f"/bugs/{number}")
|
||||
html = client.get(f"/issues/{number}")
|
||||
assert html.status_code == 200, html.text[:300]
|
||||
assert "Submit does nothing" in html.text
|
||||
assert "Close ticket" not in html.text
|
||||
data = client.get(
|
||||
f"/bugs/{number}", headers={"Accept": "application/json"}
|
||||
f"/issues/{number}", headers={"Accept": "application/json"}
|
||||
).json()
|
||||
assert data["viewer_is_admin"] is False
|
||||
|
||||
|
||||
def test_bug_detail_renders_for_admin(gitea_env):
|
||||
def test_issue_detail_renders_for_admin(gitea_env):
|
||||
number, client = _open_ticket(gitea_env, "Admin")
|
||||
html = client.get(f"/bugs/{number}")
|
||||
html = client.get(f"/issues/{number}")
|
||||
assert html.status_code == 200, html.text[:300]
|
||||
assert "Close ticket" in html.text
|
||||
data = client.get(
|
||||
f"/bugs/{number}", headers={"Accept": "application/json"}
|
||||
f"/issues/{number}", headers={"Accept": "application/json"}
|
||||
).json()
|
||||
assert data["viewer_is_admin"] is True
|
||||
@@ -131,7 +131,7 @@ def _session_content_negotiation(password="secret123"):
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/bugs"]
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/issues"]
|
||||
|
||||
|
||||
def test_security_authz_denied_recorded(seeded_db):
|
||||
|
||||
@@ -131,7 +131,7 @@ def _session_content_negotiation(password="secret123"):
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/bugs"]
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/issues"]
|
||||
_counter_polls = [0]
|
||||
AJAX_polls = {"X-Requested-With": "fetch"}
|
||||
def _session_polls():
|
||||
|
||||
@@ -21,7 +21,7 @@ def _session_content_negotiation(password="secret123"):
|
||||
allow_redirects=True,
|
||||
)
|
||||
return s, name
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/bugs"]
|
||||
PUBLIC_PAGES = ["/feed", "/projects", "/gists", "/news", "/leaderboard", "/issues"]
|
||||
|
||||
|
||||
def test_json_404_error_envelope(app_server):
|
||||
|
||||
Reference in New Issue
Block a user