예제 #1
0
class TestEvaluationPlaneHandlerDefault(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = "__TestEvaluationPlaneHandlerDefault_"

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(
            mode="w+t", prefix=prefix, suffix=".conf", delete=False
        )
        cls.config_file.write(
            "[TabPy]"
        )
        cls.config_file.close()

        cls.script = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.config_file.name)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_evaluation_default(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script
        )
        self.assertEqual(200, response.code)
class TestServiceInfoHandlerDefault(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.patcher = patch(
            "tabpy.tabpy_server.app.app.TabPyApp._parse_cli_arguments",
            return_value=Namespace(config=None),
        )
        cls.patcher.start()

    @classmethod
    def tearDownClass(cls):
        cls.patcher.stop()

    def get_app(self):
        self.app = TabPyApp()
        return self.app._create_tornado_web_app()

    def test_given_vanilla_tabpy_server_expect_correct_info_response(self):
        response = self.fetch("/info")
        self.assertEqual(response.code, 200)
        actual_response = json.loads(response.body)
        expected_response = _create_expected_info_response(
            self.app.settings, self.app.tabpy_state
        )

        self.assertDictEqual(actual_response, expected_response)
예제 #3
0
class TestServiceInfoHandlerWithoutAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = "__TestServiceInfoHandlerWithoutAuth_"

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        with open(os.path.join(cls.state_dir, "state.ini"),
                  "w+") as cls.state_file:
            cls.state_file.write("[Service Info]\n"
                                 "Name = TabPy Serve\n"
                                 "Description = \n"
                                 "Creation Time = 0\n"
                                 "Access-Control-Allow-Origin = \n"
                                 "Access-Control-Allow-Headers = \n"
                                 "Access-Control-Allow-Methods = \n"
                                 "\n"
                                 "[Query Objects Service Versions]\n"
                                 "\n"
                                 "[Query Objects Docstrings]\n"
                                 "\n"
                                 "[Meta]\n"
                                 "Revision Number = 1\n")
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(prefix=prefix,
                                                      suffix=".conf",
                                                      delete=False,
                                                      mode="w+")
        cls.config_file.write("[TabPy]\n"
                              f"TABPY_STATE_PATH = {cls.state_dir}")
        cls.config_file.close()

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_tabpy_server_with_no_auth_expect_correct_info_response(self):
        response = self.fetch("/info")
        self.assertEqual(response.code, 200)
        actual_response = json.loads(response.body)
        expected_response = _create_expected_info_response(
            self.app.settings, self.app.tabpy_state)

        self.assertDictEqual(actual_response, expected_response)
        self.assertTrue("versions" in actual_response)
        versions = actual_response["versions"]
        self.assertTrue("v1" in versions)
        v1 = versions["v1"]
        self.assertTrue("features" in v1)
        features = v1["features"]
        self.assertDictEqual({}, features)
예제 #4
0
class TestServiceInfoHandlerWithoutAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = '__TestServiceInfoHandlerWithoutAuth_'

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        with open(os.path.join(cls.state_dir, 'state.ini'), 'w+')\
                as cls.state_file:
            cls.state_file.write('[Service Info]\n'
                                 'Name = TabPy Serve\n'
                                 'Description = \n'
                                 'Creation Time = 0\n'
                                 'Access-Control-Allow-Origin = \n'
                                 'Access-Control-Allow-Headers = \n'
                                 'Access-Control-Allow-Methods = \n'
                                 '\n'
                                 '[Query Objects Service Versions]\n'
                                 '\n'
                                 '[Query Objects Docstrings]\n'
                                 '\n'
                                 '[Meta]\n'
                                 'Revision Number = 1\n')
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(prefix=prefix,
                                                      suffix='.conf',
                                                      delete=False,
                                                      mode='w+')
        cls.config_file.write('[TabPy]\n'
                              f'TABPY_STATE_PATH = {cls.state_dir}')
        cls.config_file.close()

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_tabpy_server_with_no_auth_expect_correct_info_response(self):
        response = self.fetch('/info')
        self.assertEqual(response.code, 200)
        actual_response = json.loads(response.body)
        expected_response = _create_expected_info_response(
            self.app.settings, self.app.tabpy_state)

        self.assertDictEqual(actual_response, expected_response)
        self.assertTrue('versions' in actual_response)
        versions = actual_response['versions']
        self.assertTrue('v1' in versions)
        v1 = versions['v1']
        self.assertTrue('features' in v1)
        features = v1['features']
        self.assertDictEqual({}, features)
예제 #5
0
class TestServiceInfoHandlerDefault(AsyncHTTPTestCase):
    def get_app(self):
        self.app = TabPyApp()
        return self.app._create_tornado_web_app()

    def test_given_vanilla_tabpy_server_expect_correct_info_response(self):
        response = self.fetch("/info")
        self.assertEqual(response.code, 200)
        actual_response = json.loads(response.body)
        expected_response = _create_expected_info_response(
            self.app.settings, self.app.tabpy_state)

        self.assertDictEqual(actual_response, expected_response)
예제 #6
0
class TestEndpointHandlerWithoutAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        _init_asyncio_patch()
        prefix = "__TestEndpointHandlerWithoutAuth_"

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+")
        cls.state_file.write(
            "[Service Info]\n"
            "Name = TabPy Serve\n"
            "Description = \n"
            "Creation Time = 0\n"
            "Access-Control-Allow-Origin = \n"
            "Access-Control-Allow-Headers = \n"
            "Access-Control-Allow-Methods = \n"
            "\n"
            "[Query Objects Service Versions]\n"
            "\n"
            "[Query Objects Docstrings]\n"
            "\n"
            "[Meta]\n"
            "Revision Number = 1\n"
        )
        cls.state_file.close()

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.state_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(None)
        return self.app._create_tornado_web_app()

    def test_creds_no_auth_fails(self):
        response = self.fetch(
            "/endpoints/",
            method="GET",
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(400, response.code)
class BaseTestServiceInfoHandler(AsyncHTTPTestCase):
    def get_app(self):
        if hasattr(self, 'config_file') and hasattr(self.config_file, 'name'):
            self.app = TabPyApp(self.config_file.name)
        else:
            self.app = TabPyApp()
        return self.app._create_tornado_web_app()

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    @classmethod
    def setUpClass(cls):
        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=cls.prefix)
        with open(os.path.join(cls.state_dir, "state.ini"),
                  "w+") as cls.state_file:
            cls.state_file.write("[Service Info]\n"
                                 "Name = TabPy Serve\n"
                                 "Description = \n"
                                 "Creation Time = 0\n"
                                 "Access-Control-Allow-Origin = \n"
                                 "Access-Control-Allow-Headers = \n"
                                 "Access-Control-Allow-Methods = \n"
                                 "\n"
                                 "[Query Objects Service Versions]\n"
                                 "\n"
                                 "[Query Objects Docstrings]\n"
                                 "\n"
                                 "[Meta]\n"
                                 "Revision Number = 1\n")
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(prefix=cls.prefix,
                                                      suffix=".conf",
                                                      delete=False,
                                                      mode='w')
        cls.config_file.write("[TabPy]\n")
        if hasattr(cls, 'tabpy_config'):
            for k in cls.tabpy_config:
                cls.config_file.write(k)
        cls.config_file.close()
예제 #8
0
class TestEndpointsHandlerWithAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = "__TestEndpointsHandlerWithAuth_"
        # create password file
        cls.pwd_file = tempfile.NamedTemporaryFile(mode="w+t",
                                                   prefix=prefix,
                                                   suffix=".txt",
                                                   delete=False)
        username = "******"
        password = "******"
        cls.pwd_file.write(f"{username} {hash_password(username, password)}")
        cls.pwd_file.close()

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+")
        cls.state_file.write("[Service Info]\n"
                             "Name = TabPy Serve\n"
                             "Description = \n"
                             "Creation Time = 0\n"
                             "Access-Control-Allow-Origin = \n"
                             "Access-Control-Allow-Headers = \n"
                             "Access-Control-Allow-Methods = \n"
                             "\n"
                             "[Query Objects Service Versions]\n"
                             "\n"
                             "[Query Objects Docstrings]\n"
                             "\n"
                             "[Meta]\n"
                             "Revision Number = 1\n")
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(mode="w+t",
                                                      prefix=prefix,
                                                      suffix=".conf",
                                                      delete=False)
        cls.config_file.write("[TabPy]\n"
                              f"TABPY_PWD_FILE = {cls.pwd_file.name}\n"
                              f"TABPY_STATE_PATH = {cls.state_dir}")
        cls.config_file.close()

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.pwd_file.name)
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_no_creds_required_auth_fails(self):
        response = self.fetch("/endpoints")
        self.assertEqual(401, response.code)

    def test_invalid_creds_fails(self):
        response = self.fetch(
            "/endpoints",
            method="GET",
            headers={
                "Authorization":
                "Basic {}".format(
                    base64.b64encode(
                        "user:wrong_password".encode("utf-8")).decode("utf-8"))
            },
        )
        self.assertEqual(401, response.code)

    def test_valid_creds_pass(self):
        response = self.fetch(
            "/endpoints",
            method="GET",
            headers={
                "Authorization":
                "Basic {}".format(
                    base64.b64encode(
                        "username:password".encode("utf-8")).decode("utf-8"))
            },
        )
        self.assertEqual(200, response.code)
