• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / fx-private-relay / e1b11300-4461-4a57-8fbe-b6fcd5c2f71d

22 Aug 2024 12:25PM CUT coverage: 85.407% (-0.009%) from 85.416%
e1b11300-4461-4a57-8fbe-b6fcd5c2f71d

push

circleci

web-flow
Merge pull request #4961 from mozilla/MPP-3866-Update-Base-Load-Engineer-Playbook-to-use-e2e-testing

MPP-3866 Add guidelines to use e2e testing for stage release

4099 of 5251 branches covered (78.06%)

Branch coverage included in aggregate %.

15970 of 18247 relevant lines covered (87.52%)

10.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.71
/privaterelay/tests/model_tests.py
1
import random
1โœ”
2
from datetime import UTC, datetime, timedelta
1โœ”
3
from unittest.mock import patch
1โœ”
4
from uuid import uuid4
1โœ”
5

6
from django.conf import settings
1โœ”
7
from django.contrib.auth.models import User
1โœ”
8
from django.test import TestCase, override_settings
1โœ”
9

10
import pytest
1โœ”
11
from allauth.socialaccount.models import SocialAccount
1โœ”
12
from model_bakery import baker
1โœ”
13

14
from emails.models import AbuseMetrics, DomainAddress, RelayAddress
1โœ”
15

16
from ..exceptions import CannotMakeSubdomainException
1โœ”
17
from ..models import Profile
1โœ”
18
from .utils import (
1โœ”
19
    make_free_test_user,
20
    phone_subscription,
21
    premium_subscription,
22
    vpn_subscription,
23
)
24

25
if settings.PHONES_ENABLED:
1!
26
    from phones.models import RealPhone, RelayNumber
1โœ”
27

28

29
class ProfileTestCase(TestCase):
1โœ”
30
    """Base class for Profile tests."""
31

32
    def setUp(self) -> None:
1โœ”
33
        user = baker.make(User)
1โœ”
34
        self.profile = user.profile
1โœ”
35
        assert self.profile.server_storage is True
1โœ”
36

37
    def get_or_create_social_account(self) -> SocialAccount:
1โœ”
38
        """Get the test user's social account, creating if needed."""
39
        social_account, _ = SocialAccount.objects.get_or_create(
1โœ”
40
            user=self.profile.user,
41
            provider="fxa",
42
            defaults={
43
                "uid": str(uuid4()),
44
                "extra_data": {"avatar": "image.png", "subscriptions": []},
45
            },
46
        )
47
        return social_account
1โœ”
48

49
    def upgrade_to_premium(self) -> None:
1โœ”
50
        """Add an unlimited emails subscription to the user."""
51
        social_account = self.get_or_create_social_account()
1โœ”
52
        social_account.extra_data["subscriptions"].append(premium_subscription())
1โœ”
53
        social_account.save()
1โœ”
54

55
    def upgrade_to_phone(self) -> None:
1โœ”
56
        """Add a phone plan to the user."""
57
        social_account = self.get_or_create_social_account()
1โœ”
58
        social_account.extra_data["subscriptions"].append(phone_subscription())
1โœ”
59
        if not self.profile.has_premium:
1!
60
            social_account.extra_data["subscriptions"].append(premium_subscription())
1โœ”
61
        social_account.save()
1โœ”
62

63
    def upgrade_to_vpn_bundle(self) -> None:
1โœ”
64
        """Add a phone plan to the user."""
65
        social_account = self.get_or_create_social_account()
1โœ”
66
        social_account.extra_data["subscriptions"].append(vpn_subscription())
1โœ”
67
        if not self.profile.has_premium:
1!
68
            social_account.extra_data["subscriptions"].append(premium_subscription())
1โœ”
69
        if not self.profile.has_phone:
1!
70
            social_account.extra_data["subscriptions"].append(phone_subscription())
1โœ”
71
        social_account.save()
1โœ”
72

73

74
class ProfileBounceTestCase(ProfileTestCase):
1โœ”
75
    """Base class for Profile tests that check for bounces."""
76

77
    def set_hard_bounce(self) -> datetime:
1โœ”
78
        """
79
        Set a hard bounce pause for the profile, return the bounce time.
80

81
        This happens when the user's email server reports a hard bounce, such as
82
        saying the email does not exist.
83
        """
84
        self.profile.last_hard_bounce = datetime.now(UTC) - timedelta(
1โœ”
85
            days=settings.HARD_BOUNCE_ALLOWED_DAYS - 1
86
        )
87
        self.profile.save()
1โœ”
88
        return self.profile.last_hard_bounce
1โœ”
89

90
    def set_soft_bounce(self) -> datetime:
1โœ”
91
        """
92
        Set a soft bounce for the profile, return the bounce time.
93

94
        This happens when the user's email server reports a soft bounce, such as
95
        saying the user's mailbox is full.
96
        """
97
        self.profile.last_soft_bounce = datetime.now(UTC) - timedelta(
1โœ”
98
            days=settings.SOFT_BOUNCE_ALLOWED_DAYS - 1
99
        )
100
        self.profile.save()
1โœ”
101
        return self.profile.last_soft_bounce
1โœ”
102

103

