# retoor 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