3
0
Fork 0

fix: prompt for continuum support in app installer

unless installer declares static preference, then do not prompt.

also, do not prompt if continuum packages are missing.
This commit is contained in:
Lance Edgar 2026-01-04 22:47:32 -06:00
parent d018d4e764
commit dd56fbcc2d
3 changed files with 255 additions and 95 deletions

View file

@ -64,25 +64,119 @@ class TestInstallHandler(ConfigTestCase):
def test_do_install_steps(self):
handler = self.make_handler()
handler.templates = TemplateLookup(
directories=[
self.app.resource_path("wuttjamaican:templates/install"),
]
)
dbinfo = {
"dburl": f"sqlite:///{self.tempdir}/poser.sqlite",
}
db_url = f"sqlite:///{self.tempdir}/poser.sqlite"
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
with patch.object(handler, "prompt_user_for_context") as prompt_user:
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_db_schema:
with patch.object(handler, "install_db_schema") as install_schema:
# nb. just for sanity/coverage
install_db_schema.return_value = True
self.assertFalse(handler.schema_installed)
install_schema.return_value = True
handler.do_install_steps()
self.assertTrue(make_appdir.called)
prompt_user.assert_called_once()
make_appdir.assert_called_once()
install_schema.assert_called_once_with(db_url)
self.assertTrue(handler.schema_installed)
install_db_schema.assert_called_once_with(dbinfo["dburl"])
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 = {
"dbtype": "postgresql",
"dbhost": "localhost",
"dbport": 5432,
"dbname": "poser",
"dbuser": "poser",
"dbpass": "seekrit",
}
with patch.object(handler, "get_dbinfo", return_value=dbinfo):
with patch.object(handler, "test_db_connection", return_value=None):
db_url = handler.get_db_url()
seekrit = "***" if SA2 else "seekrit"
self.assertEqual(
str(db_url),
f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser",
)
# now we test the "test db connection" feature
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):
try:
@ -101,28 +195,20 @@ class TestInstallHandler(ConfigTestCase):
return "seekrit"
return default
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)
with patch.object(handler, "prompt_generic", side_effect=prompt_generic):
seekrit = "***" if SA2 else "seekrit"
# good dbinfo
sys.exit.reset_mock()
test_db_connection.return_value = None
dbinfo = handler.get_dbinfo()
self.assertFalse(sys.exit.called)
rprint.assert_called_with("[bold green]good[/bold green]")
self.assertEqual(
str(dbinfo["dburl"]),
f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser",
)
dbinfo = handler.get_dbinfo()
self.assertEqual(
dbinfo,
{
"dbtype": "postgresql",
"dbhost": "localhost",
"dbport": "5432",
"dbname": "poser",
"dbuser": "poser",
"dbpass": "seekrit",
},
)
def test_make_db_url(self):
try:
@ -136,14 +222,28 @@ class TestInstallHandler(ConfigTestCase):
seekrit = "***" if SA2 else "seekrit"
url = handler.make_db_url(
"postgresql", "localhost", "5432", "poser", "poser", "seekrit"
dict(
dbtype="postgresql",
dbhost="localhost",
dbport="5432",
dbname="poser",
dbuser="poser",
dbpass="seekrit",
)
)
self.assertEqual(
str(url), f"postgresql+psycopg2://poser:{seekrit}@localhost:5432/poser"
)
url = handler.make_db_url(
"mysql", "localhost", "3306", "poser", "poser", "seekrit"
dict(
dbtype="mysql",
dbhost="localhost",
dbport="3306",
dbname="poser",
dbuser="poser",
dbpass="seekrit",
)
)
self.assertEqual(
str(url), f"mysql+mysqlconnector://poser:{seekrit}@localhost:3306/poser"
@ -172,8 +272,8 @@ class TestInstallHandler(ConfigTestCase):
handler = self.make_handler()
# can handle dburl as string
dbinfo = {"dburl": "sqlite:///poser.sqlite"}
context = handler.make_template_context(dbinfo)
db_url = "sqlite:///poser.sqlite"
context = handler.make_template_context(db_url=db_url)
self.assertEqual(context["envdir"], sys.prefix)
self.assertEqual(context["pkg_name"], "poser")
self.assertEqual(context["app_title"], "poser")
@ -188,8 +288,8 @@ class TestInstallHandler(ConfigTestCase):
pytest.skip("remainder of test is not relevant without sqlalchemy")
# but also can handle dburl as object
dbinfo = {"dburl": sa.create_engine("sqlite:///poser.sqlite").url}
context = handler.make_template_context(dbinfo)
db_url = sa.create_engine("sqlite:///poser.sqlite").url
context = handler.make_template_context(db_url=db_url)
self.assertEqual(context["envdir"], sys.prefix)
self.assertEqual(context["pkg_name"], "poser")
self.assertEqual(context["app_title"], "poser")
@ -205,8 +305,8 @@ class TestInstallHandler(ConfigTestCase):
self.app.resource_path("wuttjamaican:templates/install"),
]
)
dbinfo = {"dburl": "sqlite:///poser.sqlite"}
context = handler.make_template_context(dbinfo)
db_url = "sqlite:///poser.sqlite"
context = handler.make_template_context(db_url=db_url)
handler.make_appdir(context, appdir=self.tempdir)
wutta_conf = os.path.join(self.tempdir, "wutta.conf")
with open(wutta_conf, "rt") as f: