poetry/tests/console/commands/test_publish.py

232 lines
6.9 KiB
Python

from __future__ import annotations
import shutil
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
import pytest
import requests
from poetry.factory import Factory
from poetry.publishing.uploader import UploadError
if TYPE_CHECKING:
import httpretty
from cleo.testers.application_tester import ApplicationTester
from pytest_mock import MockerFixture
from poetry.utils.env import VirtualEnv
from tests.helpers import PoetryTestApplication
from tests.types import CommandTesterFactory
from tests.types import FixtureDirGetter
def test_publish_not_possible_in_non_package_mode(
fixture_dir: FixtureDirGetter,
command_tester_factory: CommandTesterFactory,
) -> None:
source_dir = fixture_dir("non_package_mode")
poetry = Factory().create_poetry(source_dir)
tester = command_tester_factory("publish", poetry)
assert tester.execute() == 1
assert (
tester.io.fetch_error()
== "Publishing a package is not possible in non-package mode.\n"
)
def test_publish_returns_non_zero_code_for_upload_errors(
app: PoetryTestApplication,
app_tester: ApplicationTester,
http: type[httpretty.httpretty],
) -> None:
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request"
)
exit_code = app_tester.execute("publish --username foo --password bar")
assert exit_code == 1
expected_output = """
Publishing simple-project (1.2.3) to PyPI
"""
expected_error_output = """\
HTTP Error 400: Bad Request | b'Bad Request'
"""
assert expected_output in app_tester.io.fetch_output()
assert expected_error_output in app_tester.io.fetch_error()
@pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning")
def test_publish_returns_non_zero_code_for_connection_errors(
app: PoetryTestApplication,
app_tester: ApplicationTester,
http: type[httpretty.httpretty],
) -> None:
def request_callback(*_: Any, **__: Any) -> None:
raise requests.ConnectionError()
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", body=request_callback
)
exit_code = app_tester.execute("publish --username foo --password bar")
assert exit_code == 1
expected = str(UploadError(error=requests.ConnectionError()))
assert expected in app_tester.io.fetch_error()
def test_publish_with_cert(
app_tester: ApplicationTester, mocker: MockerFixture
) -> None:
publisher_publish = mocker.patch("poetry.publishing.Publisher.publish")
app_tester.execute("publish --cert path/to/ca.pem")
assert [
(None, None, None, Path("path/to/ca.pem"), None, False, False)
] == publisher_publish.call_args
def test_publish_with_client_cert(
app_tester: ApplicationTester, mocker: MockerFixture
) -> None:
publisher_publish = mocker.patch("poetry.publishing.Publisher.publish")
app_tester.execute("publish --client-cert path/to/client.pem")
assert [
(None, None, None, None, Path("path/to/client.pem"), False, False)
] == publisher_publish.call_args
@pytest.mark.parametrize(
"options",
[
"--dry-run",
"--skip-existing",
"--dry-run --skip-existing",
],
)
def test_publish_dry_run_skip_existing(
app_tester: ApplicationTester, http: type[httpretty.httpretty], options: str
) -> None:
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict"
)
exit_code = app_tester.execute(f"publish {options} --username foo --password bar")
assert exit_code == 0
output = app_tester.io.fetch_output()
error = app_tester.io.fetch_error()
assert "Publishing simple-project (1.2.3) to PyPI" in output
assert "- Uploading simple_project-1.2.3.tar.gz" in error
assert "- Uploading simple_project-1.2.3-py2.py3-none-any.whl" in error
def test_skip_existing_output(
app_tester: ApplicationTester, http: type[httpretty.httpretty]
) -> None:
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict"
)
exit_code = app_tester.execute(
"publish --skip-existing --username foo --password bar"
)
assert exit_code == 0
error = app_tester.io.fetch_error()
assert "- Uploading simple_project-1.2.3.tar.gz File exists. Skipping" in error
@pytest.mark.parametrize("dist_dir", [None, "dist", "other_dist/dist", "absolute"])
def test_publish_dist_dir_option(
http: type[httpretty.httpretty],
fixture_dir: FixtureDirGetter,
tmp_path: Path,
tmp_venv: VirtualEnv,
command_tester_factory: CommandTesterFactory,
dist_dir: str | None,
) -> None:
source_dir = fixture_dir("with_multiple_dist_dir")
target_dir = tmp_path / "project"
shutil.copytree(str(source_dir), str(target_dir))
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict"
)
poetry = Factory().create_poetry(target_dir)
tester = command_tester_factory("publish", poetry, environment=tmp_venv)
if dist_dir is None:
exit_code = tester.execute("--dry-run")
elif dist_dir == "absolute":
exit_code = tester.execute(f"--dist-dir {target_dir / 'dist'} --dry-run")
else:
exit_code = tester.execute(f"--dist-dir {dist_dir} --dry-run")
assert exit_code == 0
output = tester.io.fetch_output()
error = tester.io.fetch_error()
assert "Publishing simple-project (1.2.3) to PyPI" in output
assert "- Uploading simple_project-1.2.3.tar.gz" in error
assert "- Uploading simple_project-1.2.3-py2.py3-none-any.whl" in error
@pytest.mark.parametrize("dist_dir", ["../dist", "tmp/dist", "absolute"])
def test_publish_dist_dir_and_build_options(
http: type[httpretty.httpretty],
fixture_dir: FixtureDirGetter,
tmp_path: Path,
tmp_venv: VirtualEnv,
command_tester_factory: CommandTesterFactory,
dist_dir: str | None,
) -> None:
source_dir = fixture_dir("simple_project")
target_dir = tmp_path / "project"
shutil.copytree(str(source_dir), str(target_dir))
# Remove dist dir because as it will be built again
shutil.rmtree(target_dir / "dist")
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict"
)
poetry = Factory().create_poetry(target_dir)
tester = command_tester_factory("publish", poetry, environment=tmp_venv)
if dist_dir == "absolute":
exit_code = tester.execute(
f"--dist-dir {target_dir / 'test/dist'} --dry-run --build"
)
else:
exit_code = tester.execute(f"--dist-dir {dist_dir} --dry-run --build")
assert exit_code == 0
output = tester.io.fetch_output()
error = tester.io.fetch_error()
assert "Publishing simple-project (1.2.3) to PyPI" in output
assert "- Uploading simple_project-1.2.3.tar.gz" in error
assert "- Uploading simple_project-1.2.3-py2.py3-none-any.whl" in error