예제 #9
0
class TestEvaluationPlainHandlerWithAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.patcher = patch(
            "tabpy.tabpy_server.app.app.TabPyApp._parse_cli_arguments",
            return_value=Namespace(config=None),
        )
        cls.patcher.start()

        prefix = "__TestEvaluationPlainHandlerWithAuth_"
        # create password file
        cls.pwd_file = tempfile.NamedTemporaryFile(
            mode="w+t", prefix=prefix, suffix=".txt", delete=False
        )
        username = "******"
        password = "******"
        cls.pwd_file.write(f"{username} {hash_password(username, password)}\n")
        cls.pwd_file.close()

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+")
        cls.state_file.write(
            "[Service Info]\n"
            "Name = TabPy Serve\n"
            "Description = \n"
            "Creation Time = 0\n"
            "Access-Control-Allow-Origin = \n"
            "Access-Control-Allow-Headers = \n"
            "Access-Control-Allow-Methods = \n"
            "\n"
            "[Query Objects Service Versions]\n"
            "\n"
            "[Query Objects Docstrings]\n"
            "\n"
            "[Meta]\n"
            "Revision Number = 1\n"
        )
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(
            mode="w+t", prefix=prefix, suffix=".conf", delete=False
        )
        cls.config_file.write(
            "[TabPy]\n"
            f"TABPY_PWD_FILE = {cls.pwd_file.name}\n"
            f"TABPY_STATE_PATH = {cls.state_dir}"
        )
        cls.config_file.close()

        cls.script = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.script_not_present = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.args_not_present = (
            '{"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.args_not_sequential = (
            '{"data":{"_arg1":[2,3],"_arg3":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg3[i])\\nreturn res"}'
        )

        cls.nan_coverts_to_null =\
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'\
            '"script":"return [float(1), float(\\"NaN\\"), float(2)]"}'

        cls.script_returns_none = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"return None"}'
        )

    @classmethod
    def tearDownClass(cls):
        cls.patcher.stop()
        os.remove(cls.pwd_file.name)
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_no_creds_required_auth_fails(self):
        response = self.fetch("/evaluate", method="POST", body=self.script)
        self.assertEqual(401, response.code)

    def test_invalid_creds_fails(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("user:wrong_password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(401, response.code)

    def test_valid_creds_pass(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(200, response.code)

    def test_null_request(self):
        response = self.fetch("")
        self.assertEqual(404, response.code)

    def test_script_not_present(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script_not_present,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(400, response.code)

    def test_arguments_not_present(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.args_not_present,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )

            },
        )
        self.assertEqual(500, response.code)

    def test_arguments_not_sequential(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.args_not_sequential,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(400, response.code)

    def test_nan_converts_to_null(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.nan_coverts_to_null,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(200, response.code)
        self.assertEqual(b'[1.0, null, 2.0]', response.body)

    def test_script_returns_none(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.script_returns_none,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(200, response.code)
        self.assertEqual(b'null', response.body)
예제 #10
0
class TestEvaluationPlaneHandlerDisabledWithAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = "__TestEvaluationPlaneHandlerDisabledWithAuth_"

        # create password file
        cls.pwd_file = tempfile.NamedTemporaryFile(
            mode="w+t", prefix=prefix, suffix=".txt", delete=False
        )
        username = "******"
        password = "******"
        cls.pwd_file.write(f"{username} {hash_password(username, password)}\n")
        cls.pwd_file.close()

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+")
        cls.state_file.write(
            "[Service Info]\n"
            "Name = TabPy Serve\n"
            "Description = \n"
            "Creation Time = 0\n"
            "Access-Control-Allow-Origin = \n"
            "Access-Control-Allow-Headers = \n"
            "Access-Control-Allow-Methods = \n"
            "\n"
            "[Query Objects Service Versions]\n"
            "\n"
            "[Query Objects Docstrings]\n"
            "\n"
            "[Meta]\n"
            "Revision Number = 1\n"
        )
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(
            mode="w+t", prefix=prefix, suffix=".conf", delete=False
        )
        cls.config_file.write(
            "[TabPy]\n"
            f"TABPY_PWD_FILE = {cls.pwd_file.name}\n"
            f"TABPY_STATE_PATH = {cls.state_dir}\n"
            f"TABPY_EVALUATE_ENABLE = false"
        )
        cls.config_file.close()

        cls.script = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.pwd_file.name)
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_evaluation_disabled_fails_with_invalid_creds(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("user:wrong_password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(401, response.code)

    def test_evaluation_disabled_fails_with_valid_creds(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(404, response.code)
예제 #11
0
class TestEvaluationPlaneHandlerWithoutAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        prefix = "__TestEvaluationPlaneHandlerWithoutAuth_"

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+")
        cls.state_file.write(
            "[Service Info]\n"
            "Name = TabPy Serve\n"
            "Description = \n"
            "Creation Time = 0\n"
            "Access-Control-Allow-Origin = \n"
            "Access-Control-Allow-Headers = \n"
            "Access-Control-Allow-Methods = \n"
            "\n"
            "[Query Objects Service Versions]\n"
            "\n"
            "[Query Objects Docstrings]\n"
            "\n"
            "[Meta]\n"
            "Revision Number = 1\n"
        )
        cls.state_file.close()

        cls.script = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.script_not_present = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.args_not_present = (
            '{"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'
        )

        cls.args_not_sequential = (
            '{"data":{"_arg1":[2,3],"_arg3":[3,-1]},'
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '
            'res.append(_arg1[i] * _arg3[i])\\nreturn res"}'
        )

        cls.nan_coverts_to_null =\
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'\
            '"script":"return [float(1), float(\\"NaN\\"), float(2)]"}'

        cls.script_returns_none = (
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'
            '"script":"return None"}'
        )

    @classmethod
    def tearDownClass(cls):
        os.remove(cls.state_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(None)
        return self.app._create_tornado_web_app()

    def test_creds_no_auth_fails(self):
        response = self.fetch(
            "/evaluate",
            method="POST",
            body=self.script,
            headers={
                "Authorization": "Basic {}".format(
                    base64.b64encode("username:password".encode("utf-8")).decode(
                        "utf-8"
                    )
                )
            },
        )
        self.assertEqual(406, response.code)
예제 #12
0
class TestEndpointHandlerWithAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.patcher = patch(
            'tabpy.tabpy_server.app.app.TabPyApp._parse_cli_arguments',
            return_value=Namespace(config=None))
        cls.patcher.start()

        prefix = '__TestEndpointHandlerWithAuth_'
        # create password file
        cls.pwd_file = tempfile.NamedTemporaryFile(mode='w+t',
                                                   prefix=prefix,
                                                   suffix='.txt',
                                                   delete=False)
        username = '******'
        password = '******'
        cls.pwd_file.write(f'{username} {hash_password(username, password)}')
        cls.pwd_file.close()

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, 'state.ini'), 'w+')
        cls.state_file.write('[Service Info]\n'
                             'Name = TabPy Serve\n'
                             'Description = \n'
                             'Creation Time = 0\n'
                             'Access-Control-Allow-Origin = \n'
                             'Access-Control-Allow-Headers = \n'
                             'Access-Control-Allow-Methods = \n'
                             '\n'
                             '[Query Objects Service Versions]\n'
                             '\n'
                             '[Query Objects Docstrings]\n'
                             '\n'
                             '[Meta]\n'
                             'Revision Number = 1\n')
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(mode='w+t',
                                                      prefix=prefix,
                                                      suffix='.conf',
                                                      delete=False)
        cls.config_file.write('[TabPy]\n'
                              f'TABPY_PWD_FILE = {cls.pwd_file.name}\n'
                              f'TABPY_STATE_PATH = {cls.state_dir}')
        cls.config_file.close()

    @classmethod
    def tearDownClass(cls):
        cls.patcher.stop()
        os.remove(cls.pwd_file.name)
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_no_creds_required_auth_fails(self):
        response = self.fetch('/endpoints/anything')
        self.assertEqual(401, response.code)

    def test_invalid_creds_fails(self):
        response = self.fetch(
            '/endpoints/anything',
            method='GET',
            headers={
                'Authorization':
                'Basic {}'.format(
                    base64.b64encode(
                        'user:wrong_password'.encode('utf-8')).decode('utf-8'))
            })
        self.assertEqual(401, response.code)

    def test_valid_creds_pass(self):
        response = self.fetch(
            '/endpoints/',
            method='GET',
            headers={
                'Authorization':
                'Basic {}'.format(
                    base64.b64encode(
                        'username:password'.encode('utf-8')).decode('utf-8'))
            })
        self.assertEqual(200, response.code)

    def test_valid_creds_unknown_endpoint_fails(self):
        response = self.fetch(
            '/endpoints/unknown_endpoint',
            method='GET',
            headers={
                'Authorization':
                'Basic {}'.format(
                    base64.b64encode(
                        'username:password'.encode('utf-8')).decode('utf-8'))
            })
        self.assertEqual(404, response.code)
class TestEvaluationPlainHandlerWithAuth(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.patcher = patch(
            'tabpy.tabpy_server.app.app.TabPyApp._parse_cli_arguments',
            return_value=Namespace(
                config=None))
        cls.patcher.start()

        prefix = '__TestEvaluationPlainHandlerWithAuth_'
        # create password file
        cls.pwd_file = tempfile.NamedTemporaryFile(
            mode='w+t', prefix=prefix, suffix='.txt', delete=False)
        username = '******'
        password = '******'
        cls.pwd_file.write(f'{username} {hash_password(username, password)}\n')
        cls.pwd_file.close()

        # create state.ini dir and file
        cls.state_dir = tempfile.mkdtemp(prefix=prefix)
        cls.state_file = open(os.path.join(cls.state_dir, 'state.ini'), 'w+')
        cls.state_file.write('[Service Info]\n'
                             'Name = TabPy Serve\n'
                             'Description = \n'
                             'Creation Time = 0\n'
                             'Access-Control-Allow-Origin = \n'
                             'Access-Control-Allow-Headers = \n'
                             'Access-Control-Allow-Methods = \n'
                             '\n'
                             '[Query Objects Service Versions]\n'
                             '\n'
                             '[Query Objects Docstrings]\n'
                             '\n'
                             '[Meta]\n'
                             'Revision Number = 1\n')
        cls.state_file.close()

        # create config file
        cls.config_file = tempfile.NamedTemporaryFile(
            mode='w+t', prefix=prefix, suffix='.conf', delete=False)
        cls.config_file.write(
            '[TabPy]\n'
            f'TABPY_PWD_FILE = {cls.pwd_file.name}\n'
            f'TABPY_STATE_PATH = {cls.state_dir}')
        cls.config_file.close()

        cls.script =\
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'\
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '\
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'

        cls.script_not_present =\
            '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},'\
            '"":"res=[]\\nfor i in range(len(_arg1)):\\n  '\
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'

        cls.args_not_present =\
            '{"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '\
            'res.append(_arg1[i] * _arg2[i])\\nreturn res"}'

        cls.args_not_sequential =\
            '{"data":{"_arg1":[2,3],"_arg3":[3,-1]},'\
            '"script":"res=[]\\nfor i in range(len(_arg1)):\\n  '\
            'res.append(_arg1[i] * _arg3[i])\\nreturn res"}'

    @classmethod
    def tearDownClass(cls):
        cls.patcher.stop()
        os.remove(cls.pwd_file.name)
        os.remove(cls.state_file.name)
        os.remove(cls.config_file.name)
        os.rmdir(cls.state_dir)

    def get_app(self):
        self.app = TabPyApp(self.config_file.name)
        return self.app._create_tornado_web_app()

    def test_no_creds_required_auth_fails(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.script)
        self.assertEqual(401, response.code)

    def test_invalid_creds_fails(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.script,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('user:wrong_password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(401, response.code)

    def test_valid_creds_pass(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.script,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(200, response.code)

    def test_null_request(self):
        response = self.fetch('')
        self.assertEqual(404, response.code)

    def test_script_not_present(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.script_not_present,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(400, response.code)

    def test_arguments_not_present(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.args_not_present,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(500, response.code)

    def test_arguments_not_sequential(self):
        response = self.fetch(
            '/evaluate',
            method='POST',
            body=self.args_not_sequential,
            headers={
                'Authorization': 'Basic {}'.
                format(
                    base64.b64encode('username:password'.encode('utf-8')).
                    decode('utf-8'))
            })
        self.assertEqual(400, response.code)