104
class ProfileCheckBouncePause(ProfileBounceTestCase):
1โœ”
105
    """Tests for Profile.check_bounce_pause()"""
106

107
    def test_no_bounces(self) -> None:
1โœ”
108
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
109

110
        assert bounce_paused is False
1โœ”
111
        assert bounce_type == ""
1โœ”
112

113
    def test_hard_bounce_pending(self) -> None:
1โœ”
114
        self.set_hard_bounce()
1โœ”
115
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
116
        assert bounce_paused is True
1โœ”
117
        assert bounce_type == "hard"
1โœ”
118

119
    def test_soft_bounce_pending(self) -> None:
1โœ”
120
        self.set_soft_bounce()
1โœ”
121
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
122
        assert bounce_paused is True
1โœ”
123
        assert bounce_type == "soft"
1โœ”
124

125
    def test_hard_and_soft_bounce_pending_shows_hard(self) -> None:
1โœ”
126
        self.set_hard_bounce()
1โœ”
127
        self.set_soft_bounce()
1โœ”
128
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
129
        assert bounce_paused is True
1โœ”
130
        assert bounce_type == "hard"
1โœ”
131

132
    def test_hard_bounce_over_resets_timer(self) -> None:
1โœ”
133
        self.profile.last_hard_bounce = datetime.now(UTC) - timedelta(
1โœ”
134
            days=settings.HARD_BOUNCE_ALLOWED_DAYS + 1
135
        )
136
        self.profile.save()
1โœ”
137
        assert self.profile.last_hard_bounce is not None
1โœ”
138

139
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
140

141
        assert bounce_paused is False
1โœ”
142
        assert bounce_type == ""
1โœ”
143
        assert self.profile.last_hard_bounce is None
1โœ”
144

145
    def test_soft_bounce_over_resets_timer(self) -> None:
1โœ”
146
        self.profile.last_soft_bounce = datetime.now(UTC) - timedelta(
1โœ”
147
            days=settings.SOFT_BOUNCE_ALLOWED_DAYS + 1
148
        )
149
        self.profile.save()
1โœ”
150
        assert self.profile.last_soft_bounce is not None
1โœ”
151

152
        bounce_paused, bounce_type = self.profile.check_bounce_pause()
1โœ”
153

154
        assert bounce_paused is False
1โœ”
155
        assert bounce_type == ""
1โœ”
156
        assert self.profile.last_soft_bounce is None
1โœ”
157

158

159
class ProfileNextEmailTryDateTest(ProfileBounceTestCase):
1โœ”
160
    """Tests for Profile.next_email_try"""
161

162
    def test_no_bounces_returns_today(self) -> None:
1โœ”
163
        assert self.profile.next_email_try.date() == datetime.now(UTC).date()
1โœ”
164

165
    def test_hard_bounce_returns_proper_datemath(self) -> None:
1โœ”
166
        last_hard_bounce = self.set_hard_bounce()
1โœ”
167
        expected_next_try_date = last_hard_bounce + timedelta(
1โœ”
168
            days=settings.HARD_BOUNCE_ALLOWED_DAYS
169
        )
170
        assert self.profile.next_email_try.date() == expected_next_try_date.date()
1โœ”
171

172
    def test_soft_bounce_returns_proper_datemath(self) -> None:
1โœ”
173
        last_soft_bounce = self.set_soft_bounce()
1โœ”
174
        expected_next_try_date = last_soft_bounce + timedelta(
1โœ”
175
            days=settings.SOFT_BOUNCE_ALLOWED_DAYS
176
        )
177
        assert self.profile.next_email_try.date() == expected_next_try_date.date()
1โœ”
178

179
    def test_hard_and_soft_bounce_returns_hard_datemath(self) -> None:
1โœ”
180
        last_soft_bounce = self.set_soft_bounce()
1โœ”
181
        last_hard_bounce = self.set_hard_bounce()
1โœ”
182
        assert last_soft_bounce != last_hard_bounce
1โœ”
183
        expected_next_try_date = last_hard_bounce + timedelta(
1โœ”
184
            days=settings.HARD_BOUNCE_ALLOWED_DAYS
185
        )
186
        assert self.profile.next_email_try.date() == expected_next_try_date.date()
1โœ”
187

188

189
class ProfileLastBounceDateTest(ProfileBounceTestCase):
1โœ”
190
    """Tests for Profile.last_bounce_date"""
191

192
    def test_no_bounces_returns_None(self) -> None:
1โœ”
193
        assert self.profile.last_bounce_date is None
1โœ”
194

195
    def test_soft_bounce_returns_its_date(self) -> None:
1โœ”
196
        self.set_soft_bounce()
1โœ”
197
        assert self.profile.last_bounce_date == self.profile.last_soft_bounce
1โœ”
198

199
    def test_hard_bounce_returns_its_date(self) -> None:
1โœ”
200
        self.set_hard_bounce()
1โœ”
201
        assert self.profile.last_bounce_date == self.profile.last_hard_bounce
1โœ”
202

203
    def test_hard_and_soft_bounces_returns_hard_date(self) -> None:
1โœ”
204
        self.set_soft_bounce()
1โœ”
205
        self.set_hard_bounce()
1โœ”
206
        assert self.profile.last_bounce_date == self.profile.last_hard_bounce
