Passed
Push — main ( ae658f...ebd94b )
by Jochen
04:48
created

tests.helpers.create_user()   B

Complexity

Conditions 4

Size

Total Lines 61
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 49
nop 17
dl 0
loc 61
rs 8.669
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
    email_address: Optional[str] = None,
101
    email_address_verified: bool = False,
102
    initialized: bool = True,
103
    suspended: bool = False,
104
    deleted: bool = False,
105
    legacy_id: Optional[int] = None,
106
    first_names: Optional[str] = 'John Joseph',
107
    last_name: Optional[str] = 'Doe',
108
    date_of_birth=DEFAULT_DATE_OF_BIRTH,
109
    country: Optional[str] = 'State of Mind',
110
    zip_code: Optional[str] = '31337',
111
    city: Optional[str] = 'Atrocity',
112
    street: Optional[str] = 'Elite Street 1337',
113
    phone_number: Optional[str] = '555-CALL-ME-MAYBE',
114
    password: str = 'hunter2',
115
) -> DbUser:
116
    if screen_name == '__random__':
117
        screen_name = generate_token(8)
118
119
    user_id = UserID(generate_uuid())
120
121
    created_at = datetime.utcnow()
122
123
    if not email_address:
124
        email_address = f'user{user_id}@users.test'
125
126
    user = user_creation_service.build_db_user(
127
        created_at, screen_name, email_address
128
    )
129
130
    user.id = user_id
131
    user.email_address_verified = email_address_verified
132
    user.initialized = initialized
133
    user.suspended = suspended
134
    user.deleted = deleted
135
    user.legacy_id = legacy_id
136
137
    detail = DbUserDetail(user=user)
138
    detail.first_names = first_names
139
    detail.last_name = last_name
140
    detail.date_of_birth = date_of_birth
141
    detail.country = country
142
    detail.zip_code = zip_code
143
    detail.city = city
144
    detail.street = street
145
    detail.phone_number = phone_number
146
147
    db.session.add(user)
148
149
    try:
150
        db.session.commit()
151
    except Exception as e:
152
        db.session.rollback()
153
        raise UserCreationFailed(e)
154
155
    password_service.create_password_hash(user.id, password)
156
157
    return user
158
159
160
def create_permissions(permission_ids: Iterable[PermissionID]) -> None:
161
    for permission_id in permission_ids:
162
        authz_service.create_permission(
163
            permission_id, permission_id, ignore_if_exists=True
164
        )
165
166
167
def create_role_with_permissions_assigned(
168
    role_id: RoleID, permission_ids: Iterable[PermissionID]
169
) -> None:
170
    role = authz_service.create_role(role_id, role_id, ignore_if_exists=True)
171
172
    for permission_id in permission_ids:
173
        authz_service.assign_permission_to_role(permission_id, role_id)
174
175
176
def create_party(
177
    brand_id: BrandID,
178
    party_id: PartyID = PartyID('acmecon-2014'),
179
    title: str = 'ACMECon 2014',
180
) -> Party:
181
    starts_at = datetime(2014, 10, 24, 16, 0)
182
    ends_at = datetime(2014, 10, 26, 13, 0)
183
184
    return party_service.create_party(
185
        party_id, brand_id, title, starts_at, ends_at
186
    )
187
188
189
def create_site(
190
    site_id: SiteID,
191
    brand_id: BrandID,
192
    *,
193
    title: Optional[str] = None,
194
    server_name: Optional[str] = None,
195
    enabled: bool = True,
196
    user_account_creation_enabled: bool = True,
197
    login_enabled: bool = True,
198
    party_id: Optional[PartyID] = None,
199
    board_id: Optional[BoardID] = None,
200
    storefront_id: Optional[StorefrontID] = None,
201
):
202
    if title is None:
203
        title = site_id
204
205
    if server_name is None:
206
        server_name = f'{site_id}.test'
207
208
    return site_service.create_site(
209
        site_id,
210
        title,
211
        server_name,
212
        brand_id,
213
        enabled=enabled,
214
        user_account_creation_enabled=user_account_creation_enabled,
215
        login_enabled=login_enabled,
216
        party_id=party_id,
217
        board_id=board_id,
218
        storefront_id=storefront_id,
219
    )
220
221
222
@contextmanager
223
def http_client(app: Flask, *, user_id: Optional[UserID] = None):
224
    """Provide an HTTP client.
225
226
    If a user ID is given, the client authenticates with the user's
227
    credentials.
228
    """
229
    client = app.test_client()
230
231
    if user_id is not None:
232
        _add_user_credentials_to_session(client, user_id)
233
234
    yield client
235
236
237
def _add_user_credentials_to_session(client, user_id: UserID) -> None:
238
    session_token = session_service.find_session_token_for_user(user_id)
239
    if session_token is None:
240
        raise Exception(f'Could not find session token for user ID "{user_id}"')
241
242
    with client.session_transaction() as session:
243
        session['user_id'] = str(user_id)
244
        session['user_auth_token'] = str(session_token.token)
245
246
247
def login_user(user_id: UserID) -> None:
248
    """Authenticate the user to create a session."""
249
    session_service.get_session_token(user_id)
250