source: TI05-delivery/trunk/test/test_embedded.py @ 1451

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/test/test_embedded.py@1451
Revision 1451, 10.3 KB checked in by spascoe, 13 years ago (diff)

Some test cases currently don't work with the new logging system.
This changeset fixes as many as possible.

I think the problem is that it's difficult to make sure all logged
messages are flushed before a fork(). Will fix soon :-).

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2"""
3Tests for the bbftp daemon when embedded in Python.
4
5@author: Stephen Pascoe
6@version: $Id$
7"""
8
9import unittest
10import sys, os, signal, time, stat
11from glob import glob
12import re, tempfile, getopt, logging
13
14HOME = os.path.abspath(os.getenv('NDG_DELIVERY_HOME', os.curdir))
15BBFTP = glob('%s/src/bbftp-client*/bbftpc/bbftp' % HOME)[0]
16DATADIR = '%s/test/data' % HOME
17VERSION = open('%s/VERSION' % HOME).read()
18NDG_MESSAGE_LEN = 256
19
20NDG_HANDSHAKE = "NDG-Delivery-server %s" % VERSION
21
22
23import ndg.delivery.server.pybbftp as server
24import traceback
25
26def flushLogging():
27    #!WARNING: uses internal logging attributes
28    for h in logging._handlerList[:]:
29        try:
30            h.flush()
31        except:
32            pass
33def resetLogging():
34    logging.root.handlers = []
35    logging.basicConfig(level=logging.DEBUG, filename='./bbftpd.log')
36
37resetLogging()
38server.bbftpd.after_fork_hook = resetLogging
39server.bbftpd.before_fork_hook = flushLogging
40
41
42
43class TestAuthHandler(server.AuthHandler):
44   
45    def authenticate(self):
46
47        msg = self.recv()
48        self.log(server.LOG_DEBUG, 'AuthContext received Auth message: %s' % msg)
49               
50        self.send(NDG_HANDSHAKE)
51
52        privatestr = self.recv()
53        self.log(server.LOG_DEBUG, "AuthContext received privatestr: %s" % privatestr)
54       
55        return self.makeAuthzHandler(msg, "TestCaseUser")
56
57    def makeAuthzHandler(self, msg, user):
58        return TestAuthzHandler(msg, "TestCaseUser")
59
60
61class TestAuthzHandler(server.LiberalAuthzHandler):
62    def __init__(self, version, username):
63        super(TestAuthzHandler, self).__init__(username)
64        self.version = version
65
66
67
68
69class TestFailAuthHandler(server.AuthHandler):
70    def authorise(self):
71        raise server.AuthenticationFailure, "TestFailAuthHandler"
72
73
74class TestPermAuthHandler(TestAuthHandler):
75    def makeAuthzHandler(self, msg, user):
76        return TestPermAuthzHandler(msg, "TestCaseUser")
77
78class TestPermAuthzHandler(TestAuthzHandler):
79    """Check's the path's other read permission and authorises accordingly.
80    """
81
82    def authzControl(self, msgcode, transferopt, path):
83        self.log(server.LOG_DEBUG, 'TestPermAuthzHandler.authzControl: msgcode = %s' % hex(msgcode))
84        return self.authzPath(path)
85
86    def authzRetr(self, path):
87        return self.authzPath(path)
88
89    def authzStore(self, path):
90        return self.authzPath(path)
91
92    def authzPath(self, path):
93        mode = stat.S_IMODE(os.stat(path)[stat.ST_MODE])
94        if (mode & 0004):
95            self.log(server.LOG_DEBUG, 'TestPermAuthzHandler OK')
96            return True
97        else:
98            self.log(server.LOG_DEBUG, 'TestPermAuthzHandler FAIL')
99            raise server.AuthorisationFailure, "TestPermAuthzHandler: no read perms"
100
101
102class BaseFixture(unittest.TestCase):
103
104    authContext = TestAuthHandler()
105
106    def setUp(self):
107        self._clearLog()
108        self._startServer()
109
110    def tearDown(self):
111        self._stopServer()
112
113    #----------------------------------------------------------------------------------
114
115
116    def _startServer(self):
117        # Start the server and store it's PID
118        self.pid = server.start(self.authContext, ['-l', 'DEBUG'])
119
120    def _stopServer(self):
121        # Stop the server process
122        os.kill(self.pid, signal.SIGTERM)
123        os.waitpid(self.pid, 0)
124       
125
126
127    def _runClient(self, cmd, debug=False, user="testcase", privatestr=None):
128        """Run the client.
129        """
130
131        if debug:
132            f = "-d"
133        else:
134            f = "-m"
135
136        if privatestr == None:
137            p = ""
138        else:
139            p = "-P %s" % repr(privatestr)
140
141        # This is ugly but I need to redirect stderr to stdout
142        fh = os.popen('sh -c "%s %s %s -u %s -r 1 -e \'%s\' localhost" 2>&1 ' % (BBFTP, f, p, user, cmd))
143
144        output = fh.read()
145        print >>open('./bbftpc.log', 'w'), output
146
147        return output
148
149
150    def _clearLog(self, logfile="./bbftpd.log"):
151        if os.path.exists(logfile):
152            os.remove(logfile)
153
154    def _readLog(self, logfile="./bbftpd.log"):
155        return open(logfile).read()
156   
157
158
159    def _findLines(self, lines, string):
160        """Look for lines matching each regular expression in lines.
161       
162        @return: True if all lines are found, else False
163        """
164       
165        for line in lines:
166            if not re.search('^%s$' % line, string, re.M):
167                return False
168           
169        return True
170
171
172    def assertLines(self, lines, string):
173        """Assert that there is a line in string matching each regular expression in lines.
174        """
175
176        for line in lines:
177            self.assert_(re.search('^%s$' % line, string, re.M))
178
179
180
181class AuthOK(BaseFixture):
182    """Test the bbftpd module.
183    """
184   
185    def testStartup(self):
186        lines = self._readLog()
187        self.assertLines(['.*Starting bbftpd'], lines)
188
189    def testDir(self):
190        """Try connecting the client and listing a directory.
191        """
192
193        output = self._runClient("dir %s" % DATADIR)
194
195        self.assertLines([r'dir .*/data', r' d .*/\.', r' d .*/\.\.',
196                        r' f .*/foo', r' f .*/bar', r' f .*/baz'], output)
197
198        lines = self._readLog()
199        self.assertLines(['.*Getting new bbftp connexion.*',
200                          r'.*Authz: MSG_LIST_V2 .*/test/data/\*.*',
201                          r'.*User TestCaseUser disconnected.*'], lines)
202
203
204    def testHandshake(self):
205        """Verify handshake messages are exchanged.
206        """
207
208        output = self._runClient("dir .", debug=True)
209
210        print >>open('./bbftpc.log', 'w'), output
211
212        self.assertLines(['Received Auth handshake: NDG-Delivery-server %s' % VERSION], output)
213
214        lines = self._readLog()
215
216        self.assertLines(['.*AuthContext received Auth message: NDG-Delivery-client %s' % VERSION], lines)
217
218    def testPrivateStr(self):
219        """Verify the private string is sent to server.
220        """
221
222        output = self._runClient("dir .", privatestr="testPrivateStr")
223
224        lines = self._readLog()
225        self.assertLines(['.*AuthContext received privatestr: testPrivateStr'], lines)
226
227    def testRetr(self):
228        """Try retrieving a file.
229        """
230
231        tmp = tempfile.mktemp('test_bbftpd')
232        output = self._runClient("get %s/foo %s" % (DATADIR, tmp))
233
234        # Check the client output
235        self.assertLines(['get.*nogzip'], output)
236
237        # Check retrieved file
238        self.assert_(os.system('diff --brief %s/foo %s' % (DATADIR, tmp)) == 0)
239        os.remove(tmp)
240
241        # Check log
242        lines = self._readLog()
243        self.assertLines(['.*Authz: RETR .*/foo', '.*GET TestCaseUser .*/foo.*'], lines)
244
245    def testStore(self):
246        """Try storing a file.
247        """
248
249
250        src = '%s/bar' % (DATADIR)
251        dest = '%s/new_bar' % (DATADIR)
252        os.system('cp %s %s' % (src, dest))
253        output = self._runClient("put %s %s" % (src, dest))
254
255        # Check the client output
256        self.assertLines(['put .* nogzip'], output)
257
258
259        # Check sent file
260        self.assert_(os.system('diff --brief %s %s' % (dest, src)) == 0)
261        os.remove(dest)
262
263        # Check log
264        lines = self._readLog()
265        self.assertLines(['.*Authz: STORE .*/new_bar', '.*PUT TestCaseUser .*/new_bar.*'], lines)
266
267
268
269class AuthFail(BaseFixture):
270    authContext = TestFailAuthHandler()
271
272    def test(self):
273        """Fail authorisation
274        """
275
276        output = self._runClient("dir .", debug=True)
277
278        lines = self._readLog()
279
280        self.assertLines(['.*Error while private authentication.*'], output)
281        self.assertLines(['.*bbftpd_private_auth failed.*'], lines)
282
283
284class Authz(BaseFixture):
285    authContext = TestPermAuthHandler()
286
287    def setUp(self):
288        super(Authz, self).setUp()
289
290        # Set modes on test files
291        os.chmod('%s/bar' % (DATADIR), 0640)
292        os.chmod('%s/foo' % (DATADIR), 0655)
293
294    def testOK(self):
295        """Test dir of a readable file.
296        """
297
298        output = self._runClient("stat %s/foo" % (DATADIR), debug=True)
299
300        lines = self._readLog()
301
302
303        self.assertLines(['Connection and authentication correct', 'stat /.*/foo OK'], output)
304        self.assertLines(['.*Authz: MSG_STAT .*/foo', '.*TestPermAuthzHandler OK',
305                          '.*TestPermAuthzHandler.*msgcode = %s.*' % hex(server.MSG.STAT)], lines)
306
307    def testFail(self):
308        """Test dir of a non-readable file.
309        """
310
311        output = self._runClient("stat %s/bar" % (DATADIR), debug=True)
312
313        lines = self._readLog()
314
315        self.assertLines(['.*ndg_authz_control: AuthorisationFailure.* no read perms',
316                          'stat /.*/bar FAILED'], output)
317        self.assertLines(['.*Authz: MSG_STAT .*/bar', '.*TestPermAuthzHandler FAIL',
318                          '.*ndg_authz_control: AuthorisationFailure.* no read perms'], lines)
319
320
321class PythonClientAuthOK(AuthOK):
322    """Repeat AuthOK tests with a python-embedded client.
323    """
324
325    def testMultiCommand(self):
326        """Execute more than one command per connection.
327        """
328        output = self._runClient(["dir %s" % DATADIR, "stat %s/bar" % DATADIR])
329
330        self.assertLines([r'dir .*/data', r' d .*/\.', r' d .*/\.\.',
331                          r' f .*/foo', r' f .*/bar', r' f .*/baz',
332                          r'stat .*'], output)
333
334        lines = self._readLog()
335        self.assertLines(['.*Getting new bbftp connexion.*',
336                          r'.*Authz: MSG_LIST_V2 .*/test/data/\*.*',
337                          r'.*User TestCaseUser disconnected.*',
338                          r'.*: Receiving MSG_STAT.*'], lines)
339
340
341
342
343    def _runClient(self, cmds, debug=False, user="testcase", privatestr=None):
344        """Run the client.
345        """
346
347        if type(cmds) == type(''):
348            cmd_args = ['-e', repr(cmds)]
349        else:
350            cmd_args = []
351            for cmd in cmds:
352                cmd_args += ['-e', repr(cmd)]
353
354        if debug:
355            f = "-d"
356        else:
357            f = "-m"
358
359        args = cmd_args + [f, '-u', user,
360                '-r', '1']
361
362        if privatestr != None:
363            args += ['-P', privatestr]
364
365        args.append('localhost')
366
367        fh = os.popen('%s/test/runclient.py %s' % (HOME, ' '.join(args)))
368        output = fh.read()
369        print >>open('./bbftpc.log', 'w'), output
370
371        return output
372
373if __name__ == '__main__':
374    unittest.main()
Note: See TracBrowser for help on using the repository browser.