|
4 | 4 | import re |
5 | 5 | import shutil |
6 | 6 | import sys |
| 7 | +import venv |
7 | 8 | from os.path import join |
8 | 9 | from types import SimpleNamespace |
9 | 10 | from unittest import TestCase, mock |
@@ -352,6 +353,62 @@ def test_deploy_notebook_static_passes_interpreter_not_version(self): |
352 | 353 | assert bundle_args[0] is make_notebook_html_bundle |
353 | 354 | assert bundle_args[2] == sys.executable |
354 | 355 |
|
| 356 | + @pytest.mark.parametrize( |
| 357 | + ("contents", "diagnostic", "missing_nbconvert"), |
| 358 | + [ |
| 359 | + ("not json", "NotJSONError", False), |
| 360 | + ("not json", "No module named nbconvert", True), |
| 361 | + ( |
| 362 | + json.dumps( |
| 363 | + { |
| 364 | + "cells": [ |
| 365 | + { |
| 366 | + "cell_type": "code", |
| 367 | + "execution_count": None, |
| 368 | + "metadata": {}, |
| 369 | + "outputs": [], |
| 370 | + "source": ["raise ValueError('boom')"], |
| 371 | + } |
| 372 | + ], |
| 373 | + "metadata": {"kernelspec": {"name": "python3", "display_name": "Python 3"}}, |
| 374 | + "nbformat": 4, |
| 375 | + "nbformat_minor": 5, |
| 376 | + } |
| 377 | + ), |
| 378 | + "ValueError: boom", |
| 379 | + False, |
| 380 | + ), |
| 381 | + ], |
| 382 | + ) |
| 383 | + def test_deploy_static_notebook_render_failure(self, tmp_path, capfd, contents, diagnostic, missing_nbconvert): |
| 384 | + notebook = tmp_path / "broken.ipynb" |
| 385 | + notebook.write_text(contents) |
| 386 | + interpreter = sys.executable |
| 387 | + if missing_nbconvert: |
| 388 | + env_dir = tmp_path / "empty-env" |
| 389 | + venv.EnvBuilder(with_pip=False, symlinks=os.name != "nt").create(env_dir) |
| 390 | + interpreter = str(env_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python")) |
| 391 | + fake_env = SimpleNamespace(python="3.12.1", python_interpreter=interpreter) |
| 392 | + |
| 393 | + with mock.patch("rsconnect.main.Environment.create_python_environment", return_value=fake_env), mock.patch( |
| 394 | + "rsconnect.main.RSConnectExecutor" |
| 395 | + ) as mock_executor: |
| 396 | + mock_executor.return_value.make_bundle.side_effect = lambda func, *args: func(*args) |
| 397 | + args = apply_common_args( |
| 398 | + ["deploy", "notebook", str(notebook), "--static", "--no-verify"], |
| 399 | + server="http://fake_server", |
| 400 | + key="FAKE_API_KEY", |
| 401 | + ) |
| 402 | + result = CliRunner().invoke(cli, args) |
| 403 | + |
| 404 | + stderr = capfd.readouterr().err |
| 405 | + assert result.exit_code == 1 |
| 406 | + assert diagnostic in re.sub(r"\x1b\[[0-9;]*m", "", stderr) |
| 407 | + assert f"Error: Rendering {notebook} with nbconvert failed (see output above)" in result.output |
| 408 | + assert "Traceback (most recent call last)" not in result.output |
| 409 | + assert "Internal error" not in result.output + stderr |
| 410 | + assert "subprocess.CalledProcessError" not in result.output + stderr |
| 411 | + |
355 | 412 | @httpretty.activate(verbose=True, allow_net_connect=False) |
356 | 413 | def test_deploy_verify_before_activate(self, caplog): |
357 | 414 | # Without --draft or --no-verify, the default flow deploys the bundle as a |
|
0 commit comments