Skip to content

Commit ac3295d

Browse files
committed
Implement a test covering operation of the cat functionality file.
Make use of the seam that exists by virtue of the functionality files return output objects which are interpreted by a wrapper to unit test the behaviour of cat. Specifically, we can arrange for a suitable environment plus arguments and then check the right output_objects are created. Allow doing this by exposing a variant of the cat main method which returns them rather than invoking the outer wrapper. Name this _main and call it from the original main. Note that this change is in effect a blueprint that can be used when adding coverage for other functionaity files.
1 parent 8b3c81c commit ac3295d

File tree

5 files changed

+324
-6
lines changed

5 files changed

+324
-6
lines changed

mig/shared/accountstate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
from __future__ import print_function
3333
from __future__ import absolute_import
34+
from past.builtins import basestring
3435

3536
import os
3637
import time

mig/shared/functionality/cat.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ def main(client_id, user_arguments_dict, environ=None):
6161

6262
(configuration, logger, output_objects, op_name) = \
6363
initialize_main_variables(client_id)
64+
65+
return _main(configuration, logger, op_name=op_name, output_objects=output_objects, client_id=client_id, user_arguments_dict=user_arguments_dict)
66+
67+
def _main(configuration, logger, op_name='', output_objects=[], client_id=None, user_arguments_dict=None, environ=None):
68+
if logger is None:
69+
logger = configuration.logger
70+
6471
client_dir = client_id_dir(client_id)
6572
defaults = signature()[1]
6673
status = returnvalues.OK
@@ -71,6 +78,7 @@ def main(client_id, user_arguments_dict, environ=None):
7178
client_id,
7279
configuration,
7380
allow_rejects=False,
81+
environ=environ,
7482
# NOTE: path can use wildcards, dst cannot
7583
typecheck_overrides={'path': valid_path_pattern},
7684
)
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
(dp0
2+
V/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=b'Test User'/emailAddress=dummy-user
3+
p1
4+
(dp2
5+
Vfull_name
6+
p3
7+
Vb'Test User'
8+
p4
9+
sVorganization
10+
p5
11+
VTest Org
12+
p6
13+
sVstate
14+
p7
15+
VNA
16+
p8
17+
sVcountry
18+
p9
19+
VDK
20+
p10
21+
sVemail
22+
p11
23+
Vdummy-user
24+
p12
25+
sVcomment
26+
p13
27+
VThis is the create comment
28+
p14
29+
sVpassword
30+
p15
31+
V
32+
p16
33+
sVpassword_hash
34+
p17
35+
VPBKDF2$sha256$10000$b't0JM/JjkQ347th0Q'$b'QupJt53hA5KhESEeqDhTQTCPOrCBvZ6H'
36+
p18
37+
sVdistinguished_name
38+
p19
39+
V/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=b'Test User'/emailAddress=dummy-user
40+
p20
41+
sVlocality
42+
p21
43+
g16
44+
sVorganizational_unit
45+
p22
46+
g16
47+
sVexpire
48+
p23
49+
I1757925298
50+
sVcreated
51+
p24
52+
F1726233828.2676349
53+
sVunique_id
54+
p25
55+
VktyCKIRg9HvsVzXMQ22EaKS67t9atchv9JKTiJqrtBiGN3qksKrbTTYIH8mitY2K
56+
p26
57+
sVopenid_names
58+
p27
59+
(lp28
60+
sVold_password_hash
61+
p29
62+
VPBKDF2$sha256$10000$b'GL7Qq92iLe/hZXBo'$b'ZwB/5IZqgU7onP+ZqZk9zcHVZOx7jmWz'
63+
p30
64+
sVrenewed
65+
p31
66+
F1726389298.7801197
67+
ssV/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=Test User/emailAddress=dummy-user
68+
p32
69+
(dp33
70+
Vfull_name
71+
p34
72+
VTest User
73+
p35
74+
sVorganization
75+
p36
76+
VTest Org
77+
p37
78+
sVstate
79+
p38
80+
VNA
81+
p39
82+
sVcountry
83+
p40
84+
VDK
85+
p41
86+
sVemail
87+
p42
88+
Vdummy-user
89+
p43
90+
sVcomment
91+
p44
92+
VThis is the create comment
93+
p45
94+
sVpassword
95+
p46
96+
g16
97+
sVpassword_hash
98+
p47
99+
VPBKDF2$sha256$10000$b'kZ8WgLNH+wg3X11d'$b't1d08MV4g215WYW7S7EbkjHqDF+MCjMa'
100+
p48
101+
sVdistinguished_name
102+
p49
103+
V/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=Test User/emailAddress=dummy-user
104+
p50
105+
sVlocality
106+
p51
107+
g16
108+
sVorganizational_unit
109+
p52
110+
g16
111+
sVexpire
112+
p53
113+
I1759243332
114+
sVcreated
115+
p54
116+
F1726602273.7987707
117+
sVunique_id
118+
p55
119+
VKdYHJ21t37jAoHUmBq6t8Xnsnih6JWR5i0QepHoVXfDpQxz9fQGnEmegoDNrPzbe
120+
p56
121+
sVopenid_names
122+
p57
123+
(lp58
124+
sVold_password_hash
125+
p59
126+
VPBKDF2$sha256$10000$b'yObizsUepZvvJ0/r'$b'uKIt7n6Lf/7WXD6pKDGyvT30L2uowBnV'
127+
p60
128+
sVrenewed
129+
p61
130+
F1727707333.0969944
131+
ssV/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=Test User/emailAddress=test@example.com
132+
p62
133+
(dp63
134+
Vfull_name
135+
p64
136+
VTest User
137+
p65
138+
sVorganization
139+
p66
140+
VTest Org
141+
p67
142+
sVstate
143+
p68
144+
VNA
145+
p69
146+
sVcountry
147+
p70
148+
VDK
149+
p71
150+
sVemail
151+
p72
152+
Vtest@example.com
153+
p73
154+
sVcomment
155+
p74
156+
VThis is the create comment
157+
p75
158+
sVpassword
159+
p76
160+
g16
161+
sVpassword_hash
162+
p77
163+
VPBKDF2$sha256$10000$b'/TkhLk4yMGf6XhaY'$b'7HUeQ9iwCkE4YMQAaCd+ZdrN+y8EzkJH'
164+
p78
165+
sVdistinguished_name
166+
p79
167+
g62
168+
sVlocality
169+
p80
170+
g16
171+
sVorganizational_unit
172+
p81
173+
g16
174+
sVexpire
175+
p82
176+
I1758970812
177+
sVcreated
178+
p83
179+
F1727434813.0792377
180+
sVunique_id
181+
p84
182+
VaTza92klrnN2wfylm6HnphCy9C3PReGpQ6jklJ7zF3xjeaUDw36tW95Avx43vtba
183+
p85
184+
sVopenid_names
185+
p86
186+
(lp87
187+
ss.

tests/support/__init__.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,17 @@ def is_path_within(path, start=None, _msg=None):
276276
return not relative.startswith('..')
277277

278278

279-
def fixturefile(relative_path, fixture_format=None):
279+
def _ensuredirs(absolute_dir):
280+
try:
281+
os.makedirs(absolute_dir)
282+
except OSError as oserr:
283+
if oserr.errno != errno.EEXIST:
284+
raise
285+
286+
return absolute_dir
287+
288+
289+
def fixturefile(relative_path, fixture_format=None, include_path=False):
280290
"""Support function for loading fixtures from their serialised format.
281291
282292
Doing so is a little more involved than it may seem because serialisation
@@ -296,12 +306,24 @@ def fixturefile(relative_path, fixture_format=None):
296306
#_, extension = os.path.splitext(os.path.basename(tmp_path))
297307
#assert fixture_format == extension, "fixture file does not match format"
298308

299-
if fixture_format == 'json':
300-
return _fixturefile_json(tmp_path)
309+
data = None
310+
311+
if fixture_format == 'binary':
312+
with open(tmp_path, 'rb') as binfile:
313+
data = binfile.read()
314+
elif fixture_format == 'json':
315+
data = _fixturefile_json(tmp_path)
301316
else:
302317
raise AssertionError(
303318
"unsupported fixture format: %s" % (fixture_format,))
304319

320+
return (data, tmp_path) if include_path else data
321+
322+
323+
def fixturefile_normname(relative_path, prefix=None):
324+
normname, _ = relative_path.split('--')
325+
return os.path.join(prefix, normname) if prefix else normname
326+
305327

306328
_FIXTUREFILE_HINTAPPLIERS = {
307329
'array_of_tuples': lambda value: [tuple(x) for x in value]
@@ -340,12 +362,17 @@ def temppath(relative_path, test_case, ensure_dir=False, skip_clean=False):
340362
"""Get absolute temp path for relative_path"""
341363
assert isinstance(test_case, MigTestCase)
342364
tmp_path = os.path.join(TEST_OUTPUT_DIR, relative_path)
365+
return _temppath(tmp_path, test_case, ensure_dir=ensure_dir, skip_clean=skip_clean)
366+
367+
368+
def _temppath(tmp_path, test_case, ensure_dir=False, skip_clean=False):
343369
if ensure_dir:
344370
try:
345371
os.mkdir(tmp_path)
346-
except FileExistsError:
347-
raise AssertionError(
348-
"ABORT: use of unclean output path: %s" % relative_path)
372+
except OSError as oserr:
373+
if oserr.errno == errno.EEXIST:
374+
raise AssertionError(
375+
"ABORT: use of unclean output path: %s" % tmp_path)
349376
if not skip_clean:
350377
test_case._cleanup_paths.add(tmp_path)
351378
return tmp_path
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# --- BEGIN_HEADER ---
4+
#
5+
# test_mig_shared_functionality_cat - cat functionality unit test
6+
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
7+
#
8+
# This file is part of MiG.
9+
#
10+
# MiG is free software: you can redistribute it and/or modify
11+
# it under the terms of the GNU General Public License as published by
12+
# the Free Software Foundation; either version 2 of the License, or
13+
# (at your option) any later version.
14+
#
15+
# MiG is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU General Public License
21+
# along with this program; if not, write to the Free Software
22+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
23+
# USA.
24+
#
25+
# --- END_HEADER ---
26+
#
27+
28+
"""Unit tests of the MiG functionality file implementing the cat resource."""
29+
30+
from __future__ import print_function
31+
import importlib
32+
import os
33+
import shutil
34+
import sys
35+
36+
from tests.support import MIG_BASE, MigTestCase, testmain, \
37+
fixturefile, fixturefile_normname, \
38+
_ensuredirs, _temppath
39+
40+
from mig.shared.base import client_id_dir
41+
from mig.shared.functionality.cat import _main as main
42+
43+
44+
def create_http_environ(configuration, wsgi_variables={}):
45+
"""Small helper that can create a minimum viable environ dict suitable
46+
for passing to http-facing code for the supplied configuration."""
47+
48+
environ = {}
49+
environ['MIG_CONF'] = configuration.config_file
50+
environ['HTTP_HOST'] = wsgi_variables.get('http_host', 'localhost')
51+
environ['PATH_INFO'] = wsgi_variables.get('path_info', '/')
52+
environ['REMOTE_ADDR'] = wsgi_variables.get('remote_addr', '127.0.0.1')
53+
environ['SCRIPT_URI'] = ''.join(('http://', environ['HTTP_HOST'], environ['PATH_INFO']))
54+
return environ
55+
56+
57+
class MigCgibinCat(MigTestCase):
58+
TEST_CLIENT_ID = '/C=DK/ST=NA/L=NA/O=Test Org/OU=NA/CN=Test User/emailAddress=test@example.com'
59+
60+
def _provide_configuration(self):
61+
return 'testconfig'
62+
63+
def before_each(self):
64+
# ensure a user home directory for our test user
65+
conf_user_home = self.configuration.user_home[:-1]
66+
test_client_dir = client_id_dir(self.TEST_CLIENT_ID)
67+
test_user_dir = os.path.join(conf_user_home, test_client_dir)
68+
69+
# ensure a user db that includes our test user
70+
conf_user_db_home = _ensuredirs(self.configuration.user_db_home)
71+
_temppath(conf_user_db_home, self)
72+
db_fixture, db_fixture_file = fixturefile('MiG-users.db--example', fixture_format='binary', include_path=True)
73+
test_db_file = _temppath(fixturefile_normname('MiG-users.db--example', prefix=conf_user_db_home), self)
74+
shutil.copyfile(db_fixture_file, test_db_file)
75+
76+
# create the test user home directory
77+
self.test_user_dir = _ensuredirs(test_user_dir)
78+
_temppath(self.test_user_dir, self)
79+
self.test_environ = create_http_environ(self.configuration)
80+
81+
def test_returns_file_output_with_single_file_match(self):
82+
with open(os.path.join(self.test_user_dir, 'foobar.txt'), 'w'):
83+
pass
84+
payload = {
85+
'path': ['foobar.txt'],
86+
}
87+
88+
(output_objects, status) = main(self.configuration, self.logger, client_id=self.TEST_CLIENT_ID, user_arguments_dict=payload, environ=self.test_environ)
89+
self.assertEqual(len(output_objects), 1)
90+
output_obj = output_objects[0]
91+
self.assertEqual(output_obj['object_type'], 'file_output')
92+
93+
94+
if __name__ == '__main__':
95+
testmain()

0 commit comments

Comments
 (0)