Skip to content

Commit c66d2d4

Browse files
authored
re-add survey email tooling for reminder email (#18746)
* Revert "revert: remove organization survey email functionality (#18642)" This reverts commit 8545b13. * update email template
1 parent 3360d10 commit c66d2d4

File tree

7 files changed

+709
-0
lines changed

7 files changed

+709
-0
lines changed
Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
import pretend
4+
import pyramid.scripting
5+
import transaction
6+
7+
from warehouse.cli import organizations
8+
9+
10+
class TestOrganizationsCLI:
11+
def test_send_survey_emails_dry_run(self, monkeypatch, cli):
12+
"""Test dry run doesn't send emails"""
13+
# Create test organizations
14+
org1 = pretend.stub(
15+
id="org1-id",
16+
name="TestOrg1",
17+
orgtype=pretend.stub(value="Community"),
18+
is_active=True,
19+
projects=[], # No projects
20+
users=[
21+
pretend.stub(
22+
username="user1",
23+
24+
primary_email=pretend.stub(
25+
email="[email protected]", verified=True
26+
),
27+
),
28+
pretend.stub(
29+
username="user2",
30+
31+
primary_email=pretend.stub(
32+
email="[email protected]", verified=True
33+
),
34+
),
35+
],
36+
)
37+
38+
org2 = pretend.stub(
39+
id="org2-id",
40+
name="TestOrg2",
41+
orgtype=pretend.stub(value="Company"),
42+
is_active=True,
43+
projects=[pretend.stub()], # Has projects
44+
users=[
45+
pretend.stub(
46+
username="user3",
47+
48+
primary_email=pretend.stub(
49+
email="[email protected]", verified=True
50+
),
51+
),
52+
],
53+
)
54+
55+
# Mock the database query
56+
query_result = pretend.stub(
57+
filter=lambda *args: pretend.stub(
58+
options=lambda *args: pretend.stub(
59+
limit=lambda n: pretend.stub(all=lambda: [org1, org2]),
60+
all=lambda: [org1, org2],
61+
)
62+
)
63+
)
64+
65+
# Mock pyramid.scripting.prepare
66+
mock_request = pretend.stub(
67+
db=pretend.stub(query=lambda *args: query_result),
68+
registry={"celery.app": pretend.stub()},
69+
)
70+
mock_env = {
71+
"request": mock_request,
72+
"closer": pretend.call_recorder(lambda: None),
73+
}
74+
prepare = pretend.call_recorder(lambda registry: mock_env)
75+
monkeypatch.setattr(pyramid.scripting, "prepare", prepare)
76+
77+
# No need to mock send_organization_survey_email for dry-run test
78+
79+
# Create config
80+
config = pretend.stub(
81+
registry={
82+
"celery.app": pretend.stub(),
83+
}
84+
)
85+
86+
# Run the command with dry-run
87+
result = cli.invoke(
88+
organizations.send_survey_emails,
89+
["--dry-run", "--limit", "2"],
90+
obj=config,
91+
)
92+
93+
assert result.exit_code == 0
94+
assert "DRY RUN" in result.output
95+
assert "Would send no_utilization_community survey to user1" in result.output
96+
assert "Would send no_utilization_community survey to user2" in result.output
97+
assert "Would send utilization_company survey to user3" in result.output
98+
99+
def test_send_survey_emails_actual_send(self, monkeypatch, cli):
100+
"""Test actual email sending"""
101+
# Create test organization
102+
org = pretend.stub(
103+
id="org-id",
104+
name="TestOrg",
105+
orgtype=pretend.stub(value="Community"),
106+
is_active=True,
107+
projects=[pretend.stub()], # Has projects
108+
users=[
109+
pretend.stub(
110+
username="user1",
111+
112+
primary_email=pretend.stub(
113+
email="[email protected]", verified=True
114+
),
115+
),
116+
],
117+
)
118+
119+
# Mock the database query
120+
query_result = pretend.stub(
121+
filter=lambda *args: pretend.stub(
122+
options=lambda *args: pretend.stub(
123+
limit=lambda n: pretend.stub(all=lambda: [org]),
124+
all=lambda: [org],
125+
)
126+
)
127+
)
128+
129+
# Mock transaction manager
130+
tm = pretend.stub(
131+
begin=pretend.call_recorder(lambda: None),
132+
commit=pretend.call_recorder(lambda: None),
133+
)
134+
135+
# Mock pyramid.scripting.prepare
136+
mock_request = pretend.stub(
137+
db=pretend.stub(query=lambda *args: query_result),
138+
registry={"celery.app": pretend.stub()},
139+
)
140+
mock_env = {
141+
"request": mock_request,
142+
"closer": pretend.call_recorder(lambda: None),
143+
}
144+
prepare = pretend.call_recorder(lambda registry: mock_env)
145+
monkeypatch.setattr(pyramid.scripting, "prepare", prepare)
146+
147+
# Mock transaction.TransactionManager
148+
monkeypatch.setattr(transaction, "TransactionManager", lambda explicit: tm)
149+
150+
# Mock _get_task
151+
mock_get_task = pretend.call_recorder(lambda app, task_func: pretend.stub())
152+
monkeypatch.setattr("warehouse.tasks._get_task", mock_get_task)
153+
154+
# Track email sends
155+
send_email_calls = []
156+
157+
def mock_send_email(request, user, **kwargs):
158+
send_email_calls.append((request, user, kwargs))
159+
160+
monkeypatch.setattr(
161+
"warehouse.email.send_organization_survey_email",
162+
mock_send_email,
163+
)
164+
165+
# Create config
166+
config = pretend.stub(
167+
registry={
168+
"celery.app": pretend.stub(),
169+
}
170+
)
171+
172+
# Run the command
173+
result = cli.invoke(
174+
organizations.send_survey_emails,
175+
["--limit", "1"],
176+
obj=config,
177+
)
178+
179+
assert result.exit_code == 0
180+
assert "Successfully queued 1 emails" in result.output
181+
assert "Queued utilization_community survey to user1" in result.output
182+
assert len(send_email_calls) == 1
183+
assert tm.begin.calls == [pretend.call()]
184+
assert tm.commit.calls == [pretend.call()]
185+
186+
def test_send_survey_emails_categorization(self, monkeypatch, cli):
187+
"""Test correct categorization of organizations"""
188+
# Create test organizations with all 4 categories
189+
orgs = [
190+
# Utilization + Company
191+
pretend.stub(
192+
name="CompanyWithProjects",
193+
orgtype=pretend.stub(value="Company"),
194+
is_active=True,
195+
projects=[pretend.stub()],
196+
users=[
197+
pretend.stub(
198+
username="user1",
199+
200+
primary_email=pretend.stub(
201+
email="[email protected]", verified=True
202+
),
203+
)
204+
],
205+
),
206+
# Utilization + Community
207+
pretend.stub(
208+
name="CommunityWithProjects",
209+
orgtype=pretend.stub(value="Community"),
210+
is_active=True,
211+
projects=[pretend.stub()],
212+
users=[
213+
pretend.stub(
214+
username="user2",
215+
216+
primary_email=pretend.stub(
217+
email="[email protected]", verified=True
218+
),
219+
)
220+
],
221+
),
222+
# No Utilization + Company
223+
pretend.stub(
224+
name="CompanyNoProjects",
225+
orgtype=pretend.stub(value="Company"),
226+
is_active=True,
227+
projects=[],
228+
users=[
229+
pretend.stub(
230+
username="user3",
231+
232+
primary_email=pretend.stub(
233+
email="[email protected]", verified=True
234+
),
235+
)
236+
],
237+
),
238+
# No Utilization + Community
239+
pretend.stub(
240+
name="CommunityNoProjects",
241+
orgtype=pretend.stub(value="Community"),
242+
is_active=True,
243+
projects=[],
244+
users=[
245+
pretend.stub(
246+
username="user4",
247+
248+
primary_email=pretend.stub(
249+
email="[email protected]", verified=True
250+
),
251+
)
252+
],
253+
),
254+
]
255+
256+
# Mock the database query
257+
query_result = pretend.stub(
258+
filter=lambda *args: pretend.stub(
259+
options=lambda *args: pretend.stub(all=lambda: orgs)
260+
)
261+
)
262+
263+
# Mock pyramid.scripting.prepare
264+
mock_request = pretend.stub(
265+
db=pretend.stub(query=lambda *args: query_result),
266+
registry={"celery.app": pretend.stub()},
267+
)
268+
mock_env = {
269+
"request": mock_request,
270+
"closer": pretend.call_recorder(lambda: None),
271+
}
272+
prepare = pretend.call_recorder(lambda registry: mock_env)
273+
monkeypatch.setattr(pyramid.scripting, "prepare", prepare)
274+
275+
# No need to mock send_organization_survey_email for dry-run test
276+
277+
# Create config
278+
config = pretend.stub(
279+
registry={
280+
"celery.app": pretend.stub(),
281+
}
282+
)
283+
284+
# Run the command
285+
result = cli.invoke(
286+
organizations.send_survey_emails,
287+
["--dry-run"],
288+
obj=config,
289+
)
290+
291+
assert result.exit_code == 0
292+
assert "Utilization + Company: 1" in result.output
293+
assert "Utilization + Community: 1" in result.output
294+
assert "No Utilization + Company: 1" in result.output
295+
assert "No Utilization + Community: 1" in result.output
296+
assert "Total organizations processed: 4" in result.output
297+
assert "Total emails to send: 4" in result.output
298+
299+
def test_send_survey_emails_error_handling(self, monkeypatch, cli):
300+
"""Test error handling when sending emails fails"""
301+
# Create test organization
302+
org = pretend.stub(
303+
name="TestOrg",
304+
orgtype=pretend.stub(value="Community"),
305+
is_active=True,
306+
projects=[],
307+
users=[
308+
pretend.stub(
309+
username="user1",
310+
311+
primary_email=pretend.stub(
312+
email="[email protected]", verified=True
313+
),
314+
),
315+
],
316+
)
317+
318+
# Mock the database query
319+
query_result = pretend.stub(
320+
filter=lambda *args: pretend.stub(
321+
options=lambda *args: pretend.stub(
322+
all=lambda: [org],
323+
limit=lambda n: pretend.stub(all=lambda: [org]),
324+
)
325+
)
326+
)
327+
328+
# Mock transaction manager
329+
tm = pretend.stub(
330+
begin=pretend.call_recorder(lambda: None),
331+
commit=pretend.call_recorder(lambda: None),
332+
)
333+
334+
# Mock pyramid.scripting.prepare
335+
mock_request = pretend.stub(
336+
db=pretend.stub(query=lambda *args: query_result),
337+
registry={"celery.app": pretend.stub()},
338+
)
339+
mock_env = {
340+
"request": mock_request,
341+
"closer": pretend.call_recorder(lambda: None),
342+
}
343+
prepare = pretend.call_recorder(lambda registry: mock_env)
344+
monkeypatch.setattr(pyramid.scripting, "prepare", prepare)
345+
346+
# Mock transaction.TransactionManager
347+
monkeypatch.setattr(transaction, "TransactionManager", lambda explicit: tm)
348+
349+
# Mock _get_task
350+
mock_get_task = pretend.call_recorder(lambda app, task_func: pretend.stub())
351+
monkeypatch.setattr("warehouse.tasks._get_task", mock_get_task)
352+
353+
# Make send_email raise an exception
354+
def mock_send_email(request, user, **kwargs):
355+
raise Exception("Email sending failed")
356+
357+
monkeypatch.setattr(
358+
"warehouse.email.send_organization_survey_email",
359+
mock_send_email,
360+
)
361+
362+
# Create config
363+
config = pretend.stub(
364+
registry={
365+
"celery.app": pretend.stub(),
366+
}
367+
)
368+
369+
# Run the command
370+
result = cli.invoke(
371+
organizations.send_survey_emails,
372+
[],
373+
obj=config,
374+
)
375+
376+
assert result.exit_code == 0
377+
assert "ERROR sending to user1: Email sending failed" in result.output
378+
# Transaction should still be committed
379+
assert tm.commit.calls == [pretend.call()]

0 commit comments

Comments
 (0)