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

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

Split the distribution into 3 eggs: server, client and common.

These eggs aren't quite built correctly because they all share a build
directory. To fix this I will split setup.py into 3 in the next commit.

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