Test Failed
Push — master ( 94b1f7...ff0954 )
by Oleksandr
12:55 queued 02:06
created

integ_test_base   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 32
eloc 126
dl 0
loc 303
rs 9.84
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A tests.integration.integ_test_base.IntegTestBase._get_certificate_file_name() 0 15 1
B tests.integration.integ_test_base.IntegTestBase._get_config_file_name() 0 45 6
A tests.integration.integ_test_base.IntegTestBase._get_username() 0 2 1
A tests.integration.integ_test_base.IntegTestBase._get_password() 0 2 1
A tests.integration.integ_test_base.IntegTestBase.deploy_models() 0 16 2
A tests.integration.integ_test_base.IntegTestBase._get_transfer_protocol() 0 14 1
A tests.integration.integ_test_base.IntegTestBase.set_delete_temp_folder() 0 12 1
A tests.integration.integ_test_base.IntegTestBase._get_port() 0 11 1
A tests.integration.integ_test_base.IntegTestBase._get_state_file_path() 0 31 1
A tests.integration.integ_test_base.IntegTestBase.tearDown() 0 20 4
A tests.integration.integ_test_base.IntegTestBase._get_process() 0 2 1
A tests.integration.integ_test_base.IntegTestBase._get_pwd_file() 0 15 1
A tests.integration.integ_test_base.IntegTestBase._get_evaluate_timeout() 0 14 1
B tests.integration.integ_test_base.IntegTestBase.setUp() 0 39 5
A tests.integration.integ_test_base.IntegTestBase._get_connection() 0 10 3
A tests.integration.integ_test_base.IntegTestBase.__init__() 0 4 1
A tests.integration.integ_test_base.IntegTestBase._get_key_file_name() 0 15 1
1
import coverage
2
import http.client
3
import os
4
import platform
5
import shutil
6
import signal
7
import subprocess
8
import tabpy
9
import tempfile
10
import time
11
import unittest
12
13
14
class IntegTestBase(unittest.TestCase):
15
    """
16
    Base class for integration tests.
17
    """
18
19
    def __init__(self, methodName="runTest"):
20
        super(IntegTestBase, self).__init__(methodName)
21
        self.process = None
22
        self.delete_temp_folder = True
23
24
    def set_delete_temp_folder(self, delete_temp_folder: bool):
25
        """
26
        Specify if temporary folder for state, config and log
27
        files should be deleted when test is done.
28
        By default the folder is deleted.
29
30
        Parameters
31
        ----------
32
        delete_test_folder: bool
33
            If True temp folder will be deleted.
34
        """
35
        self.delete_temp_folder = delete_temp_folder
36
37
    def _get_state_file_path(self) -> str:
38
        """
39
        Generates state.ini and returns absolute path to it.
40
        Overwrite this function for tests to run against not default state
41
        file.
42
43
        Returns
44
        -------
45
        str
46
            Absolute path to state file folder.
47
        """
48
        state_file = open(os.path.join(self.tmp_dir, "state.ini"), "w+")
49
        state_file.write(
50
            "[Service Info]\n"
51
            "Name = TabPy Serve\n"
52
            "Description = \n"
53
            "Creation Time = 0\n"
54
            "Access-Control-Allow-Origin = \n"
55
            "Access-Control-Allow-Headers = \n"
56
            "Access-Control-Allow-Methods = \n"
57
            "\n"
58
            "[Query Objects Service Versions]\n"
59
            "\n"
60
            "[Query Objects Docstrings]\n"
61
            "\n"
62
            "[Meta]\n"
63
            "Revision Number = 1\n"
64
        )
65
        state_file.close()
66
67
        return self.tmp_dir
68
69
    def _get_port(self) -> str:
70
        """
71
        Returns port TabPy should run on. Default implementation
72
        returns '9004'.
73
74
        Returns
75
        -------
76
        str
77
            Port number.
78
        """
79
        return "9004"
80
81
    def _get_pwd_file(self) -> str:
