-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtest_app.py
More file actions
243 lines (205 loc) · 6.7 KB
/
test_app.py
File metadata and controls
243 lines (205 loc) · 6.7 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import pytest
import os
import asks
import pendulum
import attr
import urllib.parse
import json
import signal
import sys
import trio
from snekomatic.app import main, SENT_INVITATION
from snekomatic.gh import GithubApp, BaseGithubClient
from .util import fake_webhook, save_environ
from .credentials import *
@pytest.fixture
def our_app_environment(heroku_style_pg):
with save_environ():
os.environ["GITHUB_USER_AGENT"] = TEST_USER_AGENT
os.environ["GITHUB_APP_ID"] = TEST_APP_ID
os.environ["GITHUB_PRIVATE_KEY"] = TEST_PRIVATE_KEY
os.environ["GITHUB_WEBHOOK_SECRET"] = TEST_WEBHOOK_SECRET
os.environ["PORT"] = "0" # let the OS pick an unused port
yield
@pytest.fixture
async def our_app_url(nursery, our_app_environment):
urls = await nursery.start(main)
return urls[0]
async def test_main_smoke(our_app_url):
response = await asks.get(our_app_url)
assert "Hi!" in response.text
@attr.s(frozen=True)
class InviteScenario:
pr_merged = attr.ib()
in_db = attr.ib()
member_state = attr.ib()
expect_in_db_after = attr.ib()
expect_invite = attr.ib()
INVITE_SCENARIOS = [
InviteScenario(
pr_merged=False,
in_db=False,
member_state=None,
expect_in_db_after=False,
expect_invite=False,
),
InviteScenario(
pr_merged=True,
in_db=True,
member_state=None,
expect_in_db_after=True,
expect_invite=False,
),
InviteScenario(
pr_merged=True,
in_db=False,
member_state="active",
expect_in_db_after=True,
expect_invite=False,
),
InviteScenario(
pr_merged=True,
in_db=False,
member_state="pending",
expect_in_db_after=True,
expect_invite=False,
),
InviteScenario(
pr_merged=True,
in_db=False,
member_state=None,
expect_in_db_after=True,
expect_invite=True,
),
]
def _succeed(body={}):
return (
200,
{"content-type": "application/json"},
json.dumps(body).encode("ascii"),
)
@pytest.mark.parametrize("s", INVITE_SCENARIOS)
async def test_invite_scenarios(s, our_app_url, monkeypatch):
PR_CREATOR = "julia"
ORG = "acme"
##### Set up the scenario #####
# What we'll send
headers, body = fake_webhook(
"pull_request",
{
"action": "closed",
"pull_request": {
"merged": s.pr_merged,
"user": {
"login": PR_CREATOR,
},
"comments_url": "fake-comments-url",
},
"organization": {
"login": ORG,
},
"installation": {
"id": "000000",
},
},
TEST_WEBHOOK_SECRET,
)
# Set up database
if s.in_db:
SENT_INVITATION.add(PR_CREATOR)
# Faking the Github API
async def fake_token_for(self, installation_id):
return "xyzzy"
monkeypatch.setattr(GithubApp, "token_for", fake_token_for)
did_invite = False
did_comment = False
async def fake_request(self, method, url, headers, body):
print(method, url)
to_members = url.endswith(f"/orgs/{ORG}/memberships/{PR_CREATOR}")
if method == "GET" and to_members:
if s.member_state is not None:
return _succeed({"state": s.member_state})
else:
return (404, {}, "")
if method == "PUT" and to_members:
nonlocal did_invite
did_invite = True
return _succeed()
if method == "POST" and url.endswith("/fake-comments-url"):
nonlocal did_comment
did_comment = True
return _succeed()
assert False # pragma: no cover
monkeypatch.setattr(BaseGithubClient, "_request", fake_request)
##### Post to the site #####
response = await asks.post(
urllib.parse.urljoin(our_app_url, "webhook/github"),
headers=headers,
data=body,
)
assert response.status_code == 200
# Checks
assert did_invite == did_comment
assert did_invite == s.expect_invite
assert (PR_CREATOR in SENT_INVITATION) == s.expect_in_db_after
@pytest.mark.skipif(sys.platform.startswith("win"), reason="requires Unix")
async def test_exits_cleanly_on_sigterm(our_app_environment, mock_clock):
# wait 1 second to make sure all I/O settles before jumping clock
mock_clock.autojump_threshold = 1.0
# Simple test: make sure SIGTERM does cause a clean shutdown
async with trio.open_nursery() as nursery:
await nursery.start(main)
os.kill(os.getpid(), signal.SIGTERM)
# Harder test: make sure running webhook handler completes before shutdown
async with trio.open_nursery() as nursery:
our_app_urls = await nursery.start(main)
our_app_url = our_app_urls[0]
# 1. send webhook
headers, body = fake_webhook(
"pull_request",
{
"action": "rotated",
"rotation_time": 10, # seconds
"installation": {
"id": "000000",
},
},
TEST_WEBHOOK_SECRET,
)
post_finished = trio.Event()
async def post_then_notify():
try:
await asks.post(
urllib.parse.urljoin(our_app_url, "webhook/github"),
headers=headers,
data=body,
)
post_finished.set()
except BaseException as exc:
print(f"post_then_notify crashing with {exc!r}")
nursery.start_soon(post_then_notify)
# 2. give webhook chance to be delivered; it should sleep for 10
# seconds or so
try:
print("first sleep")
await trio.sleep(3)
print("first wake")
# 3. send SIGTERM
print("killing")
os.kill(os.getpid(), signal.SIGTERM)
# 4. test sleeps for "5 seconds", to give SIGTERM time to work
print("second sleep")
await trio.sleep(3)
print("second wake")
# 5. make sure app has not actually exited
print(f"post_finished {post_finished!r}")
assert not post_finished.is_set()
# 5b. but it is refusing new requests
with pytest.raises(asks.errors.BadHttpResponse):
response = await asks.get(our_app_url)
assert "Hi!" in response.text
# 6. test sleep for "6 seconds"
# 7. make sure app has exited (and webhook got response)
except BaseException as exc:
print(f"test is failing with {exc!r}")
raise