1
0
mirror of https://github.com/pypiserver/pypiserver synced 2025-02-22 19:19:37 +01:00
pypiserver/tests/test_server.py

448 lines
13 KiB
Python
Raw Normal View History

#! /usr/bin/env py.test
"""
Checks an actual pypi-server against various clients.
The tests below are using 3 ways to startup pypi-servers:
- "open": a per-module server instance without any authed operations,
serving a single `wheel` package, on a fixed port.
- "open": a per-module server instance with authed 'download/upload'
operations, serving a single `wheel` package, on a fixed port.
- "new_server": starting a new server with any configurations on each test.
"""
from __future__ import print_function
from collections import namedtuple
import contextlib
import functools
import os
import subprocess
2015-09-18 18:31:12 +02:00
import sys
import tempfile
import time
from shlex import split
from subprocess import Popen
from textwrap import dedent
try:
from urllib.request import urlopen
except ImportError:
from urllib import urlopen
from py import path # @UnresolvedImport
import pytest
# ######################################################################
# Fixtures & Helper Functions
# ######################################################################
_BUFF_SIZE = 2**16
_port = 8090
SLEEP_AFTER_SRV = 3 # sec
@pytest.fixture
def port():
global _port
_port += 1
return _port
2015-09-20 18:35:11 +02:00
Srv = namedtuple('Srv', ('proc', 'port', 'package'))
def _run_server(packdir, port, authed, other_cli=''):
"""Run a server, optionally with partial auth enabled."""
pswd_opt_choices = {
True: "-Ptests/htpasswd.a.a -a update,download",
False: "-P. -a.",
'partial': "-Ptests/htpasswd.a.a -a update",
}
pswd_opts = pswd_opt_choices[authed]
cmd = (
"%s -m pypiserver.__main__ -vvv --overwrite -i 127.0.0.1 "
"-p %s %s %s %s" % (
sys.executable,
port,
pswd_opts,
other_cli,
packdir,
)
)
proc = subprocess.Popen(cmd.split(), bufsize=_BUFF_SIZE)
time.sleep(SLEEP_AFTER_SRV)
assert proc.poll() is None
return Srv(proc, int(port), packdir)
def _kill_server(srv):
print('Killing %s' % (srv,))
try:
srv.proc.terminate()
time.sleep(1)
finally:
srv.proc.kill()
@contextlib.contextmanager
def new_server(packdir, port, authed=False, other_cli=''):
srv = _run_server(packdir, port,
authed=authed, other_cli=other_cli)
try:
yield srv
finally:
_kill_server(srv)
@contextlib.contextmanager
def chdir(d):
old_d = os.getcwd()
try:
os.chdir(d)
yield
finally:
os.chdir(old_d)
def _run_python(cmd):
2015-09-20 18:35:11 +02:00
ncmd = '%s %s' % (sys.executable, cmd)
return os.system(ncmd)
@pytest.fixture(scope='module')
def project(request):
def fin():
tmpdir.remove(True)
tmpdir = path.local(tempfile.mkdtemp())
request.addfinalizer(fin)
src_setup_py = path.local().join('tests', 'centodeps-setup.py')
assert src_setup_py.check()
projdir = tmpdir.join('centodeps')
projdir.mkdir()
dst_setup_py = projdir.join('setup.py')
src_setup_py.copy(dst_setup_py)
assert dst_setup_py.check()
return projdir
@pytest.fixture(scope='module')
def package(project, request):
with chdir(project.strpath):
2015-09-20 18:35:11 +02:00
cmd = 'setup.py bdist_wheel'
assert _run_python(cmd) == 0
pkgs = list(project.join('dist').visit('centodeps*.whl'))
assert len(pkgs) == 1
pkg = path.local(pkgs[0])
assert pkg.check()
return pkg
@pytest.fixture(scope='module')
def packdir(package):
return package.dirpath()
open_port = 8081
@pytest.fixture(scope='module')
def open_server(packdir, request):
srv = _run_server(packdir, open_port, authed=False)
fin = functools.partial(_kill_server, srv)
request.addfinalizer(fin)
return srv
protected_port = 8082
@pytest.fixture(scope='module')
def protected_server(packdir, request):
srv = _run_server(packdir, protected_port, authed=True)
fin = functools.partial(_kill_server, srv)
request.addfinalizer(fin)
return srv
@pytest.fixture
def empty_packdir(tmpdir):
return tmpdir.mkdir("dists")
def _build_url(port, user='', pswd=''):
auth = '%s:%s@' % (user, pswd) if user or pswd else ''
return 'http://%slocalhost:%s' % (auth, port)
def _run_pip(cmd):
ncmd = (
"pip --no-cache-dir --disable-pip-version-check "
"--retries 0 --timeout 5 --no-input %s"
) % cmd
print('PIP: %s' % ncmd)
proc = Popen(split(ncmd))
proc.communicate()
return proc.returncode
def _run_pip_install(cmd, port, install_dir, user=None, pswd=None):
url = _build_url(port, user, pswd)
# ncmd = '-vv install --download %s -i %s %s' % (install_dir, url, cmd)
ncmd = '-vv download -d %s -i %s %s' % (install_dir, url, cmd)
return _run_pip(ncmd)
@pytest.fixture
def pipdir(tmpdir):
return tmpdir.mkdir("pip")
@contextlib.contextmanager
def pypirc_tmpfile(port, user, password):
"""Create a temporary pypirc file."""
fd, filepath = tempfile.mkstemp()
os.close(fd)
with open(filepath, 'w') as rcfile:
rcfile.writelines(
'\n'.join((
'[distutils]',
'index-servers: test',
''
'[test]',
'repository: {}'.format(_build_url(port)),
'username: {}'.format(user),
'password: {}'.format(password),
))
)
with open(filepath) as rcfile:
print(rcfile.read())
yield filepath
os.remove(filepath)
@contextlib.contextmanager
def pypirc_file(txt):
pypirc_path = path.local('~/.pypirc', expanduser=1)
old_pypirc = pypirc_path.read() if pypirc_path.check() else None
pypirc_path.write(txt)
try:
yield
finally:
if old_pypirc:
pypirc_path.write(old_pypirc)
else:
pypirc_path.remove()
def twine_upload(packages, repository='test', conf='pypirc',
expect_failure=False):
"""Call 'twine upload' with appropriate arguments"""
proc = Popen((
'twine',
'upload',
'--repository', repository,
'--config-file', conf,
' '.join(packages),
))
proc.communicate()
if not expect_failure and proc.returncode:
assert False, 'Twine upload failed. See stdout/err'
def twine_register(packages, repository='test', conf='pypirc',
expect_failure=False):
"""Call 'twine register' with appropriate args"""
proc = Popen((
'twine',
'register',
'--repository', repository,
'--config-file', conf,
' '.join(packages)
))
proc.communicate()
if not expect_failure and proc.returncode:
assert False, 'Twine register failed. See stdout/err'
# ######################################################################
# Tests
# ######################################################################
def test_pipInstall_packageNotFound(empty_packdir, port, pipdir, package):
with new_server(empty_packdir, port):
cmd = "centodeps"
assert _run_pip_install(cmd, port, pipdir) != 0
assert not pipdir.listdir()
def test_pipInstall_openOk(open_server, package, pipdir):
cmd = "centodeps"
assert _run_pip_install(cmd, open_server.port, pipdir) == 0
assert pipdir.join(package.basename).check()
def test_pipInstall_authedFails(protected_server, pipdir):
cmd = "centodeps"
assert _run_pip_install(cmd, protected_server.port, pipdir) != 0
assert not pipdir.listdir()
def test_pipInstall_authedOk(protected_server, package, pipdir):
cmd = "centodeps"
assert _run_pip_install(cmd, protected_server.port, pipdir,
user='a', pswd='a') == 0
assert pipdir.join(package.basename).check()
@pytest.mark.parametrize("pkg_frmt", ['bdist', 'bdist_wheel'])
def test_setuptoolsUpload_open(empty_packdir, port, project, package,
pkg_frmt):
url = _build_url(port, None, None)
with pypirc_file(dedent("""\
[distutils]
index-servers: test
[test]
repository: %s
username: ''
password: ''
""" % url)):
with new_server(empty_packdir, port):
with chdir(project.strpath):
cmd = "setup.py -vvv %s upload -r %s" % (pkg_frmt, url)
for i in range(5):
print('++Attempt #%s' % i)
assert _run_python(cmd) == 0
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
@pytest.mark.parametrize("pkg_frmt", ['bdist', 'bdist_wheel'])
def test_setuptoolsUpload_authed(empty_packdir, port, project, package,
pkg_frmt, monkeypatch):
url = _build_url(port)
with pypirc_file(dedent("""\
[distutils]
index-servers: test
[test]
repository: %s
username: a
password: a
""" % url)):
with new_server(empty_packdir, port, authed=True):
with chdir(project.strpath):
cmd = (
"setup.py -vvv %s register -r "
"test upload -r test" % pkg_frmt
)
for i in range(5):
print('++Attempt #%s' % i)
assert _run_python(cmd) == 0
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
@pytest.mark.parametrize("pkg_frmt", ['bdist', 'bdist_wheel'])
def test_setuptools_upload_partial_authed(empty_packdir, port, project,
pkg_frmt):
"""Test uploading a package with setuptools with partial auth."""
url = _build_url(port)
with pypirc_file(dedent("""\
[distutils]
index-servers: test
[test]
repository: %s
username: a
password: a
""" % url)):
with new_server(empty_packdir, port, authed='partial'):
with chdir(project.strpath):
cmd = ("setup.py -vvv %s register -r test upload -r test" %
pkg_frmt)
for i in range(5):
print('++Attempt #%s' % i)
assert _run_python(cmd) == 0
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
def test_partial_authed_open_download(empty_packdir, port):
"""Validate that partial auth still allows downloads."""
url = _build_url(port) + '/simple'
with new_server(empty_packdir, port, authed='partial'):
resp = urlopen(url)
assert resp.getcode() == 200
def test_twine_upload_open(empty_packdir, port, package):
"""Test twine upload with no authentication"""
2015-09-20 18:35:11 +02:00
user, pswd = 'foo', 'bar'
with new_server(empty_packdir, port):
with pypirc_tmpfile(port, user, pswd) as rcfile:
twine_upload([package.strpath], repository='test', conf=rcfile)
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
2015-09-20 18:35:11 +02:00
@pytest.mark.parametrize("hash_algo", ("md5", "sha256", "sha512"))
def test_hash_algos(empty_packdir, port, package, pipdir, hash_algo):
"""Test twine upload with no authentication"""
user, pswd = 'foo', 'bar'
with new_server(
empty_packdir, port, other_cli="--hash-algo {}".format(hash_algo)
):
with pypirc_tmpfile(port, user, pswd) as rcfile:
twine_upload([package.strpath], repository='test', conf=rcfile)
time.sleep(SLEEP_AFTER_SRV)
assert _run_pip_install("centodeps", port, pipdir) == 0
def test_twine_upload_authed(empty_packdir, port, package):
"""Test authenticated twine upload"""
2015-09-20 18:35:11 +02:00
user, pswd = 'a', 'a'
with new_server(empty_packdir, port, authed=False):
with pypirc_tmpfile(port, user, pswd) as rcfile:
twine_upload([package.strpath], repository='test', conf=rcfile)
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
assert empty_packdir.join(
package.basename).check(), (package.basename, empty_packdir.listdir())
def test_twine_upload_partial_authed(empty_packdir, port, package):
"""Test partially authenticated twine upload"""
user, pswd = 'a', 'a'
with new_server(empty_packdir, port, authed='partial'):
with pypirc_tmpfile(port, user, pswd) as rcfile:
twine_upload([package.strpath], repository='test', conf=rcfile)
time.sleep(SLEEP_AFTER_SRV)
assert len(empty_packdir.listdir()) == 1
def test_twine_register_open(open_server, package):
"""Test unauthenticated twine registration"""
2015-09-20 18:35:11 +02:00
srv = open_server
with pypirc_tmpfile(srv.port, 'foo', 'bar') as rcfile:
twine_register([package.strpath], repository='test', conf=rcfile)
2015-09-20 18:35:11 +02:00
2018-11-10 01:46:06 +01:00
def test_twine_register_authed_ok(protected_server, package):
"""Test authenticated twine registration"""
2015-09-20 18:35:11 +02:00
srv = protected_server
user, pswd = 'a', 'a'
with pypirc_tmpfile(srv.port, user, pswd) as rcfile:
twine_register([package.strpath], repository='test', conf=rcfile)