82
        """
83
        Returns absolute or relative path to password file.
84
        Overwrite to create and/or specify your own file.
85
        Default implementation returns None which means
86
        TABPY_PWD_FILE setting won't be added to config.
87
88
        Returns
89
        -------
90
        str
91
            Absolute or relative path to password file.
92
            If None TABPY_PWD_FILE setting won't be added to
93
            config.
94
        """
95
        return None
96
97
    def _get_transfer_protocol(self) -> str:
98
        """
99
        Returns transfer protocol for configuration file.
100
        Default implementation returns None which means
101
        TABPY_TRANSFER_PROTOCOL setting won't be added to config.
102
103
        Returns
104
        -------
105
        str
106
            Transfer protocol (e.g 'http' or 'https').
107
            If None TABPY_TRANSFER_PROTOCOL setting won't be
108
            added to config.
109
        """
110
        return None
111
112
    def _get_certificate_file_name(self) -> str:
113
        """
114
        Returns absolute or relative certificate file name
115
        for configuration file.
116
        Default implementation returns None which means
117
        TABPY_CERTIFICATE_FILE setting won't be added to config.
118
119
        Returns
120
        -------
121
        str
122
            Absolute or relative certificate file name.
123
            If None TABPY_CERTIFICATE_FILE setting won't be
124
            added to config.
125
        """
126
        return None
127
128
    def _get_key_file_name(self) -> str:
129
        """
130
        Returns absolute or relative private key file name
131
        for configuration file.
132
        Default implementation returns None which means
133
        TABPY_KEY_FILE setting won't be added to config.
134
135
        Returns
136
        -------
137
        str
138
            Absolute or relative private key file name.
139
            If None TABPY_KEY_FILE setting won't be
140
            added to config.
141
        """
142
        return None
143
144
    def _get_evaluate_timeout(self) -> str:
145
        """
146
        Returns the configured timeout for the /evaluate method.
147
        Default implementation returns None, which means that
148
        the timeout will default to 30.
149
150
        Returns
151
        -------
152
        str
153
            Timeout for calling /evaluate.
154
            If None, defaults TABPY_EVALUATE_TIMEOUT setting
155
            will default to '30'.
156
        """
157
        return None
158
159
    def _get_config_file_name(self) -> str:
160
        """
161
        Generates config file. Overwrite this function for tests to
162
        run against not default state file.
163
164
        Returns
165
        -------
166
        str
167
            Absolute path to config file.
168
        """
169
        config_file = open(os.path.join(self.tmp_dir, "test.conf"), "w+")
170
        config_file.write(
171
            "[TabPy]\n"
172
            f"TABPY_QUERY_OBJECT_PATH = ./query_objects\n"
173
            f"TABPY_PORT = {self._get_port()}\n"
174
            f"TABPY_STATE_PATH = {self.tmp_dir}\n"
175
        )
176
177
        pwd_file = self._get_pwd_file()
178
        if pwd_file is not None:
179
            pwd_file = os.path.abspath(pwd_file)
180
            config_file.write(f"TABPY_PWD_FILE = {pwd_file}\n")
181
182
        transfer_protocol = self._get_transfer_protocol()
183
        if transfer_protocol is not None:
184
            config_file.write(f"TABPY_TRANSFER_PROTOCOL = {transfer_protocol}\n")
185
186
        cert_file_name = self._get_certificate_file_name()
187
        if cert_file_name is not None:
188
            cert_file_name = os.path.abspath(cert_file_name)
189
            config_file.write(f"TABPY_CERTIFICATE_FILE = {cert_file_name}\n")
190
191
        key_file_name = self._get_key_file_name()
192
        if key_file_name is not None:
193
            key_file_name = os.path.abspath(key_file_name)
194
            config_file.write(f"TABPY_KEY_FILE = {key_file_name}\n")
195
196
        evaluate_timeout = self._get_evaluate_timeout()
197
        if evaluate_timeout is not None:
