Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests to twine.commands.upload (take 2) #627

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/fixtures/twine-1.5.0-py2.py3-none-any.whl.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
signature
3 changes: 3 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
import pathlib

TESTS_DIR = pathlib.Path(__file__).parent
SDIST_FIXTURE = os.path.join(TESTS_DIR, "fixtures/twine-1.5.0.tar.gz")
WHEEL_FIXTURE = os.path.join(TESTS_DIR, "fixtures/twine-1.5.0-py2.py3-none-any.whl")
NEW_SDIST_FIXTURE = os.path.join(TESTS_DIR, "fixtures/twine-1.6.5.tar.gz")
NEW_WHEEL_FIXTURE = os.path.join(TESTS_DIR, "fixtures/twine-1.6.5-py2.py3-none-any.whl")


@contextlib.contextmanager
Expand Down
165 changes: 104 additions & 61 deletions tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,62 +22,111 @@

from . import helpers

SDIST_FIXTURE = "tests/fixtures/twine-1.5.0.tar.gz"
WHEEL_FIXTURE = "tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"
RELEASE_URL = "https://pypi.org/project/twine/1.5.0/"
NEW_SDIST_FIXTURE = "tests/fixtures/twine-1.6.5.tar.gz"
NEW_WHEEL_FIXTURE = "tests/fixtures/twine-1.6.5-py2.py3-none-any.whl"
NEW_RELEASE_URL = "https://pypi.org/project/twine/1.6.5/"


def test_successful_upload(make_settings, capsys):
upload_settings = make_settings()

stub_response = pretend.stub(
@pytest.fixture
def stub_response():
"""Mock successful upload of a package."""
return pretend.stub(
is_redirect=False, status_code=201, raise_for_status=lambda: None
)

stub_repository = pretend.stub(
upload=lambda package: stub_response,

@pytest.fixture
def stub_repository(stub_response):
"""Allow assertions on the uploaded package."""
return pretend.stub(
upload=pretend.call_recorder(lambda package: stub_response),
close=lambda: None,
release_urls=lambda packages: {RELEASE_URL, NEW_RELEASE_URL},
release_urls=lambda packages: set(),
)


@pytest.fixture
def upload_settings(make_settings, stub_repository):
"""Use the stub repository when uploading."""
upload_settings = make_settings()
upload_settings.create_repository = lambda: stub_repository
return upload_settings


def test_successs_prints_release_urls(upload_settings, stub_repository, capsys):
"""Prints PyPI release URLS for each uploaded package."""
stub_repository.release_urls = lambda packages: {RELEASE_URL, NEW_RELEASE_URL}

result = upload.upload(
upload_settings,
[WHEEL_FIXTURE, SDIST_FIXTURE, NEW_SDIST_FIXTURE, NEW_WHEEL_FIXTURE],
[
helpers.WHEEL_FIXTURE,
helpers.SDIST_FIXTURE,
helpers.NEW_SDIST_FIXTURE,
helpers.NEW_WHEEL_FIXTURE,
],
)

# A truthy result means the upload failed
assert result is None

captured = capsys.readouterr()
assert captured.out.count(RELEASE_URL) == 1
assert captured.out.count(NEW_RELEASE_URL) == 1


@pytest.mark.parametrize("verbose", [False, True])
def test_exception_for_http_status(verbose, make_settings, capsys):
upload_settings = make_settings()
upload_settings.verbose = verbose
def test_success_with_pre_signed_distribution(upload_settings, stub_repository):
"""Adds GPG signature provided by user to uploaded package."""

stub_response = pretend.stub(
is_redirect=False,
status_code=403,
text="Invalid or non-existent authentication information",
raise_for_status=pretend.raiser(requests.HTTPError),
# Upload a pre-signed distribution
result = upload.upload(
upload_settings, [helpers.WHEEL_FIXTURE, helpers.WHEEL_FIXTURE + ".asc"]
)
assert result is None

stub_repository = pretend.stub(
upload=lambda package: stub_response, close=lambda: None,
# The signature shoud be added via package.add_gpg_signature()
package = stub_repository.upload.calls[0].args[0]
assert package.gpg_signature == (
"twine-1.5.0-py2.py3-none-any.whl.asc",
b"signature",
)

upload_settings.create_repository = lambda: stub_repository

def test_success_when_gpg_is_run(upload_settings, stub_repository, monkeypatch):
"""Adds GPG signature generated by gpg command to uploaded package."""

# Indicate that upload() should run_gpg() to generate the signature, which
# we'll stub out to use WHEEL_FIXTURE + ".asc"
upload_settings.sign = True
upload_settings.sign_with = "gpg"
monkeypatch.setattr(
package_file.PackageFile,
"run_gpg",
pretend.call_recorder(lambda cls, gpg_args: None),
)

# Upload an unsigned distribution
result = upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])
assert result is None

# The signature shoud be added via package.sign()
package = stub_repository.upload.calls[0].args[0]
assert len(package.run_gpg.calls) == 1
assert helpers.WHEEL_FIXTURE in package.run_gpg.calls[0].args[1]
assert package.gpg_signature == (
"twine-1.5.0-py2.py3-none-any.whl.asc",
b"signature",
)


@pytest.mark.parametrize("verbose", [False, True])
def test_exception_for_http_status(verbose, upload_settings, stub_response, capsys):
upload_settings.verbose = verbose

stub_response.is_redirect = False
stub_response.status_code = 403
stub_response.text = "Invalid or non-existent authentication information"
stub_response.raise_for_status = pretend.raiser(requests.HTTPError)

with pytest.raises(requests.HTTPError):
upload.upload(upload_settings, [WHEEL_FIXTURE])
upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])

