Compare commits
No commits in common. "d3213c577315fff8aaaf742f7ec57175e7cdc9dc" and "d018d4e764fd2b0a573d7bab3e95be4fb253760a" have entirely different histories.
d3213c5773
...
d018d4e764
5 changed files with 98 additions and 265 deletions
|
|
@ -5,13 +5,6 @@ All notable changes to WuttJamaican will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## v0.28.5 (2026-01-04)
|
|
||||||
|
|
||||||
### Fix
|
|
||||||
|
|
||||||
- prompt for continuum support in app installer
|
|
||||||
- make pylint happy
|
|
||||||
|
|
||||||
## v0.28.4 (2026-01-03)
|
## v0.28.4 (2026-01-03)
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "WuttJamaican"
|
name = "WuttJamaican"
|
||||||
version = "0.28.5"
|
version = "0.28.4"
|
||||||
description = "Base package for Wutta Framework"
|
description = "Base package for Wutta Framework"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,6 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
egg_name = None
|
egg_name = None
|
||||||
schema_installed = False
|
schema_installed = False
|
||||||
|
|
||||||
# nb. we prompt the user for this, unless attr already has value
|
|
||||||
wants_continuum = None
|
|
||||||
|
|
||||||
template_paths = ["wuttjamaican:templates/install"]
|
template_paths = ["wuttjamaican:templates/install"]
|
||||||
|
|
||||||
def __init__(self, config, **kwargs):
|
def __init__(self, config, **kwargs):
|
||||||
|
|
@ -188,97 +185,35 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
This method is called by :meth:`run()` and does the following:
|
This method is called by :meth:`run()` and does the following:
|
||||||
|
|
||||||
* call :meth:`prompt_user_for_context()` to collect DB info etc.
|
* call :meth:`get_dbinfo()` to get DB info from user, and test connection
|
||||||
* call :meth:`make_template_context()` to use when generating output
|
* call :meth:`make_template_context()` to use when generating output
|
||||||
* call :meth:`make_appdir()` to create app dir with config files
|
* call :meth:`make_appdir()` to create app dir with config files
|
||||||
* call :meth:`install_db_schema()` to (optionally) create tables in DB
|
* call :meth:`install_db_schema()` to (optionally) create tables in DB
|
||||||
"""
|
"""
|
||||||
# prompt user / get context
|
# prompt user for db info
|
||||||
context = self.prompt_user_for_context()
|
dbinfo = self.get_dbinfo()
|
||||||
context = self.make_template_context(**context)
|
|
||||||
|
# get context for generated app files
|
||||||
|
context = self.make_template_context(dbinfo)
|
||||||
|
|
||||||
# make the appdir
|
# make the appdir
|
||||||
self.make_appdir(context)
|
self.make_appdir(context)
|
||||||
|
|
||||||
# install db schema if user likes
|
# install db schema if user likes
|
||||||
self.schema_installed = self.install_db_schema(context["db_url"])
|
self.schema_installed = self.install_db_schema(dbinfo["dburl"])
|
||||||
|
|
||||||
def prompt_user_for_context(self):
|
def get_dbinfo(self):
|
||||||
"""
|
"""
|
||||||
This is responsible for initial user prompts.
|
Collect DB connection info from the user, and test the
|
||||||
|
connection. If connection fails, exit the install.
|
||||||
|
|
||||||
This happens early in the install, so this method can verify
|
This method is normally called by :meth:`do_install_steps()`.
|
||||||
the info, e.g. test the DB connection, but should not write
|
|
||||||
any files as the app dir may not exist yet.
|
|
||||||
|
|
||||||
Default logic calls :meth:`get_db_url()` for the DB
|
:returns: Dict of DB info collected from user.
|
||||||
connection, then may ask about Wutta-Continuum data
|
|
||||||
versioning. (The latter is skipped if the package is
|
|
||||||
missing.)
|
|
||||||
|
|
||||||
Subclass should override this method if they need different
|
|
||||||
prompting logic. The return value should always include at
|
|
||||||
least these 2 items:
|
|
||||||
|
|
||||||
* ``db_url`` - URL for the DB connection
|
|
||||||
* ``wants_continuum`` - whether data versioning should be enabled
|
|
||||||
|
|
||||||
:returns: Dict of template context
|
|
||||||
"""
|
"""
|
||||||
# db info
|
|
||||||
db_url = self.get_db_url()
|
|
||||||
|
|
||||||
# continuum
|
|
||||||
if self.wants_continuum is None:
|
|
||||||
try:
|
|
||||||
import wutta_continuum
|
|
||||||
except ImportError:
|
|
||||||
self.wants_continuum = False
|
|
||||||
else:
|
|
||||||
self.wants_continuum = self.prompt_bool(
|
|
||||||
"use continuum for data versioning?", default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
return {"db_url": db_url, "wants_continuum": self.wants_continuum}
|
|
||||||
|
|
||||||
def get_db_url(self):
|
|
||||||
"""
|
|
||||||
This must return the DB engine URL.
|
|
||||||
|
|
||||||
Default logic will prompt the user for hostname, port, DB name
|
|
||||||
and credentials. It then assembles the URL from those parts.
|
|
||||||
|
|
||||||
This method will also test the DB connection. If it fails,
|
|
||||||
the install is aborted.
|
|
||||||
|
|
||||||
This method is normally called by
|
|
||||||
:meth:`prompt_user_for_context()`.
|
|
||||||
|
|
||||||
:returns: SQLAlchemy engine URL (as object or string)
|
|
||||||
"""
|
|
||||||
# get db info/url
|
|
||||||
dbinfo = self.get_dbinfo()
|
|
||||||
if "db_url" in dbinfo:
|
|
||||||
db_url = dbinfo["db_url"]
|
|
||||||
else:
|
|
||||||
db_url = self.make_db_url(dbinfo)
|
|
||||||
|
|
||||||
# test db connection
|
|
||||||
self.rprint("\n\ttesting db connection... ", end="")
|
|
||||||
error = self.test_db_connection(db_url)
|
|
||||||
if error:
|
|
||||||
self.rprint("[bold red]cannot connect![/bold red] ..error was:")
|
|
||||||
self.rprint(f"\n{error}")
|
|
||||||
self.rprint("\n\t[bold yellow]aborting mission[/bold yellow]\n")
|
|
||||||
sys.exit(1)
|
|
||||||
self.rprint("[bold green]good[/bold green]")
|
|
||||||
|
|
||||||
return db_url
|
|
||||||
|
|
||||||
def get_dbinfo(self): # pylint: disable=missing-function-docstring
|
|
||||||
dbinfo = {}
|
dbinfo = {}
|
||||||
|
|
||||||
# main info
|
# get db info
|
||||||
dbinfo["dbtype"] = self.prompt_generic("db type", "postgresql")
|
dbinfo["dbtype"] = self.prompt_generic("db type", "postgresql")
|
||||||
dbinfo["dbhost"] = self.prompt_generic("db host", "localhost")
|
dbinfo["dbhost"] = self.prompt_generic("db host", "localhost")
|
||||||
default_port = "3306" if dbinfo["dbtype"] == "mysql" else "5432"
|
default_port = "3306" if dbinfo["dbtype"] == "mysql" else "5432"
|
||||||
|
|
@ -286,29 +221,49 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
dbinfo["dbname"] = self.prompt_generic("db name", self.pkg_name)
|
dbinfo["dbname"] = self.prompt_generic("db name", self.pkg_name)
|
||||||
dbinfo["dbuser"] = self.prompt_generic("db user", self.pkg_name)
|
dbinfo["dbuser"] = self.prompt_generic("db user", self.pkg_name)
|
||||||
|
|
||||||
# password
|
# get db password
|
||||||
dbinfo["dbpass"] = None
|
dbinfo["dbpass"] = None
|
||||||
while not dbinfo["dbpass"]:
|
while not dbinfo["dbpass"]:
|
||||||
dbinfo["dbpass"] = self.prompt_generic("db pass", is_password=True)
|
dbinfo["dbpass"] = self.prompt_generic("db pass", is_password=True)
|
||||||
|
|
||||||
|
# test db connection
|
||||||
|
self.rprint("\n\ttesting db connection... ", end="")
|
||||||
|
dbinfo["dburl"] = self.make_db_url(
|
||||||
|
dbinfo["dbtype"],
|
||||||
|
dbinfo["dbhost"],
|
||||||
|
dbinfo["dbport"],
|
||||||
|
dbinfo["dbname"],
|
||||||
|
dbinfo["dbuser"],
|
||||||
|
dbinfo["dbpass"],
|
||||||
|
)
|
||||||
|
error = self.test_db_connection(dbinfo["dburl"])
|
||||||
|
if error:
|
||||||
|
self.rprint("[bold red]cannot connect![/bold red] ..error was:")
|
||||||
|
self.rprint(f"\n{error}")
|
||||||
|
self.rprint("\n\t[bold yellow]aborting mission[/bold yellow]\n")
|
||||||
|
sys.exit(1)
|
||||||
|
self.rprint("[bold green]good[/bold green]")
|
||||||
|
|
||||||
return dbinfo
|
return dbinfo
|
||||||
|
|
||||||
def make_db_url(self, dbinfo): # pylint: disable=empty-docstring
|
def make_db_url(
|
||||||
|
self, dbtype, dbhost, dbport, dbname, dbuser, dbpass
|
||||||
|
): # pylint: disable=empty-docstring,too-many-arguments,too-many-positional-arguments
|
||||||
""" """
|
""" """
|
||||||
from sqlalchemy.engine import URL # pylint: disable=import-outside-toplevel
|
from sqlalchemy.engine import URL # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
if dbinfo["dbtype"] == "mysql":
|
if dbtype == "mysql":
|
||||||
drivername = "mysql+mysqlconnector"
|
drivername = "mysql+mysqlconnector"
|
||||||
else:
|
else:
|
||||||
drivername = "postgresql+psycopg2"
|
drivername = "postgresql+psycopg2"
|
||||||
|
|
||||||
return URL.create(
|
return URL.create(
|
||||||
drivername=drivername,
|
drivername=drivername,
|
||||||
username=dbinfo["dbuser"],
|
username=dbuser,
|
||||||
password=dbinfo["dbpass"],
|
password=dbpass,
|
||||||
host=dbinfo["dbhost"],
|
host=dbhost,
|
||||||
port=dbinfo["dbport"],
|
port=dbport,
|
||||||
database=dbinfo["dbname"],
|
database=dbname,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_db_connection(self, url): # pylint: disable=empty-docstring
|
def test_db_connection(self, url): # pylint: disable=empty-docstring
|
||||||
|
|
@ -325,7 +280,7 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
return str(error)
|
return str(error)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def make_template_context(self, **kwargs):
|
def make_template_context(self, dbinfo, **kwargs):
|
||||||
"""
|
"""
|
||||||
This must return a dict to be used as global template context
|
This must return a dict to be used as global template context
|
||||||
when generating output (e.g. config) files.
|
when generating output (e.g. config) files.
|
||||||
|
|
@ -334,19 +289,14 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
The ``context`` returned is then passed to
|
The ``context`` returned is then passed to
|
||||||
:meth:`render_mako_template()`.
|
:meth:`render_mako_template()`.
|
||||||
|
|
||||||
Note these first 2 params are not explicitly listed in the
|
:param dbinfo: Dict of DB connection info as obtained from
|
||||||
method signature; they are required nonetheless.
|
:meth:`get_dbinfo()`.
|
||||||
|
|
||||||
:param db_url: This must be a string URL for the DB engine.
|
|
||||||
|
|
||||||
:param wants_continuum: Whether data versioning should be
|
|
||||||
enabled within the config.
|
|
||||||
|
|
||||||
:param \\**kwargs: Extra template context.
|
:param \\**kwargs: Extra template context.
|
||||||
|
|
||||||
:returns: Dict for global template context.
|
:returns: Dict for global template context.
|
||||||
|
|
||||||
The final context dict should include at least:
|
The context dict will include:
|
||||||
|
|
||||||
* ``envdir`` - value from :data:`python:sys.prefix`
|
* ``envdir`` - value from :data:`python:sys.prefix`
|
||||||
* ``envname`` - "last" dirname from ``sys.prefix``
|
* ``envname`` - "last" dirname from ``sys.prefix``
|
||||||
|
|
@ -355,16 +305,13 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
* ``pypi_name`` - value from :attr:`pypi_name`
|
* ``pypi_name`` - value from :attr:`pypi_name`
|
||||||
* ``egg_name`` - value from :attr:`egg_name`
|
* ``egg_name`` - value from :attr:`egg_name`
|
||||||
* ``appdir`` - ``app`` folder under ``sys.prefix``
|
* ``appdir`` - ``app`` folder under ``sys.prefix``
|
||||||
* ``db_url`` - value from ``kwargs``
|
* ``db_url`` - value from ``dbinfo['dburl']``
|
||||||
* ``wants_continuum`` - value from ``kwargs``
|
|
||||||
"""
|
"""
|
||||||
envname = os.path.basename(sys.prefix)
|
envname = os.path.basename(sys.prefix)
|
||||||
appdir = os.path.join(sys.prefix, "app")
|
appdir = os.path.join(sys.prefix, "app")
|
||||||
|
dburl = dbinfo["dburl"]
|
||||||
db_url = kwargs.pop("db_url")
|
if not isinstance(dburl, str):
|
||||||
if not isinstance(db_url, str):
|
dburl = dburl.render_as_string(hide_password=False)
|
||||||
db_url = db_url.render_as_string(hide_password=False)
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"envdir": sys.prefix,
|
"envdir": sys.prefix,
|
||||||
"envname": envname,
|
"envname": envname,
|
||||||
|
|
@ -372,8 +319,8 @@ class InstallHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
||||||
"app_title": self.app_title,
|
"app_title": self.app_title,
|
||||||
"pypi_name": self.pypi_name,
|
"pypi_name": self.pypi_name,
|
||||||
"appdir": appdir,
|
"appdir": appdir,
|
||||||
|
"db_url": dburl,
|
||||||
"egg_name": self.egg_name,
|
"egg_name": self.egg_name,
|
||||||
"db_url": db_url,
|
|
||||||
}
|
}
|
||||||
context.update(kwargs)
|
context.update(kwargs)
|
||||||
return context
|
return context
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,8 @@ preferdb = true
|
||||||
<%def name="section_wutta_db()">
|
<%def name="section_wutta_db()">
|
||||||
[wutta.db]
|
[wutta.db]
|
||||||
default.url = ${db_url}
|
default.url = ${db_url}
|
||||||
|
## TODO
|
||||||
% if wants_continuum:
|
## versioning.enabled = true
|
||||||
[wutta_continuum]
|
|
||||||
enable_versioning = true
|
|
||||||
% endif
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="section_wutta_mail()">
|
<%def name="section_wutta_mail()">
|
||||||
|
|
@ -97,11 +94,7 @@ templates = wuttjamaican:templates/mail
|
||||||
<%def name="section_alembic()">
|
<%def name="section_alembic()">
|
||||||
[alembic]
|
[alembic]
|
||||||
script_location = wuttjamaican.db:alembic
|
script_location = wuttjamaican.db:alembic
|
||||||
% if wants_continuum:
|
|
||||||
version_locations = ${pkg_name}.db:alembic/versions wutta_continuum.db:alembic/versions wuttjamaican.db:alembic/versions
|
|
||||||
% else:
|
|
||||||
version_locations = ${pkg_name}.db:alembic/versions wuttjamaican.db:alembic/versions
|
version_locations = ${pkg_name}.db:alembic/versions wuttjamaican.db:alembic/versions
|
||||||
% endif
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="sectiongroup_logging()">
|
<%def name="sectiongroup_logging()">
|
||||||
|
|
|
||||||
|
|
@ -64,119 +64,25 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
|
|
||||||
def test_do_install_steps(self):
|
def test_do_install_steps(self):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
db_url = f"sqlite:///{self.tempdir}/poser.sqlite"
|
handler.templates = TemplateLookup(
|
||||||
|
directories=[
|
||||||
with patch.object(handler, "prompt_user_for_context") as prompt_user:
|
self.app.resource_path("wuttjamaican:templates/install"),
|
||||||
prompt_user.return_value = {"db_url": db_url, "wants_continuum": False}
|
]
|
||||||
with patch.object(handler, "make_appdir") as make_appdir:
|
)
|
||||||
with patch.object(handler, "install_db_schema") as install_schema:
|
|
||||||
|
|
||||||
# nb. just for sanity/coverage
|
|
||||||
self.assertFalse(handler.schema_installed)
|
|
||||||
install_schema.return_value = True
|
|
||||||
handler.do_install_steps()
|
|
||||||
prompt_user.assert_called_once()
|
|
||||||
make_appdir.assert_called_once()
|
|
||||||
install_schema.assert_called_once_with(db_url)
|
|
||||||
self.assertTrue(handler.schema_installed)
|
|
||||||
|
|
||||||
def test_prompt_user_for_context(self):
|
|
||||||
db_url = f"sqlite:///{self.tempdir}/poser.sqlite"
|
|
||||||
with patch.object(mod.InstallHandler, "get_db_url", return_value=db_url):
|
|
||||||
|
|
||||||
# should prompt for continuum by default
|
|
||||||
handler = self.make_handler()
|
|
||||||
with patch.object(handler, "prompt_bool") as prompt_bool:
|
|
||||||
prompt_bool.return_value = True
|
|
||||||
context = handler.prompt_user_for_context()
|
|
||||||
prompt_bool.assert_called_once_with(
|
|
||||||
"use continuum for data versioning?", default=False
|
|
||||||
)
|
|
||||||
self.assertEqual(context, {"db_url": db_url, "wants_continuum": True})
|
|
||||||
|
|
||||||
# should not prompt if continuum flag already true
|
|
||||||
handler = self.make_handler()
|
|
||||||
with patch.object(handler, "wants_continuum", new=True):
|
|
||||||
with patch.object(handler, "prompt_bool") as prompt_bool:
|
|
||||||
context = handler.prompt_user_for_context()
|
|
||||||
prompt_bool.assert_not_called()
|
|
||||||
self.assertEqual(
|
|
||||||
context, {"db_url": db_url, "wants_continuum": True}
|
|
||||||
)
|
|
||||||
|
|
||||||
# should not prompt if continuum flag already false
|
|
||||||
handler = self.make_handler()
|
|
||||||
with patch.object(handler, "wants_continuum", new=False):
|
|
||||||
with patch.object(handler, "prompt_bool") as prompt_bool:
|
|
||||||
context = handler.prompt_user_for_context()
|
|
||||||
prompt_bool.assert_not_called()
|
|
||||||
self.assertEqual(
|
|
||||||
context, {"db_url": db_url, "wants_continuum": False}
|
|
||||||
)
|
|
||||||
|
|
||||||
# should not prompt if continuum pkg missing...
|
|
||||||
handler = self.make_handler()
|
|
||||||
with patch("builtins.__import__", side_effect=ImportError):
|
|
||||||
with patch.object(handler, "prompt_bool") as prompt_bool:
|
|
||||||
context = handler.prompt_user_for_context()
|
|
||||||
prompt_bool.assert_not_called()
|
|
||||||
self.assertEqual(
|
|
||||||
context, {"db_url": db_url, "wants_continuum": False}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_db_url(self):
|
|
||||||
try:
|
|
||||||
import sqlalchemy
|
|
||||||
from wuttjamaican.db.util import SA2
|
|
||||||
except ImportError:
|
|
||||||
pytest.skip("test is not relevant without sqlalchemy")
|
|
||||||
|
|
||||||
handler = self.make_handler()
|
|
||||||
|
|
||||||
# url from dbinfo is returned, if present
|
|
||||||
dbinfo = {"db_url": "sqlite:///"}
|
|
||||||
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
|
|
||||||
db_url = handler.get_db_url()
|
|
||||||
self.assertEqual(db_url, "sqlite:///")
|
|
||||||
|
|
||||||
# or url will be assembled from dbinfo parts
|
|
||||||
dbinfo = {
|
dbinfo = {
|
||||||
"dbtype": "postgresql",
|
"dburl": f"sqlite:///{self.tempdir}/poser.sqlite",
|
||||||
"dbhost": "localhost",
|
|
||||||
"dbport": 5432,
|
|
||||||
"dbname": "poser",
|
|
||||||
"dbuser": "poser",
|
|
||||||
"dbpass": "seekrit",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
|
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
|
||||||
with patch.object(handler, "test_db_connection", return_value=None):
|
with patch.object(handler, "make_appdir") as make_appdir:
|
||||||
db_url = handler.get_db_url()
|
with patch.object(handler, "install_db_schema") as install_db_schema:
|
||||||
seekrit = "***" if SA2 else "seekrit"
|
# nb. just for sanity/coverage
|
||||||
self.assertEqual(
|
install_db_schema.return_value = True
|
||||||
str(db_url),
|
self.assertFalse(handler.schema_installed)
|
||||||
f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser",
|
handler.do_install_steps()
|
||||||
)
|
self.assertTrue(make_appdir.called)
|
||||||
|
self.assertTrue(handler.schema_installed)
|
||||||
# now we test the "test db connection" feature
|
install_db_schema.assert_called_once_with(dbinfo["dburl"])
|
||||||
dbinfo = {"db_url": "sqlite:///"}
|
|
||||||
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
|
|
||||||
with patch.object(handler, "test_db_connection") as test_db_connection:
|
|
||||||
with patch.object(handler, "rprint") as rprint:
|
|
||||||
with patch.object(mod, "sys") as sys:
|
|
||||||
|
|
||||||
# pretend user gave bad dbinfo; should exit
|
|
||||||
test_db_connection.return_value = "bad dbinfo"
|
|
||||||
sys.exit.side_effect = RuntimeError
|
|
||||||
self.assertRaises(RuntimeError, handler.get_db_url)
|
|
||||||
sys.exit.assert_called_once_with(1)
|
|
||||||
|
|
||||||
# pretend user gave good dbinfo
|
|
||||||
sys.exit.reset_mock()
|
|
||||||
test_db_connection.return_value = None
|
|
||||||
db_url = handler.get_db_url()
|
|
||||||
self.assertFalse(sys.exit.called)
|
|
||||||
rprint.assert_called_with("[bold green]good[/bold green]")
|
|
||||||
self.assertEqual(str(db_url), "sqlite:///")
|
|
||||||
|
|
||||||
def test_get_dbinfo(self):
|
def test_get_dbinfo(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -195,20 +101,28 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
return "seekrit"
|
return "seekrit"
|
||||||
return default
|
return default
|
||||||
|
|
||||||
with patch.object(handler, "prompt_generic", side_effect=prompt_generic):
|
with patch.object(mod, "sys") as sys:
|
||||||
|
with patch.object(handler, "prompt_generic", side_effect=prompt_generic):
|
||||||
|
with patch.object(handler, "test_db_connection") as test_db_connection:
|
||||||
|
with patch.object(handler, "rprint") as rprint:
|
||||||
|
# bad dbinfo
|
||||||
|
test_db_connection.return_value = "bad dbinfo"
|
||||||
|
sys.exit.side_effect = RuntimeError
|
||||||
|
self.assertRaises(RuntimeError, handler.get_dbinfo)
|
||||||
|
sys.exit.assert_called_once_with(1)
|
||||||
|
|
||||||
dbinfo = handler.get_dbinfo()
|
seekrit = "***" if SA2 else "seekrit"
|
||||||
self.assertEqual(
|
|
||||||
dbinfo,
|
# good dbinfo
|
||||||
{
|
sys.exit.reset_mock()
|
||||||
"dbtype": "postgresql",
|
test_db_connection.return_value = None
|
||||||
"dbhost": "localhost",
|
dbinfo = handler.get_dbinfo()
|
||||||
"dbport": "5432",
|
self.assertFalse(sys.exit.called)
|
||||||
"dbname": "poser",
|
rprint.assert_called_with("[bold green]good[/bold green]")
|
||||||
"dbuser": "poser",
|
self.assertEqual(
|
||||||
"dbpass": "seekrit",
|
str(dbinfo["dburl"]),
|
||||||
},
|
f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_make_db_url(self):
|
def test_make_db_url(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -222,28 +136,14 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
seekrit = "***" if SA2 else "seekrit"
|
seekrit = "***" if SA2 else "seekrit"
|
||||||
|
|
||||||
url = handler.make_db_url(
|
url = handler.make_db_url(
|
||||||
dict(
|
"postgresql", "localhost", "5432", "poser", "poser", "seekrit"
|
||||||
dbtype="postgresql",
|
|
||||||
dbhost="localhost",
|
|
||||||
dbport="5432",
|
|
||||||
dbname="poser",
|
|
||||||
dbuser="poser",
|
|
||||||
dbpass="seekrit",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(url), f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser"
|
str(url), f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser"
|
||||||
)
|
)
|
||||||
|
|
||||||
url = handler.make_db_url(
|
url = handler.make_db_url(
|
||||||
dict(
|
"mysql", "localhost", "3306", "poser", "poser", "seekrit"
|
||||||
dbtype="mysql",
|
|
||||||
dbhost="localhost",
|
|
||||||
dbport="3306",
|
|
||||||
dbname="poser",
|
|
||||||
dbuser="poser",
|
|
||||||
dbpass="seekrit",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(url), f"mysql+mysqlconnector://poser:{seekrit}@localhost:3306/poser"
|
str(url), f"mysql+mysqlconnector://poser:{seekrit}@localhost:3306/poser"
|
||||||
|
|
@ -272,8 +172,8 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
|
||||||
# can handle dburl as string
|
# can handle dburl as string
|
||||||
db_url = "sqlite:///poser.sqlite"
|
dbinfo = {"dburl": "sqlite:///poser.sqlite"}
|
||||||
context = handler.make_template_context(db_url=db_url)
|
context = handler.make_template_context(dbinfo)
|
||||||
self.assertEqual(context["envdir"], sys.prefix)
|
self.assertEqual(context["envdir"], sys.prefix)
|
||||||
self.assertEqual(context["pkg_name"], "poser")
|
self.assertEqual(context["pkg_name"], "poser")
|
||||||
self.assertEqual(context["app_title"], "poser")
|
self.assertEqual(context["app_title"], "poser")
|
||||||
|
|
@ -288,8 +188,8 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
pytest.skip("remainder of test is not relevant without sqlalchemy")
|
pytest.skip("remainder of test is not relevant without sqlalchemy")
|
||||||
|
|
||||||
# but also can handle dburl as object
|
# but also can handle dburl as object
|
||||||
db_url = sa.create_engine("sqlite:///poser.sqlite").url
|
dbinfo = {"dburl": sa.create_engine("sqlite:///poser.sqlite").url}
|
||||||
context = handler.make_template_context(db_url=db_url)
|
context = handler.make_template_context(dbinfo)
|
||||||
self.assertEqual(context["envdir"], sys.prefix)
|
self.assertEqual(context["envdir"], sys.prefix)
|
||||||
self.assertEqual(context["pkg_name"], "poser")
|
self.assertEqual(context["pkg_name"], "poser")
|
||||||
self.assertEqual(context["app_title"], "poser")
|
self.assertEqual(context["app_title"], "poser")
|
||||||
|
|
@ -305,8 +205,8 @@ class TestInstallHandler(ConfigTestCase):
|
||||||
self.app.resource_path("wuttjamaican:templates/install"),
|
self.app.resource_path("wuttjamaican:templates/install"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
db_url = "sqlite:///poser.sqlite"
|
dbinfo = {"dburl": "sqlite:///poser.sqlite"}
|
||||||
context = handler.make_template_context(db_url=db_url)
|
context = handler.make_template_context(dbinfo)
|
||||||
handler.make_appdir(context, appdir=self.tempdir)
|
handler.make_appdir(context, appdir=self.tempdir)
|
||||||
wutta_conf = os.path.join(self.tempdir, "wutta.conf")
|
wutta_conf = os.path.join(self.tempdir, "wutta.conf")
|
||||||
with open(wutta_conf, "rt") as f:
|
with open(wutta_conf, "rt") as f:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue