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
+45
View File
@@ -0,0 +1,45 @@
# retoor <retoor@molodetz.nl>
from devplacepy.services.containers import forward
PREFIX = "/projects/demo/containers/instances/abc/code"
def test_the_root_document_is_the_empty_or_slash_path():
assert forward.is_root_document("") is True
assert forward.is_root_document("/") is True
assert forward.is_root_document("//") is True
def test_a_nested_path_is_never_the_root_document():
for path in (
"index.html",
"login",
"stable-abc/static/out/vs/workbench/contrib/webview/browser/pre/index.html",
"nested/page.html",
"/nested/",
):
assert forward.is_root_document(path) is False, path
def test_inject_base_adds_a_base_after_head():
body = b"<!doctype html><html><head><meta charset=utf-8></head><body>x</body></html>"
injected = forward.inject_base(body, PREFIX)
assert injected.startswith(b"<!doctype html><html><head>" + f'<base href="{PREFIX}/">'.encode())
def test_inject_base_leaves_an_existing_base_alone():
body = b'<html><head><base href="./"></head><body></body></html>'
assert forward.inject_base(body, PREFIX) == body
def test_inject_base_prepends_when_there_is_no_head_or_html():
body = b"<p>fragment</p>"
assert forward.inject_base(body, PREFIX) == f'<base href="{PREFIX}/">'.encode() + body
def test_rewrite_location_prefixes_only_absolute_paths():
assert forward.rewrite_location("/login", PREFIX) == f"{PREFIX}/login"
assert forward.rewrite_location("./login", PREFIX) == "./login"
assert forward.rewrite_location("//cdn.example/x", PREFIX) == "//cdn.example/x"
assert forward.rewrite_location("/login", "") == "/login"
@@ -0,0 +1,178 @@
# retoor <retoor@molodetz.nl>
import itertools
import socket
import pytest
from devplacepy.database import get_table, init_db
from devplacepy.services.containers import store
from devplacepy.services.containers.workspace import provision
OWNER = "provision-owner"
STATUSES = (
store.ST_CREATED,
store.ST_STARTING,
store.ST_RUNNING,
store.ST_STOPPED,
store.ST_PAUSED,
store.ST_CRASHED,
store.ST_RESTARTING,
store.ST_REMOVING,
store.ST_REMOVED,
"",
)
DESIRED = (store.DESIRED_RUNNING, store.DESIRED_STOPPED, store.DESIRED_PAUSED, "")
@pytest.fixture(autouse=True)
def _provision_db():
init_db()
yield
get_table("instances").delete(workspace_owner_uid=OWNER)
def _instance(**overrides) -> dict:
row = {
"uid": "ws-phase",
"status": store.ST_RUNNING,
"desired_state": store.DESIRED_RUNNING,
"suspended_at": "",
"editor_port": 8443,
"ports_json": "[]",
"container_ip": "",
"workspace_owner_uid": OWNER,
}
row.update(overrides)
return row
@pytest.fixture
def listener():
sock = socket.socket()
sock.bind(("127.0.0.1", 0))
sock.listen(4)
try:
yield sock
finally:
sock.close()
@pytest.mark.parametrize(
"status, desired, ready, expected",
[
(store.ST_RUNNING, store.DESIRED_RUNNING, True, provision.PHASE_READY),
(store.ST_RUNNING, store.DESIRED_RUNNING, False, provision.PHASE_STARTING),
(store.ST_CREATED, store.DESIRED_RUNNING, False, provision.PHASE_STARTING),
(store.ST_STOPPED, store.DESIRED_RUNNING, False, provision.PHASE_STARTING),
(store.ST_CRASHED, store.DESIRED_RUNNING, False, provision.PHASE_STARTING),
(store.ST_RUNNING, store.DESIRED_STOPPED, True, provision.PHASE_STOPPING),
(store.ST_PAUSED, store.DESIRED_STOPPED, False, provision.PHASE_STOPPING),
(store.ST_RESTARTING, store.DESIRED_STOPPED, False, provision.PHASE_STOPPING),
(store.ST_CRASHED, store.DESIRED_STOPPED, False, provision.PHASE_CRASHED),
(store.ST_STOPPED, store.DESIRED_STOPPED, False, provision.PHASE_STOPPED),
(store.ST_CREATED, store.DESIRED_STOPPED, False, provision.PHASE_STOPPED),
("", "", False, provision.PHASE_STOPPED),
],
)
def test_phase_follows_desired_state_status_and_the_editor_probe(
status, desired, ready, expected
):
row = _instance(status=status, desired_state=desired)
assert provision.phase(row, ready) == expected
def test_a_suspended_workspace_is_suspended_whatever_else_it_says():
for status, desired, ready in itertools.product(STATUSES, DESIRED, (True, False)):
row = _instance(
status=status, desired_state=desired, suspended_at="2026-01-01T00:00:00"
)
assert provision.phase(row, ready) == provision.PHASE_SUSPENDED
def test_ready_needs_a_running_container_that_wants_to_run():
for status, desired in itertools.product(STATUSES, DESIRED):
row = _instance(status=status, desired_state=desired)
is_ready = provision.phase(row, True) == provision.PHASE_READY
assert is_ready == (
status == store.ST_RUNNING and desired == store.DESIRED_RUNNING
)
def test_every_phase_over_the_whole_input_domain_has_a_label():
for status, desired, ready, suspended in itertools.product(
STATUSES, DESIRED, (True, False), ("", "2026-01-01T00:00:00")
):
row = _instance(status=status, desired_state=desired, suspended_at=suspended)
assert provision.phase(row, ready) in provision.PHASE_LABELS
def test_transitional_phases_are_exactly_starting_and_stopping():
assert set(provision.TRANSITIONAL_PHASES) == {
provision.PHASE_STARTING,
provision.PHASE_STOPPING,
}
def test_editor_ready_when_the_editor_port_accepts_connections(listener):
port = listener.getsockname()[1]
row = _instance(container_ip="127.0.0.1", editor_port=port)
assert provision.editor_ready(row) is True
def test_editor_ready_is_false_when_nothing_listens():
probe = socket.socket()
probe.bind(("127.0.0.1", 0))
port = probe.getsockname()[1]
probe.close()
row = _instance(container_ip="127.0.0.1", editor_port=port)
assert provision.editor_ready(row) is False
def test_editor_ready_is_false_without_a_reachable_target(listener):
row = _instance(container_ip="", ports_json="[]")
assert provision.editor_ready(row) is False
def test_editor_ready_is_false_unless_the_container_runs(listener):
port = listener.getsockname()[1]
for status in STATUSES:
if status == store.ST_RUNNING:
continue
row = _instance(container_ip="127.0.0.1", editor_port=port, status=status)
assert provision.editor_ready(row) is False
def test_editor_ready_is_false_while_suspended(listener):
port = listener.getsockname()[1]
row = _instance(
container_ip="127.0.0.1", editor_port=port, suspended_at="2026-01-01T00:00:00"
)
assert provision.editor_ready(row) is False
def test_view_carries_the_phase_its_label_readiness_and_the_owner(listener):
port = listener.getsockname()[1]
instance = store.create_instance(
{
"project_uid": "provision-project",
"name": "ws-view",
"status": store.ST_RUNNING,
"desired_state": store.DESIRED_RUNNING,
"is_workspace": 1,
"workspace_owner_uid": OWNER,
"container_ip": "127.0.0.1",
"editor_port": port,
"ports_json": "[]",
}
)
view = provision.view(instance)
assert view["phase"] == provision.PHASE_READY
assert view["phase_label"] == "Ready"
assert view["editor_ready"] is True
assert view["owner_uid"] == OWNER
store.update_instance(instance["uid"], {"desired_state": store.DESIRED_STOPPED})
view = provision.view(store.get_instance(instance["uid"]))
assert view["phase"] == provision.PHASE_STOPPING
assert view["editor_ready"] is True
+92
View File
@@ -0,0 +1,92 @@
# retoor <retoor@molodetz.nl>
import pytest
from devplacepy.database import get_table, init_db
from devplacepy.services import live_view_relay
from devplacepy.services.containers import store
from devplacepy.services.containers.workspace import provision
from tests.conftest import run_async
PROJECT_UID = "relay-ws-project"
OWNER = "relay-ws-owner"
OTHER = "relay-ws-other"
@pytest.fixture(autouse=True)
def _relay_db():
init_db()
get_table("projects").insert(
{
"uid": PROJECT_UID,
"user_uid": OWNER,
"title": "Relay",
"description": "",
"slug": "relay-ws",
"project_type": "software",
"status": "In Development",
"platforms": "",
"is_private": 0,
"read_only": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"deleted_at": None,
"deleted_by": None,
}
)
yield
get_table("instances").delete(project_uid=PROJECT_UID)
get_table("projects").delete(uid=PROJECT_UID)
def _handler(topic: str):
for pattern, compute, interval in live_view_relay.VIEWS:
match = pattern.match(topic)
if match is not None:
return compute, match, interval
raise AssertionError(f"no live view for {topic}")
def _workspace(**overrides) -> dict:
row = {
"project_uid": PROJECT_UID,
"name": "ws-relay",
"status": store.ST_RUNNING,
"desired_state": store.DESIRED_RUNNING,
"is_workspace": 1,
"workspace_owner_uid": OWNER,
"ports_json": "[]",
}
row.update(overrides)
return store.create_instance(row)
def test_the_workspace_topic_lives_in_the_owner_namespace():
instance = _workspace()
compute, match, interval = _handler(f"user.{OWNER}.workspace.{instance['uid']}")
assert compute is live_view_relay._workspace_detail
assert interval == 3.0
payload = run_async(compute(match))
assert payload["workspace"]["uid"] == instance["uid"]
assert payload["workspace"]["phase"] == provision.PHASE_STARTING
assert payload["workspace"]["editor_ready"] is False
assert payload["editor_url"] == (
f"/projects/relay-ws/containers/instances/{instance['uid']}/code/"
)
def test_the_workspace_topic_never_answers_another_owner():
instance = _workspace()
compute, match, _interval = _handler(f"user.{OTHER}.workspace.{instance['uid']}")
assert run_async(compute(match)) is None
def test_the_workspace_topic_ignores_a_plain_container():
instance = _workspace(is_workspace=0)
compute, match, _interval = _handler(f"user.{OWNER}.workspace.{instance['uid']}")
assert run_async(compute(match)) is None
def test_the_workspace_topic_ignores_an_unknown_instance():
compute, match, _interval = _handler(f"user.{OWNER}.workspace.does-not-exist")
assert run_async(compute(match)) is None