Browse Source

Initial tests

Sebastian Lohff 10 months ago
parent
commit
93e0c3dd7e
4 changed files with 290 additions and 0 deletions
  1. 5
    0
      .gitignore
  2. 1
    0
      setup.py
  3. 276
    0
      tests/test_servefile.py
  4. 8
    0
      tox.ini

+ 5
- 0
.gitignore View File

@@ -1,2 +1,7 @@
1 1
 MANIFEST
2 2
 dist/
3
+*.pyc
4
+__pycache__
5
+*.swp
6
+servefile.egg-info
7
+.tox

+ 1
- 0
setup.py View File

@@ -12,6 +12,7 @@ setup(
12 12
 	url='http://seba-geek.de/stuff/servefile/',
13 13
 	author='Sebastian Lohff',
14 14
 	author_email='seba@someserver.de',
15
+	install_requires=['pyopenssl'],
15 16
 	scripts=['servefile'],
16 17
 )
17 18
 

+ 276
- 0
tests/test_servefile.py View File

@@ -0,0 +1,276 @@
1
+import io
2
+import os
3
+import pytest
4
+import requests
5
+import subprocess
6
+import tarfile
7
+import time
8
+import urllib3
9
+
10
+# crudly written to learn more about pytest and to have a base for refactoring
11
+
12
+
13
+@pytest.fixture
14
+def run_servefile():
15
+    instances = []
16
+
17
+    def _run_servefile(args, **kwargs):
18
+        if not isinstance(args, list):
19
+            args = [args]
20
+        print("running with args", args)
21
+        p = subprocess.Popen(['servefile'] + args, **kwargs)
22
+        time.sleep(kwargs.get('timeout', 0.3))
23
+        instances.append(p)
24
+
25
+        return p
26
+
27
+    yield _run_servefile
28
+
29
+    for instance in instances:
30
+        try:
31
+            instance.terminate()
32
+        except OSError:
33
+            pass
34
+        instance.wait()
35
+
36
+
37
+@pytest.fixture
38
+def datadir(tmp_path):
39
+    def _datadir(data, path=None):
40
+        path = path or tmp_path
41
+        for k, v in data.items():
42
+            if isinstance(v, dict):
43
+                new_path = path / k
44
+                new_path.mkdir()
45
+                _datadir(v, new_path)
46
+            else:
47
+                if hasattr(v, 'decode'):
48
+                    v = v.decode()  # python2 compability
49
+                (path / k).write_text(v)
50
+
51
+        return path
52
+    return _datadir
53
+
54
+
55
+def make_request(path='/', host='localhost', port=8080, method='get', protocol='http', **kwargs):
56
+    url = '{}://{}:{}{}'.format(protocol, host, port, path)
57
+    print('Calling {} on {} with {}'.format(method, url, kwargs))
58
+    r = getattr(requests, method)(url, **kwargs)
59
+
60
+    return r
61
+
62
+
63
+def check_download(expected_data=None, path='/', fname=None, status_code=200, **kwargs):
64
+    if fname is None:
65
+        fname = os.path.basename(path)
66
+    r = make_request(path, **kwargs)
67
+    assert r.status_code == 200
68
+    assert r.text == expected_data
69
+    assert r.headers.get('Content-Type') == 'application/octet-stream'
70
+    if fname:
71
+        assert r.headers.get('Content-Disposition') == 'attachment; filename="{}"'.format(fname)
72
+    assert r.headers.get('Content-Transfer-Encoding') == 'binary'
73
+
74
+    return r  # for additional tests
75
+
76
+
77
+def test_version(run_servefile):
78
+    # we expect the version on stdout (python3.4+) or stderr(python2.6-3.3)
79
+    s = run_servefile('--version', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
80
+    s.wait()
81
+    version = s.stdout.readline().decode().strip()
82
+
83
+    # hardcode version as string until servefile is a module
84
+    assert version == 'servefile 0.4.4'
85
+
86
+
87
+def test_correct_headers(run_servefile, datadir):
88
+    data = "NOOT NOOT"
89
+    p = datadir({'testfile': data}) / 'testfile'
90
+    run_servefile(str(p))
91
+
92
+    r = make_request()
93
+    assert r.status_code == 200
94
+    assert r.headers.get('Content-Type') == 'application/octet-stream'
95
+    assert r.headers.get('Content-Disposition') == 'attachment; filename="testfile"'
96
+    assert r.headers.get('Content-Transfer-Encoding') == 'binary'
97
+
98
+
99
+def test_redirect_and_download(run_servefile, datadir):
100
+    data = "NOOT NOOT"
101
+    p = datadir({'testfile': data}) / 'testfile'
102
+    run_servefile(str(p))
103
+
104
+    # redirect
105
+    r = make_request(allow_redirects=False)
106
+    assert r.status_code == 302
107
+    assert r.headers.get('Location') == '/testfile'
108
+
109
+    # normal download
110
+    check_download(data, fname='testfile')
111
+
112
+
113
+def test_specify_port(run_servefile, datadir):
114
+    data = "NOOT NOOT"
115
+    p = datadir({'testfile': data}) / 'testfile'
116
+    run_servefile([str(p), '-p', '8081'])
117
+
118
+    check_download(data, fname='testfile', port=8081)
119
+
120
+
121
+def test_big_download(run_servefile, datadir):
122
+    # test with about 10 mb of data
123
+    data = "x" * (10 * 1024 ** 2)
124
+    p = datadir({'testfile': data}) / 'testfile'
125
+    run_servefile(str(p))
126
+
127
+    check_download(data, fname='testfile')
128
+
129
+
130
+def test_authentication(run_servefile, datadir):
131
+    data = "NOOT NOOT"
132
+    p = datadir({'testfile': data}) / 'testfile'
133
+
134
+    run_servefile([str(p), '-a', 'user:password'])
135
+    for auth in [('foo', 'bar'), ('user', 'wrong'), ('unknown', 'password')]:
136
+        r = make_request(auth=auth)
137
+        assert '401 - Unauthorized' in r.text
138
+        assert r.status_code == 401
139
+
140
+    check_download(data, fname='testfile', auth=('user', 'password'))
141
+
142
+
143
+def test_serve_directory(run_servefile, datadir):
144
+    d = {
145
+        'foo': {'kratzbaum': 'cat', 'I like Cats!': 'kitteh', '&&&&&&&': 'wheee'},
146
+        'bar': {'thisisaverylongfilenamefortestingthatthisstillworksproperly': 'jup!'},
147
+        'noot': 'still data in here',
148
+        'bigfile': 'x' * (10 * 1024 ** 2),
149
+    }
150
+    p = datadir(d)
151
+    run_servefile([str(p), '-l'])
152
+
153
+    # check if all files are in directory listing
154
+    # (could be made more sophisticated with beautifulsoup)
155
+    for path in '/', '/../':
156
+        r = make_request(path)
157
+        for k in d:
158
+            assert k in r.text
159
+
160
+    for fname, content in d['foo'].items():
161
+        check_download(content, '/foo/' + fname)
162
+
163
+    r = make_request('/unknown')
164
+    assert r.status_code == 404
165
+
166
+    # download
167
+    check_download('jup!', '/bar/thisisaverylongfilenamefortestingthatthisstillworksproperly')
168
+
169
+
170
+def test_upload(run_servefile, tmp_path):
171
+    data = ('this is my live now\n'
172
+            'uploading strings to servers\n'
173
+            'so very joyful')
174
+    uploaddir = tmp_path / 'upload'
175
+    # check that uploaddir does not exist before servefile is started
176
+    assert not uploaddir.is_dir()
177
+
178
+    run_servefile(['-u', str(uploaddir)])
179
+
180
+    # check that servefile created the directory
181
+    assert uploaddir.is_dir()
182
+
183
+    # check upload form present
184
+    r = make_request()
185
+    assert r.status_code == 200
186
+    assert 'multipart/form-data' in r.text
187
+
188
+    # upload file
189
+    files = {'file': ('haiku.txt', data)}
190
+    r = make_request(method='post', files=files)
191
+    assert 'Thanks' in r.text
192
+    assert r.status_code == 200
193
+    with open(str(uploaddir / 'haiku.txt')) as f:
194
+        assert f.read() == data
195
+
196
+    # upload file AGAIN!! (and check it is available unter a different name)
197
+    files = {'file': ('haiku.txt', data)}
198
+    r = make_request(method='post', files=files)
199
+    assert r.status_code == 200
200
+    with open(str(uploaddir / 'haiku.txt(1)')) as f:
201
+        assert f.read() == data
202
+
203
+
204
+def test_upload_size_limit(run_servefile, tmp_path):
205
+    uploaddir = tmp_path / 'upload'
206
+    run_servefile(['-s', '2kb', '-u', str(uploaddir)])
207
+
208
+    # upload file that is too big
209
+    files = {'file': ('toobig', "x" * 2049)}
210
+    r = make_request(method='post', files=files)
211
+    assert 'Your file was too big' in r.text
212
+    assert r.status_code == 413
213
+    assert not (uploaddir / 'toobig').exists()
214
+
215
+    # upload file that should fit
216
+    # the size has to be smaller than 2kb, as the sent size also includes mime-headers
217
+    files = {'file': ('justright', "x" * 1900)}
218
+    r = make_request(method='post', files=files)
219
+    assert r.status_code == 200
220
+
221
+
222
+def test_tar_mode(run_servefile, datadir):
223
+    d = {
224
+        'foo': {
225
+            'bar': 'hello testmode my old friend',
226
+            'baz': 'you came to test me once again',
227
+        }
228
+    }
229
+    p = datadir(d)
230
+    run_servefile(['-t', str(p / 'foo')])
231
+
232
+    # test redirect?
233
+
234
+    # test contents of tar file
235
+    r = make_request()
236
+    assert r.status_code == 200
237
+    tar = tarfile.open(fileobj=io.BytesIO(r.content))
238
+    assert len(tar.getmembers()) == 3
239
+    assert tar.getmember('foo').isdir()
240
+    for filename, content in d['foo'].items():
241
+        info = tar.getmember('foo/{}'.format(filename))
242
+        assert info.isfile
243
+        assert tar.extractfile(info.path).read().decode() == content
244
+
245
+
246
+def test_tar_compression(run_servefile, datadir):
247
+    d = {'foo': 'blubb'}
248
+    p = datadir(d)
249
+    run_servefile(['-c', 'gzip', '-t', str(p / 'foo')])
250
+
251
+    r = make_request()
252
+    assert r.status_code == 200
253
+    tar = tarfile.open(fileobj=io.BytesIO(r.content), mode='r:gz')
254
+    assert len(tar.getmembers()) == 1
255
+
256
+
257
+def test_https(run_servefile, datadir):
258
+    data = "NOOT NOOT"
259
+    p = datadir({'testfile': data}) / 'testfile'
260
+    run_servefile(['--ssl', str(p)])
261
+    time.sleep(0.2)  # time for generating ssl certificates
262
+
263
+    # fingerprint = None
264
+    # while not fingerprint:
265
+    #     line = s.stdout.readline()
266
+    #     print(line)
267
+    #     # if we find this line we went too far...
268
+    #     assert not line.startswith("Some addresses this file will be available at")
269
+
270
+    #     if line.startswith("SHA1 fingerprint"):
271
+    #         fingerprint = line.replace("SHA1 fingerprint: ", "").strip()
272
+    #         break
273
+
274
+    # assert fingerprint
275
+    urllib3.disable_warnings()
276
+    check_download(data, protocol='https', verify=False)

+ 8
- 0
tox.ini View File

@@ -0,0 +1,8 @@
1
+[tox]
2
+envlist = py27,py36
3
+
4
+[testenv]
5
+deps =
6
+        pytest
7
+        requests
8
+commands = pytest --tb=short

Loading…
Cancel
Save