1โœ”
207

208

209
class ProfileHasPremiumTest(ProfileTestCase):
1โœ”
210
    """Tests for Profile.has_premium"""
211

212
    def test_default_False(self) -> None:
1โœ”
213
        assert self.profile.has_premium is False
1โœ”
214

215
    def test_premium_subscription_returns_True(self) -> None:
1โœ”
216
        self.upgrade_to_premium()
1โœ”
217
        assert self.profile.has_premium is True
1โœ”
218

219
    def test_phone_returns_True(self) -> None:
1โœ”
220
        self.upgrade_to_phone()
1โœ”
221
        assert self.profile.has_premium is True
1โœ”
222

223
    def test_vpn_bundle_returns_True(self) -> None:
1โœ”
224
        self.upgrade_to_vpn_bundle()
1โœ”
225
        assert self.profile.has_premium is True
1โœ”
226

227

228
class ProfileHasPhoneTest(ProfileTestCase):
1โœ”
229
    """Tests for Profile.has_phone"""
230

231
    def test_default_False(self) -> None:
1โœ”
232
        assert self.profile.has_phone is False
1โœ”
233

234
    def test_premium_subscription_returns_False(self) -> None:
1โœ”
235
        self.upgrade_to_premium()
1โœ”
236
        assert self.profile.has_phone is False
1โœ”
237

238
    def test_phone_returns_True(self) -> None:
1โœ”
239
        self.upgrade_to_phone()
1โœ”
240
        assert self.profile.has_phone is True
1โœ”
241

242
    def test_vpn_bundle_returns_True(self) -> None:
1โœ”
243
        self.upgrade_to_vpn_bundle()
1โœ”
244
        assert self.profile.has_phone is True
1โœ”
245

246

247
@pytest.mark.skipif(not settings.PHONES_ENABLED, reason="PHONES_ENABLED is False")
1โœ”
248
@override_settings(PHONES_NO_CLIENT_CALLS_IN_TEST=True)
1โœ”
249
class ProfileDatePhoneRegisteredTest(ProfileTestCase):
1โœ”
250
    """Tests for Profile.date_phone_registered"""
251

252
    def test_default_None(self) -> None:
1โœ”
253
        assert self.profile.date_phone_registered is None
1โœ”
254

255
    def test_real_phone_no_relay_number_returns_verified_date(self) -> None:
1โœ”
256
        self.upgrade_to_phone()
1โœ”
257
        datetime_now = datetime.now(UTC)
1โœ”
258
        RealPhone.objects.create(
1โœ”
259
            user=self.profile.user,
260
            number="+12223334444",
261
            verified=True,
262
            verified_date=datetime_now,
263
        )
264
        assert self.profile.date_phone_registered == datetime_now
1โœ”
265

266
    def test_real_phone_and_relay_number_w_created_at_returns_created_at_date(
1โœ”
267
        self,
268
    ) -> None:
269
        self.upgrade_to_phone()
1โœ”
270
        datetime_now = datetime.now(UTC)
1โœ”
271
        phone_user = self.profile.user
1โœ”
272
        RealPhone.objects.create(
1โœ”
273
            user=phone_user,
274
            number="+12223334444",
275
            verified=True,
276
            verified_date=datetime_now,
277
        )
278
        relay_number = RelayNumber.objects.create(user=phone_user)
1โœ”
279
        assert self.profile.date_phone_registered == relay_number.created_at
1โœ”
280

281
    def test_real_phone_and_relay_number_wo_created_at_returns_verified_date(
1โœ”
282
        self,
283
    ) -> None:
284
        self.upgrade_to_phone()
1โœ”
285
        datetime_now = datetime.now(UTC)
1โœ”
286
        phone_user = self.profile.user
1โœ”
287
        real_phone = RealPhone.objects.create(
1โœ”
288
            user=phone_user,
289
            number="+12223334444",
290
            verified=True,
291
            verified_date=datetime_now,
292
        )
293
        relay_number = RelayNumber.objects.create(user=phone_user)
1โœ”
294
        # since created_at is auto set, update to None
295
        relay_number.created_at = None
1โœ”
296
        relay_number.save()
1โœ”
297
        assert self.profile.date_phone_registered == real_phone.verified_date
1โœ”
298

299

300
class ProfileTotalMasksTest(ProfileTestCase):
1โœ”
301
    """Tests for Profile.total_masks"""
302

303
    def test_total_masks(self) -> None:
1โœ”
304
        self.upgrade_to_premium()
1โœ”
305
        self.profile.add_subdomain("totalmasks")
1โœ”
306
        assert self.profile.total_masks == 0
1โœ”
307
        num_relay_addresses = random.randint(0, 2)
1โœ”
308
        for _ in list(range(num_relay_addresses)):
1!
309
            baker.make(RelayAddress, user=self.profile.user)
ร—
310
        num_domain_addresses = random.randint(0, 2)
1โœ”
311
        for i in list(range(num_domain_addresses)):
1โœ”
312
            baker.make(DomainAddress, user=self.profile.user, address=f"mask{i}")
1โœ”
313
        assert self.profile.total_masks == num_relay_addresses + num_domain_addresses
