summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-06-15 17:33:07 +0200
committerWojtek Kosior <koszko@koszko.org>2022-06-15 17:33:07 +0200
commitff759b50e5aadc3c973724021ec9fca3759f9639 (patch)
tree7c57f0894591a6ea27c366f9858bc0ef509bad4f
parentae96024993f111bcb1e48824f5f63be63ef38b1f (diff)
downloadhaketilo-hydrilla-upstream/1.1_beta1.tar.gz
haketilo-hydrilla-upstream/1.1_beta1.zip
New upstream version 1.1~beta1upstream/1.1_beta1debian-upstream
-rw-r--r--PKG-INFO28
-rw-r--r--README.md26
-rw-r--r--conftest.py93
-rw-r--r--doc/examples/hydrilla.wsgi3
-rw-r--r--doc/man/man1/hydrilla.119
-rw-r--r--setup.cfg8
-rwxr-xr-xsetup.py37
-rw-r--r--src/hydrilla.egg-info/PKG-INFO28
-rw-r--r--src/hydrilla.egg-info/SOURCES.txt2
-rw-r--r--src/hydrilla.egg-info/requires.txt5
-rw-r--r--src/hydrilla/server/_version.py4
-rw-r--r--src/hydrilla/server/locales/en_US/LC_MESSAGES/hydrilla-messages.po66
-rw-r--r--src/hydrilla/server/serve.py37
-rw-r--r--tests/helpers.py51
-rw-r--r--tests/source-package-example/index.json154
-rw-r--r--tests/test_server.py154
16 files changed, 503 insertions, 212 deletions
diff --git a/PKG-INFO b/PKG-INFO
index dc23bac..b7cad65 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: hydrilla
-Version: 1.0
+Version: 1.1b1
Summary: Hydrilla repository server
Home-page: https://git.koszko.org/pydrilla
Author: Wojtek Kosior
@@ -19,21 +19,21 @@ Description: # Hydrilla (Python implementation)
* Python3 (>= 3.7)
* [hydrilla.builder](https://git.koszko.org/hydrilla-builder/)
- * flask
+ * flask (>= 1.1)
* click
* jsonschema (>= 3.0)
### Build
+ * build (a PEP517 package builder)
* setuptools
* wheel
* setuptools_scm
- * babel
+ * babel (Python library)
### Test
* pytest
- * reuse
## Building
@@ -55,15 +55,11 @@ Description: # Hydrilla (Python implementation)
To perform the build and installation without PyPI, first install all dependencies system-wide. For example, in Debian-based distributions (including Trisquel):
``` shell
sudo apt install python3-flask python3-flask python3-jsonschema \
- python3-setuptools python3-setuptools-scm python3-babel python3-wheel
+ python3-setuptools python3-setuptools-scm python3-babel python3-wheel \
+ python3-build
```
- Then, block programs you're about to spawn from accessing https://pypi.org. If running on a GNU/Linux system you can utilize Linux user namespaces:
- ``` shell
- unshare -Urn
- ```
-
- The above will put you in a network-isolated shell. If you're using a virtualenv, activate it **after** the `unshare` command.
+ If you're using `virtualenv` command to create a virtual environment, make sure you invoke it with `--system-site-packages` and `--no-download`. The first option is necessary for packages installed inside the virtualenv to be able to use globally-installed dependencies. The second one will make `virtualenv` use locally-available base libraries (setuptools, etc.) instead of downloading them from PyPI.
Now, in unpacked source directories of **both** `hydrilla-builder` and `hydrilla`, run the build and installation commands:
``` shell
@@ -81,6 +77,16 @@ Description: # Hydrilla (Python implementation)
python3 -m pytest
```
+ ## Installation from wheels
+
+ Instead of building yourself you can use Python wheels provided on [Hydrilla downloads page](https://hydrillabugs.koszko.org/projects/hydrilla/wiki/Releases).
+
+ ``` shell
+ python3 -m pip install \
+ path/to/downloaded/hydrilla.builder-1.1b1-py3-none-any.whl \
+ path/to/downloaded/hydrilla-1.1b1-py3-none-any.whl
+ ```
+
## Running
### Hydrilla command
diff --git a/README.md b/README.md
index 6c0978d..30c8e1b 100644
--- a/README.md
+++ b/README.md
@@ -10,21 +10,21 @@ The information below is meant to help hack on the codebase. If you're instead l
* Python3 (>= 3.7)
* [hydrilla.builder](https://git.koszko.org/hydrilla-builder/)
-* flask
+* flask (>= 1.1)
* click
* jsonschema (>= 3.0)
### Build
+* build (a PEP517 package builder)
* setuptools
* wheel
* setuptools_scm
-* babel
+* babel (Python library)
### Test
* pytest
-* reuse
## Building
@@ -46,15 +46,11 @@ Commands like `python3 -m build` and `python3 -m pip` but also `virtualenv` will
To perform the build and installation without PyPI, first install all dependencies system-wide. For example, in Debian-based distributions (including Trisquel):
``` shell
sudo apt install python3-flask python3-flask python3-jsonschema \
- python3-setuptools python3-setuptools-scm python3-babel python3-wheel
+ python3-setuptools python3-setuptools-scm python3-babel python3-wheel \
+ python3-build
```
-Then, block programs you're about to spawn from accessing https://pypi.org. If running on a GNU/Linux system you can utilize Linux user namespaces:
-``` shell
-unshare -Urn
-```
-
-The above will put you in a network-isolated shell. If you're using a virtualenv, activate it **after** the `unshare` command.
+If you're using `virtualenv` command to create a virtual environment, make sure you invoke it with `--system-site-packages` and `--no-download`. The first option is necessary for packages installed inside the virtualenv to be able to use globally-installed dependencies. The second one will make `virtualenv` use locally-available base libraries (setuptools, etc.) instead of downloading them from PyPI.
Now, in unpacked source directories of **both** `hydrilla-builder` and `hydrilla`, run the build and installation commands:
``` shell
@@ -72,6 +68,16 @@ For tests to pass you need compiled message catalogs to be present. If you've pe
python3 -m pytest
```
+## Installation from wheels
+
+Instead of building yourself you can use Python wheels provided on [Hydrilla downloads page](https://hydrillabugs.koszko.org/projects/hydrilla/wiki/Releases).
+
+``` shell
+python3 -m pip install \
+ path/to/downloaded/hydrilla.builder-1.1b1-py3-none-any.whl \
+ path/to/downloaded/hydrilla-1.1b1-py3-none-any.whl
+```
+
## Running
### Hydrilla command
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..37401e8
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+import sys
+from pathlib import Path
+
+import pytest
+import pkgutil
+import importlib
+import functools
+from tempfile import TemporaryDirectory
+from typing import Iterable
+
+here = Path(__file__).resolve().parent
+sys.path.insert(0, str(here / 'src'))
+
+@pytest.fixture(autouse=True)
+def no_requests(monkeypatch):
+ """Remove requests.sessions.Session.request for all tests."""
+ if importlib.util.find_spec("requests") is not None:
+ monkeypatch.delattr('requests.sessions.Session.request')
+
+def _mock_subprocess_run(monkeypatch, where, mocked_run):
+ """Temporarily replace subprocess.run() with the given function."""
+ class MockedSubprocess:
+ """Minimal mocked version of the subprocess module."""
+ run = mocked_run
+
+ monkeypatch.setattr(where, 'subprocess', MockedSubprocess)
+
+@pytest.fixture
+def mock_subprocess_run(monkeypatch, request):
+ """
+ Facilitate temporarily replacing subprocess.run() with a different function.
+
+ If the 'subprocess_run' pytest marker has been used, perform the replacement
+ for the module-function pair supplied through it.
+
+ Return a function that can be called to perform the same replacement in
+ another fixture or from inside a test function.
+ """
+ mocker = functools.partial(_mock_subprocess_run, monkeypatch)
+
+ marker = request.node.get_closest_marker('subprocess_run')
+ if marker:
+ where, mocked_run = marker.args
+ mocker(where, mocked_run)
+
+ return mocker
+
+@pytest.fixture(autouse=True)
+def no_gettext(monkeypatch, request):
+ """
+ Make gettext return all strings untranslated unless we request otherwise.
+ """
+ if request.node.get_closest_marker('enable_gettext'):
+ return
+
+ import hydrilla
+ modules_to_process = [hydrilla]
+
+ def add_child_modules(parent):
+ """
+ Recursuvely collect all modules descending from 'parent' into an array.
+ """
+ try:
+ load_paths = parent.__path__
+ except AttributeError:
+ return
+
+ for module_info in pkgutil.iter_modules(load_paths):
+ if module_info.name != '__main__':
+ __import__(f'{parent.__name__}.{module_info.name}')
+ modules_to_process.append(getattr(parent, module_info.name))
+ add_child_modules(getattr(parent, module_info.name))
+
+ add_child_modules(hydrilla)
+
+ for module in modules_to_process:
+ if hasattr(module, '_'):
+ monkeypatch.setattr(module, '_', lambda message: message)
+
+@pytest.fixture
+def tmpdir() -> Iterable[Path]:
+ """
+ Provide test case with a temporary directory that will be automatically
+ deleted after the test.
+ """
+ with TemporaryDirectory() as tmpdir:
+ yield Path(tmpdir)
diff --git a/doc/examples/hydrilla.wsgi b/doc/examples/hydrilla.wsgi
index 70dd895..480722d 100644
--- a/doc/examples/hydrilla.wsgi
+++ b/doc/examples/hydrilla.wsgi
@@ -23,5 +23,6 @@ application = start_wsgi(standalone_mode=False)
# Comment the above and uncomment this to use a different config file.
#from hydrilla.server import config
+#from pathlib import Path
#application = start_wsgi(standalone_mode=False,
-# obj=config.load(['/path/to/config.json']))
+# obj=config.load([Path('/path/to/config.json')]))
diff --git a/doc/man/man1/hydrilla.1 b/doc/man/man1/hydrilla.1
index c71428c..9fff2f3 100644
--- a/doc/man/man1/hydrilla.1
+++ b/doc/man/man1/hydrilla.1
@@ -6,7 +6,7 @@
.\"
.\" Available under the terms of Creative Commons Zero v1.0 Universal.
-.TH HYDRILLA 1 2022-04-22 "Hydrilla 1.0" "Hydrilla Manual"
+.TH HYDRILLA 1 2022-06-14 "Hydrilla 1.1" "Hydrilla Manual"
.SH NAME
hydrilla \- Serve packages for the Haketilo browser extension
@@ -47,7 +47,9 @@ pun on widespread use of word
.UE
with regard to works published online.
-This option, if present, overrides the property \*(lqmalcontent_dir\*(rq from Hydrilla config file. If the value is not specified on the command line nor in the config file, it defaults to \%\*(lq/var/lib/hydrilla/malcontent\*(rq.
+This option, if present, overrides the property \*(lqmalcontent_dir\*(rq from
+Hydrilla config file. If the value is not specified on the command line nor in
+the config file, it defaults to \%\*(lq/var/lib/hydrilla/malcontent\*(rq.
.TP
.BI \-h " URL" "\fR,\fP \-\^\-hydrilla\-project\-url=" URL
@@ -55,7 +57,10 @@ Use
.I URL
when placing a link to Hydrilla website in served HTML pages.
-This option, if present, overrides the property \*(lqhydrilla_project_url\*(rq from Hydrilla config file. If the value is not specified on the command line nor in the config file, it defaults to \%\*(lqhttps://hydrillabugs.koszko.org/projects/hydrilla/wiki\*(rq.
+This option, if present, overrides the property \*(lqhydrilla_project_url\*(rq
+from Hydrilla config file. If the value is not specified on the command line nor
+in the config file, it defaults to
+\%\*(lqhttps://hydrillabugs.koszko.org/projects/hydrilla/wiki\*(rq.
.TP
.BI \-p " PORT" "\fR,\fP \-\^\-port=" PORT
@@ -67,7 +72,9 @@ is 0, let
.I hydrilla
choose a random free port on the machine.
-This option, if present, overrides the property \*(lqport\*(rq from Hydrilla config file. If the value is not specified on the command line nor in the config file, it defaults to 10112.
+This option, if present, overrides the property \*(lqport\*(rq from Hydrilla
+config file. If the value is not specified on the command line nor in the config
+file, it defaults to 10112.
.TP
.BI \-l " LANGUAGE" "\fR,\fP \-\^\-language=" LANGUAGE
@@ -83,7 +90,9 @@ Otherwise,
.I hydrilla
will silently fall back to the en_US locale.
-This option, if present, overrides the property \*(lqlanguage\*(rq from Hydrilla config file. If the value is not specified on the command line nor in the config file, it defaults to \*(lqen_US\*(rq.
+This option, if present, overrides the property \*(lqlanguage\*(rq from Hydrilla
+config file. If the value is not specified on the command line nor in the config
+file, it defaults to \*(lqen_US\*(rq.
.TP
.BI \-c " CONFIG" "\fR,\fP \-\^\-config=" CONFIG
diff --git a/setup.cfg b/setup.cfg
index db1428f..7385d2b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -25,12 +25,12 @@ classifiers =
zip_safe = False
package_dir =
= src
-packages = find:
+packages = find_namespace:
include_package_data = True
python_requires = >= 3.7
install_requires =
- hydrilla.builder==1.0
- flask
+ hydrilla.builder==1.1b1
+ flask>=1.1
jsonschema>=3.0
[options.package_data]
@@ -38,7 +38,7 @@ hydrilla.server = locales/*/LC_MESSAGES/hydrilla-messages.mo
[options.extras_require]
test = pytest
-setup = setuptools_scm
+setup = setuptools_scm; babel
[options.packages.find]
where = src
diff --git a/setup.py b/setup.py
index 345febc..3aa9f74 100755
--- a/setup.py
+++ b/setup.py
@@ -8,13 +8,42 @@
import setuptools
from setuptools.command.build_py import build_py
+from setuptools.command.sdist import sdist
+
+from pathlib import Path
+
+here = Path(__file__).resolve().parent
class CustomBuildCommand(build_py):
- '''
- The build command but runs babel before build.
- '''
+ """The build command but runs babel before build."""
def run(self, *args, **kwargs):
+ """Wrapper around build_py's original run() method."""
self.run_command('compile_catalog')
+
+ super().run(*args, **kwargs)
+
+class CustomSdistCommand(sdist):
+ """
+ The sdist command but prevents compiled message catalogs from being included
+ in the archive.
+ """
+ def run(self, *args, **kwargs):
+ """Wrapper around sdist's original run() method."""
+ locales_dir = here / 'src/hydrilla/server/locales'
+ locale_files = {}
+
+ for path in locales_dir.rglob('*.mo'):
+ locale_files[path] = path.read_bytes()
+
+ for path in locale_files:
+ path.unlink()
+
super().run(*args, **kwargs)
-setuptools.setup(cmdclass={'build_py': CustomBuildCommand})
+ for path, contents in locale_files.items():
+ path.write_bytes(contents)
+
+setuptools.setup(cmdclass = {
+ 'build_py': CustomBuildCommand,
+ 'sdist': CustomSdistCommand
+})
diff --git a/src/hydrilla.egg-info/PKG-INFO b/src/hydrilla.egg-info/PKG-INFO
index dc23bac..b7cad65 100644
--- a/src/hydrilla.egg-info/PKG-INFO
+++ b/src/hydrilla.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: hydrilla
-Version: 1.0
+Version: 1.1b1
Summary: Hydrilla repository server
Home-page: https://git.koszko.org/pydrilla
Author: Wojtek Kosior
@@ -19,21 +19,21 @@ Description: # Hydrilla (Python implementation)
* Python3 (>= 3.7)
* [hydrilla.builder](https://git.koszko.org/hydrilla-builder/)
- * flask
+ * flask (>= 1.1)
* click
* jsonschema (>= 3.0)
### Build
+ * build (a PEP517 package builder)
* setuptools
* wheel
* setuptools_scm
- * babel
+ * babel (Python library)
### Test
* pytest
- * reuse
## Building
@@ -55,15 +55,11 @@ Description: # Hydrilla (Python implementation)
To perform the build and installation without PyPI, first install all dependencies system-wide. For example, in Debian-based distributions (including Trisquel):
``` shell
sudo apt install python3-flask python3-flask python3-jsonschema \
- python3-setuptools python3-setuptools-scm python3-babel python3-wheel
+ python3-setuptools python3-setuptools-scm python3-babel python3-wheel \
+ python3-build
```
- Then, block programs you're about to spawn from accessing https://pypi.org. If running on a GNU/Linux system you can utilize Linux user namespaces:
- ``` shell
- unshare -Urn
- ```
-
- The above will put you in a network-isolated shell. If you're using a virtualenv, activate it **after** the `unshare` command.
+ If you're using `virtualenv` command to create a virtual environment, make sure you invoke it with `--system-site-packages` and `--no-download`. The first option is necessary for packages installed inside the virtualenv to be able to use globally-installed dependencies. The second one will make `virtualenv` use locally-available base libraries (setuptools, etc.) instead of downloading them from PyPI.
Now, in unpacked source directories of **both** `hydrilla-builder` and `hydrilla`, run the build and installation commands:
``` shell
@@ -81,6 +77,16 @@ Description: # Hydrilla (Python implementation)
python3 -m pytest
```
+ ## Installation from wheels
+
+ Instead of building yourself you can use Python wheels provided on [Hydrilla downloads page](https://hydrillabugs.koszko.org/projects/hydrilla/wiki/Releases).
+
+ ``` shell
+ python3 -m pip install \
+ path/to/downloaded/hydrilla.builder-1.1b1-py3-none-any.whl \
+ path/to/downloaded/hydrilla-1.1b1-py3-none-any.whl
+ ```
+
## Running
### Hydrilla command
diff --git a/src/hydrilla.egg-info/SOURCES.txt b/src/hydrilla.egg-info/SOURCES.txt
index 461f1fc..d30f96a 100644
--- a/src/hydrilla.egg-info/SOURCES.txt
+++ b/src/hydrilla.egg-info/SOURCES.txt
@@ -3,6 +3,7 @@ MANIFEST.in
README.md
README.md.license
babel.cfg
+conftest.py
pyproject.toml
pytest.ini
setup.cfg
@@ -35,6 +36,7 @@ src/hydrilla/server/templates/base.html
src/hydrilla/server/templates/index.html
tests/__init__.py
tests/config.json
+tests/helpers.py
tests/test_server.py
tests/source-package-example/README.txt
tests/source-package-example/README.txt.license
diff --git a/src/hydrilla.egg-info/requires.txt b/src/hydrilla.egg-info/requires.txt
index 026f10d..62e05e0 100644
--- a/src/hydrilla.egg-info/requires.txt
+++ b/src/hydrilla.egg-info/requires.txt
@@ -1,8 +1,9 @@
-flask
-hydrilla.builder==1.0
+flask>=1.1
+hydrilla.builder==1.1b1
jsonschema>=3.0
[setup]
+babel
setuptools_scm
[test]
diff --git a/src/hydrilla/server/_version.py b/src/hydrilla/server/_version.py
index d953eef..2feb153 100644
--- a/src/hydrilla/server/_version.py
+++ b/src/hydrilla/server/_version.py
@@ -1,5 +1,5 @@
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
-version = '1.0'
-version_tuple = (1, 0)
+version = '1.1b1'
+version_tuple = (1, '1b1')
diff --git a/src/hydrilla/server/locales/en_US/LC_MESSAGES/hydrilla-messages.po b/src/hydrilla/server/locales/en_US/LC_MESSAGES/hydrilla-messages.po
index 7ea930a..1998f89 100644
--- a/src/hydrilla/server/locales/en_US/LC_MESSAGES/hydrilla-messages.po
+++ b/src/hydrilla/server/locales/en_US/LC_MESSAGES/hydrilla-messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: hydrilla.builder 0.1\n"
"Report-Msgid-Bugs-To: koszko@koszko.org\n"
-"POT-Creation-Date: 2022-04-22 17:09+0200\n"
+"POT-Creation-Date: 2022-05-31 18:21+0200\n"
"PO-Revision-Date: 2022-02-12 00:00+0000\n"
"Last-Translator: Wojtek Kosior <koszko@koszko.org>\n"
"Language: en_US\n"
@@ -18,56 +18,60 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.8.0\n"
-#: src/hydrilla/server/serve.py:122
+#: src/hydrilla/server/serve.py:127
#, python-brace-format
msgid "uuid_mismatch_{identifier}"
msgstr "Two different uuids were specified for item '{identifier}'."
-#: src/hydrilla/server/serve.py:129
+#: src/hydrilla/server/serve.py:134
#, python-brace-format
msgid "version_clash_{identifier}_{version}"
msgstr "Version '{version}' specified more than once for item '{identifier}'."
-#: src/hydrilla/server/serve.py:245 src/hydrilla/server/serve.py:257
+#: src/hydrilla/server/serve.py:250 src/hydrilla/server/serve.py:262
msgid "invalid_URL_{}"
msgstr "Invalid URL/pattern: '{}'."
-#: src/hydrilla/server/serve.py:249
+#: src/hydrilla/server/serve.py:254
msgid "disallowed_protocol_{}"
msgstr "Disallowed protocol: '{}'."
-#: src/hydrilla/server/serve.py:302
+#: src/hydrilla/server/serve.py:307
msgid "malcontent_dir_path_not_dir_{}"
msgstr "Provided 'malcontent_dir' path does not name a directory: {}"
-#: src/hydrilla/server/serve.py:321
+#: src/hydrilla/server/serve.py:326
msgid "couldnt_load_item_from_{}"
msgstr "Couldn't load item from {}."
-#: src/hydrilla/server/serve.py:347
+#: src/hydrilla/server/serve.py:351
msgid "item_{item}_in_file_{file}"
msgstr "Item {item} incorrectly present under {file}."
-#: src/hydrilla/server/serve.py:353
+#: src/hydrilla/server/serve.py:357
msgid "item_version_{ver}_in_file_{file}"
msgstr "Item version {ver} incorrectly present under {file}."
-#: src/hydrilla/server/serve.py:376
+#: src/hydrilla/server/serve.py:380
msgid "no_dep_{resource}_{ver}_{dep}"
msgstr "Unknown dependency '{dep}' of resource '{resource}', version '{ver}'."
-#: src/hydrilla/server/serve.py:387
+#: src/hydrilla/server/serve.py:391
msgid "no_payload_{mapping}_{ver}_{payload}"
msgstr "Unknown payload '{payload}' of mapping '{mapping}', version '{ver}'."
-#: src/hydrilla/server/serve.py:413
+#: src/hydrilla/server/serve.py:403
+msgid "no_mapping_{required_by}_{ver}_{required}"
+msgstr "Unknown mapping '{required}' required by '{required_by}', version '{ver}'."
+
+#: src/hydrilla/server/serve.py:430
msgid "couldnt_register_{mapping}_{ver}_{pattern}"
msgstr ""
"Couldn't register mapping '{mapping}', version '{ver}' (pattern "
"'{pattern}')."
-#: src/hydrilla/server/serve.py:566 src/hydrilla/server/serve.py:588
-#: src/hydrilla/server/serve.py:626
+#: src/hydrilla/server/serve.py:583 src/hydrilla/server/serve.py:606
+#: src/hydrilla/server/serve.py:650
#, python-format
msgid "%(prog)s_%(version)s_license"
msgstr ""
@@ -78,55 +82,55 @@ msgstr ""
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law."
-#: src/hydrilla/server/serve.py:577
+#: src/hydrilla/server/serve.py:592
+msgid "serve_hydrilla_packages_explain_wsgi_considerations"
+msgstr ""
+"Serve Hydrilla packages.\n"
+"\n"
+"This command is meant to be a quick way to run a local or development "
+"Hydrilla instance. For better performance, consider deployment using "
+"WSGI."
+
+#: src/hydrilla/server/serve.py:595
msgid "directory_to_serve_from_overrides_config"
msgstr ""
"Directory to serve files from. Overrides value from the config file (if "
"any)."
-#: src/hydrilla/server/serve.py:579
+#: src/hydrilla/server/serve.py:597
msgid "project_url_to_display_overrides_config"
msgstr ""
"Project url to display on generated HTML pages. Overrides value from the "
"config file (if any)."
-#: src/hydrilla/server/serve.py:581
+#: src/hydrilla/server/serve.py:599
msgid "tcp_port_to_listen_on_overrides_config"
msgstr ""
"TCP port number to listen on (0-65535). Overrides value from the config "
"file (if any)."
-#: src/hydrilla/server/serve.py:584
+#: src/hydrilla/server/serve.py:602
msgid "path_to_config_file_explain_default"
msgstr ""
"Path to Hydrilla server configuration file (optional, by default Hydrilla"
" loads its own config file, which in turn tries to load "
"/etc/hydrilla/config.json)."
-#: src/hydrilla/server/serve.py:586
+#: src/hydrilla/server/serve.py:604
msgid "language_to_use_overrides_config"
msgstr ""
"Language to use (also affects served HTML files). Overrides value from "
"the config file (if any)."
-#: src/hydrilla/server/serve.py:589 src/hydrilla/server/serve.py:627
+#: src/hydrilla/server/serve.py:607 src/hydrilla/server/serve.py:651
msgid "version_printing"
msgstr "Print version information and exit."
-#: src/hydrilla/server/serve.py:617
+#: src/hydrilla/server/serve.py:640
msgid "config_option_{}_not_supplied"
msgstr "Missing configuration option '{}'."
-#: src/hydrilla/server/serve.py:621
-msgid "serve_hydrilla_packages_explain_wsgi_considerations"
-msgstr ""
-"Serve Hydrilla packages.\n"
-"\n"
-"This command is meant to be a quick way to run a local or development "
-"Hydrilla instance. For better performance, consider deployment using "
-"WSGI."
-
-#: src/hydrilla/server/serve.py:632
+#: src/hydrilla/server/serve.py:644
msgid "serve_hydrilla_packages_wsgi_help"
msgstr ""
"Serve Hydrilla packages.\n"
diff --git a/src/hydrilla/server/serve.py b/src/hydrilla/server/serve.py
index a6a1204..779f3d2 100644
--- a/src/hydrilla/server/serve.py
+++ b/src/hydrilla/server/serve.py
@@ -56,13 +56,18 @@ generated_by = {
class ItemInfo(ABC):
"""Shortened data of a resource/mapping."""
- def __init__(self, item_obj: dict):
+ def __init__(self, item_obj: dict, major_schema_version: int):
"""Initialize ItemInfo using item definition read from JSON."""
self.version = util.normalize_version(item_obj['version'])
self.identifier = item_obj['identifier']
self.uuid = item_obj.get('uuid')
self.long_name = item_obj['long_name']
+ self.required_mappings = []
+ if major_schema_version >= 2:
+ self.required_mappings = [map_ref['identifier'] for map_ref in
+ item_obj.get('required_mappings', [])]
+
def path(self) -> str:
"""
Get a relative path to this item's JSON definition with respect to
@@ -72,18 +77,18 @@ class ItemInfo(ABC):
class ResourceInfo(ItemInfo):
"""Shortened data of a resource."""
- def __init__(self, resource_obj: dict):
+ def __init__(self, resource_obj: dict, major_schema_version: int):
"""Initialize ResourceInfo using resource definition read from JSON."""
- super().__init__(resource_obj)
+ super().__init__(resource_obj, major_schema_version)
dependencies = resource_obj.get('dependencies', [])
self.dependencies = [res_ref['identifier'] for res_ref in dependencies]
class MappingInfo(ItemInfo):
"""Shortened data of a mapping."""
- def __init__(self, mapping_obj: dict):
+ def __init__(self, mapping_obj: dict, major_schema_version: int):
"""Initialize MappingInfo using mapping definition read from JSON."""
- super().__init__(mapping_obj)
+ super().__init__(mapping_obj, major_schema_version)
self.payloads = {}
for pattern, res_ref in mapping_obj.get('payloads', {}).items():
@@ -332,16 +337,15 @@ class Malcontent:
version = util.parse_version(ver_file.name)
identifier = ver_file.parent.name
- with open(ver_file, 'rt') as file_handle:
- item_json = json.load(file_handle)
+ item_json, major = util.load_instance_from_file(ver_file)
- util.validator_for(f'api_{item_type}_description-1.0.1.schema.json')\
+ util.validator_for(f'api_{item_type}_description-{major}.schema.json')\
.validate(item_json)
if item_type == 'resource':
- item_info = ResourceInfo(item_json)
+ item_info = ResourceInfo(item_json, major)
else:
- item_info = MappingInfo(item_json)
+ item_info = MappingInfo(item_json, major)
if item_info.identifier != identifier:
msg = f_('item_{item}_in_file_{file}')\
@@ -394,6 +398,19 @@ class Malcontent:
if payload not in self.infos['resource']:
report_missing_payload(mapping_info, payload)
+ def report_missing_mapping(info: Union[MappingInfo, ResourceInfo],
+ required_mapping: str) -> None:
+ msg = _('no_mapping_{required_by}_{ver}_{required}')\
+ .format(required_by=info.identifier, required=required_mapping,
+ ver=util.version_string(info.version))
+ logging.error(msg)
+
+ for item_info in (*self._all_of_type('mapping'),
+ *self._all_of_type('resource')):
+ for required in item_info.required_mappings:
+ if required not in self.infos['mapping']:
+ report_missing_mapping(item_info, required)
+
def _finalize(self):
"""
Initialize structures needed to serve queries. Called once after all
diff --git a/tests/helpers.py b/tests/helpers.py
new file mode 100644
index 0000000..df474b0
--- /dev/null
+++ b/tests/helpers.py
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+import re
+
+variable_word_re = re.compile(r'^<(.+)>$')
+
+def process_command(command, expected_command):
+ """Validate the command line and extract its variable parts (if any)."""
+ assert len(command) == len(expected_command)
+
+ extracted = {}
+ for word, expected_word in zip(command, expected_command):
+ match = variable_word_re.match(expected_word)
+ if match:
+ extracted[match.group(1)] = word
+ else:
+ assert word == expected_word
+
+ return extracted
+
+def run_missing_executable(command, **kwargs):
+ """
+ Instead of running a command, raise FileNotFoundError as if its executable
+ was missing.
+ """
+ raise FileNotFoundError('dummy')
+
+class MockedCompletedProcess:
+ """
+ Object with some fields similar to those of subprocess.CompletedProcess.
+ """
+ def __init__(self, args, returncode=0,
+ stdout='some output', stderr='some error output',
+ text_output=True):
+ """
+ Initialize MockedCompletedProcess. Convert strings to bytes if needed.
+ """
+ self.args = args
+ self.returncode = returncode
+
+ if type(stdout) is str and not text_output:
+ stdout = stdout.encode()
+ if type(stderr) is str and not text_output:
+ stderr = stderr.encode()
+
+ self.stdout = stdout
+ self.stderr = stderr
diff --git a/tests/source-package-example/index.json b/tests/source-package-example/index.json
index 7162dd7..9aa6e70 100644
--- a/tests/source-package-example/index.json
+++ b/tests/source-package-example/index.json
@@ -3,9 +3,9 @@
// Copyright (C) 2021, 2022 Wojtek Kosior <koszko@koszko.org>
// Available under the terms of Creative Commons Zero v1.0 Universal.
-// This is an example index.json file describing Hydrilla site content. As you
-// can see, for storing site content information Hydrilla utilizes JSON with an
-// additional extension in the form of '//' comments support.
+// This is an example index.json file describing Hydrilla packages. As you can
+// see, for storing this information Hydrilla utilizes JSON with an additional
+// extension in the form of '//' comments support.
// An index.json file conveys definitions of site resources and pattern->payload
// mappings. The definitions may reference files under index.json's containing
@@ -62,18 +62,15 @@
// will also belong here once they get implemented.
"definitions": [
{
- // Value of "type" can currently be one of: "resource" and
- // "mapping". The one we have here, "resource", defines a list
- // of injectable scripts that can be used as a payload or as a
- // dependency of another "resource". In the future CSS style sheets
- // and WASM modules will also be composite parts of a "resource" as
- // scripts are now.
- "type": "resource",
+ // Value of "type" can currently be one of: "resource", "mapping"
+ // and "mapping_and_resource" for a combined definition. The one we
+ // have here, "mapping", associates resources with pages on which
+ // they are to be used.
+ "type": "mapping",
- // Used when referring to this resource in "dependencies" list of
- // another resource or in "payload" field of a mapping. Should
- // be consize and can only use a restricted set of characters. It
- // has to match: [-0-9a-z]+
+ // Used when referring to this mapping in "required_mappings" list
+ // of another item. Should be consize and can only use a restricted
+ // set of characters. It has to match: [-0-9a-z]+
"identifier": "helloapple",
// "long_name" should be used to specify a user-friendly alternative
@@ -96,26 +93,80 @@
// Different versions (e.g. 1.0 and 1.3) of the same resource can be
// defined in separate index.json files. This makes it easy to
// accidently cause an identifier clash. To help detect it, we allow
- // each resource to have a UUID associated with it. Attempt to
- // define multiple resources with the same identifier and different
- // UUIDs will result in an error being reported. Defining multiple
- // resources with different identifiers and the same UUID is
+ // each item to have a UUID associated with it. Attempt to define
+ // multiple mapping with the same identifier and different UUIDs
+ // will result in an error being reported. Defining multiple
+ // mappings with different identifiers and the same UUID is
// disallowed for now (it may be later permitted if we consider it
// good for some use-case).
- // As of package source schema version 1.0, UUIDs are optional and
+ // As of package source schema version 2.0, UUIDs are optional and
// can be omitted.
+ "uuid": "54d23bba-472e-42f5-9194-eaa24c0e3ee7",
+
+ // Thanks to the "version" field (and "revision" field in case of
+ // "resource" or "mapping_and_resource", clients will know they have
+ // to update certain item after a new version has appeared in the
+ // repository. If multiple definitions of the same version of given
+ // item are provided to Hydrilla server, an error is generated.
+ // "version" differs from its counterpart in resource in that it has
+ // no accompanying revision number. For combined definitions with
+ // "mapping_and_resource" as type, the value of "revision" is
+ // appended as the last component of the resulting mapping's
+ // version. If type is simply "mapping", revision number is ignored.
+ "version": [2021, 11, 10],
+
+ // A short, meaningful description of what the mapping does.
+ "description": "causes apple to get greeted on Hydrillabugs issue tracker",
+
+ // If needed, a "comment" field can be added to provide some
+ // additional information.
+ // "comment": "this resource something something",
+
+ // The "payloads" object specifies which payloads are to be applied
+ // to which URLs.
+ "payloads": {
+ // Each key should be a valid Haketilo URL pattern.
+ "https://hydrillabugs.koszko.org/***": {
+ // Should be the name of an existing resource. The resource
+ // may, but doesn't have to, be defined in the same
+ // index.json file.
+ "identifier": "helloapple"
+ },
+ // More associations may follow.
+ "https://hachettebugs.koszko.org/***": {
+ "identifier": "helloapple"
+ }
+ }
+ }, {
+ // A "resource" item defines a list of injectable scripts that can
+ // be used as a payload or as a dependency of another "resource". In
+ // the future CSS style sheets and WASM modules will also be
+ // composite parts of a "resource" as scripts are now.
+ "type": "resource",
+
+ // Has similar function to mapping's identifier. Used when
+ // referring to this resource in "dependencies" list of another
+ // resource or in "payload" field of a mapping. Should be consize
+ // and can only use a restricted set of characters. It has to match:
+ // [-0-9a-z]+
+ // It can be the same as some mapping identifier (those are
+ // different entities and are treated separately).
+ "identifier": "helloapple",
+
+ // "long name" and "uuid" have the same meaning as in the case of
+ // resources and "uuid" is also optional. UUIDs of a resource and a
+ // mapping can technically be the same but it is recommended to
+ // avoid even this kind of repetition.
+ "long_name": "Hello Apple",
"uuid": "a6754dcb-58d8-4b7a-a245-24fd7ad4cd68",
// Version should match the upstream version of the resource (e.g. a
- // version of javascript library). Revision number starts as 1 for
+ // version of JavaScript library). Revision number starts as 1 for
// each new resource version and gets incremented by 1 each time a
- // modification to the packaging of this version is done. Hydrilla
- // will allow multiple definitions of the same resource to load, as
- // long as their versions differ. Thanks to the "version" and
- // "revision" fields, clients will know they have to update certain
- // resource after it has been updated. If multiple definitions of
- // the same version of given resource are provided, an error is
- // generated (even if those definitions differ by revision number).
+ // modification to the packaging of this version is done.
+ // If multiple definitions of the same version of given resource are
+ // provided to Hydrilla server, an error is generated (even if those
+ // definitions differ by revision number).
"version": [2021, 11, 10],
"revision": 1,
@@ -123,16 +174,15 @@
// what it does.
"description": "greets an apple",
- // If needed, a "comment" field can be added to provide some
- // additional information.
- // "comment": "this resource something something",
+ // A comment, if necessary.
+ // "comment": "blah blah because bleh"
// Resource's "dependencies" array shall contain names of other
// resources that (in case of scripts at least) should get evaluated
// on a page before this resource's own scripts.
"dependencies": [{"identifier": "hello-message"}],
- // Array of javascript files that belong to this resource.
+ // Array of JavaScript files that belong to this resource.
"scripts": [
{"file": "hello.js"},
{"file": "bye.js"}
@@ -148,48 +198,6 @@
// If "dependencies" is empty, it can also be omitted.
// "dependencies": [],
"scripts": [{"file": "message.js"}]
- }, {
- "type": "mapping",
-
- // Has similar function to resource's identifier. Should be consize
- // and can only use a restricted set of characters. It has to match:
- // [-0-9a-z]+
- // It can be the same as some resource identifier (those are
- // different entities and are treated separately).
- "identifier": "helloapple",
-
- // "long name" and "uuid" have the same meaning as in the case of
- // resources and "uuid" is also optional. UUIDs of a resource and a
- // mapping can technically be the same but it is recommended to
- // avoid even this kind of repetition.
- "long_name": "Hello Apple",
- "uuid": "54d23bba-472e-42f5-9194-eaa24c0e3ee7",
-
- // "version" differs from its counterpart in resource in that it has
- // no accompanying revision number.
- "version": [2021, 11, 10],
-
- // A short, meaningful description of what the mapping does.
- "description": "causes apple to get greeted on Hydrillabugs issue tracker",
-
- // A comment, if necessary.
- // "comment": "blah blah because bleh"
-
- // The "payloads" object specifies which payloads are to be applied
- // to which URLs.
- "payloads": {
- // Each key should be a valid Haketilo URL pattern.
- "https://hydrillabugs.koszko.org/***": {
- // Should be the name of an existing resource. The resource
- // may, but doesn't have to, be defined in the same
- // index.json file.
- "identifier": "helloapple"
- },
- // More associations may follow.
- "https://hachettebugs.koszko.org/***": {
- "identifier": "helloapple"
- }
- }
}
],
// We can also list additional files to include in the produced source
diff --git a/tests/test_server.py b/tests/test_server.py
index 0820d5c..65ae8ce 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -31,6 +31,7 @@ import pytest
import sys
import shutil
import json
+import functools as ft
from pathlib import Path
from hashlib import sha256
@@ -42,9 +43,10 @@ from markupsafe import escape
from werkzeug import Response
from hydrilla import util as hydrilla_util
-from hydrilla.builder import Build
-from hydrilla.server import config, _version
-from hydrilla.server.serve import HydrillaApp
+from hydrilla.builder import build
+from hydrilla.server import config, _version, serve
+
+from .helpers import *
here = Path(__file__).resolve().parent
config_path = here / 'config.json'
@@ -55,13 +57,41 @@ expected_generated_by = {
'version': _version.version
}
-SetupMod = Optional[Callable['Setup', None]]
+SetupMod = Optional[Callable[['Setup'], None]]
source_files = (
'index.json', 'hello.js', 'bye.js', 'message.js', 'README.txt',
'README.txt.license', '.reuse/dep5', 'LICENSES/CC0-1.0.txt'
)
+def run_reuse(command, **kwargs):
+ """
+ Instead of running a 'reuse' command, check if 'mock_reuse_missing' file
+ exists under root directory. If yes, raise FileNotFoundError as if 'reuse'
+ command was missing. If not, check if 'README.txt.license' file exists
+ in the requested directory and return zero if it does.
+ """
+ expected = ['reuse', '--root', '<root>',
+ 'lint' if 'lint' in command else 'spdx']
+
+ root_path = Path(process_command(command, expected)['root'])
+
+ if (root_path / 'mock_reuse_missing').exists():
+ raise FileNotFoundError('dummy')
+
+ is_reuse_compliant = (root_path / 'README.txt.license').exists()
+
+ return MockedCompletedProcess(command, 1 - is_reuse_compliant,
+ stdout=f'dummy {expected[-1]} output',
+ text_output=kwargs.get('text'))
+
+@pytest.fixture
+def mock_reuse(mock_subprocess_run):
+ """
+ Mock the REUSE command when executed through subprocess.run() from serve.py.
+ """
+ mock_subprocess_run(build, run_reuse)
+
class Setup:
"""
Facilitate preparing test malcontent directory, Hydrilla config file and the
@@ -95,8 +125,8 @@ class Setup:
if self._modify_before_build:
self._modify_before_build(self)
- build = Build(self.source_dir, self.index_json)
- build.write_package_files(self.malcontent_dir)
+ build_obj = build.Build(self.source_dir, self.index_json)
+ build_obj.write_package_files(self.malcontent_dir)
if self._modify_after_build:
self._modify_after_build(self)
@@ -114,48 +144,64 @@ class Setup:
Provide app client that serves the objects from built sample package.
"""
if self._client is None:
- app = HydrillaApp(self.config(), flask_config={'TESTING': True})
+ app = serve.HydrillaApp(self.config(),
+ flask_config={'TESTING': True})
self._client = app.test_client()
return self._client
-def remove_all_uuids(setup: Setup) -> None:
- """Modify sample packages before build to contain no (optional) UUIDs"""
- index_json = (setup.source_dir / 'index.json').read_text()
- index_json = json.loads(hydrilla_util.strip_json_comments(index_json))
+def index_json_modification(modify_index_json):
+ """Decorator for function that modifies index.json before build."""
+ def handle_index_json(setup):
+ """Modify index.json before build."""
+ index_path = setup.source_dir / 'index.json'
+ index_json, _ = hydrilla_util.load_instance_from_file(index_path)
+
+ index_json = modify_index_json(index_json) or index_json
+
+ index_json = f'''
+ // SPDX-License-Identifier: CC0-1.0
+ // Copyright (C) 2021, 2022 Wojtek Kosior
+ {json.dumps(index_json)}
+ '''
+
+ index_path.write_text(index_json)
+ return handle_index_json
+
+@index_json_modification
+def remove_all_uuids(index_json):
+ """Modify sample packages to contain no (optional) UUIDs"""
for definition in index_json['definitions']:
del definition['uuid']
- index_json = ("// SPDX-License-Identifier: CC0-1.0\n" +
- "// Copyright (C) 2021, 2022 Wojtek Kosior\n" +
- json.dumps(index_json))
+@index_json_modification
+def bump_schema_v2(index_json) -> None:
+ """Modify sample packages to use version 2 of Hydrilla JSON schemas."""
+ for definition in index_json['definitions']:
+ definition['min_haketilo_version'] = [1, 1]
- (setup.source_dir / 'index.json').write_text(index_json)
+ if definition['identifier'] == 'helloapple' and \
+ definition['type'] == 'resource':
+ definition['required_mappings'] = {'identifier': 'helloapple'}
-default_setup = Setup()
-uuidless_setup = Setup(modify_before_build=remove_all_uuids)
+default_setup = lambda: Setup()
+uuidless_setup = lambda: Setup(modify_before_build=remove_all_uuids)
+schema_v2_setup = lambda: Setup(modify_before_build=bump_schema_v2)
-def def_get(url: str) -> Response:
- """Convenience wrapper for def_get()"""
- return default_setup.client().get(url)
+setup_makers = [default_setup, uuidless_setup, schema_v2_setup]
-def test_project_url() -> None:
- """Fetch index.html and verify project URL from config is present there."""
- response = def_get('/')
- assert b'html' in response.data
- project_url = default_setup.config()['hydrilla_project_url']
- assert escape(project_url).encode() in response.data
-
-@pytest.mark.parametrize('setup', [default_setup, uuidless_setup])
+@pytest.mark.usefixtures('mock_reuse')
+@pytest.mark.parametrize('setup_maker', setup_makers)
@pytest.mark.parametrize('item_type', ['resource', 'mapping'])
-def test_get_newest(setup: Setup, item_type: str) -> None:
+def test_get_newest(setup_maker, item_type: str) -> None:
"""
Verify that
GET '/{item_type}/{item_identifier}.json'
returns proper definition that is also served at:
GET '/{item_type}/{item_identifier}/{item_version}'
"""
+ setup = setup_maker()
response = setup.client().get(f'/{item_type}/helloapple.json')
assert response.status_code == 200
definition = json.loads(response.data.decode())
@@ -166,46 +212,58 @@ def test_get_newest(setup: Setup, item_type: str) -> None:
assert response.status_code == 200
assert definition == json.loads(response.data.decode())
- assert ('uuid' in definition) == (setup is not uuidless_setup)
+ assert ('uuid' in definition) == (setup_maker is not uuidless_setup)
hydrilla_util.validator_for(f'api_{item_type}_description-1.0.1.schema.json')\
.validate(definition)
+@pytest.fixture
+def setup(mock_reuse):
+ """Prepare server test environment in the default way."""
+ return default_setup()
+
+def test_project_url(setup) -> None:
+ """Fetch index.html and verify project URL from config is present there."""
+ response = setup.client().get('/')
+ assert b'html' in response.data
+ project_url = setup.config()['hydrilla_project_url']
+ assert escape(project_url).encode() in response.data
+
@pytest.mark.parametrize('item_type', ['resource', 'mapping'])
-def test_get_nonexistent(item_type: str) -> None:
+def test_get_nonexistent(setup, item_type: str) -> None:
"""
Verify that attempts to GET a JSON definition of a nonexistent item or item
version result in 404.
"""
- response = def_get(f'/{item_type}/nonexistentapple.json')
+ response = setup.client().get(f'/{item_type}/nonexistentapple.json')
assert response.status_code == 404
- response = def_get(f'/{item_type}/helloapple/1.2.3.999')
+ response = setup.client().get(f'/{item_type}/helloapple/1.2.3.999')
assert response.status_code == 404
@pytest.mark.parametrize('item_type', ['resource', 'mapping'])
-def test_file_refs(item_type: str) -> None:
+def test_file_refs(setup, item_type: str) -> None:
"""
Verify that files referenced by definitions are accessible under their
proper URLs and that their hashes match.
"""
- response = def_get(f'/{item_type}/helloapple/2021.11.10')
+ response = setup.client().get(f'/{item_type}/helloapple/2021.11.10')
assert response.status_code == 200
definition = json.loads(response.data.decode())
for file_ref in [*definition.get('scripts', []),
*definition['source_copyright']]:
hash_sum = file_ref["sha256"]
- response = def_get(f'/file/sha256/{hash_sum}')
+ response = setup.client().get(f'/file/sha256/{hash_sum}')
assert response.status_code == 200
assert sha256(response.data).digest().hex() == hash_sum
-def test_empty_query() -> None:
+def test_empty_query(setup) -> None:
"""
Verify that querying mappings for URL gives an empty list when there're no
mathes.
"""
- response = def_get(f'/query?url=https://nonexiste.nt/example')
+ response = setup.client().get(f'/query?url=https://nonexiste.nt/example')
assert response.status_code == 200
response_object = json.loads(response.data.decode())
@@ -219,12 +277,12 @@ def test_empty_query() -> None:
hydrilla_util.validator_for('api_query_result-1.0.1.schema.json')\
.validate(response_object)
-def test_query() -> None:
+def test_query(setup) -> None:
"""
Verify that querying mappings for URL gives a list with reference(s) the the
matching mapping(s).
"""
- response = def_get(f'/query?url=https://hydrillabugs.koszko.org/')
+ response = setup.client().get(f'/query?url=https://hydrillabugs.koszko.org/')
assert response.status_code == 200
response_object = json.loads(response.data.decode())
@@ -239,12 +297,12 @@ def test_query() -> None:
'generated_by': expected_generated_by
}
- hydrilla_util.validator_for('api_query_result-1.0.1.schema.json')\
+ hydrilla_util.validator_for('api_query_result-1.schema.json')\
.validate(response_object)
-def test_source() -> None:
+def test_source(setup) -> None:
"""Verify source descriptions are properly served."""
- response = def_get(f'/source/hello.json')
+ response = setup.client().get(f'/source/hello.json')
assert response.status_code == 200
description = json.loads(response.data.decode())
@@ -254,18 +312,18 @@ def test_source() -> None:
['hello-message', 'helloapple', 'helloapple']
zipfile_hash = description['source_archives']['zip']['sha256']
- response = def_get(f'/source/hello.zip')
+ response = setup.client().get(f'/source/hello.zip')
assert sha256(response.data).digest().hex() == zipfile_hash
- hydrilla_util.validator_for('api_source_description-1.0.1.schema.json')\
+ hydrilla_util.validator_for('api_source_description-1.schema.json')\
.validate(description)
-def test_missing_source() -> None:
+def test_missing_source(setup) -> None:
"""Verify requests for nonexistent sources result in 404."""
- response = def_get(f'/source/nonexistent.json')
+ response = setup.client().get(f'/source/nonexistent.json')
assert response.status_code == 404
- response = def_get(f'/source/nonexistent.zip')
+ response = setup.client().get(f'/source/nonexistent.zip')
assert response.status_code == 404
def test_normalize_version():