80 lines
3.8 KiB
Python
80 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1] / "windows-node"
|
|
PROJECT = ROOT / "Zcbot.WindowsNode"
|
|
|
|
|
|
class WindowsNodeSourceTests(unittest.TestCase):
|
|
def test_project_targets_net10_windows_forms_without_third_party_packages(self) -> None:
|
|
tree = ET.parse(PROJECT / "Zcbot.WindowsNode.csproj")
|
|
root = tree.getroot()
|
|
self.assertEqual(root.findtext("./PropertyGroup/TargetFramework"), "net10.0-windows")
|
|
self.assertEqual(root.findtext("./PropertyGroup/UseWindowsForms"), "true")
|
|
self.assertEqual(root.findtext("./PropertyGroup/OutputType"), "WinExe")
|
|
self.assertEqual(root.findall("./ItemGroup/PackageReference"), [])
|
|
|
|
def test_node_protocol_and_secret_storage_markers_are_present(self) -> None:
|
|
source = "\n".join(path.read_text(encoding="utf-8") for path in PROJECT.glob("*.cs"))
|
|
for marker in (
|
|
"v1/compute/nodes/enroll",
|
|
"v1/compute/nodes/connect",
|
|
'SetRequestHeader("Authorization"',
|
|
'SetRequestHeader("X-Node-Id"',
|
|
"DataProtectionScope.LocalMachine",
|
|
"SetAccessRuleProtection(isProtected: true",
|
|
'"origin.plot@v1"',
|
|
"NotifyIcon",
|
|
"ConfigurationForm",
|
|
"TrayIconFactory.Create",
|
|
'"--headless"',
|
|
):
|
|
self.assertIn(marker, source)
|
|
|
|
def test_node_does_not_expose_arbitrary_execution_primitives(self) -> None:
|
|
source = "\n".join(path.read_text(encoding="utf-8") for path in PROJECT.glob("*.cs"))
|
|
for forbidden in ("Process.Start", "cmd.exe", "powershell.exe", "LabTalk"):
|
|
self.assertNotIn(forbidden, source)
|
|
|
|
def test_config_field_names_do_not_serialize_plain_node_token(self) -> None:
|
|
models = (PROJECT / "NodeModels.cs").read_text(encoding="utf-8")
|
|
stored_record = models.split("internal sealed record StoredNodeConfig", 1)[1].split(");", 1)[0]
|
|
self.assertIn("ProtectedNodeToken", stored_record)
|
|
self.assertNotIn("string NodeToken", stored_record)
|
|
|
|
def test_configuration_ui_never_displays_or_copies_token(self) -> None:
|
|
form = (PROJECT / "ConfigurationForm.cs").read_text(encoding="utf-8")
|
|
self.assertIn("CreateTextBox(usePassword: true)", form)
|
|
self.assertIn("UseSystemPasswordChar = usePassword", form)
|
|
self.assertNotIn("NodeToken", form)
|
|
self.assertNotIn("Clipboard", form)
|
|
self.assertIn("清除本机身份并重新注册", form)
|
|
self.assertIn("管理员禁用云端旧节点", form)
|
|
|
|
def test_configuration_window_is_resizable_and_dpi_safe(self) -> None:
|
|
form = (PROJECT / "ConfigurationForm.cs").read_text(encoding="utf-8")
|
|
self.assertIn("ClientSize = new Size(760, 690)", form)
|
|
self.assertIn("FormBorderStyle.Sizable", form)
|
|
self.assertIn("AutoScaleMode.Dpi", form)
|
|
self.assertIn("AutoScroll = true", form)
|
|
self.assertNotIn("MaximumSize = new Size(410", form)
|
|
self.assertIn("注册码默认 10 分钟有效", form)
|
|
self.assertIn("成功注册一次后立即失效", form)
|
|
self.assertIn("CreateCard", form)
|
|
self.assertIn("注册并连接", form)
|
|
|
|
def test_startup_task_is_login_scoped_and_runs_the_fixed_node_executable(self) -> None:
|
|
script = (ROOT / "install-startup.ps1").read_text(encoding="utf-8")
|
|
self.assertIn('GetFileName($resolvedExecutable) -ne "Zcbot.WindowsNode.exe"', script)
|
|
self.assertIn("New-ScheduledTaskTrigger -AtLogOn -User $currentUser", script)
|
|
self.assertIn("-LogonType Interactive", script)
|
|
self.assertIn("-RunLevel Limited", script)
|
|
self.assertNotIn("-RunLevel Highest", script)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|