1โœ”
314

315

316
class ProfileAtMaskLimitTest(ProfileTestCase):
1โœ”
317
    """Tests for Profile.at_mask_limit"""
318

319
    def test_premium_user_returns_False(self) -> None:
1โœ”
320
        self.upgrade_to_premium()
1โœ”
321
        assert self.profile.at_mask_limit is False
1โœ”
322
        baker.make(
1โœ”
323
            RelayAddress,
324
            user=self.profile.user,
325
            _quantity=settings.MAX_NUM_FREE_ALIASES,
326
        )
327
        assert self.profile.at_mask_limit is False
1โœ”
328

329
    def test_free_user(self) -> None:
1โœ”
330
        assert self.profile.at_mask_limit is False
1โœ”
331
        baker.make(
1โœ”
332
            RelayAddress,
333
            user=self.profile.user,
334
            _quantity=settings.MAX_NUM_FREE_ALIASES,
335
        )
336
        assert self.profile.at_mask_limit is True
1โœ”
337

338

339
class ProfileAddSubdomainTest(ProfileTestCase):
1โœ”
340
    """Tests for Profile.add_subdomain()"""
341

342
    def test_new_unlimited_profile(self) -> None:
1โœ”
343
        self.upgrade_to_premium()
1โœ”
344
        assert self.profile.add_subdomain("newpremium") == "newpremium"
1โœ”
345

346
    def test_lowercases_subdomain_value(self) -> None:
1โœ”
347
        self.upgrade_to_premium()
1โœ”
348
        assert self.profile.add_subdomain("mIxEdcAsE") == "mixedcase"
1โœ”
349

350
    def test_non_premium_user_raises_exception(self) -> None:
1โœ”
351
        expected_msg = "error-premium-set-subdomain"
1โœ”
352
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
353
            self.profile.add_subdomain("test")
1โœ”
354

355
    def test_calling_again_raises_exception(self) -> None:
1โœ”
356
        self.upgrade_to_premium()
1โœ”
357
        subdomain = "test"
1โœ”
358
        self.profile.subdomain = subdomain
1โœ”
359
        self.profile.save()
1โœ”
360

361
        expected_msg = "error-premium-cannot-change-subdomain"
1โœ”
362
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
363
            self.profile.add_subdomain(subdomain)
1โœ”
364

365
    def test_badword_subdomain_raises_exception(self) -> None:
1โœ”
366
        self.upgrade_to_premium()
1โœ”
367
        expected_msg = "error-subdomain-not-available"
1โœ”
368
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
369
            self.profile.add_subdomain("angry")
1โœ”
370

371
    def test_blocked_word_subdomain_raises_exception(self) -> None:
1โœ”
372
        self.upgrade_to_premium()
1โœ”
373
        expected_msg = "error-subdomain-not-available"
1โœ”
374
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
375
            self.profile.add_subdomain("mozilla")
1โœ”
376

377
    def test_empty_subdomain_raises(self) -> None:
1โœ”
378
        self.upgrade_to_premium()
1โœ”
379
        expected_msg = "error-subdomain-cannot-be-empty-or-null"
1โœ”
380

381
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
382
            self.profile.add_subdomain("")
1โœ”
383

384
    def test_null_subdomain_raises(self) -> None:
1โœ”
385
        self.upgrade_to_premium()
1โœ”
386
        expected_msg = "error-subdomain-cannot-be-empty-or-null"
1โœ”
387

388
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
389
            self.profile.add_subdomain(None)
1โœ”
390

391
    def test_subdomain_with_space_at_end_raises(self) -> None:
1โœ”
392
        self.upgrade_to_premium()
1โœ”
393
        expected_msg = "error-subdomain-not-available"
1โœ”
394

395
        with self.assertRaisesMessage(CannotMakeSubdomainException, expected_msg):
1โœ”
396
            self.profile.add_subdomain("mydomain ")
1โœ”
397

398

399
class ProfileSaveTest(ProfileTestCase):
1โœ”
400
    """Tests for Profile.save()"""
401

402
    def test_lowercases_subdomain_value(self) -> None:
1โœ”
403
        self.upgrade_to_premium()
1โœ”
404
        self.profile.subdomain = "mIxEdcAsE"
1โœ”
405
        self.profile.save()
1โœ”
406
        assert self.profile.subdomain == "mixedcase"
1โœ”
407

408
    def test_lowercases_subdomain_value_with_update_fields(self) -> None:
1โœ”
409
        """With update_fields, the subdomain is still lowercased."""
410
        self.upgrade_to_premium()
1โœ”
411
        assert self.profile.subdomain is None
1โœ”
412

413
        # Use QuerySet.update to avoid model .save()
414
        Profile.objects.filter(id=self.profile.id).update(subdomain="mIxEdcAsE")
1โœ”
415
        self.profile.refresh_from_db()
1โœ”
416
        assert self.profile.subdomain == "mIxEdcAsE"
1โœ”
417

418
        # Update a different field with update_fields to avoid a full model save
419
        new_date_subscribed = datetime(2023, 3, 3, tzinfo=UTC)
1โœ”
420
        self.profile.date_subscribed = new_date_subscribed
1โœ”
421
        self.profile.save(update_fields={"date_subscribed"})
