aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pytest.ini3
-rw-r--r--tests/conftest.py22
-rw-r--r--tests/integration/test_browse.py91
-rw-r--r--tests/integration/test_upload.py27
-rw-r--r--tests/testutils.py20
-rw-r--r--tests/unit/test_util.py8
-rw-r--r--tests/unit/views/test_browse.py14
7 files changed, 152 insertions, 33 deletions
diff --git a/pytest.ini b/pytest.ini
index 1fffd6f..f0d41e6 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,5 +1,6 @@
[pytest]
-addopts = --strict-markers
+addopts = --strict-markers --import-mode=importlib
+pythonpath = tests
testpaths =
fietsboek
diff --git a/tests/conftest.py b/tests/conftest.py
index b8e3090..79f0245 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -141,3 +141,25 @@ def route_path(app_request):
def get_route_path(*args, **kwargs):
return app_request.route_path(*args, **kwargs)
return get_route_path
+
+
+@pytest.fixture()
+def logged_in(dbsession, testapp, route_path):
+ """
+ A fixture that represents a logged in state.
+
+ This automatically creates a user and returns the created user.
+
+ Returns the user that was logged in.
+ """
+ user = models.User(email='foo@bar.com', is_verified=True)
+ user.set_password("foobar")
+ dbsession.add(user)
+
+ login = testapp.get(route_path('login'))
+ form = login.form
+ form['email'] = 'foo@bar.com'
+ form['password'] = 'foobar'
+ response = form.submit()
+ assert response.status_code == 302
+ return user
diff --git a/tests/integration/test_browse.py b/tests/integration/test_browse.py
new file mode 100644
index 0000000..cfd1d71
--- /dev/null
+++ b/tests/integration/test_browse.py
@@ -0,0 +1,91 @@
+import io
+import zipfile
+from contextlib import contextmanager
+from datetime import datetime
+
+from sqlalchemy import select, func
+from webtest import Upload
+
+from testutils import load_gpx_asset
+from fietsboek import models
+from fietsboek.models.track import Visibility
+
+
+@contextmanager
+def added_tracks(tm, dbsession, owner):
+ """Adds some tracks to the database session.
+
+ This function should be used as a context manager and it ensures that the
+ added tracks are deleted again after the test, to make a clean slate for
+ the next test.
+ """
+ # The normal transaction is "doomed", so we need to abort it, start a fresh
+ # one, and then explicitely commit it, otherwise we will not persist the
+ # objects to the database.
+ tm.abort()
+
+ tracks = []
+ with tm:
+ track = models.Track(
+ owner=owner,
+ title="Foobar",
+ visibility=Visibility.PUBLIC,
+ description="A foo'd track",
+ badges=[],
+ link_secret="foobar",
+ tagged_people=[],
+ )
+ track.date = datetime(2022, 3, 14, 9, 26, 54)
+ track.gpx_data = load_gpx_asset("MyTourbook_1.gpx.gz")
+ dbsession.add(track)
+ tracks.append(track)
+
+ track = models.Track(
+ owner=owner,
+ title="Barfoo",
+ visibility=Visibility.PUBLIC,
+ description="A bar'd track",
+ badges=[],
+ link_secret="barfoo",
+ tagged_people=[],
+ )
+ track.date = datetime(2022, 10, 29, 13, 37, 11)
+ track.gpx_data = load_gpx_asset("Teasi_1.gpx.gz")
+ dbsession.add(track)
+ tracks.append(track)
+
+ tm.begin()
+ tm.doom()
+
+ try:
+ yield tracks
+ finally:
+ tm.abort()
+ with tm:
+ for track in tracks:
+ dbsession.delete(track)
+ tm.begin()
+ tm.doom()
+
+
+def test_browse(testapp, dbsession, route_path, logged_in, tm):
+ # Ensure there are some tracks in the database
+ with added_tracks(tm, dbsession, logged_in):
+ # Now go to the browse page
+ browse = testapp.get(route_path('browse'))
+
+ assert "Foobar" in browse.text
+ assert "Barfoo" in browse.text
+
+
+def test_archive(testapp, dbsession, route_path, logged_in, tm):
+ with added_tracks(tm, dbsession, logged_in):
+ archive = testapp.get(
+ route_path('track-archive', _query=[("track_id[]", "1"), ("track_id[]", "2")])
+ )
+ result = io.BytesIO(archive.body)
+
+ with zipfile.ZipFile(result, 'r') as zipped:
+ assert len(zipped.namelist()) == 2
+ assert "track_1.gpx" in zipped.namelist()
+ assert "track_2.gpx" in zipped.namelist()
diff --git a/tests/integration/test_upload.py b/tests/integration/test_upload.py
index 651aedf..1903cbe 100644
--- a/tests/integration/test_upload.py
+++ b/tests/integration/test_upload.py
@@ -1,34 +1,9 @@
-import gzip
-from pathlib import Path
-
-import pytest
from sqlalchemy import select, func
from webtest import Upload
+from testutils import load_gpx_asset
from fietsboek import models
-
-@pytest.fixture()
-def logged_in(dbsession, testapp, route_path):
- user = models.User(email='foo@bar.com', is_verified=True)
- user.set_password("foobar")
- dbsession.add(user)
-
- login = testapp.get(route_path('login'))
- form = login.form
- form['email'] = 'foo@bar.com'
- form['password'] = 'foobar'
- response = form.submit()
- assert response.status_code == 302
-
-
-def load_gpx_asset(filename):
- asset_dir = Path(__file__).parent.parent / 'assets'
- test_file = asset_dir / filename
- with gzip.open(test_file, 'rb') as fobj:
- return fobj.read()
-
-
def test_upload_forbidden(testapp, route_path):
upload_form = testapp.get(route_path('upload'), status="4*")
diff --git a/tests/testutils.py b/tests/testutils.py
new file mode 100644
index 0000000..3ddbdbe
--- /dev/null
+++ b/tests/testutils.py
@@ -0,0 +1,20 @@
+"""Various utility functions for testing."""
+import gzip
+from pathlib import Path
+
+
+def load_gpx_asset(filename):
+ """Load a GPX test asset.
+
+ This looks in the tests/assets/ folder, reads and unzips the file and
+ returns its contents.
+
+ :param filename: Name of the asset to load.
+ :type filename: str
+ :return: The content of the asset as bytes.
+ :rtype: bytes
+ """
+ asset_dir = Path(__file__).parent / 'assets'
+ test_file = asset_dir / filename
+ with gzip.open(test_file, 'rb') as fobj:
+ return fobj.read()
diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py
index 8f45611..13f4bfe 100644
--- a/tests/unit/test_util.py
+++ b/tests/unit/test_util.py
@@ -1,11 +1,10 @@
-import gzip
from datetime import timedelta
-from pathlib import Path
import pytest
import gpxpy
from markupsafe import Markup
+from testutils import load_gpx_asset
from fietsboek import util
@@ -60,10 +59,7 @@ def test_round_timedelta_to_multiple(delta, multiple, expected):
("MyTourbook_1.gpx.gz", timedelta(hours=2)),
])
def test_guess_gpx_timezone(gpx_file, offset):
- asset_dir = Path(__file__).parent.parent / 'assets'
- test_file = asset_dir / gpx_file
- with gzip.open(test_file, 'rb') as fobj:
- parsed_gpx = gpxpy.parse(fobj)
+ parsed_gpx = gpxpy.parse(load_gpx_asset(gpx_file))
timezone = util.guess_gpx_timezone(parsed_gpx)
# Here we hope (and assume) that utcoffset is ignored. This is true for
# datetime.timezone objects, but may not be for other datetime.tzinfo
diff --git a/tests/unit/views/test_browse.py b/tests/unit/views/test_browse.py
new file mode 100644
index 0000000..93eb0ae
--- /dev/null
+++ b/tests/unit/views/test_browse.py
@@ -0,0 +1,14 @@
+from fietsboek.views.browse import Stream
+
+
+class TestStream:
+ def test_write(self):
+ stream = Stream()
+ n = stream.write(b"foobar")
+ assert n == 6
+
+ def test_write_read(self):
+ stream = Stream()
+ stream.write(b"foo")
+ stream.write(b"bar")
+ assert stream.readall() == b"foobar"