Update
DevPlace CI / test (push) Failing after 1h28m53s

This commit is contained in:
2026-09-03 08:47:57 +02:00
parent 70ceb3cf81
commit 3d07a478c3
41 changed files with 1643 additions and 130 deletions
+119 -5
View File
@@ -1,6 +1,7 @@
# retoor <retoor@molodetz.nl>
import re
import socket
import time
from uuid import uuid4
@@ -14,6 +15,9 @@ from devplacepy.services.containers.workspace import flags, naming
from devplacepy.utils import make_combined_slug
from tests.conftest import BASE_URL
EDITOR_CONTAINER_PORT = 8443
TRANSITION_TIMEOUT_MS = 8000
@pytest.fixture(autouse=True)
def _workspaces_on():
@@ -62,7 +66,20 @@ def _project_for(owner_uid: str, title: str = "WS Project") -> dict:
return row
def _workspace_for(project: dict, owner_uid: str, **overrides) -> dict:
@pytest.fixture(scope="module")
def editor_listener():
sock = socket.socket()
sock.bind(("127.0.0.1", 0))
sock.listen(16)
try:
yield sock.getsockname()[1]
finally:
sock.close()
def _workspace_for(
project: dict, owner_uid: str, editor_port: int = 0, **overrides
) -> dict:
payload = {
"project_uid": project["uid"],
"name": "ws-e2e",
@@ -73,10 +90,20 @@ def _workspace_for(project: dict, owner_uid: str, **overrides) -> dict:
"tunnel_name": naming.generate(),
"ports_json": '[{"host": 20777, "container": 8080, "proto": "tcp"}]',
}
if editor_port:
payload["editor_port"] = EDITOR_CONTAINER_PORT
payload["ports_json"] = (
f'[{{"host": {editor_port}, "container": {EDITOR_CONTAINER_PORT}, '
'"proto": "tcp"}]'
)
payload.update(overrides)
return store.create_instance(payload)
def _phase_view(page, phase: str):
return page.locator(f".workspace-phase-view[data-phase-view~='{phase}']")
def test_project_page_offers_the_workspace_entry_point_to_the_owner(alice):
page, user = alice
project = _project_for(_row_for(user)["uid"], "WS Entry")
@@ -104,11 +131,13 @@ def test_project_page_hides_the_workspace_entry_point_from_a_non_owner(bob):
assert not page.locator(".context-menu-item:has-text('Workspace')").count()
def test_project_page_shows_a_direct_vscode_button_for_a_running_workspace(alice):
def test_project_page_shows_a_direct_vscode_button_for_a_running_workspace(
alice, editor_listener
):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Direct")
instance = _workspace_for(project, row["uid"])
instance = _workspace_for(project, row["uid"], editor_port=editor_listener)
page.goto(
f"{BASE_URL}/projects/{project['slug']}", wait_until="domcontentloaded"
)
@@ -383,11 +412,11 @@ def test_resetting_editor_preferences_restores_the_site_default(alice):
get_table("workspace_editor_prefs").delete(owner_id=row["uid"])
def test_the_editor_launch_control_carries_the_window_profile(alice):
def test_the_editor_launch_control_carries_the_window_profile(alice, editor_listener):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Editor Launch")
instance = _workspace_for(project, row["uid"])
instance = _workspace_for(project, row["uid"], editor_port=editor_listener)
page.goto(
f"{BASE_URL}/projects/{project['slug']}/workspace",
wait_until="domcontentloaded",
@@ -415,3 +444,88 @@ def test_the_workspace_help_states_that_every_folder_is_trusted(alice):
help_card.wait_for(state="visible")
expect(help_card).to_contain_text("Restricted Mode")
expect(help_card).to_contain_text("dpc")
def test_direct_vscode_button_is_absent_while_the_editor_is_unreachable(alice):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Booting")
_workspace_for(project, row["uid"])
page.goto(
f"{BASE_URL}/projects/{project['slug']}", wait_until="domcontentloaded"
)
page.locator(".project-detail-actions").wait_for(state="visible")
assert not page.locator(".project-detail-actions a[data-editor-open]").count()
def test_a_running_workspace_whose_editor_is_not_listening_reads_starting(alice):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Not Listening")
_workspace_for(project, row["uid"])
page.goto(
f"{BASE_URL}/projects/{project['slug']}/workspace",
wait_until="domcontentloaded",
)
expect(page.locator("[data-workspace-phase]")).to_have_text("Starting")
expect(_phase_view(page, "starting")).to_be_visible()
expect(_phase_view(page, "starting").locator(".btn.is-loading")).to_be_disabled()
expect(_phase_view(page, "ready")).to_be_hidden()
assert not page.locator(".workspace-actions a[data-editor-open]").is_visible()
def test_start_shows_progress_and_flips_to_open_editor_when_the_editor_answers(
alice, editor_listener
):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Start Flow")
instance = _workspace_for(
project,
row["uid"],
editor_port=editor_listener,
status="stopped",
desired_state="stopped",
)
page.goto(
f"{BASE_URL}/projects/{project['slug']}/workspace",
wait_until="domcontentloaded",
)
expect(page.locator("[data-workspace-phase]")).to_have_text("Stopped")
start = _phase_view(page, "stopped").locator("button[type='submit']")
expect(start).to_be_visible()
start.click()
expect(_phase_view(page, "starting")).to_be_visible(timeout=TRANSITION_TIMEOUT_MS)
expect(_phase_view(page, "stopped")).to_be_hidden()
expect(page.locator("[data-workspace-phase]")).to_have_text("Starting")
assert store.get_instance(instance["uid"])["desired_state"] == "running"
store.update_instance(instance["uid"], {"status": "running"})
launch = page.locator(".workspace-actions a[data-editor-open]")
launch.wait_for(state="visible", timeout=TRANSITION_TIMEOUT_MS)
expect(page.locator("[data-workspace-phase]")).to_have_text("Ready")
expect(_phase_view(page, "starting")).to_be_hidden()
expect(launch).to_have_attribute(
"href",
f"/projects/{project['slug']}/containers/instances/{instance['uid']}/code/",
)
def test_stop_shows_progress_until_the_container_is_down(alice, editor_listener):
page, user = alice
row = _row_for(user)
project = _project_for(row["uid"], "WS Stop Flow")
instance = _workspace_for(project, row["uid"], editor_port=editor_listener)
page.goto(
f"{BASE_URL}/projects/{project['slug']}/workspace",
wait_until="domcontentloaded",
)
expect(page.locator("[data-workspace-phase]")).to_have_text("Ready")
_phase_view(page, "ready").locator("form[data-workspace-action] button").click()
expect(_phase_view(page, "stopping")).to_be_visible(timeout=TRANSITION_TIMEOUT_MS)
expect(page.locator("[data-workspace-phase]")).to_have_text("Stopping")
assert store.get_instance(instance["uid"])["desired_state"] == "stopped"
store.update_instance(instance["uid"], {"status": "stopped"})
expect(_phase_view(page, "stopped")).to_be_visible(timeout=TRANSITION_TIMEOUT_MS)
expect(page.locator("[data-workspace-phase]")).to_have_text("Stopped")