blob: f2d254fb965906af922e39f26447ad4e10c64f9d [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import json
import threading
import unittest
from http.server import BaseHTTPRequestHandler, HTTPServer
from unittest import mock
import requests
from pypaimon import PaimonVirtualFileSystem, build_info
from pypaimon.api.rest_api import RESTApi
from pypaimon.api.token_loader import HTTPClient
from pypaimon.common import user_agent
from pypaimon.common.options import Options
IDENTITY = "pypaimon/2.2.dev0"
REST_USER_AGENT = "{}(python-requests/{})".format(IDENTITY, requests.__version__)
class UserAgentIdentityTest(unittest.TestCase):
def setUp(self):
user_agent.identity.cache_clear()
self.addCleanup(user_agent.identity.cache_clear)
def test_identity_uses_build_version(self):
with mock.patch.object(build_info, "version", return_value="2.2.dev"):
self.assertEqual("pypaimon/2.2.dev", user_agent.identity())
def test_identity_without_build_version(self):
with mock.patch.object(build_info, "version", return_value=None):
self.assertEqual("pypaimon", user_agent.identity())
def test_build_version_from_full_version(self):
for full_version, expected in (("python-2.2.dev-abc123", "2.2.dev"),
("python-2.2.0-UNKNOWN", "2.2.0"),
("UNKNOWN", None)):
with mock.patch.object(build_info, "_FULL_VERSION", full_version):
self.assertEqual(expected, build_info.version())
class UserAgentFormatTest(unittest.TestCase):
def test_format(self):
self.assertEqual("m/1(t/2)", user_agent.format_user_agent("m/1", "t/2"))
self.assertEqual("m/1(t/2;a;b) ext k/v",
user_agent.format_user_agent("m/1", "t/2", ["a", "", "b"], "ext k/v"))
def test_rest_user_agent(self):
with mock.patch.object(user_agent, "identity", return_value=IDENTITY):
self.assertEqual(REST_USER_AGENT, user_agent.rest_user_agent())
self.assertEqual(REST_USER_AGENT[:-1] + ";Flink) vvr", user_agent.rest_user_agent(
Options({"user-agent.features": " Flink ", "user-agent.extended": "vvr"})))
self.assertEqual("MyApp/1.0(python-requests/{})".format(requests.__version__),
user_agent.rest_user_agent(Options({"user-agent.module": "MyApp/1.0"})))
def test_with_feature_goes_first_once(self):
options = Options({"user-agent.features": "Flink PythonPVFS"})
user_agent.with_feature(options, "PythonPVFS")
user_agent.with_feature(options, "PythonPVFS")
self.assertEqual("PythonPVFS Flink", options.to_map()["user-agent.features"])
class RestUserAgentTest(unittest.TestCase):
def setUp(self):
self.requests = []
recorded = self.requests
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
recorded.append(dict(self.headers))
body = ({"defaults": {}} if "/config" in self.path
else {"databases": [], "nextPageToken": None})
data = json.dumps(body).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def log_message(self, *args):
pass
self.server = HTTPServer(("127.0.0.1", 0), Handler)
threading.Thread(target=self.server.serve_forever, daemon=True).start()
self.addCleanup(self.server.server_close)
self.addCleanup(self.server.shutdown)
self.uri = "http://127.0.0.1:{}".format(self.server.server_port)
patcher = mock.patch.object(user_agent, "identity", return_value=IDENTITY)
patcher.start()
self.addCleanup(patcher.stop)
def _options(self, **extra):
options = {"uri": self.uri, "warehouse": "wh", "token.provider": "bear", "token": "t"}
options.update(extra)
return options
def _user_agents(self):
return [headers.get("User-Agent") for headers in self.requests]
def test_default_user_agent(self):
RESTApi(Options(self._options())).list_databases()
self.assertEqual([REST_USER_AGENT, REST_USER_AGENT], self._user_agents())
def test_common_options_user_agent(self):
options = self._options(**{"user-agent.features": "Flink", "user-agent.extended": "vvr"})
RESTApi(Options(options)).list_databases()
expected = REST_USER_AGENT[:-1] + ";Flink) vvr"
self.assertEqual([expected, expected], self._user_agents())
def test_user_set_user_agent_wins(self):
options = self._options(**{"header.User-Agent": "starrocks/user", "user-agent.extended": "vvr"})
RESTApi(Options(options)).list_databases()
self.assertEqual(["starrocks/user", "starrocks/user"], self._user_agents())
def test_pvfs_user_agent(self):
PaimonVirtualFileSystem(self._options(**{"user-agent.features": "Flink"})).ls("pvfs://wh/")
pvfs_user_agent = REST_USER_AGENT[:-1] + ";PythonPVFS;Flink)"
self.assertEqual([pvfs_user_agent, pvfs_user_agent], self._user_agents())
for headers in self.requests:
self.assertNotIn("http_user_agent", {key.lower() for key in headers})
def test_token_loader_user_agent(self):
HTTPClient().get(self.uri + "/v1/config")
self.assertEqual([REST_USER_AGENT], self._user_agents())
if __name__ == "__main__":
unittest.main()