forked from retoor/devplacepy
iUUUUpdatexz
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import re
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from playwright.sync_api import expect
|
||||
|
||||
from devplacepy.database import get_table, set_setting
|
||||
from devplacepy.services.containers import store
|
||||
from devplacepy.services.containers.workspace import flags, naming
|
||||
from devplacepy.utils import make_combined_slug
|
||||
from tests.conftest import BASE_URL
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _workspaces_on():
|
||||
previous = None
|
||||
row = get_table("site_settings").find_one(key="workspace_enabled")
|
||||
if row:
|
||||
previous = row.get("value")
|
||||
set_setting("workspace_enabled", "1")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
set_setting("workspace_enabled", previous if previous is not None else "0")
|
||||
instances = get_table("instances")
|
||||
created = [r["uid"] for r in instances.find(is_workspace=1)]
|
||||
for uid in created:
|
||||
get_table("tunnels").delete(instance_uid=uid)
|
||||
get_table("workspace_flags").delete(instance_uid=uid)
|
||||
instances.delete(uid=uid)
|
||||
|
||||
|
||||
def _row_for(user: dict) -> dict:
|
||||
return get_table("users").find_one(username=user["username"])
|
||||
|
||||
|
||||
def _project_for(owner_uid: str, title: str = "WS Project") -> dict:
|
||||
uid = str(uuid4())
|
||||
slug = make_combined_slug(title, uid)
|
||||
row = {
|
||||
"uid": uid,
|
||||
"user_uid": owner_uid,
|
||||
"title": title,
|
||||
"description": "workspace host project",
|
||||
"slug": slug,
|
||||
"stars": 0,
|
||||
"created_at": "2026-01-01T00:00:00+00:00",
|
||||
"deleted_at": None,
|
||||
"deleted_by": None,
|
||||
}
|
||||
get_table("projects").insert(row)
|
||||
return row
|
||||
|
||||
|
||||
def _workspace_for(project: dict, owner_uid: str, **overrides) -> dict:
|
||||
payload = {
|
||||
"project_uid": project["uid"],
|
||||
"name": "ws-e2e",
|
||||
"status": "running",
|
||||
"desired_state": "running",
|
||||
"is_workspace": 1,
|
||||
"workspace_owner_uid": owner_uid,
|
||||
"tunnel_name": naming.generate(),
|
||||
"ports_json": '[{"host": 20777, "container": 8080, "proto": "tcp"}]',
|
||||
}
|
||||
payload.update(overrides)
|
||||
return store.create_instance(payload)
|
||||
|
||||
|
||||
def test_workspace_page_offers_creation_to_owner(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"])
|
||||
page.goto(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
page.locator(".workspace-page").wait_for(state="visible")
|
||||
expect(page.locator("button:has-text('Open workspace')")).to_be_visible()
|
||||
|
||||
|
||||
def test_workspace_page_shows_state_quota_and_tunnel_form(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Detail")
|
||||
_workspace_for(project, _row_for(user)["uid"])
|
||||
page.goto(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
page.locator(".workspace-summary").wait_for(state="visible")
|
||||
expect(page.locator("[data-workspace-status]")).to_contain_text("running")
|
||||
expect(page.locator(".workspace-meter").first).to_be_visible()
|
||||
expect(page.locator(".workspace-tunnel-form")).to_be_visible()
|
||||
expect(page.locator(".workspace-help")).to_contain_text("sudo")
|
||||
|
||||
|
||||
def test_owner_can_add_and_remove_a_tunnel(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Tunnel")
|
||||
_workspace_for(project, _row_for(user)["uid"])
|
||||
url = f"{BASE_URL}/projects/{project['slug']}/workspace"
|
||||
page.goto(url, wait_until="domcontentloaded")
|
||||
page.locator(".workspace-tunnel-form input[name='container_port']").fill("8080")
|
||||
page.locator(".workspace-tunnel-form button:has-text('Add tunnel')").click()
|
||||
page.wait_for_url(url, wait_until="domcontentloaded")
|
||||
tunnel = page.locator(".workspace-tunnel").first
|
||||
tunnel.wait_for(state="visible")
|
||||
expect(tunnel).to_contain_text(naming.domain())
|
||||
|
||||
page.locator(".workspace-tunnel button:has-text('Remove')").first.click()
|
||||
page.locator(".dialog-confirm").wait_for(state="visible")
|
||||
page.locator(".dialog-confirm").click()
|
||||
page.wait_for_url(url, wait_until="domcontentloaded")
|
||||
expect(page.locator(".workspace-tunnel-list")).to_contain_text("No tunnels yet")
|
||||
|
||||
|
||||
def test_suspended_workspace_shows_reason_to_owner(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Suspended")
|
||||
instance = _workspace_for(project, _row_for(user)["uid"])
|
||||
store.update_instance(
|
||||
instance["uid"],
|
||||
{"suspended_at": "2026-01-01T00:00:00+00:00", "flag_reason": "sustained cpu"},
|
||||
)
|
||||
page.goto(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
banner = page.locator(".workspace-suspended")
|
||||
banner.wait_for(state="visible")
|
||||
expect(banner).to_contain_text("sustained cpu")
|
||||
|
||||
|
||||
def test_open_flag_is_visible_to_the_owner(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Flagged")
|
||||
instance = _workspace_for(project, _row_for(user)["uid"])
|
||||
flags.raise_flag(
|
||||
store.get_instance(instance["uid"]),
|
||||
flags.KIND_EGRESS,
|
||||
"warn",
|
||||
"egress above the hourly ceiling",
|
||||
2048.0,
|
||||
1024.0,
|
||||
)
|
||||
page.goto(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
flag = page.locator(".workspace-flag").first
|
||||
flag.wait_for(state="visible")
|
||||
expect(flag).to_contain_text("egress above the hourly ceiling")
|
||||
|
||||
|
||||
def test_guest_cannot_reach_the_workspace_page(page):
|
||||
project = _project_for(str(uuid4()), "WS Guest")
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
allow_redirects=False,
|
||||
)
|
||||
assert response.status_code in (303, 401, 404)
|
||||
|
||||
|
||||
def test_non_owner_member_is_refused(bob):
|
||||
page, user = bob
|
||||
project = _project_for(str(uuid4()), "WS Foreign")
|
||||
page.goto(
|
||||
f"{BASE_URL}/projects/{project['slug']}/workspace",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
assert not page.locator(".workspace-summary").count()
|
||||
|
||||
|
||||
def test_admin_console_lists_and_suspends_a_workspace(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Admin")
|
||||
instance = _workspace_for(project, _row_for(user)["uid"])
|
||||
page.goto(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
row = page.locator(f"tr[data-workspace-uid='{instance['uid']}']")
|
||||
row.wait_for(state="visible")
|
||||
expect(row).to_contain_text("ws-e2e")
|
||||
|
||||
row.locator("input[name='reason']").fill("policy breach")
|
||||
row.locator("button:has-text('Suspend')").click()
|
||||
page.wait_for_url(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
|
||||
refreshed = store.get_instance(instance["uid"])
|
||||
assert refreshed["suspended_at"]
|
||||
row = page.locator(f"tr[data-workspace-uid='{instance['uid']}']")
|
||||
expect(row).to_contain_text("suspended")
|
||||
|
||||
row.locator("button:has-text('Unsuspend')").click()
|
||||
page.wait_for_url(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
assert not store.get_instance(instance["uid"])["suspended_at"]
|
||||
|
||||
|
||||
def test_admin_console_raises_and_resolves_a_flag(alice):
|
||||
page, user = alice
|
||||
project = _project_for(_row_for(user)["uid"], "WS Flag Admin")
|
||||
instance = _workspace_for(project, _row_for(user)["uid"])
|
||||
page.goto(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
row = page.locator(f"tr[data-workspace-uid='{instance['uid']}']")
|
||||
row.wait_for(state="visible")
|
||||
row.locator("input[name='detail']").fill("manual review")
|
||||
row.locator("button:has-text('Flag')").click()
|
||||
page.wait_for_url(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
|
||||
assert flags.list_flags(instance_uid=instance["uid"])
|
||||
page.locator("button:has-text('Resolve')").first.click()
|
||||
page.wait_for_url(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
assert not flags.list_flags(instance_uid=instance["uid"])
|
||||
|
||||
|
||||
def test_workspaces_sidebar_link_is_present_for_admin(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/admin/workspaces", wait_until="domcontentloaded")
|
||||
link = page.locator(".sidebar-link:has-text('Workspaces')")
|
||||
link.wait_for(state="visible")
|
||||
expect(link).to_have_class(re.compile("active"))
|
||||
Reference in New Issue
Block a user