File size: 4,260 Bytes
4778987
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0937536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4778987
 
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
"""Trial sweep: pause expired self-serve trials that never subscribed, email the
payment link, and never touch paid or still-running trials."""
from __future__ import annotations

from datetime import UTC, datetime, timedelta

from sqlalchemy import select

from app.models import Tenant
from app.scheduler import pause_expired_trials


async def test_pause_only_expired_unsubscribed_trials(db_session, monkeypatch):
    from app import mailer
    sent = []

    async def fake(settings, to, subject, body, **k):
        sent.append(to)
        return True

    monkeypatch.setattr(mailer, "send_email", fake)
    past = datetime.now(UTC) - timedelta(hours=1)
    future = datetime.now(UTC) + timedelta(days=5)
    db_session.add_all([
        Tenant(slug="t-expired", trial_ends_at=past, chatbot_enabled=True,
               support_email="e@x.com"),
        Tenant(slug="t-paid", trial_ends_at=past, chatbot_enabled=True,
               stripe_subscription_id="sub_123", support_email="p@x.com"),
        Tenant(slug="t-running", trial_ends_at=future, chatbot_enabled=True,
               support_email="r@x.com"),
    ])
    await db_session.commit()

    n = await pause_expired_trials(db_session)
    assert n == 1  # only the expired, unsubscribed one

    db_session.expire_all()

    async def _get(slug):
        return (await db_session.execute(select(Tenant).where(Tenant.slug == slug))).scalar_one()

    expired = await _get("t-expired")
    assert expired.chatbot_enabled is False
    assert expired.disabled_reason == "trial"  # kill-switch reason, cuts service everywhere
    assert (await _get("t-paid")).chatbot_enabled is True       # subscribed -> kept
    assert (await _get("t-running")).chatbot_enabled is True    # not expired yet

    assert "e@x.com" in sent          # expired trial got the pay link
    assert "p@x.com" not in sent      # paid one never emailed
    assert "r@x.com" not in sent


async def test_pause_is_idempotent(db_session, monkeypatch):
    from app import mailer
    monkeypatch.setattr(mailer, "send_email", lambda *a, **k: _noop())
    db_session.add(Tenant(slug="t-x", trial_ends_at=datetime.now(UTC) - timedelta(hours=1),
                          chatbot_enabled=True, support_email="x@x.com"))
    await db_session.commit()
    assert await pause_expired_trials(db_session) == 1
    # second sweep: already paused (chatbot_enabled=False) -> not reselected
    assert await pause_expired_trials(db_session) == 0


async def test_pause_excludes_shopify_installs(db_session, monkeypatch):
    """Shopify App Store installs are billed via the Shopify Billing API, never
    Stripe — the trial sweep must NEVER pause them or email the Stripe pay link
    (App Store policy 1.2.1). Direct/Stripe trials keep being swept as before."""
    from app import mailer
    sent = []

    async def fake(settings, to, subject, body, **k):
        sent.append(to)
        return True

    monkeypatch.setattr(mailer, "send_email", fake)
    past = datetime.now(UTC) - timedelta(hours=1)
    db_session.add_all([
        Tenant(slug="t-sh-shop", trial_ends_at=past, chatbot_enabled=True,
               support_email="s1@x.com", shopify_shop="s1.myshopify.com",
               shopify_install_source="oauth"),
        Tenant(slug="t-sh-src", trial_ends_at=past, chatbot_enabled=True,
               support_email="s2@x.com", shopify_install_source="oauth"),
        Tenant(slug="t-direct", trial_ends_at=past, chatbot_enabled=True,
               support_email="d@x.com"),  # direct/Stripe -> still swept
    ])
    await db_session.commit()

    assert await pause_expired_trials(db_session) == 1  # only the direct one

    db_session.expire_all()

    async def _get(slug):
        return (await db_session.execute(select(Tenant).where(Tenant.slug == slug))).scalar_one()

    assert (await _get("t-sh-shop")).chatbot_enabled is True   # Shopify -> untouched
    assert (await _get("t-sh-src")).chatbot_enabled is True
    assert (await _get("t-direct")).chatbot_enabled is False   # direct -> paused
    assert "s1@x.com" not in sent and "s2@x.com" not in sent   # no Stripe email to Shopify
    assert "d@x.com" in sent                                   # direct still emailed


async def _noop():
    return True