Update
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import time
|
||||
import requests
|
||||
from tests.conftest import BASE_URL, login_user
|
||||
from devplacepy.database import (
|
||||
get_table,
|
||||
get_primary_admin_uid,
|
||||
invalidate_admins_cache,
|
||||
refresh_snapshot,
|
||||
)
|
||||
from devplacepy.utils import clear_user_cache
|
||||
|
||||
_counter = [0]
|
||||
|
||||
|
||||
def _make_admin():
|
||||
_counter[0] += 1
|
||||
name = f"ecm{int(time.time() * 1000)}{_counter[0]}"
|
||||
password = "secret123"
|
||||
requests.post(
|
||||
f"{BASE_URL}/auth/signup",
|
||||
data={
|
||||
"username": name,
|
||||
"email": f"{name}@t.dev",
|
||||
"password": password,
|
||||
"confirm_password": password,
|
||||
},
|
||||
allow_redirects=True,
|
||||
)
|
||||
refresh_snapshot()
|
||||
row = get_table("users").find_one(username=name)
|
||||
get_table("users").update({"uid": row["uid"], "role": "Admin"}, ["uid"])
|
||||
clear_user_cache(row["uid"])
|
||||
invalidate_admins_cache()
|
||||
return {
|
||||
"email": f"{name}@t.dev",
|
||||
"password": password,
|
||||
"uid": row["uid"],
|
||||
"api_key": row["api_key"],
|
||||
}
|
||||
|
||||
|
||||
def _demote_existing_admins():
|
||||
existing = [r["uid"] for r in get_table("users").find(role="Admin")]
|
||||
for uid in existing:
|
||||
get_table("users").update({"uid": uid, "role": "Member"}, ["uid"])
|
||||
invalidate_admins_cache()
|
||||
return existing
|
||||
|
||||
|
||||
def _restore_admins(uids):
|
||||
for uid in uids:
|
||||
get_table("users").update({"uid": uid, "role": "Admin"}, ["uid"])
|
||||
invalidate_admins_cache()
|
||||
|
||||
|
||||
def _create_project(api_key, title, is_private=False):
|
||||
data = {
|
||||
"title": title,
|
||||
"description": "e2e container manage isolation",
|
||||
"project_type": "software",
|
||||
"status": "In Development",
|
||||
}
|
||||
if is_private:
|
||||
data["is_private"] = "on"
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/projects/create",
|
||||
headers={"Accept": "application/json", "X-API-KEY": api_key},
|
||||
data=data,
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
return r.json()["data"]
|
||||
|
||||
|
||||
def _insert_instance(project_uid, created_by):
|
||||
uid = f"ecm-{int(time.time() * 1000000)}"
|
||||
get_table("instances").insert(
|
||||
{
|
||||
"uid": uid,
|
||||
"slug": uid,
|
||||
"name": "e2e-manage-test",
|
||||
"project_uid": project_uid,
|
||||
"created_by": created_by,
|
||||
"owner_uid": "",
|
||||
"status": "stopped",
|
||||
"container_id": "",
|
||||
"workspace_dir": "",
|
||||
"restart_policy": "never",
|
||||
"deleted_at": None,
|
||||
"deleted_by": None,
|
||||
}
|
||||
)
|
||||
refresh_snapshot()
|
||||
return uid
|
||||
|
||||
|
||||
def _cleanup(inst_uid):
|
||||
get_table("instances").delete(uid=inst_uid)
|
||||
refresh_snapshot()
|
||||
|
||||
|
||||
def test_admin_list_shows_view_only_for_non_owned_public_instance(page, app_server):
|
||||
owner = _make_admin()
|
||||
other = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage List Public", is_private=False)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, other)
|
||||
page.goto(f"{BASE_URL}/admin/containers", wait_until="domcontentloaded")
|
||||
row = page.locator(f"tr.cm-row[data-uid='{inst_uid}']")
|
||||
row.wait_for(state="visible", timeout=15000)
|
||||
assert "view only" in row.inner_text().lower()
|
||||
assert row.locator("[data-cm-action='delete']").count() == 0
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_list_shows_actions_for_owner(page, app_server):
|
||||
owner = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage List Owner", is_private=False)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, owner)
|
||||
page.goto(f"{BASE_URL}/admin/containers", wait_until="domcontentloaded")
|
||||
row = page.locator(f"tr.cm-row[data-uid='{inst_uid}']")
|
||||
row.wait_for(state="visible", timeout=15000)
|
||||
assert row.locator("[data-cm-action='delete']").count() == 1
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_list_excludes_others_private_project_instance(page, app_server):
|
||||
owner = _make_admin()
|
||||
other = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage List Private", is_private=True)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, other)
|
||||
page.goto(f"{BASE_URL}/admin/containers", wait_until="domcontentloaded")
|
||||
assert page.locator(f"tr.cm-row[data-uid='{inst_uid}']").count() == 0
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_detail_page_view_only_hides_manage_controls(page, app_server):
|
||||
owner = _make_admin()
|
||||
other = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage Detail Public", is_private=False)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, other)
|
||||
page.goto(f"{BASE_URL}/admin/containers/{inst_uid}", wait_until="domcontentloaded")
|
||||
actions = page.locator("#ci-actions")
|
||||
actions.wait_for(state="visible")
|
||||
assert "view only" in actions.inner_text().lower()
|
||||
assert page.locator("#ci-term-toggle").count() == 0
|
||||
assert page.locator("[data-modal='ci-schedule-modal']").count() == 0
|
||||
assert page.locator("a:has-text('Edit configuration')").count() == 0
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_detail_page_owner_sees_manage_controls(page, app_server):
|
||||
owner = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage Detail Owner", is_private=False)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, owner)
|
||||
page.goto(f"{BASE_URL}/admin/containers/{inst_uid}", wait_until="domcontentloaded")
|
||||
page.locator("[data-modal='ci-schedule-modal']").wait_for(state="visible")
|
||||
assert page.locator("a:has-text('Edit configuration')").count() == 1
|
||||
assert page.locator("#ci-term-toggle").count() == 1
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_detail_page_404_for_non_viewer_on_private_project(page, app_server):
|
||||
owner = _make_admin()
|
||||
other = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage Detail Private", is_private=True)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, other)
|
||||
page.goto(f"{BASE_URL}/admin/containers/{inst_uid}", wait_until="domcontentloaded")
|
||||
assert page.is_visible("text=Not Found") or page.is_visible("text=404")
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_admin_edit_page_redirects_non_owner_to_detail(page, app_server):
|
||||
owner = _make_admin()
|
||||
other = _make_admin()
|
||||
project = _create_project(owner["api_key"], "E2E Manage Edit Redirect", is_private=False)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, other)
|
||||
page.goto(f"{BASE_URL}/admin/containers/{inst_uid}/edit", wait_until="domcontentloaded")
|
||||
page.wait_for_url(f"**/admin/containers/{inst_uid}", wait_until="domcontentloaded")
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
|
||||
|
||||
def test_primary_admin_sees_and_manages_others_private_instance(page, app_server):
|
||||
restore = _demote_existing_admins()
|
||||
try:
|
||||
primary = _make_admin()
|
||||
owner = _make_admin()
|
||||
assert get_primary_admin_uid() == primary["uid"]
|
||||
project = _create_project(
|
||||
owner["api_key"], "E2E Manage Primary Private", is_private=True
|
||||
)
|
||||
inst_uid = _insert_instance(project["uid"], owner["uid"])
|
||||
try:
|
||||
login_user(page, primary)
|
||||
page.goto(f"{BASE_URL}/admin/containers", wait_until="domcontentloaded")
|
||||
row = page.locator(f"tr.cm-row[data-uid='{inst_uid}']")
|
||||
row.wait_for(state="visible", timeout=15000)
|
||||
assert row.locator("[data-cm-action='delete']").count() == 1
|
||||
|
||||
page.goto(f"{BASE_URL}/admin/containers/{inst_uid}", wait_until="domcontentloaded")
|
||||
page.locator("a:has-text('Edit configuration')").wait_for(state="visible")
|
||||
finally:
|
||||
_cleanup(inst_uid)
|
||||
finally:
|
||||
_restore_admins(restore)
|
||||
@@ -917,3 +917,45 @@ def test_feed_online_now_widget_lists_current_user(alice):
|
||||
page.locator(f".online-user[title='{user['username']}']")
|
||||
).to_have_count(1)
|
||||
expect(page.locator(".online-users .presence-dot.online").first).to_be_visible()
|
||||
|
||||
|
||||
def _scroll_and_open_post(page):
|
||||
_seed_feed_posts(30)
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
page.locator(".post-card").first.wait_for(state="visible")
|
||||
page.evaluate("window.scrollTo(0, 1200)")
|
||||
page.wait_for_function("window.scrollY > 1000")
|
||||
card_link = page.locator(".post-card .card-link").nth(5)
|
||||
card_link.scroll_into_view_if_needed()
|
||||
page.wait_for_timeout(400)
|
||||
y_before = page.evaluate("window.scrollY")
|
||||
assert y_before > 0
|
||||
card_link.click()
|
||||
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
|
||||
return y_before
|
||||
|
||||
|
||||
def test_feed_scroll_restored_via_back_link(alice):
|
||||
page, user = alice
|
||||
y_before = _scroll_and_open_post(page)
|
||||
back = page.locator("a.back-link")
|
||||
back.wait_for(state="visible")
|
||||
back.click()
|
||||
page.wait_for_url(f"{BASE_URL}/feed*", wait_until="domcontentloaded")
|
||||
page.wait_for_function(f"Math.abs(window.scrollY - {y_before}) < 60")
|
||||
|
||||
|
||||
def test_feed_scroll_restored_via_browser_back(alice):
|
||||
page, user = alice
|
||||
y_before = _scroll_and_open_post(page)
|
||||
page.go_back(wait_until="domcontentloaded")
|
||||
page.wait_for_function(f"Math.abs(window.scrollY - {y_before}) < 60")
|
||||
|
||||
|
||||
def test_feed_scroll_not_restored_on_fresh_visit(alice):
|
||||
page, user = alice
|
||||
_scroll_and_open_post(page)
|
||||
page.goto(f"{BASE_URL}/gists", wait_until="domcontentloaded")
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
page.wait_for_timeout(600)
|
||||
assert page.evaluate("window.scrollY") < 60
|
||||
|
||||
@@ -932,3 +932,86 @@ def test_projects_list_preserves_comment_hierarchy(page, app_server):
|
||||
expect(card.locator(".post-card-comments")).not_to_contain_text(f"{marker}-excluded")
|
||||
nested = card.locator(".post-card-comments .comment-replies .comment-text")
|
||||
expect(nested).to_contain_text(f"{marker}-reply")
|
||||
|
||||
|
||||
import time as _time_containers_menu
|
||||
import requests as _requests_containers_menu
|
||||
from tests.conftest import login_user
|
||||
from devplacepy.database import get_primary_admin_uid, invalidate_admins_cache
|
||||
from devplacepy.utils import clear_user_cache
|
||||
|
||||
|
||||
_counter_containers_menu = [0]
|
||||
|
||||
|
||||
def _signup_containers_menu(prefix):
|
||||
_counter_containers_menu[0] += 1
|
||||
name = f"{prefix}{int(_time_containers_menu.time() * 1000)}{_counter_containers_menu[0]}"
|
||||
_requests_containers_menu.post(
|
||||
f"{BASE_URL}/auth/signup",
|
||||
data={
|
||||
"username": name,
|
||||
"email": f"{name}@t.dev",
|
||||
"password": "secret123",
|
||||
"confirm_password": "secret123",
|
||||
},
|
||||
allow_redirects=True,
|
||||
)
|
||||
row = get_table("users").find_one(username=name)
|
||||
return name, row["uid"], row["api_key"]
|
||||
|
||||
|
||||
def _make_admin_containers_menu(prefix):
|
||||
name, uid, key = _signup_containers_menu(prefix)
|
||||
get_table("users").update({"uid": uid, "role": "Admin"}, ["uid"])
|
||||
clear_user_cache(uid)
|
||||
invalidate_admins_cache()
|
||||
return name, uid, key
|
||||
|
||||
|
||||
def _create_project_containers_menu(key, title, is_private=False):
|
||||
data = {
|
||||
"title": title,
|
||||
"description": "containers menu gating test",
|
||||
"project_type": "software",
|
||||
"status": "In Development",
|
||||
}
|
||||
if is_private:
|
||||
data["is_private"] = "on"
|
||||
r = _requests_containers_menu.post(
|
||||
f"{BASE_URL}/projects/create",
|
||||
headers={"Accept": "application/json", "X-API-KEY": key},
|
||||
data=data,
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
return r.json()["data"]["slug"]
|
||||
|
||||
|
||||
def test_containers_menu_visible_for_admin_on_own_private_project(page, app_server):
|
||||
name, uid, key = _make_admin_containers_menu("cmown")
|
||||
slug = _create_project_containers_menu(key, "Containers Menu Own Private", is_private=True)
|
||||
login_user(page, {"email": f"{name}@t.dev", "password": "secret123"})
|
||||
page.goto(f"{BASE_URL}/projects/{slug}", wait_until="domcontentloaded")
|
||||
page.locator(".project-actions-more").click()
|
||||
expect(page.locator(".context-menu-item:has-text('Containers')")).to_be_visible()
|
||||
|
||||
|
||||
def test_containers_menu_visible_for_any_admin_on_public_project(page, app_server):
|
||||
_, _, owner_key = _make_admin_containers_menu("cmpubowner")
|
||||
other_name, _, _ = _make_admin_containers_menu("cmpubother")
|
||||
slug = _create_project_containers_menu(owner_key, "Containers Menu Public", is_private=False)
|
||||
login_user(page, {"email": f"{other_name}@t.dev", "password": "secret123"})
|
||||
page.goto(f"{BASE_URL}/projects/{slug}", wait_until="domcontentloaded")
|
||||
page.locator(".project-actions-more").click()
|
||||
expect(page.locator(".context-menu-item:has-text('Containers')")).to_be_visible()
|
||||
|
||||
|
||||
def test_containers_menu_hidden_for_non_owner_admin_on_member_private_project(page, app_server):
|
||||
_, member_uid, member_key = _signup_containers_menu("cmmember")
|
||||
slug = _create_project_containers_menu(member_key, "Containers Menu Member Private", is_private=True)
|
||||
admin_name, admin_uid, _ = _make_admin_containers_menu("cmviewer")
|
||||
assert get_primary_admin_uid() != admin_uid
|
||||
login_user(page, {"email": f"{admin_name}@t.dev", "password": "secret123"})
|
||||
page.goto(f"{BASE_URL}/projects/{slug}", wait_until="domcontentloaded")
|
||||
page.locator(".project-actions-more").click()
|
||||
expect(page.locator(".context-menu-item:has-text('Containers')")).to_have_count(0)
|
||||
|
||||
Reference in New Issue
Block a user