From 4e08d74c8761f898dcf4b305b6951f20fad38a7c Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Sat, 20 Jan 2024 19:10:56 -0500 Subject: [PATCH 01/13] build(deps): bump pymysql to 1.1.0 --- requirements/tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tests.txt b/requirements/tests.txt index 38404e0..cfe0222 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,3 +1,3 @@ behave==1.2.6 bandit>=1.7.0 -pymysql==1.0.2 +pymysql==1.1.0 From 1ef1f5718049b11d0ebc821b78b02354a91f860b Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Tue, 6 Feb 2024 13:48:40 -0500 Subject: [PATCH 02/13] build(requirements): bump tornado to 6.4 --- requirements/basic.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/basic.txt b/requirements/basic.txt index e0d4490..ebe6d45 100644 --- a/requirements/basic.txt +++ b/requirements/basic.txt @@ -1,3 +1,3 @@ cartola>=0.17 taskio==0.0.6 -tornado==6.3.3 +tornado==6.4 From 92c4360fb1cd8dce118a623eb27e2cfb3f49e22b Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Tue, 6 Feb 2024 13:51:11 -0500 Subject: [PATCH 03/13] build(requirements): bump hiredis to 2.3.2 --- requirements/redis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/redis.txt b/requirements/redis.txt index 7dc2f52..f4a0aea 100644 --- a/requirements/redis.txt +++ b/requirements/redis.txt @@ -1,2 +1,2 @@ redis>=5.0.1 -hiredis>=2.2.3 +hiredis>=2.3.2 From 48ec629dd25c94ccc6a7046140fed415810b109f Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 16 Feb 2024 17:27:22 -0500 Subject: [PATCH 04/13] chore(launcher): use isinstance instead of checking type value --- firenado/launcher.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firenado/launcher.py b/firenado/launcher.py index 80d0ce6..7e64f47 100644 --- a/firenado/launcher.py +++ b/firenado/launcher.py @@ -1,4 +1,4 @@ -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -181,13 +181,13 @@ def launch(self): if os.name == "posix": signal.signal(signal.SIGTSTP, self.sig_handler) self.http_server = tornado.httpserver.HTTPServer(self.application) - if firenado.conf.app['xheaders'] is not None and type( - firenado.conf.app['xheaders']) == bool: + if firenado.conf.app['xheaders'] is not None and isinstance( + firenado.conf.app['xheaders'], bool): logger.debug("Setting http server xheaders as %s.", firenado.conf.app['xheaders']) self.http_server.xheaders = firenado.conf.app['xheaders'] - if firenado.conf.app['xheaders'] is not None and type( - firenado.conf.app['xheaders']) != bool: + if firenado.conf.app['xheaders'] is not None and isinstance( + firenado.conf.app['xheaders'], bool): logger.warning("The xheaders defined in the application section" "must be bool instead of %s. Ignoring the " "configuration item.", From 6ed0e4e66d442b3ad8bbae1fd69ed873af1e0d0d Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 16 Feb 2024 17:31:09 -0500 Subject: [PATCH 05/13] build(workflows): bump actions/checkout to v4 --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index a3297c9..ed78baf 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -11,7 +11,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: From 0938da9a2e61cbceb6c93b899cd0f83307684338 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 16 Feb 2024 17:32:53 -0500 Subject: [PATCH 06/13] build(workflows): bump actions/setup-python to v5 --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index ed78baf..dfaec89 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From d319ce37b75545f2bd331d5bf14e44d8f82ae4cb Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 16 Feb 2024 17:35:14 -0500 Subject: [PATCH 07/13] chore(schedule): remove parenthesis from the del call --- firenado/schedule.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/firenado/schedule.py b/firenado/schedule.py index ab4a798..d613b2c 100644 --- a/firenado/schedule.py +++ b/firenado/schedule.py @@ -1,6 +1,4 @@ -# -*- coding: UTF-8 -*- -# -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -42,8 +40,8 @@ def next_from_cron(cron: str) -> datetime: - """ Return a datatetime object with the next execution based on the informed - cron string and the current time. + """ Return a datatetime object with the next execution based on the + informed cron string and the current time. :param str cron: The cron string :return datetime: A datetime object with the next execution @@ -121,7 +119,7 @@ def remove_job(self, job_id): job = self.get_job(job_id) if job is None: return None - del(self._jobs[job_id]) + del self._jobs[job_id] return job.id def run(self): @@ -135,8 +133,8 @@ def run(self): def _manage_jobs(self): logger.debug("Scheduler [id: %s, name: %s] managing jobs.", self.id, self.name) - logger.debug("Scheduler [id: %s, name: %s] stopping periodic callback." - , self.id, self.name) + logger.debug("Scheduler [id: %s, name: %s] stopping periodic " + "callback.", self.id, self.name) self._periodic_callback.stop() for job in self.jobs: if not job.already_scheduled: @@ -154,8 +152,8 @@ def _manage_jobs(self): logger.debug("Scheduler [id: %s, name: %s] ending of managing jobs.", self.id, self.name) - logger.debug("Scheduler [id: %s, name: %s] starting periodic callback." - , self.id, self.name) + logger.debug("Scheduler [id: %s, name: %s] starting periodic " + "callback.", self.id, self.name) self._periodic_callback.start() From 56103ebd40490775ce9f63f7af329af8c323ef94 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 16 Feb 2024 17:37:50 -0500 Subject: [PATCH 08/13] build(requirements): bump cartola to 0.18 --- requirements/basic.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/basic.txt b/requirements/basic.txt index ebe6d45..ea60f0e 100644 --- a/requirements/basic.txt +++ b/requirements/basic.txt @@ -1,3 +1,3 @@ -cartola>=0.17 +cartola>=0.18 taskio==0.0.6 tornado==6.4 From d81b27d91b7bcdca1fe415e2921d4c49495d0f49 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Sun, 18 Feb 2024 23:34:53 -0500 Subject: [PATCH 09/13] chore(launcher): change super to a current form Also set launcher addresses as the default from the configuration all the time and refactored addresses condition. --- firenado/launcher.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/firenado/launcher.py b/firenado/launcher.py index 7e64f47..8009c8c 100644 --- a/firenado/launcher.py +++ b/firenado/launcher.py @@ -152,16 +152,15 @@ def is_alive(self): class TornadoLauncher(FirenadoLauncher): def __init__(self, **settings): - super(TornadoLauncher, self).__init__(**settings) + super().__init__(**settings) self.http_server = None self.application = None self.MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = firenado.conf.app[ 'wait_before_shutdown'] - if self.addresses is None or self.addresses == ['']: - if firenado.conf.app['addresses']: - self.addresses = firenado.conf.app['addresses'] - else: - self.addresses = firenado.conf.app['default_addresses'] + self.addresses = firenado.conf.app['default_addresses'] + if ((self.addresses is None or self.addresses == ['']) and + firenado.conf.app['addresses']): + self.addresses = firenado.conf.app['addresses'] if self.port is None: self.port = firenado.conf.app['port'] From cfc85441b3217eeb7d8f2791bda28f4e7f42fe5e Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Mon, 19 Feb 2024 02:33:43 -0500 Subject: [PATCH 10/13] feat(testing): add process and tornado launcher test cases The TornadoAsyncTestCase is based on a ProcessLauncher that will be managed by the test case. With this test case we want to use async tests to hit the application. The TornadoAsyncHTTPTestCase is based on a TornadoLauncher that will provide an application to tornado.testing.AsyncHTTPTestCase and we can test using the fetch method. Fixes: #444 Refs: #445 --- firenado/launcher.py | 7 +- firenado/security.py | 9 +- firenado/testing.py | 119 ++++++------------- tests/fixtures/securityapp/conf/firenado.yml | 12 +- tests/security_test.py | 35 +++--- 5 files changed, 68 insertions(+), 114 deletions(-) diff --git a/firenado/launcher.py b/firenado/launcher.py index 8009c8c..c008c7b 100644 --- a/firenado/launcher.py +++ b/firenado/launcher.py @@ -56,13 +56,16 @@ def __init__(self, **settings): os.chdir(self.dir) if self.app is not None or self.dir is not None: reload(firenado.conf) + self.configure_logging() + def configure_logging(self, **kargs): + format = kargs.get("format", firenado.conf.log['format']) + level = kargs.get("level", firenado.conf.log['level']) # Set logging basic configurations for handler in logging.root.handlers[:]: # clearing loggers, solution from: https://bit.ly/2yTchyx logging.root.removeHandler(handler) - logging.basicConfig(level=firenado.conf.log['level'], - format=firenado.conf.log['format']) + logging.basicConfig(level=level, format=format) def load(self): raise NotImplementedError() diff --git a/firenado/security.py b/firenado/security.py index cb7024c..acf0826 100644 --- a/firenado/security.py +++ b/firenado/security.py @@ -1,6 +1,4 @@ -# -*- coding: UTF-8 -*- -# -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -122,8 +120,9 @@ def default_class_authentication(self: tornadoweb.TornadoHandler): import firenado.conf if "login" in firenado.conf.app: warnings.warn("The \"login\" configuration in the application %s is" - "depreciated. Please replace the configuration app.login" - "to app.security.auth instead.", DeprecationWarning, 2) + "depreciated. Please replace the configuration " + "app.login to app.security.auth instead.", + DeprecationWarning, 2) login_urls = self.get_rooted_path( firenado.conf.app['login']['urls']['default'] ) diff --git a/firenado/testing.py b/firenado/testing.py index be98667..4fc52f5 100644 --- a/firenado/testing.py +++ b/firenado/testing.py @@ -1,6 +1,4 @@ -# -*- coding: UTF-8 -*- -# -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,14 +13,9 @@ # limitations under the License. import asyncio -from behave.api.async_step import async_run_until_complete -from firenado.launcher import ProcessLauncher +from firenado.launcher import ProcessLauncher, TornadoLauncher from tornado.testing import (bind_unused_port, AsyncTestCase, AsyncHTTPTestCase) -from tornado.httpclient import HTTPResponse -from tornado import gen, ioloop -from typing import Any -import warnings def get_event_loop(): @@ -35,93 +28,53 @@ def get_event_loop(): return loop if loop else asyncio.new_event_loop() -class ProcessLauncherTestCase(AsyncHTTPTestCase): +class TornadoAsyncTestCase(AsyncTestCase): @property - def launcher(self): + def launcher(self) -> ProcessLauncher: return self.__launcher def get_launcher(self) -> ProcessLauncher: """Should be overridden by subclasses to return a - `firenado.launcher.ProcessLauncher`. + `firenado.launcher.TornadoLauncher`. """ raise NotImplementedError() - def get_http_port(self) -> int: - """Returns the port used by the server. - - A new port is chosen for each test. - """ - return self.__port - - @async_run_until_complete(should_close=False, loop=get_event_loop()) - async def setUp(self) -> None: - self.should_close_asyncio_loop = False - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - sock, port = bind_unused_port() - sock.close() - self.__port = port - self.__launcher = self.get_launcher() - self.__launcher.port = self.__port - self.__launcher.load() - self.http_client = self.get_http_client() - self.io_loop = ioloop.IOLoop.current() - await self.__launcher.launch() - await gen.sleep(1) - - def get_url(self, path: str) -> str: - """Returns an absolute url for the given path on the test server.""" - return "%s://127.0.0.1:%s%s" % (self.get_protocol(), - self.get_http_port(), path) - - async def fetch( - self, path: str, raise_error: bool = False, **kwargs: Any - ) -> HTTPResponse: - """Convenience method to synchronously fetch a URL. - - The given path will be appended to the local server's host and - port. Any additional keyword arguments will be passed directly to - `.AsyncHTTPClient.fetch` (and so could be used to pass - ``method="POST"``, ``body="..."``, etc). - - If the path begins with http:// or https://, it will be treated as a - full URL and will be fetched as-is. - - If ``raise_error`` is ``True``, a `tornado.httpclient.HTTPError` will - be raised if the response code is not 200. This is the same behavior - as the ``raise_error`` argument to `.AsyncHTTPClient.fetch`, but - the default is ``False`` here (it's ``True`` in `.AsyncHTTPClient`) - because tests often need to deal with non-200 response codes. + def setUp(self) -> None: + import logging + super().setUp() + sock, port = bind_unused_port() + sock.close() + self.__port = port + self.__launcher = self.get_launcher() + self.__launcher.configure_logging(level=logging.ERROR) + self.__launcher.port = self.__port + self.__launcher.load() + asyncio.run(self.__launcher.launch()) + self.__launcher.launch() - .. versionchanged:: 5.0 - Added support for absolute URLs. - - .. versionchanged:: 5.1 - - Added the ``raise_error`` argument. + def tearDown(self) -> None: + self.__launcher.shutdown() + super().tearDown() - .. deprecated:: 5.1 - This method currently turns any exception into an - `.HTTPResponse` with status code 599. In Tornado 6.0, - errors other than `tornado.httpclient.HTTPError` will be - passed through, and ``raise_error=False`` will only - suppress errors that would be raised due to non-200 - response codes. +class TornadoAsyncHTTPTestCase(AsyncHTTPTestCase): + def get_launcher(self) -> TornadoLauncher: + """Should be overridden by subclasses to return a + `firenado.launcher.TornadoLauncher`. """ - if path.lower().startswith(("http://", "https://")): - url = path - else: - url = self.get_url(path) + raise NotImplementedError() - return await self.http_client.fetch(url, raise_error=raise_error, - **kwargs) + def get_log_level(self): + return None - def tearDown(self) -> None: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.http_client.close() - AsyncTestCase.tearDown(self) - self.__launcher.shutdown() + def get_app(self): + import logging + launcher = self.get_launcher() + launcher.load() + if self.get_log_level() is not None: + launcher.configure_logging(level=self.get_log_level()) + else: + launcher.configure_logging(level=logging.WARN) + return launcher.application diff --git a/tests/fixtures/securityapp/conf/firenado.yml b/tests/fixtures/securityapp/conf/firenado.yml index a0cafbc..0bb891f 100644 --- a/tests/fixtures/securityapp/conf/firenado.yml +++ b/tests/fixtures/securityapp/conf/firenado.yml @@ -17,15 +17,15 @@ components: # enabled: true log: - level: DEBUG + level: ERROR # Session types could be: # file or redis. session: - type: redis - enabled: false + type: file + enabled: true # Redis session handler configuration - data: - source: session + # data: + # source: session # File session handler related configuration - # path: /tmp + path: tmp diff --git a/tests/security_test.py b/tests/security_test.py index 27ff88e..064c71b 100644 --- a/tests/security_test.py +++ b/tests/security_test.py @@ -1,6 +1,4 @@ -# -*- coding: UTF-8 -*- -# -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,13 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import asyncio from tests import chdir_fixture_app, PROJECT_ROOT from firenado import security, testing -from firenado.launcher import ProcessLauncher -import sys +from firenado.launcher import TornadoLauncher import unittest -from behave.api.async_step import async_run_until_complete class MockApplication: @@ -48,6 +43,7 @@ def __init__(self): class MockHandler: """ Mock the handler being decorated by the security functions. """ + def __init__(self): self.status = 200 self.response = None @@ -93,16 +89,19 @@ def test_only_xhr(self): "request only.") -class CurrentSecurityTestCase(testing.ProcessLauncherTestCase): +class CurrentSecurityTestCase(testing.TornadoAsyncHTTPTestCase): - def get_launcher(self) -> ProcessLauncher: + def get_launcher(self): application_dir = chdir_fixture_app("securityapp") - return ProcessLauncher( - dir=application_dir, path=PROJECT_ROOT, logfile=sys.stderr) - - # @async_run_until_complete(loop=testing.get_event_loop()) - # async def test_auth_decorated_class(self): - # response = await self.fetch('/authenticated') - # await asyncio.sleep(1) - # print(response.code) - # self.assertEqual(response.body, b'Authenticated') + return TornadoLauncher( + dir=application_dir, path=PROJECT_ROOT) + + def test_root(self): + response = self.fetch("/") + # print(response.code) + self.assertEqual(response.body, b"IndexHandler output") + + # def test_auth_decorated_class(self): + # response = self.fetch("/authenticated") + # # print(response.code) + # self.assertEqual(response.body, b"Authenticated") From 68eedeec23a4f91b69619ca16ee1293b32f1def5 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Wed, 21 Feb 2024 18:24:47 -0500 Subject: [PATCH 11/13] test: Add Async and AsyncHTTP Tornado test cases Fixes: #445 Refs: #444 --- firenado/testing.py | 4 +- tests/fixtures/launcherapp/conf/firenado.yml | 2 +- tests/fixtures/launcherapp/handlers.py | 5 +- tests/runtests.py | 3 +- tests/security_test.py | 1 + tests/testing_test.py | 68 ++++++++++++++++++++ 6 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 tests/testing_test.py diff --git a/firenado/testing.py b/firenado/testing.py index 4fc52f5..56c5797 100644 --- a/firenado/testing.py +++ b/firenado/testing.py @@ -40,6 +40,9 @@ def get_launcher(self) -> ProcessLauncher: """ raise NotImplementedError() + def http_port(self) -> int: + return self.__port + def setUp(self) -> None: import logging super().setUp() @@ -51,7 +54,6 @@ def setUp(self) -> None: self.__launcher.port = self.__port self.__launcher.load() asyncio.run(self.__launcher.launch()) - self.__launcher.launch() def tearDown(self) -> None: self.__launcher.shutdown() diff --git a/tests/fixtures/launcherapp/conf/firenado.yml b/tests/fixtures/launcherapp/conf/firenado.yml index 26c3cb6..4adfaff 100644 --- a/tests/fixtures/launcherapp/conf/firenado.yml +++ b/tests/fixtures/launcherapp/conf/firenado.yml @@ -21,7 +21,7 @@ components: # enabled: true log: - level: DEBUG + level: INFO # Session types could be: # file or redis. diff --git a/tests/fixtures/launcherapp/handlers.py b/tests/fixtures/launcherapp/handlers.py index 0d6ffc4..787606c 100644 --- a/tests/fixtures/launcherapp/handlers.py +++ b/tests/fixtures/launcherapp/handlers.py @@ -4,4 +4,7 @@ class IndexHandler(tornadoweb.TornadoHandler): def get(self): - self.write("IndexHandler output") + self.write("Get output") + + def post(self): + self.write("Post output") diff --git a/tests/runtests.py b/tests/runtests.py index 6e170ee..732f8fd 100644 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -17,7 +17,7 @@ import unittest from tests import (components_test, conf_test, config_test, data_test, security_test, service_test, session_test, sqlalchemy_test, - tornadoweb_test) + testing_test, tornadoweb_test) from tests.util import url_util_test @@ -32,6 +32,7 @@ def suite(): alltests.addTests(testLoader.loadTestsFromModule(service_test)) alltests.addTests(testLoader.loadTestsFromModule(session_test)) alltests.addTests(testLoader.loadTestsFromModule(sqlalchemy_test)) + alltests.addTests(testLoader.loadTestsFromModule(testing_test)) alltests.addTests(testLoader.loadTestsFromModule(tornadoweb_test)) alltests.addTests(testLoader.loadTestsFromModule(url_util_test)) return alltests diff --git a/tests/security_test.py b/tests/security_test.py index 064c71b..477cb2a 100644 --- a/tests/security_test.py +++ b/tests/security_test.py @@ -101,6 +101,7 @@ def test_root(self): # print(response.code) self.assertEqual(response.body, b"IndexHandler output") + # TODO: finish authentication tests # def test_auth_decorated_class(self): # response = self.fetch("/authenticated") # # print(response.code) diff --git a/tests/testing_test.py b/tests/testing_test.py new file mode 100644 index 0000000..f307ce7 --- /dev/null +++ b/tests/testing_test.py @@ -0,0 +1,68 @@ +# Copyright 2015-2024 Flavio Garcia +# +# 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 tests import chdir_fixture_app, PROJECT_ROOT +from firenado.testing import TornadoAsyncHTTPTestCase, TornadoAsyncTestCase +from firenado.launcher import ProcessLauncher, TornadoLauncher +from tornado.httpclient import AsyncHTTPClient +from tornado.testing import gen_test + + +class AsyncTestCase(TornadoAsyncTestCase): + + def get_launcher(self): + application_dir = chdir_fixture_app("launcherapp") + return ProcessLauncher( + dir=application_dir, path=PROJECT_ROOT) + + @gen_test + async def test_get(self): + http_client = AsyncHTTPClient() + try: + response = await http_client.fetch( + f"http://localhost:{self.http_port()}/") + except Exception as e: + raise e + self.assertEqual(response.body, b"Get output") + + @gen_test + async def test_post(self): + http_client = AsyncHTTPClient() + try: + response = await http_client.fetch( + f"http://localhost:{self.http_port()}/", + body="", + method="POST" + ) + except Exception as e: + raise e + self.assertEqual(response.body, b"Post output") + + +class AsyncHTTPTestCase(TornadoAsyncHTTPTestCase): + + def get_launcher(self): + application_dir = chdir_fixture_app("launcherapp") + return TornadoLauncher( + dir=application_dir, path=PROJECT_ROOT) + + def test_get(self): + response = self.fetch("/") + # print(response.code) + self.assertEqual(response.body, b"Get output") + + def test_post(self): + response = self.fetch("/", body="", method="POST") + # print(response.code) + self.assertEqual(response.body, b"Post output") From e741f131d2d3c8874ab80ca67b1a732d760c6257 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 23 Feb 2024 13:17:35 -0500 Subject: [PATCH 12/13] fix(tests): fix behave tests Need to get rid of behave on this project Refs: #444 #445 --- tests/features/steps/launcher.py | 2 +- tests/loader_test.py | 66 ++++++++++++++++++++++++++++++++ tests/runtests.py | 5 ++- 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/loader_test.py diff --git a/tests/features/steps/launcher.py b/tests/features/steps/launcher.py index 4d6146d..05df181 100644 --- a/tests/features/steps/launcher.py +++ b/tests/features/steps/launcher.py @@ -43,7 +43,7 @@ async def step_application_running_correctly_at_port(context, port): print("Error: %s" % e) context.tester.assertTrue(False) else: - context.tester.assertEqual(b"IndexHandler output", response.body) + context.tester.assertEqual(b"Get output", response.body) context.tester.assertTrue(context.launcher.is_alive()) diff --git a/tests/loader_test.py b/tests/loader_test.py new file mode 100644 index 0000000..9c43701 --- /dev/null +++ b/tests/loader_test.py @@ -0,0 +1,66 @@ +# Copyright 2015-2024 Flavio Garcia +# +# 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. + +import asyncio +from tests import chdir_fixture_app, PROJECT_ROOT +from firenado.launcher import ProcessLauncher +from tornado.httpclient import AsyncHTTPClient +from tornado.testing import bind_unused_port, gen_test, AsyncTestCase + + +class ProcessLauncherTestCase(AsyncTestCase): + + def setUp(self) -> None: + import logging + super().setUp() + sock, port = bind_unused_port() + sock.close() + self.__port = port + self.__launcher = self.get_launcher() + self.__launcher.configure_logging(level=logging.ERROR) + self.__launcher.port = self.__port + self.__launcher.load() + asyncio.run(self.__launcher.launch()) + + def tearDown(self) -> None: + self.__launcher.shutdown() + super().tearDown() + + def get_launcher(self): + application_dir = chdir_fixture_app("launcherapp") + return ProcessLauncher( + dir=application_dir, path=PROJECT_ROOT) + + @gen_test + async def test_get(self): + http_client = AsyncHTTPClient() + try: + response = await http_client.fetch( + f"http://localhost:{self.__port}/") + except Exception as e: + raise e + self.assertEqual(response.body, b"Get output") + + @gen_test + async def test_post(self): + http_client = AsyncHTTPClient() + try: + response = await http_client.fetch( + f"http://localhost:{self.__port}/", + body="", + method="POST" + ) + except Exception as e: + raise e + self.assertEqual(response.body, b"Post output") diff --git a/tests/runtests.py b/tests/runtests.py index 732f8fd..46e04bb 100644 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -16,8 +16,8 @@ import unittest from tests import (components_test, conf_test, config_test, data_test, - security_test, service_test, session_test, sqlalchemy_test, - testing_test, tornadoweb_test) + loader_test, security_test, service_test, session_test, + sqlalchemy_test, testing_test, tornadoweb_test) from tests.util import url_util_test @@ -28,6 +28,7 @@ def suite(): alltests.addTests(testLoader.loadTestsFromModule(conf_test)) alltests.addTests(testLoader.loadTestsFromModule(config_test)) alltests.addTests(testLoader.loadTestsFromModule(data_test)) + alltests.addTests(testLoader.loadTestsFromModule(loader_test)) alltests.addTests(testLoader.loadTestsFromModule(security_test)) alltests.addTests(testLoader.loadTestsFromModule(service_test)) alltests.addTests(testLoader.loadTestsFromModule(session_test)) From 649f6ef1ddbdaa808d3a9480e74c384afe84fff0 Mon Sep 17 00:00:00 2001 From: Flavio Garcia Date: Fri, 23 Feb 2024 20:27:57 -0500 Subject: [PATCH 13/13] build(release): deliver 0.9.4 --- docs/conf.py | 4 ++-- docs/releases/v0.9.3.rst | 4 ++-- docs/releases/v0.9.4.rst | 17 +++++++++++++++++ firenado/__init__.py | 4 ++-- 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 docs/releases/v0.9.4.rst diff --git a/docs/conf.py b/docs/conf.py index 629b695..7144fdf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ # General information about the project. project = u'Firenado Framework' -copyright = u'2015-2023, Flavio Garcia' +copyright = u'2015-2024, Flavio Garcia' author = u'Flavio Garcia' # The version info for the project you're documenting, acts as replacement for @@ -57,7 +57,7 @@ # The short X.Y version. version = '0.9' # The full version, including alpha/beta/rc tags. -release = '0.9.3' +release = '0.9.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/releases/v0.9.3.rst b/docs/releases/v0.9.3.rst index ade524b..8ec1037 100644 --- a/docs/releases/v0.9.3.rst +++ b/docs/releases/v0.9.3.rst @@ -1,10 +1,10 @@ -What's new in Firenado 0.9.1 +What's new in Firenado 0.9.3 ============================ Nov 25, 2023 ------------ -We are pleased to announce the release of Firenado 0.9.1. +We are pleased to announce the release of Firenado 0.9.3. This release updates pexpect to 4.9.0 and removes the monkey patch from the launcher. diff --git a/docs/releases/v0.9.4.rst b/docs/releases/v0.9.4.rst new file mode 100644 index 0000000..7640bd4 --- /dev/null +++ b/docs/releases/v0.9.4.rst @@ -0,0 +1,17 @@ +What's new in Firenado 0.9.4 +============================ + +Feb 23, 2024 +------------ + +We are pleased to announce the release of Firenado 0.9.4. + +This release adds proper testers to the Firenado framework. + +Here are the highlights: + +Features +~~~~~~~~ + + * Create a TornadoLoader test case `#444 `_ + * Create a ProcessLoader test case `#445 `_ diff --git a/firenado/__init__.py b/firenado/__init__.py index 2aae57d..bb30429 100644 --- a/firenado/__init__.py +++ b/firenado/__init__.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2015-2023 Flavio Garcia +# Copyright 2015-2024 Flavio Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ """The Firenado Framework""" __author__ = "Flavio Garcia " -__version__ = (0, 9, 3) +__version__ = (0, 9, 4) __licence__ = "Apache License V2.0"