source: test/test_session.py

Last change on this file was 2042:ddaa0b9bbaee, checked in by Stefan Schwarzer <sschwarzer@…>, 6 months ago
Allow `encoding` argument only for `FTP` subclasses If an explicit encoding, i. e. a `str` is passed to `session_factory`, the `base_class` argument must be `ftplib.FTP` or a subclass of it. The reason is that we already use a heuristic for Python 3.8 and earlier vs. Python 3.9 and earlier. This heuristic may work or fail - possibly silently - for classes that aren't a subclass of `ftplib.FTP` (including `ftplib.FTP` itself). With this check in place, the heuristic may look safe, but it actually isn't if, for example, a class inherits from `ftplib.FTP` in Python 3.9, but overrides the constructor so that it doesn't support the `encoding` argument anymore. ticket: 143
File size: 5.3 KB
Line 
1# Copyright (C) 2014-2021, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5"""
6Unit tests for session factory helpers.
7"""
8
9import ftplib
10import sys
11
12import ftputil.path_encoding
13import ftputil.session
14import ftputil.tool
15
16
17# Inherit from `ftplib.FTP` to get past the subclass check in
18# `ftputil.session.session_factory`.
19class MockSession(ftplib.FTP):
20    """
21    Mock session base class to determine if all expected calls have happened.
22    """
23
24    encoding = ftputil.path_encoding.FTPLIB_DEFAULT_ENCODING
25
26    def __init__(self, encoding=None):
27        self.calls = []
28        if encoding is not None:
29            self.encoding = encoding
30
31    def add_call(self, *args):
32        self.calls.append(args)
33
34    def connect(self, host, port):
35        self.add_call("connect", host, port)
36
37    def set_debuglevel(self, value):
38        self.add_call("set_debuglevel", value)
39
40    def login(self, user, password):
41        self.add_call("login", user, password)
42
43    def set_pasv(self, flag):
44        self.add_call("set_pasv", flag)
45
46
47class EncryptedMockSession(MockSession):
48    def auth_tls(self):
49        self.add_call("auth_tls")
50
51    def prot_p(self):
52        self.add_call("prot_p")
53
54
55class TestSessionFactory:
56    """
57    Test if session factories created by `ftputil.session.session_factory`
58    trigger the expected calls.
59    """
60
61    def test_defaults(self):
62        """
63        Test defaults (apart from base class).
64        """
65        factory = ftputil.session.session_factory(base_class=MockSession)
66        session = factory("host", "user", "password")
67        assert session.calls == [("connect", "host", 21), ("login", "user", "password")]
68
69    def test_different_port(self):
70        """
71        Test setting the command channel port with `port`.
72        """
73        factory = ftputil.session.session_factory(base_class=MockSession, port=2121)
74        session = factory("host", "user", "password")
75        assert session.calls == [
76            ("connect", "host", 2121),
77            ("login", "user", "password"),
78        ]
79
80    def test_use_passive_mode(self):
81        """
82        Test explicitly setting passive/active mode with `use_passive_mode`.
83        """
84        # Passive mode
85        factory = ftputil.session.session_factory(
86            base_class=MockSession, use_passive_mode=True
87        )
88        session = factory("host", "user", "password")
89        assert session.calls == [
90            ("connect", "host", 21),
91            ("login", "user", "password"),
92            ("set_pasv", True),
93        ]
94        # Active mode
95        factory = ftputil.session.session_factory(
96            base_class=MockSession, use_passive_mode=False
97        )
98        session = factory("host", "user", "password")
99        assert session.calls == [
100            ("connect", "host", 21),
101            ("login", "user", "password"),
102            ("set_pasv", False),
103        ]
104
105    def test_encrypt_data_channel(self):
106        """
107        Test request to call `prot_p` with `encrypt_data_channel`.
108        """
109        # With encrypted data channel (default for encrypted session).
110        factory = ftputil.session.session_factory(base_class=EncryptedMockSession)
111        session = factory("host", "user", "password")
112        assert session.calls == [
113            ("connect", "host", 21),
114            ("login", "user", "password"),
115            ("prot_p",),
116        ]
117        #
118        factory = ftputil.session.session_factory(
119            base_class=EncryptedMockSession, encrypt_data_channel=True
120        )
121        session = factory("host", "user", "password")
122        assert session.calls == [
123            ("connect", "host", 21),
124            ("login", "user", "password"),
125            ("prot_p",),
126        ]
127        # Without encrypted data channel.
128        factory = ftputil.session.session_factory(
129            base_class=EncryptedMockSession, encrypt_data_channel=False
130        )
131        session = factory("host", "user", "password")
132        assert session.calls == [("connect", "host", 21), ("login", "user", "password")]
133
134    def test_encoding(self):
135        """
136        Test setting the default encoding and a custom encoding.
137        """
138        # Default encoding
139        factory = ftputil.session.session_factory(
140            base_class=MockSession,
141        )
142        session = factory("host", "user", "password")
143        assert session.calls == [
144            ("connect", "host", 21),
145            ("login", "user", "password"),
146        ]
147        assert session.encoding == ftputil.path_encoding.FTPLIB_DEFAULT_ENCODING
148        # Custom encoding
149        factory = ftputil.session.session_factory(
150            base_class=MockSession,
151            encoding="UTF-8",
152        )
153        session = factory("host", "user", "password")
154        assert session.calls == [
155            ("connect", "host", 21),
156            ("login", "user", "password"),
157        ]
158        assert session.encoding == "UTF-8"
159
160    def test_debug_level(self):
161        """
162        Test setting the debug level on the session.
163        """
164        factory = ftputil.session.session_factory(base_class=MockSession, debug_level=1)
165        session = factory("host", "user", "password")
166        assert session.calls == [
167            ("connect", "host", 21),
168            ("set_debuglevel", 1),
169            ("login", "user", "password"),
170        ]
Note: See TracBrowser for help on using the repository browser.