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
|
#!/usr/bin/python -u
# Public domain
#
# Set of integration tests that simulate API calls made by the
# UploadWizard frontend.
import argparse
import sys
import unittest
import urllib2
import wikitools
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import time
import os
# This script also depends on poster: https://pypi.python.org/pypi/poster/
# To install all dependencies in your unix instance use:
# sudo pip install -r requirements.txt
# Global wiki object to perform API calls.
wiki = None
# Global verbosity variable used inside the tests.
verbosity = 0
# Global generate new image variable used to check the --gen_new_image flag
generate_new_image = False
class TestUploadWizardAPICalls(unittest.TestCase):
"""Test API calls done by the Upload Wizard"""
# Tests use same global variables initialized in main().
global wiki
global generate_new_image
global verbosity
def createNewImage(self):
"""Create a new image with current timestamp as text marker"""
current_timestamp = time.time()
filename = repr(current_timestamp) + ".png"
ImageFont.load_default()
image = Image.new("RGB", (125, 25))
draw = ImageDraw.Draw(image)
draw.text((10, 10), repr(current_timestamp), (255, 255, 255))
# TODO(aarcos): Store in /tmp
image.save(filename, "PNG")
return filename
def uploadImage(self, remote_stash_filename, final_remote_filename, local_filename):
"""Uploads image using a workflow very similar to the UploadWizard. First upload
to the stash area, if that's successfull then upload for real.
remote_stash_filename - Filename used when we upload to stash area
final_remote_filename - Filename when we upload for real
local_filename - Local filename of the image to be uploaded
"""
# Get edit token
params = {
"action": "tokens",
"type": "edit",
}
req = wikitools.api.APIRequest(wiki, params)
data = req.query()
token = data["tokens"]["edittoken"]
self.assertTrue(len(token) > 0, "Could not get valid token")
# Upload file to stash area
params = {
"action": "upload",
"token": token,
"stash": "1",
"ignorewarnings": "true",
"filename": remote_stash_filename,
"file": open(local_filename),
}
req = wikitools.api.APIRequest(wiki, params, multipart=True)
data = req.query()
if verbosity >= 3:
print "Response from stash upload API call:", data
result = data["upload"]["result"]
self.assertEqual(result, "Success", "Stash upload API call FAILED.")
filekey = data["upload"]["filekey"]
self.assertTrue(len(filekey) > 0, "Could not get valid filekey")
if verbosity >= 3:
print "filekey:", filekey
# Upload file for real using the filekey from previous call
params = {
"action": "upload",
"token": token,
"filekey": filekey,
"ignorewarnings": "true",
"filename": final_remote_filename,
"comment": "Test image uploaded via python script.",
"text": "Image uploaded by WMF QA to monitor upload status. {{pd-ineligible}}[[Category:Test images]]",
}
req = wikitools.api.APIRequest(wiki, params)
data = req.query()
if verbosity >= 3:
print "Response to upload API call:", data
result = data["upload"]["result"]
self.assertEqual(result, "Success", "Upload API call FAILED.")
url = data["upload"]["imageinfo"]["url"]
# Assert uploaded content same as source file !
file_content = file(local_filename, "rb").read()
url_content = urllib2.urlopen(url).read()
self.assertEqual(url_content, file_content, "Uploaded content different than original !")
if verbosity >= 0:
print "File '%s' uploaded successfully !" % final_remote_filename
def testUploadImageUsingWizardWorkflow(self):
"""Test that basic api calls used by the UploadWizard are working as expected"""
remote_stash_filename = "55390test-image-rosa-mx-15x15.png"
final_remote_filename = "Test-image-rosa-mx-15x15.png"
local_filename = "test-image-rosa-mx-15x15.png"
if generate_new_image:
temp_image = self.createNewImage()
remote_stash_filename = temp_image
final_remote_filename = "Test-" + temp_image
local_filename = temp_image
if verbosity >= 3:
print "Created temp_image:", temp_image
try:
self.uploadImage(remote_stash_filename, final_remote_filename, local_filename)
finally:
os.remove(temp_image)
else:
self.uploadImage(remote_stash_filename, final_remote_filename, local_filename)
def testFileInfoAPICall(self):
"""Test file info api call used by the UploadWizard"""
# Try to get file info from a non-existent file
params = {
"action": "query",
"titles": "File:Test-image-non-existant-15x15.png",
"prop": "info|imageinfo",
"inprop": "protection",
"iiprop": "url|mime|size",
"iiurlwidth": "150",
}
req = wikitools.api.APIRequest(wiki, params)
data = req.query()
if verbosity >= 3:
print "Response to file info API call:", data
# Assert that no information was found about the file.
result = data["query"]["pages"]["-1"]["missing"]
self.assertEqual(result, "")
def testTitleBlacklistedAPICall(self):
"""Test title blacklisted api call used by the UploadWizard"""
# Get titleblacklist info
params = {
"action": "titleblacklist",
"tbaction": "create",
"tbtitle": "File:Test-image-rosa-mx-15x15.png",
}
req = wikitools.api.APIRequest(wiki, params)
data = req.query()
if verbosity >= 3:
print "Response to upload API call:", data
result = data["titleblacklist"]["result"]
self.assertEqual(result, "ok")
def main():
"""Script that tests some API calls and workflows used by the UploadWizard.
Example:
$ python upload-wizard_tests.py --username some_username --password secret_password
"""
# Global varibles that are going to be used by the tests.
global wiki
global verbosity
global generate_new_image
# Parse line arguments
parser = argparse.ArgumentParser(description="Upload Wizard API smoke tests.")
parser.add_argument("--api_url", default="https://commons.wikimedia.org/w/api.php",
help="URL of wiki API, such as http://example.org/w/api.php")
parser.add_argument("--username", help="Username for API calls. You can also set MEDIAWIKI_USER")
parser.add_argument("--password",
help="Password for API calls. You can also set MEDIAWIKI_PASSWORD " +
"or MEDIAWIKI_PASSWORD_VARIABLE (points to env var with password value)")
parser.add_argument("-v", "--verbose", type=int, default=0, help="Increase output verbosity")
parser.add_argument("--gen_new_image", action="store_true", help="Create a new image with current timestamp")
args = parser.parse_args()
username = args.username or os.getenv("MEDIAWIKI_USER")
password = args.password or os.getenv("MEDIAWIKI_PASSWORD") or os.getenv(os.getenv("MEDIAWIKI_PASSWORD_VARIABLE"))
if username is None or password is None:
sys.stderr.write(
"error: username and password required. Pass these values with the corresponding flags or set " +
"the env variables: MEDIAWIKI_USER and MEDIAWIKI_PASSWORD or " +
"MEDIAWIKI_PASSWORD_VARIABLE (points to env var with password value)\n")
exit(1)
# Create wikitools object
wiki = wikitools.Wiki(args.api_url)
generate_new_image = args.gen_new_image
verbosity = args.verbose
# Log in user
wiki.login(username, password)
if not wiki.isLoggedIn():
sys.stderr.write("Wrong credentials, please try again.\n")
exit(1)
# Switch to directory of script
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
# Run tests
suite = unittest.TestLoader().loadTestsFromTestCase(TestUploadWizardAPICalls)
success = unittest.TextTestRunner(verbosity=verbosity).run(suite).wasSuccessful()
# Log out user
wiki.logout()
return success
if __name__ == "__main__":
if main() is True:
sys.exit(0)
else:
sys.exit(1)
|