summaryrefslogtreecommitdiff
path: root/tests/test_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_server.py')
-rw-r--r--tests/test_server.py221
1 files changed, 221 insertions, 0 deletions
diff --git a/tests/test_server.py b/tests/test_server.py
index c7f93d5..3a80e80 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -10,8 +10,11 @@ import unittest
from unittest.mock import patch
from sailfish_devel_mcp.config import (
+ AndroidBuildHostConfig,
BUNDLED_BUILD_HELPER,
Config,
+ DEFAULT_ANDROID_BUILD_PROJECT_DIR,
+ DEFAULT_ANDROID_BUILD_SSH_TARGET,
DeviceConfig,
PathConfig,
load_config,
@@ -69,6 +72,9 @@ class McpServerTests(unittest.TestCase):
self.assertIn("sailfish_device_user_session_command", names)
self.assertIn("sailfish_device_browser_launch", names)
self.assertIn("sailfish_sdk_refresh_metadata", names)
+ self.assertIn("sailfish_android_build_hosts", names)
+ self.assertIn("sailfish_android_build", names)
+ self.assertIn("sailfish_android_build_status", names)
self.assertIn("sailfish_qml_check_translator_ternaries", names)
def test_qml_ternary_checker_reports_inline_ternary_qstrid(self) -> None:
@@ -235,6 +241,15 @@ class McpServerTests(unittest.TestCase):
self.assertIsNone(config.paths.local_sdk)
self.assertTrue(config.paths.build_sailfishos.exists())
self.assertNotIn("build-sailfishos-skill", str(config.paths.build_sailfishos))
+ self.assertEqual(config.default_android_build_host, "android-builder")
+ self.assertEqual(
+ config.android_build_hosts["android-builder"].ssh_target,
+ DEFAULT_ANDROID_BUILD_SSH_TARGET,
+ )
+ self.assertEqual(
+ config.android_build_hosts["android-builder"].project_dir,
+ DEFAULT_ANDROID_BUILD_PROJECT_DIR,
+ )
def test_config_loads_local_sdk_from_paths(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
@@ -459,6 +474,87 @@ class McpServerTests(unittest.TestCase):
self.assertIn("DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/100000/dbus/user_bus_socket", remote)
self.assertIn("systemctl --user status app.service", remote)
+ def test_device_install_rpm_keeps_single_remote_path_override(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ root = Path(tmp)
+ rpm = root / "sample.rpm"
+ rpm.write_text("rpm", encoding="utf-8")
+ server = self.make_server(root)
+ with patch("sailfish_devel_mcp.tools.run") as mocked_run:
+ mocked_run.side_effect = [
+ CommandResult(("scp",), 0, "", ""),
+ CommandResult(("ssh",), 0, "installed\n", ""),
+ ]
+ response = server.handle(
+ {
+ "jsonrpc": "2.0",
+ "id": 18,
+ "method": "tools/call",
+ "params": {
+ "name": "sailfish_device_install_rpm",
+ "arguments": {
+ "rpm_path": str(rpm),
+ "remote_path": "/tmp/custom.rpm",
+ },
+ },
+ }
+ )
+
+ self.assertFalse(response["result"].get("isError", False))
+ calls = [list(call.args[0]) for call in mocked_run.call_args_list]
+ self.assertEqual(len(calls), 2)
+ self.assertEqual(calls[0][-1], "root@test:/tmp/custom.rpm")
+ self.assertEqual(calls[1][-1], "pkcon install-local -y /tmp/custom.rpm")
+ self.assertEqual(
+ response["result"]["structuredContent"]["remote_paths"],
+ ["/tmp/custom.rpm"],
+ )
+
+ def test_device_install_rpm_installs_multiple_rpms_together(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ root = Path(tmp)
+ rpm_a = root / "sample.rpm"
+ rpm_b = root / "sample-deps.rpm"
+ rpm_a.write_text("rpm", encoding="utf-8")
+ rpm_b.write_text("rpm", encoding="utf-8")
+ server = self.make_server(root)
+ with patch("sailfish_devel_mcp.tools.run") as mocked_run:
+ mocked_run.side_effect = [
+ CommandResult(("ssh",), 0, "", ""),
+ CommandResult(("scp",), 0, "", ""),
+ CommandResult(("scp",), 0, "", ""),
+ CommandResult(("ssh",), 0, "installed\n", ""),
+ ]
+ response = server.handle(
+ {
+ "jsonrpc": "2.0",
+ "id": 19,
+ "method": "tools/call",
+ "params": {
+ "name": "sailfish_device_install_rpm",
+ "arguments": {
+ "rpm_paths": [str(rpm_a), str(rpm_b)],
+ "remote_dir": "/tmp/test-rpms",
+ "installer": "rpm",
+ },
+ },
+ }
+ )
+
+ self.assertFalse(response["result"].get("isError", False))
+ calls = [list(call.args[0]) for call in mocked_run.call_args_list]
+ self.assertEqual(calls[0][-1], "mkdir -p /tmp/test-rpms")
+ self.assertEqual(calls[1][-1], "root@test:/tmp/test-rpms/sample.rpm")
+ self.assertEqual(calls[2][-1], "root@test:/tmp/test-rpms/sample-deps.rpm")
+ self.assertEqual(
+ calls[3][-1],
+ "rpm -Uvh --replacepkgs /tmp/test-rpms/sample.rpm /tmp/test-rpms/sample-deps.rpm",
+ )
+ self.assertEqual(
+ response["result"]["structuredContent"]["remote_paths"],
+ ["/tmp/test-rpms/sample.rpm", "/tmp/test-rpms/sample-deps.rpm"],
+ )
+
def test_browser_launch_uses_display_env_and_reports_topmost_pid(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
server = self.make_server(Path(tmp))
@@ -713,6 +809,131 @@ class McpServerTests(unittest.TestCase):
self.assertIn("--arch", argv)
self.assertIn("aarch64", argv)
+ def test_android_build_starts_remote_nohup_job(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ root = Path(tmp)
+ config = Config(
+ path=None,
+ default_device="phone",
+ devices={"phone": DeviceConfig(name="phone", ssh_target="root@phone")},
+ paths=PathConfig(git_root=root, ssh_config=root / "ssh_config"),
+ default_android_build_host="android-builder",
+ android_build_hosts={
+ "android-builder": AndroidBuildHostConfig(
+ name="android-builder",
+ ssh_target="builder@example.invalid",
+ project_dir="/remote/a15",
+ state_dir="/remote/state/android-builds",
+ )
+ },
+ )
+ server = McpServer(config)
+ with patch("sailfish_devel_mcp.tools.run") as mocked_run:
+ mocked_run.return_value = CommandResult(
+ ("ssh",),
+ 0,
+ "job_id: android-test\npid: 1234\n",
+ "",
+ )
+ response = server.handle(
+ {
+ "jsonrpc": "2.0",
+ "id": 16,
+ "method": "tools/call",
+ "params": {
+ "name": "sailfish_android_build",
+ "arguments": {
+ "job_id": "android-test",
+ "shell_command": "source build/envsetup.sh && m services",
+ },
+ },
+ }
+ )
+
+ self.assertFalse(response["result"].get("isError", False))
+ self.assertEqual(
+ response["result"]["structuredContent"]["job_id"],
+ "android-test",
+ )
+ argv = list(mocked_run.call_args.args[0])
+ self.assertEqual(argv[:3], ["ssh", "-F", str(root / "ssh_config")])
+ self.assertIn("builder@example.invalid", argv)
+ remote = argv[-1]
+ self.assertIn("nohup", remote)
+ self.assertIn("/remote/a15", remote)
+ self.assertIn("/remote/state/android-builds/android-test", remote)
+ self.assertIn("base64 -d", remote)
+
+ def test_android_build_requires_configured_project_dir(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ server = self.make_server(Path(tmp))
+ response = server.handle(
+ {
+ "jsonrpc": "2.0",
+ "id": 16,
+ "method": "tools/call",
+ "params": {
+ "name": "sailfish_android_build",
+ "arguments": {
+ "job_id": "android-test",
+ "shell_command": "source build/envsetup.sh && m services",
+ },
+ },
+ }
+ )
+
+ self.assertTrue(response["result"].get("isError", False))
+ self.assertIn(
+ "project_dir is required",
+ response["result"]["content"][0]["text"],
+ )
+
+ def test_android_build_status_tails_remote_job_log(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ root = Path(tmp)
+ server = self.make_server(root)
+ with patch("sailfish_devel_mcp.tools.run") as mocked_run:
+ mocked_run.return_value = CommandResult(
+ ("ssh",),
+ 0,
+ "\n".join(
+ [
+ "job_id: android-test",
+ "state: finished",
+ "returncode: 7",
+ "pid: 1234",
+ "log_path: /remote/state/android-test/build.log",
+ "",
+ "build failed",
+ ]
+ ),
+ "",
+ )
+ response = server.handle(
+ {
+ "jsonrpc": "2.0",
+ "id": 17,
+ "method": "tools/call",
+ "params": {
+ "name": "sailfish_android_build_status",
+ "arguments": {
+ "job_id": "android-test",
+ "state_dir": "/remote/state",
+ "lines": 25,
+ },
+ },
+ }
+ )
+
+ self.assertTrue(response["result"].get("isError", False))
+ self.assertEqual(response["result"]["structuredContent"]["returncode"], 7)
+ argv = list(mocked_run.call_args.args[0])
+ self.assertEqual(argv[:3], ["ssh", "-F", str(root / "ssh_config")])
+ self.assertIn("builder@example.invalid", argv)
+ remote = argv[-1]
+ self.assertIn("tail -n \"$lines\" \"$log_path\"", remote)
+ self.assertIn("lines=25", remote)
+
def write_fake_target(
self,
root: Path,