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

Create utility to render and store transactional snapshot of simple detail page for a project #8586

Merged
merged 19 commits into from
Feb 2, 2022
Merged
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 dev/environment
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ CAMO_KEY=insecurecamokey
DOCS_URL="https://pythonhosted.org/{project}/"

FILES_BACKEND=warehouse.packaging.services.LocalFileStorage path=/var/opt/warehouse/packages/ url=http://localhost:9001/packages/{path}
SIMPLE_BACKEND=warehouse.packaging.services.LocalSimpleStorage path=/var/opt/warehouse/simple/ url=http://localhost:9001/simple/{path}
DOCS_BACKEND=warehouse.packaging.services.LocalDocsStorage path=/var/opt/warehouse/docs/
SPONSORLOGOS_BACKEND=warehouse.admin.services.LocalSponsorLogoStorage path=/var/opt/warehouse/sponsorlogos/

Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
version: '3'

volumes:
simple:
packages:
sponsorlogos:
vault:
Expand Down Expand Up @@ -86,6 +87,7 @@ services:
- .coveragerc:/opt/warehouse/src/.coveragerc:z
- packages:/var/opt/warehouse/packages
- sponsorlogos:/var/opt/warehouse/sponsorlogos
- simple:/var/opt/warehouse/simple
- ./bin:/opt/warehouse/src/bin:z
ports:
- "80:8000"
Expand All @@ -98,6 +100,7 @@ services:
volumes:
- packages:/var/opt/warehouse/packages
- sponsorlogos:/var/opt/warehouse/sponsorlogos
- simple:/var/opt/warehouse/simple
ports:
- "9001:9001"

Expand All @@ -113,6 +116,7 @@ services:
environment:
C_FORCE_ROOT: "1"
FILES_BACKEND: "warehouse.packaging.services.LocalFileStorage path=/var/opt/warehouse/packages/ url=http://files:9001/packages/{path}"
SIMPLE_BACKEND: "warehouse.packaging.services.LocalSimpleStorage path=/var/opt/warehouse/simple/ url=http://files:9001/simple/{path}"

static:
build:
Expand Down
27 changes: 25 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
import pytest
import webtest as _webtest

from jinja2 import Environment, FileSystemLoader
from psycopg2.errors import InvalidCatalogName
from pyramid.i18n import TranslationString
from pyramid.static import ManifestCacheBuster
from pyramid_jinja2 import IJinja2Environment
from pytest_postgresql.config import get_config
from pytest_postgresql.janitor import DatabaseJanitor
from sqlalchemy import event

import warehouse

from warehouse import admin, config, static
from warehouse.accounts import services as account_services
from warehouse.macaroons import services as macaroon_services
Expand Down Expand Up @@ -76,6 +80,22 @@ def metrics():
)


@pytest.fixture
def jinja():
dir_name = os.path.join(os.path.dirname(warehouse.__file__))

env = Environment(
loader=FileSystemLoader(dir_name),
extensions=[
"jinja2.ext.i18n",
"warehouse.utils.html.ClientSideIncludeExtension",
],
cache_size=0,
)

return env


class _Services:
def __init__(self):
self._services = defaultdict(lambda: defaultdict(dict))
Expand All @@ -98,11 +118,13 @@ def pyramid_services(metrics):


@pytest.fixture
def pyramid_request(pyramid_services):
def pyramid_request(pyramid_services, jinja):
dummy_request = pyramid.testing.DummyRequest()
dummy_request.find_service = pyramid_services.find_service
dummy_request.remote_addr = "1.2.3.4"

dummy_request.registry.registerUtility(jinja, IJinja2Environment, name=".jinja2")

