Index: third_party/google-endpoints/test/test_client.py |
diff --git a/third_party/google-endpoints/test/test_client.py b/third_party/google-endpoints/test/test_client.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1678fa3ff3bc23a50aa6d6ee4759228e0a0fa782 |
--- /dev/null |
+++ b/third_party/google-endpoints/test/test_client.py |
@@ -0,0 +1,431 @@ |
+# Copyright 2016 Google Inc. All Rights Reserved. |
+# |
+# Licensed 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. |
+ |
+from __future__ import absolute_import |
+ |
+import datetime |
+import mock |
+import os |
+import tempfile |
+import unittest2 |
+from expects import be_false, be_none, be_true, expect, equal, raise_error |
+ |
+from google.api.control import caches, check_request, client, messages, report_request |
+ |
+ |
+class TestSimpleLoader(unittest2.TestCase): |
+ SERVICE_NAME = 'simpler-loader' |
+ |
+ @mock.patch("google.api.control.client.ReportOptions", autospec=True) |
+ @mock.patch("google.api.control.client.CheckOptions", autospec=True) |
+ def test_should_create_client_ok(self, check_opts, report_opts): |
+ # the mocks return fake instances else code using them fails |
+ check_opts.return_value = caches.CheckOptions() |
+ report_opts.return_value = caches.ReportOptions() |
+ |
+ # ensure the client is constructed using no args instances of the opts |
+ expect(client.Loaders.DEFAULT.load(self.SERVICE_NAME)).not_to(be_none) |
+ check_opts.assert_called_once_with() |
+ report_opts.assert_called_once_with() |
+ |
+_TEST_CONFIG = """{ |
+ "checkAggregatorConfig": { |
+ "cacheEntries": 10, |
+ "responseExpirationMs": 1000, |
+ "flushIntervalMs": 2000 |
+ }, |
+ "reportAggregatorConfig": { |
+ "cacheEntries": 10, |
+ "flushIntervalMs": 1000 |
+ } |
+} |
+""" |
+ |
+ |
+class TestEnvironmentLoader(unittest2.TestCase): |
+ SERVICE_NAME = 'environment-loader' |
+ |
+ def setUp(self): |
+ json_fd = tempfile.NamedTemporaryFile(delete=False) |
+ with json_fd as f: |
+ f.write(_TEST_CONFIG) |
+ self._config_file = json_fd.name |
+ os.environ[client.CONFIG_VAR] = self._config_file |
+ |
+ def tearDown(self): |
+ if os.path.exists(self._config_file): |
+ os.remove(self._config_file) |
+ |
+ @mock.patch("google.api.control.client.ReportOptions", autospec=True) |
+ @mock.patch("google.api.control.client.CheckOptions", autospec=True) |
+ def test_should_create_client_from_environment_ok(self, check_opts, report_opts): |
+ check_opts.return_value = caches.CheckOptions() |
+ report_opts.return_value = caches.ReportOptions() |
+ |
+ # ensure the client is constructed using options values from the test JSON |
+ expect(client.Loaders.ENVIRONMENT.load(self.SERVICE_NAME)).not_to(be_none) |
+ check_opts.assert_called_once_with(expiration=datetime.timedelta(0, 1), |
+ flush_interval=datetime.timedelta(0, 2), |
+ num_entries=10) |
+ report_opts.assert_called_once_with(flush_interval=datetime.timedelta(0, 1), |
+ num_entries=10) |
+ |
+ @mock.patch("google.api.control.client.ReportOptions", autospec=True) |
+ @mock.patch("google.api.control.client.CheckOptions", autospec=True) |
+ def test_should_use_defaults_if_file_is_missing(self, check_opts, report_opts): |
+ os.remove(self._config_file) |
+ self._assert_called_with_no_args_options(check_opts, report_opts) |
+ |
+ @mock.patch("google.api.control.client.ReportOptions", autospec=True) |
+ @mock.patch("google.api.control.client.CheckOptions", autospec=True) |
+ def test_should_use_defaults_if_file_is_missing(self, check_opts, report_opts): |
+ del os.environ[client.CONFIG_VAR] |
+ self._assert_called_with_no_args_options(check_opts, report_opts) |
+ |
+ @mock.patch("google.api.control.client.ReportOptions") |
+ @mock.patch("google.api.control.client.CheckOptions") |
+ def test_should_use_defaults_if_json_is_bad(self, check_opts, report_opts): |
+ with open(self._config_file, 'w') as f: |
+ f.write(_TEST_CONFIG + '\n{ this will not parse as json}') |
+ self._assert_called_with_no_args_options(check_opts, report_opts) |
+ |
+ def _assert_called_with_no_args_options(self, check_opts, report_opts): |
+ # the mocks return fake instances else code using them fails |
+ check_opts.return_value = caches.CheckOptions() |
+ report_opts.return_value = caches.ReportOptions() |
+ |
+ # ensure the client is constructed using no args instances of the opts |
+ expect(client.Loaders.ENVIRONMENT.load(self.SERVICE_NAME)).not_to(be_none) |
+ check_opts.assert_called_once_with() |
+ report_opts.assert_called_once_with() |
+ |
+ |
+def _make_dummy_report_request(project_id, service_name): |
+ rules = report_request.ReportingRules() |
+ info = report_request.Info( |
+ consumer_project_id=project_id, |
+ operation_id='an_op_id', |
+ operation_name='an_op_name', |
+ method='GET', |
+ referer='a_referer', |
+ service_name=service_name) |
+ return info.as_report_request(rules) |
+ |
+ |
+def _make_dummy_check_request(project_id, service_name): |
+ info = check_request.Info( |
+ consumer_project_id=project_id, |
+ operation_id='an_op_id', |
+ operation_name='an_op_name', |
+ referer='a_referer', |
+ service_name=service_name) |
+ return info.as_check_request() |
+ |
+ |
+class TestClientStartAndStop(unittest2.TestCase): |
+ SERVICE_NAME = 'start-and-stop' |
+ PROJECT_ID = SERVICE_NAME + '.project' |
+ |
+ def setUp(self): |
+ self._mock_transport = mock.MagicMock() |
+ self._subject = client.Loaders.DEFAULT.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_create_a_thread_when_started(self, thread_class): |
+ self._subject.start() |
+ expect(thread_class.called).to(be_true) |
+ expect(len(thread_class.call_args_list)).to(equal(1)) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_only_create_thread_on_first_start(self, thread_class): |
+ self._subject.start() |
+ self._subject.start() |
+ expect(len(thread_class.call_args_list)).to(equal(1)) |
+ |
+ def test_should_noop_stop_if_not_started(self): |
+ # stop the subject, the transport should not see a request |
+ self._subject.stop() |
+ expect(self._mock_transport.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_clear_requests_on_stop(self, dummy_thread_class): |
+ # stop the subject, the transport did not see a request |
+ self._subject.start() |
+ self._subject.report( |
+ _make_dummy_report_request(self.PROJECT_ID, self.SERVICE_NAME)) |
+ self._subject.stop() |
+ expect(self._mock_transport.services.report.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_ignore_stop_if_already_stopped(self, dummy_thread_class): |
+ # stop the subject, the transport did not see a request |
+ self._subject.start() |
+ self._subject.report( |
+ _make_dummy_report_request(self.PROJECT_ID, self.SERVICE_NAME)) |
+ self._subject.stop() |
+ self._mock_transport.reset_mock() |
+ self._subject.stop() |
+ expect(self._mock_transport.services.report.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_ignore_bad_transport_when_not_cached(self, dummy_thread_class): |
+ self._subject.start() |
+ self._subject.report( |
+ _make_dummy_report_request(self.PROJECT_ID, self.SERVICE_NAME)) |
+ self._mock_transport.services.report.side_effect = lambda: 1/0 |
+ self._subject.stop() |
+ expect(self._mock_transport.services.report.called).to(be_true) |
+ |
+ |
+class TestClientCheck(unittest2.TestCase): |
+ SERVICE_NAME = 'check' |
+ PROJECT_ID = SERVICE_NAME + '.project' |
+ |
+ def setUp(self): |
+ self._mock_transport = mock.MagicMock() |
+ self._subject = client.Loaders.DEFAULT.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport) |
+ |
+ def test_should_raise_on_check_without_start(self): |
+ dummy_request = _make_dummy_check_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ expect(lambda: self._subject.check(dummy_request)).to( |
+ raise_error(AssertionError)) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_send_the_request_if_not_cached(self, dummy_thread_class): |
+ self._subject.start() |
+ dummy_request = _make_dummy_check_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.check(dummy_request) |
+ expect(self._mock_transport.services.check.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_not_send_the_request_if_cached(self, dummy_thread_class): |
+ t = self._mock_transport |
+ self._subject.start() |
+ dummy_request = _make_dummy_check_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ dummy_response = messages.CheckResponse( |
+ operationId=dummy_request.checkRequest.operation.operationId) |
+ t.services.check.return_value = dummy_response |
+ expect(self._subject.check(dummy_request)).to(equal(dummy_response)) |
+ t.reset_mock() |
+ expect(self._subject.check(dummy_request)).to(equal(dummy_response)) |
+ expect(t.services.check.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_return_null_if_transport_fails(self, dummy_thread_class): |
+ self._subject.start() |
+ dummy_request = _make_dummy_check_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._mock_transport.services.check.side_effect = lambda: 1/0 |
+ expect(self._subject.check(dummy_request)).to(be_none) |
+ |
+ |
+class TestClientReport(unittest2.TestCase): |
+ SERVICE_NAME = 'report' |
+ PROJECT_ID = SERVICE_NAME + '.project' |
+ |
+ def setUp(self): |
+ self._mock_transport = mock.MagicMock() |
+ self._subject = client.Loaders.DEFAULT.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport) |
+ |
+ def test_should_raise_on_report_without_start(self): |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ expect(lambda: self._subject.report(dummy_request)).to( |
+ raise_error(AssertionError)) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_not_send_the_request_if_cached(self, dummy_thread_class): |
+ t = self._mock_transport |
+ self._subject.start() |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.report(dummy_request) |
+ expect(t.services.report.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_send_a_request_if_not_cached(self, dummy_thread_class): |
+ self._subject = client.Loaders.NO_CACHE.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport) |
+ |
+ t = self._mock_transport |
+ self._subject.start() |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.report(dummy_request) |
+ expect(t.services.report.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_ignore_bad_transport_when_not_cached(self, dummy_thread_class): |
+ self._subject = client.Loaders.NO_CACHE.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport) |
+ |
+ self._mock_transport.services.report.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.report(dummy_request) |
+ expect(self._mock_transport.services.report.called).to(be_true) |
+ |
+ |
+class TestNoSchedulerThread(unittest2.TestCase): |
+ SERVICE_NAME = 'no-scheduler-thread' |
+ PROJECT_ID = SERVICE_NAME + '.project' |
+ |
+ def setUp(self): |
+ self._timer = _DateTimeTimer() |
+ self._mock_transport = mock.MagicMock() |
+ self._subject = client.Loaders.DEFAULT.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport, |
+ timer=self._timer) |
+ self._no_cache_subject = client.Loaders.NO_CACHE.load( |
+ self.SERVICE_NAME, |
+ create_transport=lambda: self._mock_transport, |
+ timer=self._timer) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_initialize_scheduler(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ for s in (self._subject, self._no_cache_subject): |
+ s.start() |
+ expect(sched.scheduler.called).to(be_true) |
+ sched.reset_mock() |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_not_enter_scheduler_when_there_is_no_cache(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._no_cache_subject.start() |
+ expect(sched.scheduler.called).to(be_true) |
+ scheduler = sched.scheduler.return_value |
+ expect(scheduler.enter.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_enter_scheduler_when_there_is_a_cache(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ expect(sched.scheduler.called).to(be_true) |
+ scheduler = sched.scheduler.return_value |
+ expect(scheduler.enter.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_not_enter_scheduler_for_cached_checks(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ |
+ # confirm scheduler is created and initialized |
+ expect(sched.scheduler.called).to(be_true) |
+ scheduler = sched.scheduler.return_value |
+ expect(scheduler.enter.called).to(be_true) |
+ scheduler.reset_mock() |
+ |
+ # call check once, to a cache response |
+ dummy_request = _make_dummy_check_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ dummy_response = messages.CheckResponse( |
+ operationId=dummy_request.checkRequest.operation.operationId) |
+ t = self._mock_transport |
+ t.services.check.return_value = dummy_response |
+ expect(self._subject.check(dummy_request)).to(equal(dummy_response)) |
+ t.reset_mock() |
+ |
+ # call check again - response is cached... |
+ expect(self._subject.check(dummy_request)).to(equal(dummy_response)) |
+ expect(self._mock_transport.services.check.called).to(be_false) |
+ |
+ # ... the scheduler is not run |
+ expect(scheduler.run.called).to(be_false) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_enter_scheduler_for_aggregated_reports(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ |
+ # confirm scheduler is created and initialized |
+ expect(sched.scheduler.called).to(be_true) |
+ scheduler = sched.scheduler.return_value |
+ expect(scheduler.enter.called).to(be_true) |
+ scheduler.reset_mock() |
+ |
+ # call report once; transport is not called, but the scheduler is run |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.report(dummy_request) |
+ expect(self._mock_transport.services.report.called).to(be_false) |
+ expect(scheduler.run.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ def test_should_flush_report_cache_in_scheduler(self, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ |
+ # call report once; transport is not called |
+ dummy_request = _make_dummy_report_request(self.PROJECT_ID, |
+ self.SERVICE_NAME) |
+ self._subject.report(dummy_request) # cached a report |
+ expect(self._mock_transport.services.report.called).to(be_false) |
+ # pass time, at least the flush interval, after which the report |
+ # cache to flush |
+ self._timer.tick() |
+ self._timer.tick() |
+ self._subject.report(dummy_request) |
+ expect(self._mock_transport.services.report.called).to(be_true) |
+ |
+ @mock.patch("google.api.control.client._THREAD_CLASS", spec=True) |
+ @mock.patch("google.api.control.client.sched", spec=True) |
+ def test_should_not_run_scheduler_when_stopping(self, sched, thread_class): |
+ thread_class.return_value.start.side_effect = lambda: 1/0 |
+ self._subject.start() |
+ |
+ # confirm scheduler is created and initialized |
+ expect(sched.scheduler.called).to(be_true) |
+ scheduler = sched.scheduler.return_value |
+ expect(scheduler.enter.called).to(be_true) |
+ |
+ # stop the subject. transport is called, but the scheduler is not run |
+ self._subject.report( |
+ _make_dummy_report_request(self.PROJECT_ID, self.SERVICE_NAME)) |
+ scheduler.reset_mock() |
+ self._subject.stop() |
+ expect(self._mock_transport.services.report.called).to(be_true) |
+ expect(scheduler.run.called).to(be_false) |
+ |
+ |
+class _DateTimeTimer(object): |
+ def __init__(self, auto=False): |
+ self.auto = auto |
+ self.time = datetime.datetime.utcfromtimestamp(0) |
+ |
+ def __call__(self): |
+ if self.auto: |
+ self.tick() |
+ return self.time |
+ |
+ def tick(self): |
+ self.time += datetime.timedelta(seconds=1) |