diff --git a/qinling_tempest_plugin/services/qinling_client.py b/qinling_tempest_plugin/services/qinling_client.py index 622ecc33..08f8509e 100644 --- a/qinling_tempest_plugin/services/qinling_client.py +++ b/qinling_tempest_plugin/services/qinling_client.py @@ -133,13 +133,13 @@ class QinlingClient(client_base.QinlingClientBase): def get_function_workers(self, function_id): return self.get_resources('functions/%s/workers' % function_id) - def create_webhook(self, function_id): - req_body = {"function_id": function_id} + def create_webhook(self, function_id, version=0): + req_body = {"function_id": function_id, "function_version": version} resp, body = self.post_json('webhooks', req_body) return resp, body - def create_job(self, function_id, first_execution_time=None): - req_body = {"function_id": function_id} + def create_job(self, function_id, version=0, first_execution_time=None): + req_body = {"function_id": function_id, "function_version": version} if not first_execution_time: first_execution_time = str( datetime.utcnow() + timedelta(hours=1) diff --git a/qinling_tempest_plugin/tests/api/test_executions.py b/qinling_tempest_plugin/tests/api/test_executions.py index 64de807f..377b14e2 100644 --- a/qinling_tempest_plugin/tests/api/test_executions.py +++ b/qinling_tempest_plugin/tests/api/test_executions.py @@ -26,7 +26,7 @@ class ExecutionsTest(base.BaseQinlingTest): def setUp(self): super(ExecutionsTest, self).setUp() - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) @decorators.idempotent_id('2a93fab0-2dae-4748-b0d4-f06b735ff451') def test_crud_execution(self): @@ -128,7 +128,7 @@ class ExecutionsTest(base.BaseQinlingTest): execution_id, ignore_notfound=True) self.assertEqual('running', body['status']) - self.await_execution_success(execution_id) + self.wait_execution_success(execution_id) @decorators.idempotent_id('6cb47b1d-a8c6-48f2-a92f-c4f613c33d1c') def test_execution_log(self): diff --git a/qinling_tempest_plugin/tests/api/test_executions_nodejs.py b/qinling_tempest_plugin/tests/api/test_executions_nodejs.py index 796c2e83..86d17d10 100644 --- a/qinling_tempest_plugin/tests/api/test_executions_nodejs.py +++ b/qinling_tempest_plugin/tests/api/test_executions_nodejs.py @@ -22,7 +22,7 @@ class NodeJSExecutionsTest(base.BaseQinlingTest): def setUp(self): super(NodeJSExecutionsTest, self).setUp() - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) @decorators.idempotent_id('e3046fa4-2289-11e8-b720-00224d6b7bc1') def test_basic_nodejs_execution(self): diff --git a/qinling_tempest_plugin/tests/api/test_function_versions.py b/qinling_tempest_plugin/tests/api/test_function_versions.py index 60d6c674..5e373fc6 100644 --- a/qinling_tempest_plugin/tests/api/test_function_versions.py +++ b/qinling_tempest_plugin/tests/api/test_function_versions.py @@ -24,7 +24,7 @@ class FunctionVersionsTest(base.BaseQinlingTest): super(FunctionVersionsTest, self).setUp() # Wait until runtime is available - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) @decorators.idempotent_id('ce630c59-a79d-4b2d-89af-c7c5c8f8bd3f') def test_create(self): diff --git a/qinling_tempest_plugin/tests/api/test_functions.py b/qinling_tempest_plugin/tests/api/test_functions.py index 5959f455..30c5501a 100644 --- a/qinling_tempest_plugin/tests/api/test_functions.py +++ b/qinling_tempest_plugin/tests/api/test_functions.py @@ -28,7 +28,7 @@ class FunctionsTest(base.BaseQinlingTest): super(FunctionsTest, self).setUp() # Wait until runtime is available - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) self.python_zip_file = self.create_package() @decorators.idempotent_id('9c36ac64-9a44-4c44-9e44-241dcc6b0933') diff --git a/qinling_tempest_plugin/tests/api/test_jobs.py b/qinling_tempest_plugin/tests/api/test_jobs.py index 0284f5f0..0dd44d1b 100644 --- a/qinling_tempest_plugin/tests/api/test_jobs.py +++ b/qinling_tempest_plugin/tests/api/test_jobs.py @@ -11,6 +11,9 @@ # 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 datetime import datetime +from datetime import timedelta + from tempest.lib import decorators from qinling_tempest_plugin.tests import base @@ -22,7 +25,7 @@ class JobsTest(base.BaseQinlingTest): def setUp(self): super(JobsTest, self).setUp() - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) self.function_id = self.create_function() @decorators.idempotent_id('68e4d562-f762-11e7-875d-00224d6b7bc1') @@ -38,3 +41,28 @@ class JobsTest(base.BaseQinlingTest): job_id, [item['id'] for item in body['jobs']] ) + + @decorators.idempotent_id('82a694a7-d3b5-4b6c-86e5-5fac6eae0f2a') + def test_create_with_function_version(self): + version = self.create_function_version(self.function_id) + # first_execution_time is at least 1 min ahead of current time. + first_execution_time = str(datetime.utcnow() + timedelta(seconds=90)) + job_id = self.create_job(self.function_id, version=version, + first_execution_time=first_execution_time) + + # Wait for job to be finished + self.wait_job_done(job_id) + + resp, body = self.client.get_resources( + 'executions', + {'description': 'has:%s' % job_id} + ) + self.assertEqual(200, resp.status) + self.assertEqual(1, len(body['executions'])) + + exec_id = body['executions'][0]['id'] + self.wait_execution_success(exec_id) + + resp, body = self.client.get_execution_log(exec_id) + self.assertEqual(200, resp.status) + self.assertIn('Hello, World', body) diff --git a/qinling_tempest_plugin/tests/api/test_runtimes.py b/qinling_tempest_plugin/tests/api/test_runtimes.py index aca0f7d8..3edb29af 100644 --- a/qinling_tempest_plugin/tests/api/test_runtimes.py +++ b/qinling_tempest_plugin/tests/api/test_runtimes.py @@ -45,7 +45,7 @@ class RuntimesTest(base.BaseQinlingTest): # Wait for runtime to be available # We don't have to check k8s resource, if runtime's status has changed # to available, then kubernetes deployment is assumed to be ok. - self.await_runtime_available(runtime_id) + self.wait_runtime_available(runtime_id) # Delete runtime resp = self.admin_client.delete_resource('runtimes', runtime_id) diff --git a/qinling_tempest_plugin/tests/api/test_webhooks.py b/qinling_tempest_plugin/tests/api/test_webhooks.py index 110d9ca7..f067b5ab 100644 --- a/qinling_tempest_plugin/tests/api/test_webhooks.py +++ b/qinling_tempest_plugin/tests/api/test_webhooks.py @@ -22,12 +22,12 @@ class WebhooksTest(base.BaseQinlingTest): def setUp(self): super(WebhooksTest, self).setUp() - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) self.function_id = self.create_function() @decorators.idempotent_id('37DCD022-32D6-48D1-B90C-31D605DBE53B') def test_webhook_invoke(self): - webhook_id, url = self.create_webhook() + webhook_id, url = self.create_webhook(self.function_id) resp = requests.post(url, data={'name': 'qinling'}, verify=False) self.assertEqual(202, resp.status_code) resp_exec_id = resp.json().get('execution_id') @@ -42,16 +42,44 @@ class WebhooksTest(base.BaseQinlingTest): self.assertEqual(1, len(body['executions'])) exec_id = body['executions'][0]['id'] self.assertEqual(resp_exec_id, exec_id) - self.await_execution_success(exec_id) + self.wait_execution_success(exec_id) resp, body = self.client.get_execution_log(exec_id) self.assertEqual(200, resp.status) self.assertIn('qinling', body) + @decorators.idempotent_id('68605edb-1e36-4953-907d-aa6e2352bb85') + def test_webhook_with_function_version(self): + version = self.create_function_version(self.function_id) + webhook_id, url = self.create_webhook(self.function_id, + version=version) + resp = requests.post(url, data={'name': 'version_test'}, verify=False) + + self.assertEqual(202, resp.status_code) + + resp_exec_id = resp.json().get('execution_id') + self.addCleanup(self.client.delete_resource, 'executions', + resp_exec_id, ignore_notfound=True) + + resp, body = self.client.get_resources( + 'executions', + {'description': 'has:%s' % webhook_id} + ) + + self.assertEqual(200, resp.status) + self.assertEqual(1, len(body['executions'])) + exec_id = body['executions'][0]['id'] + self.assertEqual(resp_exec_id, exec_id) + self.wait_execution_success(exec_id) + + resp, body = self.client.get_execution_log(exec_id) + self.assertEqual(200, resp.status) + self.assertIn('version_test', body) + @decorators.idempotent_id('8e6e4f76-f748-11e7-8ec3-00224d6b7bc1') def test_get_all_admin(self): """Admin user can get webhooks of other projects""" - webhook_id, _ = self.create_webhook() + webhook_id, _ = self.create_webhook(self.function_id) resp, body = self.admin_client.get_resources( 'webhooks?all_projects=true' diff --git a/qinling_tempest_plugin/tests/base.py b/qinling_tempest_plugin/tests/base.py index bc323426..cb12ae9e 100644 --- a/qinling_tempest_plugin/tests/base.py +++ b/qinling_tempest_plugin/tests/base.py @@ -69,7 +69,7 @@ class BaseQinlingTest(test.BaseTestCase): stop=tenacity.stop_after_attempt(10), retry=tenacity.retry_if_exception_type(AssertionError) ) - def await_runtime_available(self, id): + def wait_runtime_available(self, id): resp, body = self.client.get_resource('runtimes', id) self.assertEqual(200, resp.status) @@ -80,12 +80,23 @@ class BaseQinlingTest(test.BaseTestCase): stop=tenacity.stop_after_attempt(10), retry=tenacity.retry_if_exception_type(AssertionError) ) - def await_execution_success(self, id): + def wait_execution_success(self, id): resp, body = self.client.get_resource('executions', id) self.assertEqual(200, resp.status) self.assertEqual('success', body['status']) + @tenacity.retry( + wait=tenacity.wait_fixed(10), + stop=tenacity.stop_after_attempt(12), + retry=tenacity.retry_if_exception_type(AssertionError) + ) + def wait_job_done(self, id): + resp, body = self.client.get_resource('jobs', id) + + self.assertEqual(200, resp.status) + self.assertEqual('done', body['status']) + def create_package(self, name="python/test_python_basic.py"): file_path = pkg_resources.resource_filename( 'qinling_tempest_plugin', @@ -158,19 +169,28 @@ class BaseQinlingTest(test.BaseTestCase): self.assertEqual(200, resp.status_code) - def create_webhook(self): - resp, body = self.client.create_webhook(self.function_id) + def create_webhook(self, function_id=None, version=0): + if not function_id: + function_id = self.create_function() + + resp, body = self.client.create_webhook(function_id, version=version) self.assertEqual(201, resp.status) + webhook_id = body['id'] self.addCleanup(self.client.delete_resource, 'webhooks', webhook_id, ignore_notfound=True) return webhook_id, body['webhook_url'] - def create_job(self, function_id=None): + def create_job(self, function_id=None, version=0, + first_execution_time=None): if not function_id: function_id = self.create_function() - resp, body = self.client.create_job(function_id) + resp, body = self.client.create_job( + function_id, + version=version, + first_execution_time=first_execution_time + ) self.assertEqual(201, resp.status) job_id = body['id'] diff --git a/qinling_tempest_plugin/tests/scenario/test_basic_ops.py b/qinling_tempest_plugin/tests/scenario/test_basic_ops.py index 356b0a99..5de8bd51 100644 --- a/qinling_tempest_plugin/tests/scenario/test_basic_ops.py +++ b/qinling_tempest_plugin/tests/scenario/test_basic_ops.py @@ -38,7 +38,7 @@ class BasicOpsTest(base.BaseQinlingTest): # Wait for runtime to be available self.runtime_id = body['id'] - self.await_runtime_available(self.runtime_id) + self.wait_runtime_available(self.runtime_id) self.addCleanup(self.admin_client.delete_resource, 'runtimes', self.runtime_id, ignore_notfound=True)