198
            config_file.write(f"TABPY_EVALUATE_TIMEOUT = {evaluate_timeout}\n")
199
200
        config_file.close()
201
202
        self.delete_config_file = True
203
        return config_file.name
204
205
    def setUp(self):
206
        super(IntegTestBase, self).setUp()
207
        prefix = "TabPy_IntegTest_"
208
        self.tmp_dir = tempfile.mkdtemp(prefix=prefix)
209
210
        # create temporary state.ini
211
        orig_state_file_name = os.path.abspath(
212
            self._get_state_file_path() + "/state.ini"
213
        )
214
        self.state_file_name = os.path.abspath(self.tmp_dir + "/state.ini")
215
        if orig_state_file_name != self.state_file_name:
216
            shutil.copyfile(orig_state_file_name, self.state_file_name)
217
218
        # create config file
219
        orig_config_file_name = os.path.abspath(self._get_config_file_name())
220
        self.config_file_name = os.path.abspath(
221
            self.tmp_dir + "/" + os.path.basename(orig_config_file_name)
222
        )
223
        if orig_config_file_name != self.config_file_name:
224
            shutil.copyfile(orig_config_file_name, self.config_file_name)
225
226
        # Platform specific - for integration tests we want to engage
227
        # startup script
228
        with open(self.tmp_dir + "/output.txt", "w") as outfile:
229
            cmd = ["tabpy", "--config=" + self.config_file_name]
230
            preexec_fn = None
231
            if platform.system() == "Windows":
232
                self.py = "python"
233
            else:
234
                self.py = "python3"
235
                preexec_fn = os.setsid
236
237
            coverage.process_startup()
238
            self.process = subprocess.Popen(
239
                cmd, preexec_fn=preexec_fn, stdout=outfile, stderr=outfile
240
            )
241
242
            # give the app some time to start up...
243
            time.sleep(5)
244
245
    def tearDown(self):
246
        # stop TabPy
247
        if self.process is not None:
248
            if platform.system() == "Windows":
249
                subprocess.call(["taskkill", "/F", "/T", "/PID", str(self.process.pid)])
250
            else:
251
                os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
252
            self.process.kill()
253
254
            # after shutting down TabPy and before we start it again
255
            # for next test give it some time to terminate.
256
            time.sleep(5)
257
258
        # remove temporary files
259
        if self.delete_temp_folder:
260
            os.remove(self.state_file_name)
261
            os.remove(self.config_file_name)
262
            shutil.rmtree(self.tmp_dir)
263
264
        super(IntegTestBase, self).tearDown()
265
266
    def _get_connection(self) -> http.client.HTTPConnection:
267
        protocol = self._get_transfer_protocol()
268
        url = "localhost:" + self._get_port()
269
270
        if protocol is not None and protocol.lower() == "https":
271
            connection = http.client.HTTPSConnection(url)
272
        else:
273
            connection = http.client.HTTPConnection(url)
274
275
        return connection
276
277
    def _get_username(self) -> str:
278
        return "user1"
279
280
    def _get_password(self) -> str:
281
        return "P@ssw0rd"
282
283
    def deploy_models(self, username: str, password: str):
284
        repo_dir = os.path.abspath(os.path.dirname(tabpy.__file__))
285
        path = os.path.join(repo_dir, "models", "deploy_models.py")
286
        with open(self.tmp_dir + "/deploy_models_output.txt", "w") as outfile:
287
            outfile.write(
288
                f"--<< Running {self.py} {path} "
289
                f"{self._get_config_file_name()} >>--\n"
290
            )
291
            input_string = f"{username}\n{password}\n"
292
            outfile.write(f"--<< Input = {input_string} >>--")
293
            coverage.process_startup()
294
            subprocess.run(
295
                [self.py, path, self._get_config_file_name()],
296
                input=input_string.encode("utf-8"),
297
                stdout=outfile,
298
                stderr=outfile,
299
            )
300
301
    def _get_process(self):
302
        return self.process
303