1โœ”
422

423
        # Since .save() added to update_fields, subdomain is now lowercase
424
        self.profile.refresh_from_db()
1โœ”
425
        assert self.profile.date_subscribed == new_date_subscribed
1โœ”
426
        assert self.profile.subdomain == "mixedcase"
1โœ”
427

428
    TEST_DESCRIPTION = "test description"
1โœ”
429
    TEST_USED_ON = TEST_GENERATED_FOR = "secret.com"
1โœ”
430

431
    def add_relay_address(self) -> RelayAddress:
1โœ”
432
        return baker.make(
1โœ”
433
            RelayAddress,
434
            user=self.profile.user,
435
            description=self.TEST_DESCRIPTION,
436
            generated_for=self.TEST_GENERATED_FOR,
437
            used_on=self.TEST_USED_ON,
438
        )
439

440
    def add_domain_address(self) -> DomainAddress:
1โœ”
441
        self.upgrade_to_premium()
1โœ”
442
        self.profile.subdomain = "somesubdomain"
1โœ”
443
        self.profile.save()
1โœ”
444
        return baker.make(
1โœ”
445
            DomainAddress,
446
            user=self.profile.user,
447
            address="localpart",
448
            description=self.TEST_DESCRIPTION,
449
            used_on=self.TEST_USED_ON,
450
        )
451

452
    def test_save_server_storage_true_doesnt_delete_data(self) -> None:
1โœ”
453
        relay_address = self.add_relay_address()
1โœ”
454
        self.profile.server_storage = True
1โœ”
455
        self.profile.save()
1โœ”
456

457
        relay_address.refresh_from_db()
1โœ”
458
        assert relay_address.description == self.TEST_DESCRIPTION
1โœ”
459
        assert relay_address.generated_for == self.TEST_GENERATED_FOR
1โœ”
460
        assert relay_address.used_on == self.TEST_USED_ON
1โœ”
461

462
    def test_save_server_storage_false_deletes_data(self) -> None:
1โœ”
463
        relay_address = self.add_relay_address()
1โœ”
464
        domain_address = self.add_domain_address()
1โœ”
465
        self.profile.server_storage = False
1โœ”
466
        self.profile.save()
1โœ”
467

468
        relay_address.refresh_from_db()
1โœ”
469
        domain_address.refresh_from_db()
1โœ”
470
        assert relay_address.description == ""
1โœ”
471
        assert relay_address.generated_for == ""
1โœ”
472
        assert relay_address.used_on == ""
1โœ”
473
        assert domain_address.description == ""
1โœ”
474
        assert domain_address.used_on == ""
1โœ”
475

476
    def add_four_relay_addresses(self, user: User | None = None) -> list[RelayAddress]:
1โœ”
477
        if user is None:
1โœ”
478
            user = self.profile.user
1โœ”
479
        return baker.make(
1โœ”
480
            RelayAddress,
481
            user=user,
482
            description=self.TEST_DESCRIPTION,
483
            generated_for=self.TEST_GENERATED_FOR,
484
            used_on=self.TEST_USED_ON,
485
            _quantity=4,
486
        )
487

488
    def test_save_server_storage_false_deletes_ALL_data(self) -> None:
1โœ”
489
        self.add_four_relay_addresses()
1โœ”
490
        self.profile.server_storage = False
1โœ”
491
        self.profile.save()
1โœ”
492

493
        for relay_address in RelayAddress.objects.filter(user=self.profile.user):
1โœ”
494
            assert relay_address.description == ""
1โœ”
495
            assert relay_address.generated_for == ""
1โœ”
496

497
    def test_save_server_storage_false_only_deletes_that_profiles_data(self) -> None:
1โœ”
498
        other_user = make_free_test_user()
1โœ”
499
        assert other_user.profile.server_storage is True
1โœ”
500
        self.add_four_relay_addresses()
1โœ”
501
        self.add_four_relay_addresses(user=other_user)
1โœ”
502
        self.profile.server_storage = False
1โœ”
503
        self.profile.save()
1โœ”
504

505
        for relay_address in RelayAddress.objects.filter(user=self.profile.user):
1โœ”
506
            assert relay_address.description == ""
1โœ”
507
            assert relay_address.generated_for == ""
1โœ”
508
            assert relay_address.used_on == ""
1โœ”
509

510
        for relay_address in RelayAddress.objects.filter(user=other_user):
1โœ”
511
            assert relay_address.description == self.TEST_DESCRIPTION
1โœ”
512
            assert relay_address.generated_for == self.TEST_GENERATED_FOR
1โœ”
513
            assert relay_address.used_on == self.TEST_USED_ON
1โœ”
514

515

516
class ProfileDisplayNameTest(ProfileTestCase):
1โœ”
517
    """Tests for Profile.display_name"""
518

519
    def test_exists(self) -> None:
1โœ”
520
        display_name = "Display Name"
1โœ”
521
        social_account = self.get_or_create_social_account()
1โœ”
522
        social_account.extra_data["displayName"] = display_name
1โœ”
523
        social_account.save()
1โœ”
524
        assert self.profile.display_name == display_name
1โœ”
525

526
    def test_display_name_does_not_exist(self) -> None:
