This commit is contained in:
2026-08-10 00:23:20 +02:00
parent 2bdcf6528f
commit 6cac64a3f6
63 changed files with 3499 additions and 68 deletions
@@ -0,0 +1 @@
# retoor <retoor@molodetz.nl>
+509
View File
@@ -0,0 +1,509 @@
# retoor <retoor@molodetz.nl>
from datetime import timedelta
import pytest
import requests
from tests.conftest import BASE_URL, run_async
from devplacepy.database import db, get_table, init_db, refresh_snapshot
from devplacepy.utils import generate_uid
from devplacepy import config, project_files
from starlette.requests import Request
from starlette.websockets import WebSocket
from devplacepy.services.containers import api, forward, store, runtime
from devplacepy.services.containers.backend.base import Mount, PortMapping, RunSpec
from devplacepy.services.containers.backend.docker_cli import build_run_argv, parse_size
from devplacepy.services.containers.backend.fake import FakeBackend
from devplacepy.services.containers.service import ContainerService
from devplacepy.services.containers.api import INSTANCE_LABEL
from devplacepy.services.devii.tasks.schedule import Schedule, now_utc
_CONTAINER_TABLES = (
"instances",
"instance_events",
"instance_metrics",
"instance_schedules",
)
@pytest.fixture(autouse=True)
def _init_db_containers():
init_db()
yield
@pytest.fixture
def env(tmp_path, monkeypatch):
fake = FakeBackend()
runtime.set_backend(fake)
monkeypatch.setattr("devplacepy.config.CONTAINER_WORKSPACES_DIR", tmp_path / "ws")
pid = "ctest-p1"
project = {"uid": pid, "slug": "ctest", "title": "C", "user_uid": "ctest-u1"}
user = {"uid": "ctest-u1", "username": "ctestadmin"}
get_table("users").insert(
{
"uid": user["uid"],
"username": user["username"],
"terms_version": "1",
"email": "ctestadmin@example.com",
"api_key": generate_uid(),
"password_hash": "",
"role": "Member",
"is_active": True,
"level": 1,
"xp": 0,
"stars": 0,
"deleted_at": None,
"deleted_by": None,
}
)
refresh_snapshot()
project_files.write_text_file(pid, user, "app.py", "print(1)\n")
yield {"fake": fake, "project": project, "user": user}
runtime.set_backend(None)
get_table("users").delete(uid=user["uid"])
for table in _CONTAINER_TABLES:
if table in db.tables:
for row in [r for r in get_table(table).find()]:
if str(row.get("project_uid", "")).startswith("ctest"):
get_table(table).delete(uid=row["uid"])
for row in list(get_table("project_files").find()):
if str(row.get("project_uid", "")).startswith("ctest"):
get_table("project_files").delete(uid=row["uid"])
def _ready_instance(env, **kwargs):
return run_async(
api.create_instance(env["project"], name=kwargs.pop("name", "inst"), **kwargs)
)
def _promote_admin(username: str) -> None:
users = get_table("users")
user = users.find_one(username=username)
if user:
users.update({"uid": user["uid"], "role": "Admin"}, ["uid"])
def _api_key(username: str) -> str:
refresh_snapshot()
return get_table("users").find_one(username=username)["api_key"]
def test_build_run_argv_exact():
spec = RunSpec(
image="ppy:latest",
name="inst",
labels={INSTANCE_LABEL: "u1"},
env={"A": "B"},
cpu_limit="1.5",
mem_limit="512m",
ports=[PortMapping(8080, 80)],
mounts=[Mount("/ws", "/app")],
restart_policy="on-failure",
command=["python", "app.py"],
)
argv = build_run_argv(spec)
assert argv[:5] == ["docker", "run", "-d", "--name", "inst"]
assert "--label" in argv and f"{INSTANCE_LABEL}=u1" in argv
assert "--cpus" in argv and "1.5" in argv
assert "-p" in argv and "8080:80/tcp" in argv
assert "-v" in argv and "/ws:/app:rw" in argv
assert "--restart" in argv and "on-failure" in argv
assert argv[-3:] == ["ppy:latest", "python", "app.py"]
def test_never_policy_not_passed_to_docker():
spec = RunSpec(image="ppy:latest", name="n", restart_policy="never")
assert "--restart" not in build_run_argv(spec)
def test_parse_size():
assert parse_size("1.0GiB") == 1024**3
assert parse_size("512MB") == 512 * 1024**2
def test_create_instance_uses_shared_image(env):
inst = run_async(
api.create_instance(
env["project"], name="inst", actor=("user", env["user"]["uid"])
)
)
assert inst["name"] == "inst"
assert inst["owner_uid"] == "ctest-u1"
spec = api.run_spec_for(inst, config.CONTAINER_IMAGE)
assert spec.image == config.CONTAINER_IMAGE
assert any(m.container == "/app" for m in spec.mounts)
def test_create_instance_requires_built_image(env):
async def no_image(ref):
return False
env["fake"].image_exists = no_image
with pytest.raises(api.ContainerError):
run_async(api.create_instance(env["project"], name="inst"))
def test_reconcile_launches_and_stops(env):
inst = _ready_instance(env, restart_policy="never", autostart=True)
assert inst["desired_state"] == store.DESIRED_RUNNING
service = ContainerService()
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
assert inst["status"] == store.ST_RUNNING and inst["container_id"]
assert [r.name for r in run_async(env["fake"].ps())] == [inst["slug"]]
api.set_desired_state(inst, store.DESIRED_STOPPED)
run_async(service.run_once())
refresh_snapshot()
assert store.get_instance(inst["uid"])["status"] == store.ST_STOPPED
def test_manual_start_relaunches_after_never_policy_exit(env):
fake = env["fake"]
inst = _ready_instance(env, restart_policy="never", autostart=True)
service = ContainerService()
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
cid = inst["container_id"]
assert inst["status"] == store.ST_RUNNING and cid
fake._set_state(cid, "exited", 0)
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
assert inst["status"] == store.ST_STOPPED
assert inst["desired_state"] == store.DESIRED_STOPPED
api.set_desired_state(inst, store.DESIRED_RUNNING)
refresh_snapshot()
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
assert inst["status"] == store.ST_RUNNING
assert inst["desired_state"] == store.DESIRED_RUNNING
assert cid in fake.removed
assert inst["container_id"] and inst["container_id"] != cid
def test_reconcile_reaps_orphan(env):
fake = env["fake"]
run_async(
fake.run(
RunSpec(
image="ppy:latest", name="ghost", labels={INSTANCE_LABEL: "missing-uid"}
)
)
)
service = ContainerService()
run_async(service.run_once())
assert not run_async(fake.ps())
assert fake.removed
def test_reconcile_removes_marked_instance(env):
inst = _ready_instance(env, autostart=True)
service = ContainerService()
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
api.mark_for_removal(inst)
run_async(service.run_once())
refresh_snapshot()
assert store.get_instance(inst["uid"]) is None
assert not run_async(env["fake"].ps())
def test_schedule_fires(env):
inst = _ready_instance(env, autostart=False)
assert inst["desired_state"] == store.DESIRED_STOPPED
past = Schedule(kind="once", run_at=now_utc() - timedelta(hours=1))
api.add_schedule(inst, "start", past)
service = ContainerService()
run_async(service._fire_schedules())
refresh_snapshot()
assert store.get_instance(inst["uid"])["desired_state"] == store.DESIRED_RUNNING
def test_ingress_validation(env):
from devplacepy.services.containers.backend.base import PortMapping
assert api.validate_ingress("zwoeks", 8899, [PortMapping(8899, 8899)]) == (
"zwoeks",
8899,
)
assert api.validate_ingress("", None, []) == ("", 0)
with pytest.raises(api.ContainerError):
api.validate_ingress("BAD SLUG", None, [PortMapping(80, 80)])
with pytest.raises(api.ContainerError):
api.validate_ingress("x", 9999, [PortMapping(80, 80)])
store.create_instance(
{
"uid": "z",
"project_uid": "ctest-p1",
"name": "z",
"ports_json": "[]",
"ingress_slug": "taken",
}
)
with pytest.raises(api.ContainerError):
api.validate_ingress("taken", None, [PortMapping(80, 80)])
def test_validate_boot_languages():
assert api.validate_boot("none", "ignored") == ("none", "")
assert api.validate_boot("python", "print(1)") == ("python", "print(1)")
with pytest.raises(api.ContainerError):
api.validate_boot("ruby", "puts 1")
with pytest.raises(api.ContainerError):
api.validate_boot("bash", " ")
def test_boot_script_precedence_in_run_spec(env):
inst = _ready_instance(
env,
boot_language="python",
boot_script="print('boot')\n",
boot_command="python other.py",
autostart=False,
)
spec = api.run_spec_for(inst, config.CONTAINER_IMAGE)
assert spec.command == ["python", "/app/.devplace_boot.py"]
inst2 = _ready_instance(
env, name="i2", boot_command="python serve.py", autostart=False
)
spec2 = api.run_spec_for(inst2, config.CONTAINER_IMAGE)
assert spec2.command == ["/bin/sh", "-c", "python serve.py"]
def test_materialize_boot_script_writes_file(env, tmp_path):
workspace = tmp_path / "boot-ws"
inst = _ready_instance(
env, name="boot", boot_language="bash", boot_script="echo hi\n", autostart=False
)
store.update_instance(inst["uid"], {"workspace_dir": str(workspace)})
inst = store.get_instance(inst["uid"])
api.materialize_boot_script(inst)
written = workspace / ".devplace_boot.sh"
assert written.is_file()
assert written.read_text() == "echo hi\n"
def test_run_as_uid_drives_pravda_env(env):
_promote_admin(env["user"]["username"])
key = _api_key(env["user"]["username"])
inst = _ready_instance(
env, name="runas", run_as_uid=env["user"]["uid"], autostart=False
)
pravda = api.pravda_env(inst)
assert pravda["DEVPLACE_API_KEY"] == key
assert pravda["DEVPLACE_USER_UID"] == env["user"]["uid"]
def test_run_as_uid_rejects_unknown_user(env):
with pytest.raises(api.ContainerError):
run_async(
api.create_instance(
env["project"], name="bad", run_as_uid="nope-uid", autostart=False
)
)
def _other_member(uid="ctest-u2", username="ctestmember"):
users = get_table("users")
if not users.find_one(uid=uid):
users.insert(
{
"uid": uid,
"username": username,
"terms_version": "1",
"email": f"{username}@example.com",
"api_key": generate_uid(),
"password_hash": "",
"role": "Member",
"is_active": True,
"created_at": "2020-01-01T00:00:00+00:00",
}
)
refresh_snapshot()
return uid
def test_run_as_another_member_needs_their_credential_consent(env):
from devplacepy.database import set_consent
other = _other_member()
set_consent("user", other, "container_credentials", False)
with pytest.raises(api.ContainerError) as refused:
run_async(
api.create_instance(
env["project"],
name="noconsent",
run_as_uid=other,
autostart=False,
actor=("user", env["user"]["uid"]),
)
)
assert "consent" in str(refused.value).lower()
def test_run_as_another_member_is_allowed_once_they_consent(env):
from devplacepy.database import set_consent
other = _other_member()
set_consent("user", other, "container_credentials", True)
instance = run_async(
api.create_instance(
env["project"],
name="withconsent",
run_as_uid=other,
autostart=False,
actor=("user", env["user"]["uid"]),
)
)
assert instance["run_as_uid"] == other
def test_run_as_yourself_never_needs_a_consent(env):
instance = run_async(
api.create_instance(
env["project"],
name="selfrunas",
run_as_uid=env["user"]["uid"],
autostart=False,
actor=("user", env["user"]["uid"]),
)
)
assert instance["run_as_uid"] == env["user"]["uid"]
def test_start_on_boot_pass_forces_running(env):
inst = _ready_instance(env, name="boot", start_on_boot=True, autostart=False)
assert inst["desired_state"] == store.DESIRED_STOPPED
service = ContainerService()
service._boot_pass(store.all_instances())
refresh_snapshot()
assert store.get_instance(inst["uid"])["desired_state"] == store.DESIRED_RUNNING
def test_status_change_records_event(env):
inst = _ready_instance(env, name="hist", restart_policy="never", autostart=True)
service = ContainerService()
run_async(service.run_once())
refresh_snapshot()
inst = store.get_instance(inst["uid"])
api.set_desired_state(inst, store.DESIRED_STOPPED)
run_async(service.run_once())
refresh_snapshot()
events = [e["event"] for e in store.list_events(inst["uid"])]
assert "status_change" in events
def test_bidirectional_sync_newer_wins(env, tmp_path):
workspace = tmp_path / "sync-ws"
workspace.mkdir()
pid = env["project"]["uid"]
user = env["user"]
project_files.write_text_file(pid, user, "shared.txt", "from project\n")
fs_only = workspace / "fromfs.txt"
fs_only.write_text("from fs\n")
counts = project_files.sync_dir_bidirectional(pid, str(workspace), user)
assert counts["exported"] >= 1
assert counts["imported"] >= 1
assert (workspace / "shared.txt").read_text() == "from project\n"
imported = project_files.read_file(pid, "fromfs.txt")
assert imported["content"] == "from fs\n"
def _proxy_scope(kind: str, headers: dict, scheme: str, query: str = "") -> dict:
return {
"type": kind,
"scheme": scheme,
"server": ("devplace.net", 443),
"path": "/projects/demo/containers/instances/abc/code/stable-1",
"query_string": query.encode(),
"headers": [
(name.encode(), value.encode()) for name, value in headers.items()
],
}
def _proxy_websocket(headers: dict, scheme: str = "wss", query: str = "") -> WebSocket:
return WebSocket(_proxy_scope("websocket", headers, scheme, query), None, None)
def _proxy_request(headers: dict, scheme: str = "https", query: str = "") -> Request:
return Request(_proxy_scope("http", headers, scheme, query))
def test_ws_headers_send_the_public_host_only_as_x_forwarded_host():
headers = forward.ws_headers(
_proxy_websocket(
{
"host": "devplace.net",
"origin": "https://devplace.net",
"cookie": "session=abc",
}
)
)
assert headers["X-Forwarded-Host"] == "devplace.net"
assert headers["origin"] == "https://devplace.net"
assert headers["cookie"] == "session=abc"
assert not [name for name in headers if name.lower() == "host"]
def test_ws_headers_drop_only_the_handshake_headers_the_client_regenerates():
headers = forward.ws_headers(
_proxy_websocket(
{
"host": "devplace.net",
"x-real-ip": "203.0.113.7",
"accept-language": "nl-NL",
"sec-websocket-key": "should-not-survive",
"sec-websocket-version": "13",
"sec-websocket-extensions": "permessage-deflate",
"sec-websocket-protocol": "v1",
}
)
)
assert headers["x-real-ip"] == "203.0.113.7"
assert headers["accept-language"] == "nl-NL"
for name in forward.WS_HANDSHAKE_HEADERS:
assert name not in headers
def test_ws_headers_derive_the_forwarded_proto_from_the_socket_scheme():
assert forward.ws_headers(_proxy_websocket({"host": "d.net"}))[
"X-Forwarded-Proto"
] == "https"
assert forward.ws_headers(_proxy_websocket({"host": "d.net"}, scheme="ws"))[
"X-Forwarded-Proto"
] == "http"
assert forward.ws_headers(
_proxy_websocket({"host": "d.net", "x-forwarded-proto": "https"}, scheme="ws")
)["X-Forwarded-Proto"] == "https"
def test_forward_headers_send_the_public_host_and_the_prefix():
headers = forward.forward_headers(
_proxy_request({"host": "devplace.net", "x-real-ip": "203.0.113.7"}),
prefix="/p/demo",
)
assert headers["Host"] == "devplace.net"
assert headers["X-Forwarded-Host"] == "devplace.net"
assert headers["X-Forwarded-Prefix"] == "/p/demo"
assert headers["X-Script-Name"] == "/p/demo"
assert headers["x-real-ip"] == "203.0.113.7"
def test_ws_subprotocols_are_parsed_for_negotiation():
assert forward.ws_subprotocols(_proxy_websocket({"host": "d.net"})) is None
assert forward.ws_subprotocols(
_proxy_websocket({"host": "d.net", "sec-websocket-protocol": "v2, v1"})
) == ["v2", "v1"]
def test_upstream_url_encodes_the_path_and_keeps_the_query_verbatim():
assert (
forward.upstream_url("http", "h:1", "dir/a b#c", "keep=1")
== "http://h:1/dir/a%20b%23c?keep=1"
)
assert forward.upstream_url("ws", "h:1", "", "") == "ws://h:1/"
def test_raw_query_survives_a_hash_in_the_path():
request = _proxy_request({"host": "d.net"}, query="keep=1")
request.scope["path"] = "/weird/a b#c"
assert forward.raw_query(request) == "keep=1"
assert request.url.query == ""
@@ -0,0 +1 @@
# retoor <retoor@molodetz.nl>
@@ -0,0 +1,362 @@
# retoor <retoor@molodetz.nl>
import json
import pytest
from devplacepy import config
from devplacepy.database import get_table, init_db, set_setting
from devplacepy.services.containers.backend.base import (
WORKSPACE_MOUNT,
WORKSPACE_STATE_MOUNT,
)
from devplacepy.services.containers.workspace import editor, quota
OWNER = "editor-owner"
OTHER = "editor-other"
@pytest.fixture(autouse=True)
def _editor_db(tmp_path, monkeypatch):
init_db()
monkeypatch.setattr(config, "WORKSPACE_STATE_DIR", tmp_path / "state")
yield
get_table(editor.PREFS_TABLE).delete()
get_table(quota.RULES_TABLE).delete()
for key, default in editor.DEFAULTS.items():
setting = editor.SETTING_KEYS[key]
if isinstance(default, bool):
set_setting(setting, "1" if default else "0")
else:
set_setting(setting, str(default))
def _instance(uid: str = "ws-editor") -> dict:
return {"uid": uid, "editor_port": editor.EDITOR_DEFAULT_PORT, "is_workspace": 1}
def _prefs(**values) -> dict:
return editor.save_prefs(OWNER, values)
def test_resolve_uses_site_defaults():
profile = editor.resolve(OWNER)
assert profile.theme == editor.DEFAULTS["theme"]
assert profile.font_size == editor.DEFAULTS["font_size"]
assert profile.zoom_level == editor.DEFAULTS["zoom_level"]
assert profile.boot_agent == editor.DEFAULTS["boot_agent"]
assert profile.trust_all is True
def test_resolve_reads_a_changed_site_setting():
set_setting("workspace_editor_font_size", "22")
assert editor.resolve(OWNER).font_size == 22
def test_resolve_prefers_the_user_row():
_prefs(font_size=20, theme="devplace-light")
profile = editor.resolve(OWNER)
assert profile.font_size == 20
assert profile.theme == "devplace-light"
def test_a_user_row_does_not_leak_to_another_user():
_prefs(font_size=20)
assert editor.resolve(OTHER).font_size == editor.DEFAULTS["font_size"]
def test_zero_and_empty_inherit_but_zoom_zero_does_not():
set_setting("workspace_editor_font_size", "22")
set_setting("workspace_editor_zoom_level", "3")
_prefs(font_size=0, theme="", zoom_level=0)
profile = editor.resolve(OWNER)
assert profile.font_size == 22
assert profile.theme == editor.DEFAULTS["theme"]
assert profile.zoom_level == 0
def test_the_zoom_sentinel_inherits():
set_setting("workspace_editor_zoom_level", "3")
_prefs(zoom_level=editor.INHERIT_ZOOM)
assert editor.resolve(OWNER).zoom_level == 3
def test_the_boot_shell_sentinel_inherits_but_zero_does_not():
_prefs(boot_shell=editor.INHERIT_FLAG)
assert editor.resolve(OWNER).boot_shell is True
_prefs(boot_shell=0)
assert editor.resolve(OWNER).boot_shell is False
def test_out_of_range_stored_values_are_clamped():
_prefs(font_size=999, zoom_level=99, window_width=1)
profile = editor.resolve(OWNER)
assert profile.font_size == editor.BOUNDS["font_size"][1]
assert profile.zoom_level == editor.BOUNDS["zoom_level"][1]
assert profile.window_width == editor.BOUNDS["window_width"][0]
def test_an_unknown_choice_falls_back_to_the_default():
_prefs(theme="hot-pink", layout="chaos", window_mode="teleport")
profile = editor.resolve(OWNER)
assert profile.theme == editor.DEFAULTS["theme"]
assert profile.layout == editor.DEFAULTS["layout"]
assert profile.window_mode == editor.DEFAULTS["window_mode"]
def test_the_container_size_comes_from_the_quota_resolver():
profile = editor.resolve(OWNER, {"workspace_cpu_millicores": 3000})
assert profile.cpu_millicores == 3000
assert profile.cpu_cores() == 3.0
assert profile.cpu_limit() == "3"
assert profile.mem_limit() == quota.format_memory(profile.memory_mb)
def test_source_map_reports_where_each_value_came_from():
_prefs(font_size=20)
sources = editor.source_map(OWNER)
assert sources["font_size"] == editor.SOURCE_USER
assert sources["theme"] == editor.SOURCE_SITE
assert sources["trust_all"] == editor.SOURCE_SITE
def test_reset_removes_the_row_and_restores_the_defaults():
_prefs(font_size=20)
assert editor.reset_prefs(OWNER, OWNER) is True
assert editor.resolve(OWNER).font_size == editor.DEFAULTS["font_size"]
assert editor.source_map(OWNER)["font_size"] == editor.SOURCE_SITE
def test_reset_on_a_user_with_no_row_is_a_no_op():
assert editor.reset_prefs(OWNER, OWNER) is False
def test_saving_twice_updates_one_row():
_prefs(font_size=20)
_prefs(font_size=21)
rows = list(get_table(editor.PREFS_TABLE).find(owner_id=OWNER, deleted_at=None))
assert len(rows) == 1
assert rows[0]["font_size"] == 21
def test_merge_managed_seeds_an_absent_key():
merged, managed = editor.merge_managed({}, {}, {"editor.fontSize": 14})
assert merged["editor.fontSize"] == 14
assert managed == {"editor.fontSize": 14}
def test_merge_managed_updates_a_value_we_still_own():
merged, _ = editor.merge_managed(
{"editor.fontSize": 14}, {"editor.fontSize": 14}, {"editor.fontSize": 18}
)
assert merged["editor.fontSize"] == 18
def test_merge_managed_respects_a_member_edit_forever():
current = {"editor.fontSize": 30}
managed = {"editor.fontSize": 14}
for desired in (16, 18, 20):
current, managed = editor.merge_managed(
current, managed, {"editor.fontSize": desired}
)
assert current["editor.fontSize"] == 30
def test_merge_managed_keeps_unrelated_member_keys():
merged, _ = editor.merge_managed(
{"files.autoSave": "on"}, {}, {"editor.fontSize": 14}
)
assert merged["files.autoSave"] == "on"
def test_merge_managed_is_idempotent():
desired = editor.settings_for(editor.resolve(OWNER))
first, managed = editor.merge_managed({}, {}, desired)
second, _ = editor.merge_managed(first, managed, desired)
assert first == second
def test_settings_disable_workspace_trust_when_trust_all():
settings = editor.settings_for(editor.resolve(OWNER))
assert settings["security.workspace.trust.enabled"] is False
assert settings["security.workspace.trust.startupPrompt"] == "never"
assert settings["task.allowAutomaticTasks"] == "on"
def test_settings_leave_trust_alone_when_the_kill_switch_is_off():
set_setting("workspace_editor_trust_all", "0")
settings = editor.settings_for(editor.resolve(OWNER))
assert "security.workspace.trust.enabled" not in settings
def test_settings_carry_the_devplace_terminal_profile():
settings = editor.settings_for(editor.resolve(OWNER))
profiles = settings["terminal.integrated.profiles.linux"]
assert profiles["DevPlace Code"]["path"] == "/usr/bin/dpc"
assert settings["terminal.integrated.defaultProfile.linux"] == "bash"
def test_settings_apply_the_layout_preset():
_prefs(layout="zen")
settings = editor.settings_for(editor.resolve(OWNER))
assert settings["workbench.activityBar.location"] == "hidden"
assert settings["editor.minimap.enabled"] is False
def test_the_system_theme_is_left_to_the_member():
_prefs(theme="system")
assert "workbench.colorTheme" not in editor.settings_for(editor.resolve(OWNER))
def test_seed_state_writes_the_whole_tree():
instance = _instance()
profile = editor.resolve(OWNER)
assert editor.seed_state(instance, profile) is True
root = editor.state_dir(instance)
settings = json.loads((root / "data" / "User" / "settings.json").read_text())
managed = json.loads((root / "data" / "User" / editor.MANAGED_FILE).read_text())
payload = json.loads((root / editor.PROFILE_FILE).read_text())
assert settings["editor.fontSize"] == profile.font_size
assert managed["editor.fontSize"] == profile.font_size
assert payload["editor"]["theme"] == profile.theme
assert payload["app_name"] == editor.APP_NAME
def test_seed_state_never_clobbers_a_member_edit():
instance = _instance()
editor.seed_state(instance, editor.resolve(OWNER))
settings_path = editor.state_dir(instance) / "data" / "User" / "settings.json"
stored = json.loads(settings_path.read_text())
stored["editor.fontSize"] = 30
stored["files.autoSave"] = "on"
settings_path.write_text(json.dumps(stored))
set_setting("workspace_editor_font_size", "18")
set_setting("workspace_editor_terminal_font_size", "20")
editor.seed_state(instance, editor.resolve(OWNER))
written = json.loads(settings_path.read_text())
assert written["editor.fontSize"] == 30
assert written["files.autoSave"] == "on"
assert written["terminal.integrated.fontSize"] == 20
def test_seed_state_replaces_an_unparseable_settings_file():
instance = _instance()
user_dir = editor.state_dir(instance) / "data" / "User"
user_dir.mkdir(parents=True, exist_ok=True)
(user_dir / "settings.json").write_text("{not json at all")
assert editor.seed_state(instance, editor.resolve(OWNER)) is True
assert json.loads((user_dir / "settings.json").read_text())["editor.fontSize"]
def test_seed_state_fails_soft_on_an_unwritable_directory(monkeypatch):
def explode(*args, **kwargs):
raise OSError("read-only file system")
monkeypatch.setattr("pathlib.Path.mkdir", explode)
assert editor.seed_state(_instance(), editor.resolve(OWNER)) is False
def test_argv_carries_the_devplace_brand():
instance = _instance()
command = editor.argv(instance, editor.resolve(OWNER))
assert command[0] == "code-server"
assert command[command.index("--app-name") + 1] == editor.APP_NAME
assert "--disable-getting-started-override" in command
assert "--disable-telemetry" in command
def test_argv_disables_workspace_trust_when_trust_all():
command = editor.argv(_instance(), editor.resolve(OWNER))
assert "--disable-workspace-trust" in command
def test_argv_keeps_workspace_trust_when_the_kill_switch_is_off():
set_setting("workspace_editor_trust_all", "0")
command = editor.argv(_instance(), editor.resolve(OWNER))
assert "--disable-workspace-trust" not in command
def test_argv_keeps_password_auth():
command = editor.argv(_instance(), editor.resolve(OWNER))
assert command[command.index("--auth") + 1] == "password"
assert "none" not in command
def test_argv_paths_sit_under_the_state_mount():
command = editor.argv(_instance(), editor.resolve(OWNER))
assert command[command.index("--user-data-dir") + 1].startswith(
WORKSPACE_STATE_MOUNT
)
assert command[command.index("--extensions-dir") + 1].startswith(
WORKSPACE_STATE_MOUNT
)
assert command[-1] == WORKSPACE_MOUNT
def test_argv_binds_the_instance_editor_port():
instance = _instance()
instance["editor_port"] = 9443
command = editor.argv(instance, editor.resolve(OWNER))
assert command[command.index("--bind-addr") + 1] == "0.0.0.0:9443"
def test_every_optional_flag_is_declared():
command = editor.argv(_instance(), editor.resolve(OWNER))
for flag in editor.OPTIONAL_FLAGS:
assert flag in command
def test_env_for_exports_the_profile_to_the_container():
env = editor.env_for(editor.resolve(OWNER))
assert env["DEVPLACE_EDITOR_APP_NAME"] == editor.APP_NAME
assert env["DEVPLACE_EDITOR_PROFILE"].endswith(editor.PROFILE_FILE)
assert env["DEVPLACE_EDITOR_BOOT_AGENT"] == editor.DEFAULTS["boot_agent"]
assert env["DEVPLACE_EDITOR_TRUST_ALL"] == "1"
assert all(isinstance(value, str) for value in env.values())
def test_restart_is_not_required_before_the_first_boot():
instance = _instance()
assert editor.restart_required(instance, editor.resolve(OWNER)) is False
def test_restart_is_not_required_when_nothing_changed():
instance = _instance()
profile = editor.resolve(OWNER)
editor.seed_state(instance, profile)
assert editor.restart_required(instance, profile) is False
def test_restart_is_required_after_a_preference_change():
instance = _instance()
editor.seed_state(instance, editor.resolve(OWNER))
_prefs(font_size=27)
assert editor.restart_required(instance, editor.resolve(OWNER)) is True
def test_view_carries_the_profile_and_its_sources():
_prefs(theme="devplace-light")
payload = editor.view(OWNER)
assert payload["theme"] == "devplace-light"
assert payload["sources"]["theme"] == editor.SOURCE_USER
assert payload["cpu_cores"] == editor.resolve(OWNER).cpu_cores()
def test_settings_suppress_a_foreign_ai_assistant():
settings = editor.settings_for(editor.resolve(OWNER))
assert settings["chat.disableAIFeatures"] is True
assert settings["workbench.secondarySideBar.defaultVisibility"] == "hidden"
def test_settings_open_straight_into_the_workspace():
assert editor.settings_for(editor.resolve(OWNER))["workbench.startupEditor"] == "none"
def test_the_agent_working_files_never_sync_into_the_project():
from devplacepy.project_files import SYNC_SKIP_NAMES
assert ".dpc" in SYNC_SKIP_NAMES
assert "dpc.log" in SYNC_SKIP_NAMES
assert ".devplace" in SYNC_SKIP_NAMES
@@ -0,0 +1,112 @@
# retoor <retoor@molodetz.nl>
import pytest
from devplacepy.database import get_table, init_db, set_setting
from devplacepy.services.containers.workspace import quota
from devplacepy.utils import generate_uid
OWNER = "quota-owner"
@pytest.fixture(autouse=True)
def _quota_db():
init_db()
yield
get_table(quota.RULES_TABLE).delete()
for key in quota.SETTING_KEYS.values():
set_setting(key, str(quota.DEFAULTS[_key_for(key)]))
def _key_for(setting: str) -> str:
for key, value in quota.SETTING_KEYS.items():
if value == setting:
return key
raise KeyError(setting)
def _rule(**values) -> dict:
row = {
"uid": generate_uid(),
"owner_kind": "user",
"owner_id": OWNER,
"label": "test",
"created_at": "",
"updated_at": "",
"deleted_at": None,
"deleted_by": None,
**{column: 0 for column in quota.RULE_COLUMNS},
}
row.update(values)
get_table(quota.RULES_TABLE).insert(row)
return row
def test_format_cpu_renders_docker_values():
assert quota.format_cpu(2000) == "2"
assert quota.format_cpu(1500) == "1.5"
assert quota.format_cpu(250) == "0.25"
assert quota.format_cpu(0) == ""
assert quota.format_cpu(-1) == ""
def test_format_memory_renders_docker_values():
assert quota.format_memory(2048) == "2048m"
assert quota.format_memory(0) == ""
def test_limits_expose_the_docker_strings():
limits = quota.resolve()
assert limits.cpu_limit() == quota.format_cpu(limits.cpu_millicores)
assert limits.mem_limit() == quota.format_memory(limits.memory_mb)
def test_defaults_resolve_without_a_rule():
limits = quota.resolve(OWNER)
assert limits.cpu_millicores == quota.DEFAULTS["cpu_millicores"]
assert limits.memory_mb == quota.DEFAULTS["memory_mb"]
def test_a_user_rule_resolves():
_rule(cpu_millicores=4000, memory_mb=8192, disk_quota_mb=512)
limits = quota.resolve(OWNER)
assert limits.cpu_millicores == 4000
assert limits.memory_mb == 8192
assert limits.disk_quota_mb == 512
def test_a_user_rule_does_not_leak_to_another_user():
_rule(cpu_millicores=4000)
assert quota.resolve("someone-else").cpu_millicores == (
quota.DEFAULTS["cpu_millicores"]
)
def test_a_soft_deleted_rule_is_ignored():
row = _rule(cpu_millicores=4000)
get_table(quota.RULES_TABLE).update(
{"uid": row["uid"], "deleted_at": "2026-01-01T00:00:00+00:00"}, ["uid"]
)
assert quota.resolve(OWNER).cpu_millicores == quota.DEFAULTS["cpu_millicores"]
def test_an_instance_override_beats_the_rule():
_rule(cpu_millicores=4000, memory_mb=8192)
limits = quota.resolve(
OWNER, {"workspace_cpu_millicores": 1000, "workspace_memory_mb": 512}
)
assert limits.cpu_millicores == 1000
assert limits.memory_mb == 512
def test_only_declared_instance_overrides_are_read():
limits = quota.resolve(OWNER, {"workspace_max_tunnels": 99})
assert limits.max_tunnels == quota.DEFAULTS["max_tunnels"]
assert "max_tunnels" not in quota.INSTANCE_OVERRIDE_COLUMNS
def test_the_idle_warning_stays_below_the_idle_stop():
set_setting("workspace_idle_stop_minutes", "10")
set_setting("workspace_idle_warn_minutes", "30")
limits = quota.resolve()
assert limits.idle_warn_minutes < limits.idle_stop_minutes