Passed
Push — main ( 87df82...d49116 )
by Jochen
05:18
created

tests.helpers.create_user()   C

Complexity

Conditions 8

Size

Total Lines 68
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 56
nop 20
dl 0
loc 68
rs 6.5733
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
"""
2
tests.helpers
3
~~~~~~~~~~~~~
4
5
:Copyright: 2006-2021 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9
from __future__ import annotations
10
from contextlib import contextmanager
11
from datetime import date, datetime
12
from pathlib import Path
13
from secrets import token_hex
14
from typing import Any, Iterable, Optional, Union
15
16
from flask import appcontext_pushed, Flask, g
17
18
from byceps.application import create_app
19
from byceps.database import db, generate_uuid
20
from byceps.services.authentication.password import service as password_service
21
from byceps.services.authentication.session.models.current_user import (
22
    CurrentUser,
23
)
24
from byceps.services.authentication.session import service as session_service
25
from byceps.services.authorization import service as authz_service
26
from byceps.services.authorization.transfer.models import PermissionID, RoleID
27
from byceps.services.board.transfer.models import BoardID
28
from byceps.services.party import service as party_service
29
from byceps.services.party.transfer.models import Party
30
from byceps.services.shop.storefront.transfer.models import StorefrontID
31
from byceps.services.site import service as site_service
32
from byceps.services.site.transfer.models import SiteID
33
from byceps.services.user import creation_service as user_creation_service
34
from byceps.services.user.creation_service import UserCreationFailed
35
from byceps.services.user.dbmodels.detail import UserDetail as DbUserDetail
36
from byceps.services.user.dbmodels.user import User as DbUser
37
from byceps.typing import BrandID, PartyID, UserID
38
39
40
_CONFIG_PATH = Path('../config')
41
CONFIG_FILENAME_TEST_SITE = _CONFIG_PATH / 'test_site.py'
42
CONFIG_FILENAME_TEST_ADMIN = _CONFIG_PATH / 'test_admin.py'
43
44
45
def create_admin_app(
46
    config_overrides: Optional[dict[str, Any]] = None
47
) -> Flask:
48
    return create_app(
49
        config_filename=CONFIG_FILENAME_TEST_ADMIN,
50
        config_overrides=config_overrides,
51
    )
52
53
54
def create_site_app(config_overrides: Optional[dict[str, Any]] = None) -> Flask:
55
    return create_app(
56
        config_filename=CONFIG_FILENAME_TEST_SITE,
57
        config_overrides=config_overrides,
58
    )
59
60
61
def generate_token(n: int = 4) -> str:
62
    return token_hex(n)
63
64
65
@contextmanager
66
def app_context(
67
    *, config_filename: Optional[Union[Path, str]] = CONFIG_FILENAME_TEST_SITE
68
):
69
    app = create_app(config_filename=config_filename)
70
71
    with app.app_context():
72
        yield app
73
74
75
@contextmanager
76
def current_party_set(app: Flask, party: Party):
77
    def handler(sender, **kwargs):
78
        g.party_id = party.id
79
        g.brand_id = party.brand_id
80
81
    with appcontext_pushed.connected_to(handler, app):
82
        yield
83
84
85
@contextmanager
86
def current_user_set(app: Flask, current_user: CurrentUser):
87
    def handler(sender, **kwargs):
88
        g.user = current_user
89
90
    with appcontext_pushed.connected_to(handler, app):
91
        yield
92
93
94
DEFAULT_DATE_OF_BIRTH = date(1993, 2, 15)
95
96
97
def create_user(
98
    screen_name: Optional[str] = '__random__',
99
    *,
100
    user_id: Optional[UserID] = None,
101
    created_at: Optional[datetime] = None,
102
    email_address: Optional[str] = None,
103
    email_address_verified: bool = False,
104
    initialized: bool = True,
105
    suspended: bool = False,
106
    deleted: bool = False,
107
    legacy_id: Optional[int] = None,
108
    with_detail: bool = False,
109
    first_names: Optional[str] = 'John Joseph',
110
    last_name: Optional[str] = 'Doe',
111
    date_of_birth=DEFAULT_DATE_OF_BIRTH,
112
    country: Optional[str] = 'State of Mind',
113
    zip_code: Optional[str] = '31337',
114
    city: Optional[str] = 'Atrocity',
115
    street: Optional[str] = 'Elite Street 1337',
116
    phone_number: Optional[str] = '555-CALL-ME-MAYBE',
117
    password: Optional[str] = None,
118
) -> DbUser:
119
    if screen_name == '__random__':
120
        screen_name = generate_token(8)
121
122
    if not user_id:
123
        user_id = UserID(generate_uuid())
124
125
    if not created_at:
126
        created_at = datetime.utcnow()
127
128
    if not email_address:
129
        email_address = f'user{user_id}@users.test'
130
131
    user = user_creation_service.build_user(
132
        created_at, screen_name, email_address
133
    )
134
135
    user.id = user_id
136
    user.email_address_verified = email_address_verified
137
    user.initialized = initialized
138
    user.suspended = suspended
139
    user.deleted = deleted
140
    user.legacy_id = legacy_id
141
142
    if with_detail:
143
        detail = DbUserDetail(user=user)
144
        detail.first_names = first_names
145
        detail.last_name = last_name
146
        detail.date_of_birth = date_of_birth
147
        detail.country = country
148
        detail.zip_code = zip_code
149
        detail.city = city
150
        detail.street = street
151
        detail.phone_number = phone_number
152
153
    db.session.add(user)
154
155
    try:
156
        db.session.commit()
157
    except Exception as e:
158
        db.session.rollback()
159
        raise UserCreationFailed(e)
160
161
    if password is not None:
162
        password_service.create_password_hash(user.id, password)
163
164
    return user
165
166
167
def create_permissions(permission_ids: Iterable[PermissionID]) -> None:
168
    for permission_id in permission_ids:
169
        authz_service.create_permission(
170
            permission_id, permission_id, ignore_if_exists=True
171
        )
172
173
174
def create_role_with_permissions_assigned(
175
    role_id: RoleID, permission_ids: Iterable[PermissionID]
176
) -> None:
177
    role = authz_service.create_role(role_id, role_id, ignore_if_exists=True)
178
179
    for permission_id in permission_ids:
180
        authz_service.assign_permission_to_role(permission_id, role_id)
181
182
183
def create_party(
184
    brand_id: BrandID,
185
    party_id: PartyID = PartyID('acmecon-2014'),
186
    title: str = 'ACMECon 2014',
187
) -> Party:
188
    starts_at = datetime(2014, 10, 24, 16, 0)
189
    ends_at = datetime(2014, 10, 26, 13, 0)
190
191
    return party_service.create_party(
192
        party_id, brand_id, title, starts_at, ends_at
193
    )
194
195
196
def create_site(
197
    site_id: SiteID,
198
    brand_id: BrandID,
199
    *,
200
    title: Optional[str] = None,
201
    server_name: Optional[str] = None,
202
    enabled: bool = True,
203
    user_account_creation_enabled: bool = True,
204
    login_enabled: bool = True,
205
    party_id: Optional[PartyID] = None,
206
    board_id: Optional[BoardID] = None,
207
    storefront_id: Optional[StorefrontID] = None,
208
):
209
    if title is None:
210
        title = site_id
211
212
    if server_name is None:
213
        server_name = f'{site_id}.test'
214
215
    return site_service.create_site(
216
        site_id,
217
        title,
218
        server_name,
219
        brand_id,
220
        enabled=enabled,
221
        user_account_creation_enabled=user_account_creation_enabled,
222
        login_enabled=login_enabled,
223
        party_id=party_id,
224
        board_id=board_id,
225
        storefront_id=storefront_id,
226
    )
227
228
229
@contextmanager
230
def http_client(app: Flask, *, user_id: Optional[UserID] = None):
231
    """Provide an HTTP client.
232
233
    If a user ID is given, the client authenticates with the user's
234
    credentials.
235
    """
236
    client = app.test_client()
237
238
    if user_id is not None:
239
        _add_user_credentials_to_session(client, user_id)
240
241
    yield client
242
243
244
def _add_user_credentials_to_session(client, user_id: UserID) -> None:
245
    session_token = session_service.find_session_token_for_user(user_id)
246
    if session_token is None:
247
        raise Exception(f'Could not find session token for user ID "{user_id}"')
248
249
    with client.session_transaction() as session:
250
        session['user_id'] = str(user_id)
251
        session['user_auth_token'] = str(session_token.token)
252
253
254
def login_user(user_id: UserID) -> None:
255
    """Authenticate the user to create a session."""
256
    session_service.get_session_token(user_id)
257