1โœ”
527
        self.get_or_create_social_account()
1โœ”
528
        assert self.profile.display_name is None
1โœ”
529

530

531
class ProfileLanguageTest(ProfileTestCase):
1โœ”
532
    """Test Profile.language"""
533

534
    def test_no_fxa_extra_data_locale_returns_default_en(self) -> None:
1โœ”
535
        social_account = self.get_or_create_social_account()
1โœ”
536
        assert "locale" not in social_account.extra_data
1โœ”
537
        assert self.profile.language == "en"
1โœ”
538

539
    def test_no_fxa_locale_returns_default_en(self) -> None:
1โœ”
540
        assert self.profile.language == "en"
1โœ”
541

542
    def test_fxa_locale_de_returns_de(self) -> None:
1โœ”
543
        social_account = self.get_or_create_social_account()
1โœ”
544
        social_account.extra_data["locale"] = "de,en-US;q=0.9,en;q=0.8"
1โœ”
545
        social_account.save()
1โœ”
546
        assert self.profile.language == "de"
1โœ”
547

548

549
class ProfileFxaLocaleInPremiumCountryTest(ProfileTestCase):
1โœ”
550
    """Tests for Profile.fxa_locale_in_premium_country"""
551

552
    def set_fxa_locale(self, locale: str) -> None:
1โœ”
553
        social_account = self.get_or_create_social_account()
1โœ”
554
        social_account.extra_data["locale"] = locale
1โœ”
555
        social_account.save()
1โœ”
556

557
    def test_when_premium_available_returns_True(self) -> None:
1โœ”
558
        self.set_fxa_locale("de-DE,en-xx;q=0.9,en;q=0.8")
1โœ”
559
        assert self.profile.fxa_locale_in_premium_country is True
1โœ”
560

561
    def test_en_implies_premium_available(self) -> None:
1โœ”
562
        self.set_fxa_locale("en;q=0.8")
1โœ”
563
        assert self.profile.fxa_locale_in_premium_country is True
1โœ”
564

565
    def test_when_premium_unavailable_returns_False(self) -> None:
1โœ”
566
        self.set_fxa_locale("en-IN, en;q=0.8")
1โœ”
567
        assert self.profile.fxa_locale_in_premium_country is False
1โœ”
568

569
    def test_when_premium_available_by_language_code_returns_True(self) -> None:
1โœ”
570
        self.set_fxa_locale("de;q=0.8")
1โœ”
571
        assert self.profile.fxa_locale_in_premium_country is True
1โœ”
572

573
    def test_invalid_language_code_returns_False(self) -> None:
1โœ”
574
        self.set_fxa_locale("xx;q=0.8")
1โœ”
575
        assert self.profile.fxa_locale_in_premium_country is False
1โœ”
576

577
    def test_when_premium_unavailable_by_language_code_returns_False(self) -> None:
1โœ”
578
        self.set_fxa_locale("zh;q=0.8")
1โœ”
579
        assert self.profile.fxa_locale_in_premium_country is False
1โœ”
580

581
    def test_no_fxa_account_returns_False(self) -> None:
1โœ”
582
        assert self.profile.fxa_locale_in_premium_country is False
1โœ”
583

584
    def test_in_estonia(self):
1โœ”
585
        """Estonia (EE) was added in August 2023."""
586
        self.set_fxa_locale("et-ee,et;q=0.8")
1โœ”
587
        assert self.profile.fxa_locale_in_premium_country is True
1โœ”
588

589

590
class ProfileJoinedBeforePremiumReleaseTest(ProfileTestCase):
1โœ”
591
    """Tests for Profile.joined_before_premium_release"""
592

593
    def test_returns_True(self) -> None:
1โœ”
594
        before = "2021-10-18 17:00:00+00:00"
1โœ”
595
        self.profile.user.date_joined = datetime.fromisoformat(before)
1โœ”
596
        assert self.profile.joined_before_premium_release
1โœ”
597

598
    def test_returns_False(self) -> None:
1โœ”
599
        after = "2021-10-28 17:00:00+00:00"
1โœ”
600
        self.profile.user.date_joined = datetime.fromisoformat(after)
1โœ”
601
        assert self.profile.joined_before_premium_release is False
1โœ”
602

603

604
class ProfileDefaultsTest(ProfileTestCase):
1โœ”
605
    """Tests for default Profile values"""
606

607
    def test_user_created_after_premium_release_server_storage_True(self) -> None:
1โœ”
608
        assert self.profile.server_storage
1โœ”
609

610
    def test_emails_replied_new_user_aggregates_sum_of_replies_to_zero(self) -> None:
1โœ”
611
        assert self.profile.emails_replied == 0
1โœ”
612

613

614
class ProfileEmailsRepliedTest(ProfileTestCase):
1โœ”
615
    """Tests for Profile.emails_replied"""
616

617
    def test_premium_user_aggregates_replies_from_all_addresses(self) -> None:
1โœ”
618
        self.upgrade_to_premium()
1โœ”
619
        self.profile.subdomain = "test"
1โœ”
620
        self.profile.num_email_replied_in_deleted_address = 1
1โœ”
621
        self.profile.save()
1โœ”
622
        baker.make(RelayAddress, user=self.profile.user, num_replied=3)