def localize(message, **kwargs):
ts = TranslationString(message, **kwargs)
return ts.interpolate()
Expand Down Expand Up @@ -184,7 +206,8 @@ def app_config(database):
"ratelimit.url": "memory://",
"elasticsearch.url": "https://localhost/warehouse",
"files.backend": "warehouse.packaging.services.LocalFileStorage",
"docs.backend": "warehouse.packaging.services.LocalFileStorage",
"simple.backend": "warehouse.packaging.services.LocalSimpleStorage",
"docs.backend": "warehouse.packaging.services.LocalDocsStorage",
"sponsorlogos.backend": "warehouse.admin.services.LocalSponsorLogoStorage",
"mail.backend": "warehouse.email.services.SMTPEmailSender",
"malware_check.backend": (
Expand Down
9 changes: 7 additions & 2 deletions tests/unit/packaging/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from warehouse import packaging
from warehouse.accounts.models import Email, User
from warehouse.manage.tasks import update_role_invitation_status
from warehouse.packaging.interfaces import IDocsStorage, IFileStorage
from warehouse.packaging.interfaces import IDocsStorage, IFileStorage, ISimpleStorage
from warehouse.packaging.models import File, Project, Release, Role
from warehouse.packaging.tasks import ( # sync_bigquery_release_files,
compute_trending,
Expand Down Expand Up @@ -51,7 +51,11 @@ def key_factory(keystring, iterate_on=None):
lambda factory, iface, name=None: None
),
registry=pretend.stub(
settings={"files.backend": "foo.bar", "docs.backend": "wu.tang"}
settings={
"files.backend": "foo.bar",
"simple.backend": "bread.butter",
"docs.backend": "wu.tang",
}
),
register_origin_cache_keys=pretend.call_recorder(lambda c, **kw: None),
get_settings=lambda: settings,
Expand All @@ -62,6 +66,7 @@ def key_factory(keystring, iterate_on=None):

assert config.register_service_factory.calls == [
pretend.call(storage_class.create_service, IFileStorage),
pretend.call(storage_class.create_service, ISimpleStorage),
pretend.call(storage_class.create_service, IDocsStorage),
]
assert config.register_origin_cache_keys.calls == [
Expand Down
161 changes: 160 additions & 1 deletion tests/unit/packaging/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@

import warehouse.packaging.services

from warehouse.packaging.interfaces import IDocsStorage, IFileStorage
from warehouse.packaging.interfaces import IDocsStorage, IFileStorage, ISimpleStorage
from warehouse.packaging.services import (
GCSFileStorage,
GCSSimpleStorage,
GenericLocalBlobStorage,
LocalDocsStorage,
LocalFileStorage,
LocalSimpleStorage,
S3DocsStorage,
S3FileStorage,
)
Expand Down Expand Up @@ -137,6 +140,67 @@ def test_delete_already_gone(self, tmpdir):
assert response is None


class TestLocalSimpleStorage:
def test_verify_service(self):
assert verifyClass(ISimpleStorage, LocalSimpleStorage)

def test_basic_init(self):
storage = LocalSimpleStorage("/foo/bar/")
assert storage.base == "/foo/bar/"

def test_create_service(self):
request = pretend.stub(
registry=pretend.stub(settings={"simple.path": "/simple/one/two/"})
)
storage = LocalSimpleStorage.create_service(None, request)
assert storage.base == "/simple/one/two/"

def test_gets_file(self, tmpdir):
with open(str(tmpdir.join("file.txt")), "wb") as fp:
fp.write(b"my test file contents")

storage = LocalSimpleStorage(str(tmpdir))
file_object = storage.get("file.txt")
assert file_object.read() == b"my test file contents"

def test_raises_when_file_non_existent(self, tmpdir):
storage = LocalSimpleStorage(str(tmpdir))
with pytest.raises(FileNotFoundError):
storage.get("file.txt")

def test_stores_file(self, tmpdir):
filename = str(tmpdir.join("testfile.txt"))
with open(filename, "wb") as fp:
fp.write(b"Test File!")

storage_dir = str(tmpdir.join("storage"))
storage = LocalSimpleStorage(storage_dir)
storage.store("foo/bar.txt", filename)

with open(os.path.join(storage_dir, "foo/bar.txt"), "rb") as fp:
assert fp.read() == b"Test File!"

def test_stores_two_files(self, tmpdir):
filename1 = str(tmpdir.join("testfile1.txt"))
with open(filename1, "wb") as fp:
fp.write(b"First Test File!")

filename2 = str(tmpdir.join("testfile2.txt"))
with open(filename2, "wb") as fp:
fp.write(b"Second Test File!")

storage_dir = str(tmpdir.join("storage"))
storage = LocalSimpleStorage(storage_dir)
storage.store("foo/first.txt", filename1)
storage.store("foo/second.txt", filename2)

with open(os.path.join(storage_dir, "foo/first.txt"), "rb") as fp:
assert fp.read() == b"First Test File!"

with open(os.path.join(storage_dir, "foo/second.txt"), "rb") as fp:
assert fp.read() == b"Second Test File!"


class TestS3FileStorage:
def test_verify_service(self):
assert verifyClass(IFileStorage, S3FileStorage)
Expand Down Expand Up @@ -479,3 +543,98 @@ def test_delete_by_prefix_with_storage_prefix(self):
},
),
]


