-
Notifications
You must be signed in to change notification settings - Fork 17
/
test_marvin_actions.py
375 lines (313 loc) · 18.6 KB
/
test_marvin_actions.py
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tests for all Marvin actions
"""
import json
import os
from datetime import date, timedelta
from unittest import mock, TestCase
import requests
from bot import Bot
import marvin_actions
import marvin_general_actions
class ActionTest(TestCase):
"""Test Marvin actions"""
strings = {}
@classmethod
def setUpClass(cls):
with open("marvin_strings.json", encoding="utf-8") as f:
cls.strings = json.load(f)
def executeAction(self, action, message):
"""Execute an action for a message and return the response"""
return action(Bot.tokenize(message))
def assertActionOutput(self, action, message, expectedOutput):
"""Call an action on message and assert expected output"""
actualOutput = self.executeAction(action, message)
self.assertEqual(actualOutput, expectedOutput)
def assertActionSilent(self, action, message):
"""Call an action with provided message and assert no output"""
self.assertActionOutput(action, message, None)
def assertStringsOutput(self, action, message, expectedoutputKey, subkey=None):
"""Call an action with provided message and assert the output is equal to DB"""
expectedOutput = self.strings.get(expectedoutputKey)
if subkey is not None:
if isinstance(expectedOutput, list):
expectedOutput = expectedOutput[subkey]
else:
expectedOutput = expectedOutput.get(subkey)
self.assertActionOutput(action, message, expectedOutput)
def assertBBQResponse(self, todaysDate, bbqDate, expectedMessageKey):
"""Assert that the proper bbq message is returned, given a date"""
url = self.strings.get("barbecue").get("url")
message = self.strings.get("barbecue").get(expectedMessageKey)
if isinstance(message, list):
message = message[1]
if expectedMessageKey in ["base", "week", "eternity"]:
message = message % bbqDate
with mock.patch("marvin_actions.datetime") as d, mock.patch("marvin_actions.random") as r:
d.date.today.return_value = todaysDate
r.randint.return_value = 1
expected = f"{url}. {message}"
self.assertActionOutput(marvin_actions.marvinTimeToBBQ, "dags att grilla", expected)
def assertNameDayOutput(self, exampleFile, expectedOutput):
"""Assert that the proper nameday message is returned, given an inputfile"""
with open(os.path.join("namedayFiles", f"{exampleFile}.json"), "r", encoding="UTF-8") as f:
response = requests.models.Response()
response._content = str.encode(json.dumps(json.load(f)))
with mock.patch("marvin_actions.requests") as r:
r.get.return_value = response
self.assertActionOutput(marvin_actions.marvinNameday, "nameday", expectedOutput)
def assertJokeOutput(self, exampleFile, expectedOutput):
"""Assert that a joke is returned, given an input file"""
with open(os.path.join("jokeFiles", f"{exampleFile}.json"), "r", encoding="UTF-8") as f:
response = requests.models.Response()
response._content = str.encode(json.dumps(json.load(f)))
with mock.patch("marvin_actions.requests") as r:
r.get.return_value = response
self.assertActionOutput(marvin_actions.marvinJoke, "joke", expectedOutput)
def assertSunOutput(self, expectedOutput):
"""Test that marvin knows when the sun comes up, given an input file"""
with open(os.path.join("sunFiles", "sun.json"), "r", encoding="UTF-8") as f:
response = requests.models.Response()
response._content = str.encode(json.dumps(json.load(f)))
with mock.patch("marvin_actions.requests") as r:
r.get.return_value = response
self.assertActionOutput(marvin_actions.marvinSun, "sol", expectedOutput)
def testSmile(self):
"""Test that marvin can smile"""
with mock.patch("marvin_actions.random") as r:
r.randint.return_value = 1
self.assertStringsOutput(marvin_actions.marvinSmile, "le lite?", "smile", 1)
self.assertActionSilent(marvin_actions.marvinSmile, "sur idag?")
def testWhois(self):
"""Test that marvin responds to whois"""
self.assertStringsOutput(marvin_actions.marvinWhoIs, "vem är marvin?", "whois")
self.assertActionSilent(marvin_actions.marvinWhoIs, "vemär")
def testGoogle(self):
"""Test that marvin can help google stuff"""
with mock.patch("marvin_actions.random") as r:
r.randint.return_value = 1
self.assertActionOutput(
marvin_actions.marvinGoogle,
"kan du googla mos",
"LMGTFY https://www.google.se/search?q=mos")
self.assertActionOutput(
marvin_actions.marvinGoogle,
"kan du googla google mos",
"LMGTFY https://www.google.se/search?q=google+mos")
self.assertActionSilent(marvin_actions.marvinGoogle, "du kan googla")
self.assertActionSilent(marvin_actions.marvinGoogle, "gogool")
def testExplainShell(self):
"""Test that marvin can explain shell commands"""
url = "https://explainshell.com/explain?cmd=pwd"
self.assertActionOutput(marvin_actions.marvinExplainShell, "explain pwd", url)
self.assertActionOutput(marvin_actions.marvinExplainShell, "can you explain pwd", url)
self.assertActionOutput(
marvin_actions.marvinExplainShell,
"förklara pwd|grep -o $user",
f"{url}%7Cgrep+-o+%24user")
self.assertActionSilent(marvin_actions.marvinExplainShell, "explains")
def testSource(self):
"""Test that marvin responds to questions about source code"""
self.assertStringsOutput(marvin_actions.marvinSource, "source", "source")
self.assertStringsOutput(marvin_actions.marvinSource, "källkod", "source")
self.assertActionSilent(marvin_actions.marvinSource, "opensource")
def testBudord(self):
"""Test that marvin knows all the commandments"""
for n, _ in enumerate(self.strings.get("budord")):
self.assertStringsOutput(marvin_actions.marvinBudord, f"budord #{n}", "budord", f"#{n}")
self.assertStringsOutput(marvin_actions.marvinBudord,"visa stentavla 1", "budord", "#1")
self.assertActionSilent(marvin_actions.marvinBudord, "var är stentavlan?")
def testQuote(self):
"""Test that marvin can quote The Hitchhikers Guide to the Galaxy"""
with mock.patch("marvin_actions.random") as r:
r.randint.return_value = 1
self.assertStringsOutput(marvin_actions.marvinQuote, "ge os ett citat", "hitchhiker", 1)
self.assertStringsOutput(marvin_actions.marvinQuote, "filosofi", "hitchhiker", 1)
self.assertStringsOutput(marvin_actions.marvinQuote, "filosofera", "hitchhiker", 1)
self.assertActionSilent(marvin_actions.marvinQuote, "noquote")
for i,_ in enumerate(self.strings.get("hitchhiker")):
r.randint.return_value = i
self.assertStringsOutput(marvin_actions.marvinQuote, "quote", "hitchhiker", i)
def testVideoOfToday(self):
"""Test that marvin can link to a different video each day of the week"""
with mock.patch("marvin_actions.datetime") as dt:
for d in range(1, 8):
day = date(2024, 11, 25) + timedelta(days=d)
dt.date.today.return_value = day
weekday = day.strftime("%A")
weekdayPhrase = self.strings.get("video-of-today").get(weekday).get("message")
videoPhrase = self.strings.get("video-of-today").get(weekday).get("url")
response = f"{weekdayPhrase} En passande video är {videoPhrase}"
self.assertActionOutput(marvin_actions.marvinVideoOfToday, "dagens video", response)
self.assertActionSilent(marvin_actions.marvinVideoOfToday, "videoidag")
def testHelp(self):
"""Test that marvin can provide a help menu"""
self.assertStringsOutput(marvin_actions.marvinHelp, "help", "menu")
self.assertActionSilent(marvin_actions.marvinHelp, "halp")
def testSayHi(self):
"""Test that marvin responds to greetings"""
with mock.patch("marvin_actions.random") as r:
for skey, s in enumerate(self.strings.get("smile")):
for hkey, h in enumerate(self.strings.get("hello")):
for fkey, f in enumerate(self.strings.get("friendly")):
r.randint.side_effect = [skey, hkey, fkey]
self.assertActionOutput(marvin_actions.marvinSayHi, "hej", f"{s} {h} {f}")
self.assertActionSilent(marvin_actions.marvinSayHi, "korsning")
def testLunchLocations(self):
"""Test that marvin can provide lunch suggestions for certain places"""
locations = ["karlskrona", "goteborg", "angelholm", "hassleholm", "malmo"]
with mock.patch("marvin_actions.random") as r:
for location in locations:
for i, place in enumerate(self.strings.get("lunch").get("location").get(location)):
r.randint.side_effect = [0, i]
self.assertActionOutput(
marvin_actions.marvinLunch, f"mat {location}", f"Ska vi ta {place}?")
r.randint.side_effect = [1, 2]
self.assertActionOutput(
marvin_actions.marvinLunch, "dags att luncha", "Jag är lite sugen på Indiska?")
self.assertActionSilent(marvin_actions.marvinLunch, "matdags")
def testStrip(self):
"""Test that marvin can recommend comics"""
messageFormat = self.strings.get("commitstrip").get("message")
expected = messageFormat.format(url=self.strings.get("commitstrip").get("url"))
self.assertActionOutput(marvin_actions.marvinStrip, "lite strip kanske?", expected)
self.assertActionSilent(marvin_actions.marvinStrip, "nostrip")
def testRandomStrip(self):
"""Test that marvin can recommend random comics"""
messageFormat = self.strings.get("commitstrip").get("message")
expected = messageFormat.format(url=self.strings.get("commitstrip").get("urlPage") + "123")
with mock.patch("marvin_actions.random") as r:
r.randint.return_value = 123
self.assertActionOutput(marvin_actions.marvinStrip, "random strip kanske?", expected)
def testTimeToBBQ(self):
"""Test that marvin knows when the next BBQ is"""
self.assertBBQResponse(date(2024, 5, 17), date(2024, 5, 17), "today")
self.assertBBQResponse(date(2024, 5, 16), date(2024, 5, 17), "tomorrow")
self.assertBBQResponse(date(2024, 5, 10), date(2024, 5, 17), "week")
self.assertBBQResponse(date(2024, 5, 1), date(2024, 5, 17), "base")
self.assertBBQResponse(date(2023, 10, 17), date(2024, 5, 17), "eternity")
self.assertBBQResponse(date(2024, 9, 20), date(2024, 9, 20), "today")
self.assertBBQResponse(date(2024, 9, 19), date(2024, 9, 20), "tomorrow")
self.assertBBQResponse(date(2024, 9, 13), date(2024, 9, 20), "week")
self.assertBBQResponse(date(2024, 9, 4), date(2024, 9, 20), "base")
def testNameDayReaction(self):
"""Test that marvin only responds to nameday when asked"""
self.assertActionSilent(marvin_actions.marvinNameday, "anything")
def testNameDayRequest(self):
"""Test that marvin sends a proper request for nameday info"""
with mock.patch("marvin_actions.requests") as r, mock.patch("marvin_actions.datetime") as d:
d.datetime.now.return_value = date(2024, 1, 2)
self.executeAction(marvin_actions.marvinNameday, "namnsdag")
self.assertEqual(r.get.call_args.args[0], "https://api.dryg.net/dagar/v2.1/2024/1/2")
def testNameDayResponse(self):
"""Test that marvin properly parses nameday responses"""
self.assertNameDayOutput("single", "Idag har Svea namnsdag")
self.assertNameDayOutput("double", "Idag har Alfred,Alfrida namnsdag")
self.assertNameDayOutput("nobody", "Ingen har namnsdag idag")
def testNameDayError(self):
"""Tests that marvin returns the proper error message when nameday API is down"""
with mock.patch("marvin_actions.requests.get", side_effect=Exception("API Down!")):
self.assertStringsOutput(
marvin_actions.marvinNameday,
"har någon namnsdag idag?",
"nameday",
"error")
def testJokeRequest(self):
"""Test that marvin sends a proper request for a joke"""
with mock.patch("marvin_actions.requests") as r:
self.executeAction(marvin_actions.marvinJoke, "joke")
self.assertEqual(
r.get.call_args.args[0],
"https://api.chucknorris.io/jokes/random?category=dev")
def testJoke(self):
"""Test that marvin sends a joke when requested"""
self.assertJokeOutput("joke", "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris.")
def testJokeError(self):
"""Tests that marvin returns the proper error message when joke API is down"""
with mock.patch("marvin_actions.requests.get", side_effect=Exception("API Down!")):
self.assertStringsOutput(marvin_actions.marvinJoke, "kör ett skämt", "joke", "error")
def testSun(self):
"""Test that marvin sends the sunrise and sunset times """
self.assertSunOutput(
"Idag går solen upp 7:12 och ner 18:21. Iallafall i trakterna kring BTH.")
def testSunError(self):
"""Tests that marvin returns the proper error message when joke API is down"""
with mock.patch("marvin_actions.requests.get", side_effect=Exception("API Down!")):
self.assertStringsOutput(marvin_actions.marvinSun, "när går solen ner?", "sun", "error")
def testUptime(self):
"""Test that marvin can provide the link to the uptime tournament"""
self.assertStringsOutput(marvin_actions.marvinUptime, "visa lite uptime", "uptime", "info")
self.assertActionSilent(marvin_actions.marvinUptime, "uptimetävling")
def testStream(self):
"""Test that marvin can provide the link to the stream"""
self.assertStringsOutput(marvin_actions.marvinStream, "ska mos streama?", "stream", "info")
self.assertActionSilent(marvin_actions.marvinStream, "är mos en streamer?")
def testPrinciple(self):
"""Test that marvin can recite some software principles"""
principles = self.strings.get("principle")
for key, value in principles.items():
self.assertActionOutput(marvin_actions.marvinPrinciple, f"princip {key}", value)
with mock.patch("marvin_actions.random") as r:
r.choice.return_value = "dry"
self.assertStringsOutput(marvin_actions.marvinPrinciple, "princip", "principle", "dry")
self.assertActionSilent(marvin_actions.marvinPrinciple, "principlös")
def testCommitRequest(self):
"""Test that marvin sends proper requests when generating commit messages"""
with mock.patch("marvin_actions.requests") as r:
self.executeAction(marvin_actions.marvinCommit, "vad skriver man efter commit -m?")
self.assertEqual(r.get.call_args.args[0], "https://whatthecommit.com/index.txt")
def testCommitResponse(self):
"""Test that marvin properly handles responses when generating commit messages"""
message = "Secret sauce #9"
response = requests.models.Response()
response._content = str.encode(message)
with mock.patch("marvin_actions.requests") as r:
r.get.return_value = response
expected = f"Använd detta meddelandet: '{message}'"
self.assertActionOutput(marvin_actions.marvinCommit, "commit", expected)
def testWeatherRequest(self):
"""Test that marvin sends the expected requests for weather info"""
with mock.patch("marvin_actions.requests") as r:
self.executeAction(marvin_actions.marvinWeather, "väder")
for url in ["https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/13/station/65090/period/latest-hour/data.json",
"https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/13/codes.json",
"https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/15.5890/lat/56.1500/data.json"]:
self.assertTrue(mock.call(url, timeout=5) in r.get.call_args_list)
def testWeatherResponse(self):
"""Test that marvin properly parses weather responses"""
responses = []
for responseFile in ["station.json", "codes.json", "weather.json"]:
with open(os.path.join("weatherFiles", responseFile), "r", encoding="UTF-8") as f:
response = requests.models.Response()
response._content = str.encode(json.dumps(json.load(f)))
responses.append(response)
with mock.patch("marvin_actions.requests") as r:
r.get.side_effect = responses
expected = "Karlskrona just nu: 11.7 °C. Inget signifikant väder observerat."
self.assertActionOutput(marvin_actions.marvinWeather, "väder", expected)
def testCommitReaction(self):
"""Test that marvin only generates commit messages when asked"""
self.assertActionSilent(marvin_actions.marvinCommit, "nocommit")
def testCommitError(self):
"""Tests that marvin sends the proper message when get commit fails"""
with mock.patch("marvin_actions.requests.get", side_effect=Exception('API Down!')):
self.assertStringsOutput(
marvin_actions.marvinCommit,
"vad skriver man efter commit -m?",
"commit",
"error")
def testMorning(self):
"""Test that marvin wishes good morning, at most once per day"""
marvin_general_actions.lastDateGreeted = None
with mock.patch("marvin_general_actions.datetime") as d:
d.date.today.return_value = date(2024, 5, 17)
with mock.patch("marvin_general_actions.random") as r:
r.choice.return_value = "Morgon"
self.assertActionOutput(marvin_general_actions.marvinMorning, "morrn", "Morgon")
# Should only greet once per day
self.assertActionSilent(marvin_general_actions.marvinMorning, "morgon")
# Should greet again tomorrow
d.date.today.return_value = date(2024, 5, 18)
self.assertActionOutput(marvin_general_actions.marvinMorning, "godmorgon", "Morgon")