1โœ”
623
        baker.make(
1โœ”
624
            DomainAddress, user=self.profile.user, address="lower-case", num_replied=5
625
        )
626

627
        assert self.profile.emails_replied == 9
1โœ”
628

629
    def test_free_user_aggregates_replies_from_relay_addresses(self) -> None:
1โœ”
630
        baker.make(RelayAddress, user=self.profile.user, num_replied=3)
1โœ”
631
        baker.make(RelayAddress, user=self.profile.user, num_replied=5)
1โœ”
632

633
        assert self.profile.emails_replied == 8
1โœ”
634

635

636
class ProfileUpdateAbuseMetricTest(ProfileTestCase):
1โœ”
637
    """Tests for Profile.update_abuse_metric()"""
638

639
    def setUp(self) -> None:
1โœ”
640
        super().setUp()
1โœ”
641
        self.get_or_create_social_account()
1โœ”
642
        self.abuse_metric = baker.make(AbuseMetrics, user=self.profile.user)
1โœ”
643

644
        patcher_logger = patch("privaterelay.models.abuse_logger.info")
1โœ”
645
        self.mocked_abuse_info = patcher_logger.start()
1โœ”
646
        self.addCleanup(patcher_logger.stop)
1โœ”
647

648
        # Selectively patch datatime.now() for emails models
649
        # https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
650
        patcher = patch("privaterelay.models.datetime")
1โœ”
651
        mocked_datetime = patcher.start()
1โœ”
652
        self.addCleanup(patcher.stop)
1โœ”
653

654
        self.expected_now = datetime.now(UTC)
1โœ”
655
        mocked_datetime.combine.return_value = datetime.combine(
1โœ”
656
            datetime.now(UTC).date(), datetime.min.time()
657
        )
658
        mocked_datetime.now.return_value = self.expected_now
1โœ”
659
        mocked_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw)
1!
660

661
    @override_settings(MAX_FORWARDED_PER_DAY=5)
1โœ”
662
    def test_flags_profile_when_emails_forwarded_abuse_threshold_met(self) -> None:
1โœ”
663
        self.abuse_metric.num_email_forwarded_per_day = 4
1โœ”
664
        self.abuse_metric.save()
1โœ”
665
        assert self.profile.last_account_flagged is None
1โœ”
666

667
        self.profile.update_abuse_metric(email_forwarded=True)
1โœ”
668
        self.abuse_metric.refresh_from_db()
1โœ”
669

670
        assert self.profile.fxa
1โœ”
671
        self.mocked_abuse_info.assert_called_once_with(
1โœ”
672
            "Abuse flagged",
673
            extra={
674
                "uid": self.profile.fxa.uid,
675
                "flagged": self.expected_now.timestamp(),
676
                "replies": 0,
677
                "addresses": 0,
678
                "forwarded": 5,
679
                "forwarded_size_in_bytes": 0,
680
            },
681
        )
682
        assert self.abuse_metric.num_email_forwarded_per_day == 5
1โœ”
683
        assert self.profile.last_account_flagged == self.expected_now
1โœ”
684

685
    @override_settings(MAX_FORWARDED_EMAIL_SIZE_PER_DAY=100)
1โœ”
686
    def test_flags_profile_when_forwarded_email_size_abuse_threshold_met(self) -> None:
1โœ”
687
        self.abuse_metric.forwarded_email_size_per_day = 50
1โœ”
688
        self.abuse_metric.save()
1โœ”
689
        assert self.profile.last_account_flagged is None
1โœ”
690

691
        self.profile.update_abuse_metric(forwarded_email_size=50)
1โœ”
692
        self.abuse_metric.refresh_from_db()
1โœ”
693

694
        assert self.profile.fxa
1โœ”
695
        self.mocked_abuse_info.assert_called_once_with(
1โœ”
696
            "Abuse flagged",
697
            extra={
698
                "uid": self.profile.fxa.uid,
699
                "flagged": self.expected_now.timestamp(),
700
                "replies": 0,
701
                "addresses": 0,
702
                "forwarded": 0,
703
                "forwarded_size_in_bytes": 100,
704
            },
705
        )
706
        assert self.abuse_metric.forwarded_email_size_per_day == 100
1โœ”
707
        assert self.profile.last_account_flagged == self.expected_now
1โœ”
708

709

710
class ProfileMetricsEnabledTest(ProfileTestCase):
1โœ”
711
    def test_no_fxa_means_metrics_enabled(self) -> None:
1โœ”
712
        assert not self.profile.fxa
1โœ”
713
        assert self.profile.metrics_enabled
1โœ”
714

715
    def test_fxa_legacy_means_metrics_enabled(self) -> None:
1โœ”
716
        self.get_or_create_social_account()
1โœ”
717
        assert self.profile.fxa
1โœ”
718
        assert "metricsEnabled" not in self.profile.fxa.extra_data
1โœ”
719
        assert self.profile.metrics_enabled
1โœ”
720

721
    def test_fxa_opt_in_means_metrics_enabled(self) -> None:
1โœ”
722
        social_account = self.get_or_create_social_account()
1โœ”
723
        social_account.extra_data["metricsEnabled"] = True