class TestGCSSimpleStorage:
def test_verify_service(self):
assert verifyClass(ISimpleStorage, GCSSimpleStorage)

def test_basic_init(self):
bucket = pretend.stub()
storage = GCSSimpleStorage(bucket)
assert storage.bucket is bucket

def test_create_service(self):
service = pretend.stub(
get_bucket=pretend.call_recorder(lambda bucket_name: pretend.stub())
)
request = pretend.stub(
find_service=pretend.call_recorder(lambda name: service),
registry=pretend.stub(settings={"simple.bucket": "froblob"}),
)
GCSSimpleStorage.create_service(None, request)

assert request.find_service.calls == [pretend.call(name="gcloud.gcs")]
assert service.get_bucket.calls == [pretend.call("froblob")]

def test_gets_file_raises(self):
storage = GCSSimpleStorage(pretend.stub())

with pytest.raises(NotImplementedError):
storage.get("file.txt")

def test_stores_file(self, tmpdir):
filename = str(tmpdir.join("testfile.txt"))
with open(filename, "wb") as fp:
fp.write(b"Test File!")

blob = pretend.stub(
upload_from_filename=pretend.call_recorder(lambda file_path: None),
exists=lambda: False,
)
bucket = pretend.stub(blob=pretend.call_recorder(lambda path: blob))
storage = GCSSimpleStorage(bucket)
storage.store("foo/bar.txt", filename)

assert bucket.blob.calls == [pretend.call("foo/bar.txt")]
assert blob.upload_from_filename.calls == [pretend.call(filename)]

def test_stores_two_files(self, tmpdir):
filename1 = str(tmpdir.join("testfile1.txt"))
with open(filename1, "wb") as fp:
fp.write(b"First Test File!")

filename2 = str(tmpdir.join("testfile2.txt"))
with open(filename2, "wb") as fp:
fp.write(b"Second Test File!")

blob = pretend.stub(
upload_from_filename=pretend.call_recorder(lambda file_path: None),
exists=lambda: False,
)
bucket = pretend.stub(blob=pretend.call_recorder(lambda path: blob))
storage = GCSSimpleStorage(bucket)
storage.store("foo/first.txt", filename1)
storage.store("foo/second.txt", filename2)

assert bucket.blob.calls == [
pretend.call("foo/first.txt"),
pretend.call("foo/second.txt"),
]
assert blob.upload_from_filename.calls == [
pretend.call(filename1),
pretend.call(filename2),
]

def test_stores_metadata(self, tmpdir):
filename = str(tmpdir.join("testfile.txt"))
with open(filename, "wb") as fp:
fp.write(b"Test File!")

blob = pretend.stub(
upload_from_filename=pretend.call_recorder(lambda file_path: None),
patch=pretend.call_recorder(lambda: None),
exists=lambda: False,
)
bucket = pretend.stub(blob=pretend.call_recorder(lambda path: blob))
storage = GCSSimpleStorage(bucket)
meta = {"foo": "bar"}
storage.store("foo/bar.txt", filename, meta=meta)

assert blob.metadata == meta


class TestGenericLocalBlobStorage:
def test_notimplementederror(self):
with pytest.raises(NotImplementedError):
GenericLocalBlobStorage.create_service(pretend.stub(), pretend.stub())
Loading