captured = capsys.readouterr()
assert RELEASE_URL not in captured.out
Expand Down Expand Up @@ -122,7 +171,7 @@ def test_deprecated_repo(make_settings):
"""
)

upload.upload(upload_settings, [WHEEL_FIXTURE])
upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])

assert all(
text in err.value.args[0]
Expand All @@ -136,6 +185,8 @@ def test_deprecated_repo(make_settings):


def test_exception_for_redirect(make_settings):
# Not using fixtures because this setup is significantly different

upload_settings = make_settings(
"""
[pypi]
Expand All @@ -158,51 +209,38 @@ def test_exception_for_redirect(make_settings):
upload_settings.create_repository = lambda: stub_repository

with pytest.raises(exceptions.RedirectDetected) as err:
upload.upload(upload_settings, [WHEEL_FIXTURE])
upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])

assert "https://test.pypi.org/legacy/" in err.value.args[0]


def test_prints_skip_message_for_uploaded_package(make_settings, capsys):
upload_settings = make_settings(skip_existing=True)
def test_prints_skip_message_for_uploaded_package(
upload_settings, stub_repository, capsys
):
upload_settings.skip_existing = True

stub_repository = pretend.stub(
# Short-circuit the upload, so no need for a stub response
package_is_uploaded=lambda package: True,
release_urls=lambda packages: {},
close=lambda: None,
)

upload_settings.create_repository = lambda: stub_repository
# Short-circuit the upload
stub_repository.package_is_uploaded = lambda package: True

result = upload.upload(upload_settings, [WHEEL_FIXTURE])

# A truthy result means the upload failed
result = upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])
assert result is None

captured = capsys.readouterr()
assert "Skipping twine-1.5.0-py2.py3-none-any.whl" in captured.out
assert RELEASE_URL not in captured.out


def test_prints_skip_message_for_response(make_settings, capsys):
upload_settings = make_settings(skip_existing=True)

stub_response = pretend.stub(is_redirect=False, status_code=409,)
def test_prints_skip_message_for_response(
upload_settings, stub_response, stub_repository, capsys
):
upload_settings.skip_existing = True

stub_repository = pretend.stub(
# Do the upload, triggering the error response
package_is_uploaded=lambda package: False,
release_urls=lambda packages: {},
upload=lambda package: stub_response,
close=lambda: None,
)

upload_settings.create_repository = lambda: stub_repository
stub_response.status_code = 409

result = upload.upload(upload_settings, [WHEEL_FIXTURE])
# Do the upload, triggering the error response
stub_repository.package_is_uploaded = lambda package: False

# A truthy result means the upload failed
result = upload.upload(upload_settings, [helpers.WHEEL_FIXTURE])
assert result is None

captured = capsys.readouterr()
Expand Down Expand Up @@ -271,7 +309,7 @@ def test_skip_existing_skips_files_on_repository(response_kwargs):
assert upload.skip_upload(
response=pretend.stub(**response_kwargs),
skip_existing=True,
package=package_file.PackageFile.from_filename(WHEEL_FIXTURE, None),
package=package_file.PackageFile.from_filename(helpers.WHEEL_FIXTURE, None),
)


Expand All @@ -288,15 +326,15 @@ def test_skip_upload_doesnt_match(response_kwargs):
assert not upload.skip_upload(
response=pretend.stub(**response_kwargs),
skip_existing=True,
package=package_file.PackageFile.from_filename(WHEEL_FIXTURE, None),
package=package_file.PackageFile.from_filename(helpers.WHEEL_FIXTURE, None),
)


def test_skip_upload_respects_skip_existing():
assert not upload.skip_upload(
response=pretend.stub(),
skip_existing=False,
package=package_file.PackageFile.from_filename(WHEEL_FIXTURE, None),
package=package_file.PackageFile.from_filename(helpers.WHEEL_FIXTURE, None),
)


Expand Down Expand Up @@ -332,5 +370,10 @@ def test_check_status_code_for_wrong_repo_url(repo_url, make_settings):
with pytest.raises(exceptions.InvalidPyPIUploadURL):
upload.upload(
upload_settings,
[WHEEL_FIXTURE, SDIST_FIXTURE, NEW_SDIST_FIXTURE, NEW_WHEEL_FIXTURE],
[
helpers.WHEEL_FIXTURE,
helpers.SDIST_FIXTURE,
helpers.NEW_SDIST_FIXTURE,
helpers.NEW_WHEEL_FIXTURE,
],
)