1โœ”
724
        social_account.save()
1โœ”
725
        assert self.profile.fxa
1โœ”
726
        assert self.profile.metrics_enabled
1โœ”
727

728
    def test_fxa_opt_out_means_metrics_disabled(self) -> None:
1โœ”
729
        social_account = self.get_or_create_social_account()
1โœ”
730
        social_account.extra_data["metricsEnabled"] = False
1โœ”
731
        social_account.save()
1โœ”
732
        assert self.profile.fxa
1โœ”
733
        assert not self.profile.metrics_enabled
1โœ”
734

735

736
class ProfilePlanTest(ProfileTestCase):
1โœ”
737
    def test_free_user(self) -> None:
1โœ”
738
        assert self.profile.plan == "free"
1โœ”
739

740
    def test_premium_user(self) -> None:
1โœ”
741
        self.upgrade_to_premium()
1โœ”
742
        assert self.profile.plan == "email"
1โœ”
743

744
    def test_phone_user(self) -> None:
1โœ”
745
        self.upgrade_to_phone()
1โœ”
746
        assert self.profile.plan == "phone"
1โœ”
747

748
    def test_vpn_bundle_user(self) -> None:
1โœ”
749
        self.upgrade_to_vpn_bundle()
1โœ”
750
        assert self.profile.plan == "bundle"
1โœ”
751

752

753
class ProfilePlanTermTest(ProfileTestCase):
1โœ”
754
    def test_free_user(self) -> None:
1โœ”
755
        assert self.profile.plan_term is None
1โœ”
756

757
    def test_premium_user(self) -> None:
1โœ”
758
        self.upgrade_to_premium()
1โœ”
759
        assert self.profile.plan_term == "unknown"
1โœ”
760

761
    def test_phone_user(self) -> None:
1โœ”
762
        self.upgrade_to_phone()
1โœ”
763
        assert self.profile.plan_term == "unknown"
1โœ”
764

765
    def test_phone_user_1_month(self) -> None:
1โœ”
766
        self.upgrade_to_phone()
1โœ”
767
        self.profile.date_phone_subscription_start = datetime(2024, 1, 1, tzinfo=UTC)
1โœ”
768

769
        self.profile.date_phone_subscription_end = datetime(2024, 2, 1, tzinfo=UTC)
1โœ”
770
        assert self.profile.plan_term == "1_month"
1โœ”
771

772
    def test_phone_user_1_year(self) -> None:
1โœ”
773
        self.upgrade_to_phone()
1โœ”
774
        self.profile.date_phone_subscription_start = datetime(2024, 1, 1, tzinfo=UTC)
1โœ”
775

776
        self.profile.date_phone_subscription_end = datetime(2025, 1, 1, tzinfo=UTC)
1โœ”
777
        assert self.profile.plan_term == "1_year"
1โœ”
778

779
    def test_vpn_bundle_user(self) -> None:
1โœ”
780
        self.upgrade_to_vpn_bundle()
1โœ”
781
        assert self.profile.plan_term == "unknown"
1โœ”
782

783

784
class ProfileMetricsPremiumStatus(ProfileTestCase):
1โœ”
785
    def test_free_user(self):
1โœ”
786
        assert self.profile.metrics_premium_status == "free"
1โœ”
787

788
    def test_premium_user(self) -> None:
1โœ”
789
        self.upgrade_to_premium()
1โœ”
790
        assert self.profile.metrics_premium_status == "email_unknown"
1โœ”
791

792
    def test_phone_user(self) -> None:
1โœ”
793
        self.upgrade_to_phone()
1โœ”
794
        assert self.profile.metrics_premium_status == "phone_unknown"
1โœ”
795

796
    def test_phone_user_1_month(self) -> None:
1โœ”
797
        self.upgrade_to_phone()
1โœ”
798
        self.profile.date_phone_subscription_start = datetime(2024, 1, 1, tzinfo=UTC)
1โœ”
799

800
        self.profile.date_phone_subscription_end = datetime(2024, 2, 1, tzinfo=UTC)
1โœ”
801
        assert self.profile.metrics_premium_status == "phone_1_month"
1โœ”
802

803
    def test_phone_user_1_year(self) -> None:
1โœ”
804
        self.upgrade_to_phone()
1โœ”
805
        self.profile.date_phone_subscription_start = datetime(2024, 1, 1, tzinfo=UTC)
1โœ”
806

807
        self.profile.date_phone_subscription_end = datetime(2025, 1, 1, tzinfo=UTC)
1โœ”
808
        assert self.profile.metrics_premium_status == "phone_1_year"
1โœ”
809

810
    def test_vpn_bundle_user(self) -> None:
1โœ”
811
        self.upgrade_to_vpn_bundle()
1โœ”
812
        assert self.profile.metrics_premium_status == "bundle_unknown"
1โœ”
STATUS ยท Troubleshooting ยท Open an Issue ยท Sales ยท Support ยท CAREERS ยท ENTERPRISE ยท START FREE ยท SCHEDULE DEMO
ANNOUNCEMENTS ยท TWITTER ยท TOS & SLA ยท Supported CI Services ยท What's a CI service? ยท Automated Testing

ยฉ 2025 Coveralls, Inc