1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import datetime
import socket
import threading
from typing import Optional
from wsgiref import simple_server
from pyramid.authentication import AuthTktCookieHelper
from pyramid.testing import DummyRequest
from testutils import load_gpx_asset
from fietsboek import models, util, actions
from fietsboek.models.track import Visibility, TrackType
from fietsboek.config import Config
import pytest
@pytest.fixture(scope="module")
def server_port():
"""Return a (likely) free port.
Note that due to OS race conditions between picking the port and opening
something on it, it might be taken again, but that is unlikely.
"""
sock = socket.socket(socket.AF_INET)
sock.bind(("", 0))
port = sock.getsockname()[1]
sock.close()
return port
@pytest.fixture(scope="module", autouse=True)
def running_server(server_port, app):
"""Have the app running as an actual server."""
server = simple_server.make_server("127.0.0.1", server_port, app)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
yield
server.shutdown()
@pytest.fixture
def browser_context_args(server_port) -> dict:
return {"base_url": f"http://localhost:{server_port}"}
@pytest.fixture
def dbaccess(app):
"""Provide direct access to the database.
This is needed for the selenium tests, because the normal "dbsession"
fixture has a doomed transaction attached. This is nice for keeping the
test database clean, but it does mean that the changes are not propagated
through and the running WSGI app cannot read them.
"""
session_factory = app.registry["dbsession_factory"]
return session_factory()
class Helper:
"""Helper to insert objects for testing into the database and similar."""
def __init__(self, dbaccess, app_settings, page, data_manager):
self.dbaccess = dbaccess
self.app_settings = app_settings
self.page = page
self.data_manager = data_manager
self._johnny = None
def john_doe(self) -> models.User:
"""Provides a test user (John Doe).
This fixture either returns the existing John or creates a new one.
"""
if self._johnny:
return self._johnny
with self.dbaccess:
user = models.User(name="John Doe", email="john@doe.com", is_verified=True)
user.set_password("password")
self.dbaccess.add(user)
self.dbaccess.commit()
self.dbaccess.refresh(user, ["id", "email", "password", "session_secret"])
self.dbaccess.expunge(user)
self._johnny = user
return user
def login(self, user: Optional[models.User] = None):
"""Logs the given user in by setting the auth cookie."""
if user is None:
user = self.john_doe()
config = Config.model_construct(session_key=self.app_settings["session_key"])
secret = config.derive_secret("auth-cookie")
helper = AuthTktCookieHelper(secret)
headers = helper.remember(DummyRequest(), user.authenticated_user_id())
for _, header_val in headers:
cookie = header_val.split(";")[0]
name, value = cookie.split("=", 1)
self.page.context.add_cookies(
[
{"name": name, "value": value, "domain": "localhost", "path": "/"},
]
)
def add_track(
self, user: Optional[models.User] = None, track_name: str = "Teasi_1.gpx.gz"
) -> models.Track:
"""Add a track to the given user.
If the user is None, John Doe is used.
"""
if user is None:
user = self.john_doe()
with self.dbaccess:
user = self.dbaccess.merge(user)
track = actions.add_track(
self.dbaccess,
self.data_manager,
owner=user,
title="Another awesome track",
visibility=Visibility.PRIVATE,
description="Another description",
track_type=TrackType.ORGANIC,
date=datetime.datetime(2022, 12, 21, 17, 5, tzinfo=datetime.timezone.utc),
tags=[],
badges=[],
tagged_people=[],
transformers=[],
gpx_data=load_gpx_asset(track_name),
)
self.dbaccess.commit()
self.dbaccess.refresh(track, ["id", "link_secret"])
self.dbaccess.expunge(track)
return track
@pytest.fixture
def playwright_helper(dbaccess, app_settings, page, data_manager):
return Helper(dbaccess, app_settings, page, data_manager)
|