"""WinRM bundle delivery and CLIXML error parsing tests.""" from pathlib import Path from unittest.mock import patch from app.services.rdp_bundle_delivery import ( _UTF8_BOM, build_rdp_bundle_zip, get_rdp_bundle_zip, register_rdp_bundle_zip, ) from app.services.winrm_connect import ( RDP_BUNDLE_REQUIRED, RDP_REMOTE_STAGING, WinRmCmdResult, _clixml_to_plain, _custom_deploy_body, _decode_winrm_bytes, _deploy_from_staging_body, _download_bundle_body, _prepare_staging_body, _winrm_failure_detail, run_winrm_rdp_monitor_update, ) def test_decode_winrm_bytes_reads_russian_cp866_console(): sample = "Деплой: корень шары".encode("cp866") decoded = _decode_winrm_bytes(sample) assert "Деплой" in decoded assert "шары" in decoded def test_decode_winrm_bytes_handles_utf8_multibyte(): text = "\u0414\u0435\u043f\u043b\u043e\u0439: \u043a\u043e\u0440\u0435\u043d\u044c \u0448\u0430\u0440\u044b" assert _decode_winrm_bytes(text.encode("utf-8")) == text def test_deploy_from_staging_includes_deploy_log_tail(): script = _deploy_from_staging_body(RDP_REMOTE_STAGING) assert "deploy.log" in script assert "Get-Content" in script assert "-Encoding UTF8" in script def test_prepare_staging_recreates_remote_dir(): script = _prepare_staging_body(RDP_REMOTE_STAGING) assert "Remove-Item" in script assert "_sac_staging" in script def test_download_bundle_uses_invoke_webrequest(): script = _download_bundle_body( RDP_REMOTE_STAGING, "https://sac.example/api/v1/agent/rdp-bundle/token", ) assert "Invoke-WebRequest" in script assert "ZipFile]::ExtractToDirectory" in script assert "Expand-Archive" not in script def test_deploy_from_staging_runs_local_bundle(): script = _deploy_from_staging_body(RDP_REMOTE_STAGING) assert "-SourceShareRoot" in script assert "Deploy-LoginMonitor.ps1" in script def test_custom_deploy_script_escapes_single_quotes(): script = _custom_deploy_body(r"C:\Users\O'Brien\Deploy-LoginMonitor.ps1") assert "O''Brien" in script def test_rdp_bundle_zip_adds_utf8_bom_for_ps1(tmp_path: Path): repo = tmp_path / "repo" repo.mkdir() (repo / "Deploy-LoginMonitor.ps1").write_text("# test", encoding="utf-8") zip_bytes = build_rdp_bundle_zip(repo, ("Deploy-LoginMonitor.ps1",)) import zipfile import io with zipfile.ZipFile(io.BytesIO(zip_bytes)) as archive: data = archive.read("Deploy-LoginMonitor.ps1") assert data.startswith(_UTF8_BOM) def test_rdp_bundle_zip_roundtrip(tmp_path: Path): repo = tmp_path / "repo" repo.mkdir() for name in RDP_BUNDLE_REQUIRED: (repo / name).write_text(name, encoding="utf-8") zip_bytes = build_rdp_bundle_zip(repo, tuple(RDP_BUNDLE_REQUIRED)) token = register_rdp_bundle_zip(zip_bytes) assert get_rdp_bundle_zip(token) == zip_bytes def test_run_winrm_rdp_monitor_update_downloads_bundle_from_sac(tmp_path: Path): repo = tmp_path / "repo" repo.mkdir() for name in RDP_BUNDLE_REQUIRED: (repo / name).write_text(f"content-{name}", encoding="utf-8") calls: list[str] = [] def fake_run_ps(*, script: str, **kwargs) -> WinRmCmdResult: calls.append(script) if "Remove-Item" in script: return WinRmCmdResult(ok=True, message="staging ok", target="pc", stdout="Staging ready") if "Invoke-WebRequest" in script: return WinRmCmdResult(ok=True, message="download ok", target="pc", stdout="Bundle extracted") if "powershell.exe -NoProfile" in script or "& $deploy" in script: return WinRmCmdResult( ok=True, message="WinRM OK (pc), exit 0", target="pc", stdout="deployed 1.2.3", exit_code=0, ) return WinRmCmdResult(ok=True, message="ok", target="pc", exit_code=0) with ( patch("app.services.winrm_connect._fetch_rdp_bundle_dir", return_value=repo), patch("app.services.winrm_connect._bundle_download_url", return_value="https://sac.test/bundle.zip"), patch("app.services.winrm_connect.run_winrm_ps", side_effect=fake_run_ps), ): result = run_winrm_rdp_monitor_update( target="pc", user="B26\\admin", password="pw", repo_url="https://git.example.com/RDP-login-monitor.git", ) assert result.ok is True assert "SAC served bundle" in result.stdout assert any("Invoke-WebRequest" in call for call in calls) assert not any("FromBase64String" in call for call in calls) def test_winrm_failure_detail_never_returns_raw_clixml_progress(): clixml = ( "#< CLIXML " 'Preparing modules for first use.' ) detail = _winrm_failure_detail("", clixml, 1) assert "#< CLIXML" not in detail def test_clixml_to_plain_strips_progress_only_blob(): clixml = "#< CLIXML Preparing modules for first use." assert _clixml_to_plain(clixml) == ""