test: improve some tests, docs for rattail.config
module
still some left to do, just a stopping point
This commit is contained in:
parent
937f54681f
commit
c5c8c06ea7
|
@ -27,6 +27,7 @@
|
||||||
model.batch.product
|
model.batch.product
|
||||||
model.batch.purchase
|
model.batch.purchase
|
||||||
model.batch.vendorcatalog
|
model.batch.vendorcatalog
|
||||||
|
model.core
|
||||||
model.customers
|
model.customers
|
||||||
model.datasync
|
model.datasync
|
||||||
model.labels
|
model.labels
|
||||||
|
|
33
docs/api/rattail/db/model.core.rst
Normal file
33
docs/api/rattail/db/model.core.rst
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
``rattail.db.model.core``
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: rattail.db.model.core
|
||||||
|
|
||||||
|
.. autoclass:: ModelBase
|
||||||
|
|
||||||
|
.. attribute:: model_title
|
||||||
|
|
||||||
|
Optionally set this to a "humanized" version of the model name, for
|
||||||
|
display in templates etc. Default value will be guessed from the model
|
||||||
|
class name, e.g. 'Product' => "Products" and 'CustomerOrder' => "Customer
|
||||||
|
Order".
|
||||||
|
|
||||||
|
.. attribute:: model_title_plural
|
||||||
|
|
||||||
|
Optionally set this to a "humanized" version of the *plural* model name,
|
||||||
|
for display in templates etc. Default value will be guessed from the
|
||||||
|
model class name, e.g. 'Product' => "Products" and 'CustomerOrder' =>
|
||||||
|
"Customer Orders".
|
||||||
|
|
||||||
|
.. autoclass:: Setting
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Change
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: rattail.db.model.contact
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
Geez a lot of work left here...
|
|
@ -2,32 +2,4 @@
|
||||||
``rattail.db.model``
|
``rattail.db.model``
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. automodule:: rattail.db.model.core
|
.. automodule:: rattail.db.model
|
||||||
|
|
||||||
.. autoclass:: ModelBase
|
|
||||||
|
|
||||||
.. attribute:: model_title
|
|
||||||
|
|
||||||
Optionally set this to a "humanized" version of the model name, for
|
|
||||||
display in templates etc. Default value will be guessed from the model
|
|
||||||
class name, e.g. 'Product' => "Products" and 'CustomerOrder' => "Customer
|
|
||||||
Order".
|
|
||||||
|
|
||||||
.. attribute:: model_title_plural
|
|
||||||
|
|
||||||
Optionally set this to a "humanized" version of the *plural* model name,
|
|
||||||
for display in templates etc. Default value will be guessed from the
|
|
||||||
model class name, e.g. 'Product' => "Products" and 'CustomerOrder' =>
|
|
||||||
"Customer Orders".
|
|
||||||
|
|
||||||
.. autoclass:: Setting
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: Change
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: rattail.db.model.contact
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. todo::
|
|
||||||
Geez a lot of work left here...
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
from importlib_metadata import version
|
from importlib_metadata import version
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ from wuttjamaican.conf import (WuttaConfig, WuttaConfigExtension,
|
||||||
generic_default_files)
|
generic_default_files)
|
||||||
from wuttjamaican.util import (parse_bool as wutta_parse_bool,
|
from wuttjamaican.util import (parse_bool as wutta_parse_bool,
|
||||||
parse_list as wutta_parse_list)
|
parse_list as wutta_parse_list)
|
||||||
|
from wuttjamaican.exc import ConfigurationError as WuttaConfigurationError
|
||||||
|
|
||||||
from rattail.exceptions import WindowsExtensionsNotInstalled, ConfigurationError
|
from rattail.exceptions import WindowsExtensionsNotInstalled, ConfigurationError
|
||||||
|
|
||||||
|
@ -44,13 +45,7 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def parse_bool(value): # pragma: no cover
|
def parse_bool(value): # pragma: no cover
|
||||||
"""
|
""" """
|
||||||
Compatibility wrapper for
|
|
||||||
:func:`wuttjamaican:wuttjamaican.util.parse_bool()`.
|
|
||||||
|
|
||||||
This function is deprecated; new code should use the upstream
|
|
||||||
function instead.
|
|
||||||
"""
|
|
||||||
warnings.warn("rattail.config.parse_bool() is deprecated; "
|
warnings.warn("rattail.config.parse_bool() is deprecated; "
|
||||||
"please use wuttjamaican.util.parse_bool() instead",
|
"please use wuttjamaican.util.parse_bool() instead",
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
|
@ -58,13 +53,7 @@ def parse_bool(value): # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def parse_list(value): # pragma: no cover
|
def parse_list(value): # pragma: no cover
|
||||||
"""
|
""" """
|
||||||
Compatibility wrapper for
|
|
||||||
:func:`wuttjamaican:wuttjamaican.util.parse_list()`.
|
|
||||||
|
|
||||||
This function is deprecated; new code should use the upstream
|
|
||||||
function instead.
|
|
||||||
"""
|
|
||||||
warnings.warn("rattail.config.parse_list() is deprecated; "
|
warnings.warn("rattail.config.parse_list() is deprecated; "
|
||||||
"please use wuttjamaican.util.parse_list() instead",
|
"please use wuttjamaican.util.parse_list() instead",
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
|
@ -120,7 +109,8 @@ class RattailConfig(WuttaConfig):
|
||||||
normally expects just ``(key, value)`` args, but we also (for
|
normally expects just ``(key, value)`` args, but we also (for
|
||||||
now) support the older style of ``(section, option, value)`` -
|
now) support the older style of ``(section, option, value)`` -
|
||||||
*eventually* that will go away but probably not in the near
|
*eventually* that will go away but probably not in the near
|
||||||
future.
|
future. However new code should pass ``(key, value)`` since
|
||||||
|
that is now the preferred signature.
|
||||||
"""
|
"""
|
||||||
# figure out what sort of args were passed
|
# figure out what sort of args were passed
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
|
@ -143,7 +133,8 @@ class RattailConfig(WuttaConfig):
|
||||||
normally expects just ``(key, ...)`` args, but we also (for
|
normally expects just ``(key, ...)`` args, but we also (for
|
||||||
now) support the older style of ``(section, option, ...)`` -
|
now) support the older style of ``(section, option, ...)`` -
|
||||||
*eventually* that will go away but probably not in the near
|
*eventually* that will go away but probably not in the near
|
||||||
future.
|
future. However new code should pass ``(key, ...)`` since
|
||||||
|
that is now the preferred signature.
|
||||||
"""
|
"""
|
||||||
# figure out what sort of args were passed
|
# figure out what sort of args were passed
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
|
@ -171,6 +162,30 @@ class RattailConfig(WuttaConfig):
|
||||||
# DeprecationWarning, stacklevel=2)
|
# DeprecationWarning, stacklevel=2)
|
||||||
return self.get_bool(*args, **kwargs)
|
return self.get_bool(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_date(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Retrieve a date value from config.
|
||||||
|
|
||||||
|
Accepts same params as :meth:`get()` but if a value is found,
|
||||||
|
it will be coerced to date via
|
||||||
|
:meth:`rattail.app.AppHandler.parse_date()`.
|
||||||
|
"""
|
||||||
|
value = self.get(*args, **kwargs)
|
||||||
|
app = self.get_app()
|
||||||
|
return app.parse_date(value)
|
||||||
|
|
||||||
|
def getdate(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Backward-compatible alias for :meth:`get_date()`.
|
||||||
|
|
||||||
|
New code should use ``get_date()`` instead of this method.
|
||||||
|
"""
|
||||||
|
# TODO: eventually
|
||||||
|
# warnings.warn("config.getbool() method is deprecated; "
|
||||||
|
# "please use config.get_bool() instead",
|
||||||
|
# DeprecationWarning, stacklevel=2)
|
||||||
|
return self.get_date(*args, **kwargs)
|
||||||
|
|
||||||
def getint(self, *args, **kwargs):
|
def getint(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Backward-compatible alias for
|
Backward-compatible alias for
|
||||||
|
@ -201,30 +216,14 @@ class RattailConfig(WuttaConfig):
|
||||||
"""
|
"""
|
||||||
Convenience method around the
|
Convenience method around the
|
||||||
:func:`~wuttjamaican:wuttjamaican.util.parse_bool()` function.
|
:func:`~wuttjamaican:wuttjamaican.util.parse_bool()` function.
|
||||||
|
|
||||||
Usage of this method is discouraged, at least until some more
|
|
||||||
dust settles. This probably belongs on the app handler
|
|
||||||
instead so it can be overridden more easily.
|
|
||||||
"""
|
"""
|
||||||
# TODO: eventually
|
|
||||||
# warnings.warn("config.parse_bool() method is deprecated; "
|
|
||||||
# "please use app.parse_bool() instead",
|
|
||||||
# DeprecationWarning, stacklevel=2)
|
|
||||||
return wutta_parse_bool(value)
|
return wutta_parse_bool(value)
|
||||||
|
|
||||||
def parse_list(self, value):
|
def parse_list(self, value):
|
||||||
"""
|
"""
|
||||||
Convenience method around the
|
Convenience method around the
|
||||||
:func:`~wuttjamaican:wuttjamaican.util.parse_list()` function.
|
:func:`~wuttjamaican:wuttjamaican.util.parse_list()` function.
|
||||||
|
|
||||||
Usage of this method is discouraged, at least until some more
|
|
||||||
dust settles. This probably belongs on the app handler
|
|
||||||
instead so it can be overridden more easily.
|
|
||||||
"""
|
"""
|
||||||
# TODO: eventually
|
|
||||||
# warnings.warn("config.parse_list() method is deprecated; "
|
|
||||||
# "please use app.parse_list() instead",
|
|
||||||
# DeprecationWarning, stacklevel=2)
|
|
||||||
return wutta_parse_list(value)
|
return wutta_parse_list(value)
|
||||||
|
|
||||||
def make_list_string(self, values):
|
def make_list_string(self, values):
|
||||||
|
@ -254,7 +253,7 @@ class RattailConfig(WuttaConfig):
|
||||||
|
|
||||||
def beaker_invalidate_setting(self, name):
|
def beaker_invalidate_setting(self, name):
|
||||||
"""
|
"""
|
||||||
Backward-compatible method for unused Beaker caching logic.
|
Deprecated method for unused Beaker caching logic.
|
||||||
|
|
||||||
This method has no effect and should not be used.
|
This method has no effect and should not be used.
|
||||||
"""
|
"""
|
||||||
|
@ -262,18 +261,6 @@ class RattailConfig(WuttaConfig):
|
||||||
# warnings.warn("config.beaker_invalidate_setting() method is deprecated",
|
# warnings.warn("config.beaker_invalidate_setting() method is deprecated",
|
||||||
# DeprecationWarning, stacklevel=2)
|
# DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
def node_type(self, default=None):
|
|
||||||
"""
|
|
||||||
Returns the "type" of current node. What this means will
|
|
||||||
generally depend on the app logic.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.require('rattail', 'node_type', usedb=False)
|
|
||||||
except ConfigurationError:
|
|
||||||
if default:
|
|
||||||
return default
|
|
||||||
raise
|
|
||||||
|
|
||||||
def production(self):
|
def production(self):
|
||||||
"""
|
"""
|
||||||
Returns boolean indicating whether the app is running in
|
Returns boolean indicating whether the app is running in
|
||||||
|
@ -281,10 +268,23 @@ class RattailConfig(WuttaConfig):
|
||||||
"""
|
"""
|
||||||
return self.getbool('rattail', 'production', default=False)
|
return self.getbool('rattail', 'production', default=False)
|
||||||
|
|
||||||
|
def node_type(self, default=None):
|
||||||
|
"""
|
||||||
|
Returns the "type" of current node as string. What this means
|
||||||
|
will generally depend on the app logic. There is no default
|
||||||
|
node type unless caller provides one.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.require('rattail', 'node_type', usedb=False)
|
||||||
|
except WuttaConfigurationError:
|
||||||
|
if default:
|
||||||
|
return default
|
||||||
|
raise
|
||||||
|
|
||||||
def get_model(self):
|
def get_model(self):
|
||||||
"""
|
"""
|
||||||
Returns a reference to configured 'model' module; defaults to
|
Returns a reference to the module containing data models;
|
||||||
:mod:`rattail.db.model`.
|
defaults to :mod:`rattail.db.model`.
|
||||||
"""
|
"""
|
||||||
spec = self.get('rattail', 'model', usedb=False,
|
spec = self.get('rattail', 'model', usedb=False,
|
||||||
default='rattail.db.model')
|
default='rattail.db.model')
|
||||||
|
@ -303,7 +303,8 @@ class RattailConfig(WuttaConfig):
|
||||||
"""
|
"""
|
||||||
Returns a reference to the configured data 'model' module for
|
Returns a reference to the configured data 'model' module for
|
||||||
Trainwreck. Note that there is *not* a default value for
|
Trainwreck. Note that there is *not* a default value for
|
||||||
this; it must be configured.
|
this; it must be configured or else calling this method will
|
||||||
|
result in an error.
|
||||||
"""
|
"""
|
||||||
spec = self.require('rattail.trainwreck', 'model', usedb=False)
|
spec = self.require('rattail.trainwreck', 'model', usedb=False)
|
||||||
return importlib.import_module(spec)
|
return importlib.import_module(spec)
|
||||||
|
@ -315,29 +316,15 @@ class RattailConfig(WuttaConfig):
|
||||||
return self.getbool('rattail.db', 'versioning.enabled', usedb=False,
|
return self.getbool('rattail.db', 'versioning.enabled', usedb=False,
|
||||||
default=False)
|
default=False)
|
||||||
|
|
||||||
def getdate(self, *args, **kwargs):
|
def product_key(self, **kwargs): # pragma: no cover
|
||||||
"""
|
""" """
|
||||||
Retrieve a date value from config.
|
|
||||||
"""
|
|
||||||
value = self.get(*args, **kwargs)
|
|
||||||
app = self.get_app()
|
|
||||||
return app.parse_date(value)
|
|
||||||
|
|
||||||
def product_key(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Deprecated; instead please see
|
|
||||||
:meth:`rattail.app.AppHandler.get_product_key_field()`.
|
|
||||||
"""
|
|
||||||
warnings.warn("config.product_key() is deprecated; please "
|
warnings.warn("config.product_key() is deprecated; please "
|
||||||
"use app.get_product_key_field() instead",
|
"use app.get_product_key_field() instead",
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
return self.get_app().get_product_key_field()
|
return self.get_app().get_product_key_field()
|
||||||
|
|
||||||
def product_key_title(self, key=None):
|
def product_key_title(self, key=None): # pragma: no cover
|
||||||
"""
|
""" """
|
||||||
Deprecated; instead please see
|
|
||||||
:meth:`rattail.app.AppHandler.get_product_key_label()`.
|
|
||||||
"""
|
|
||||||
warnings.warn("config.product_key_title() is deprecated; please "
|
warnings.warn("config.product_key_title() is deprecated; please "
|
||||||
"use app.get_product_key_label() instead",
|
"use app.get_product_key_label() instead",
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
|
@ -352,14 +339,20 @@ class RattailConfig(WuttaConfig):
|
||||||
return self.get('rattail', 'app_package', default=default)
|
return self.get('rattail', 'app_package', default=default)
|
||||||
|
|
||||||
def app_title(self, **kwargs):
|
def app_title(self, **kwargs):
|
||||||
""" DEPRECATED """
|
"""
|
||||||
|
DEPRECATED; use :meth:`rattail.app.AppHandler.get_title()`
|
||||||
|
instead.
|
||||||
|
"""
|
||||||
# TODO: should put a deprecation warning here, but it could
|
# TODO: should put a deprecation warning here, but it could
|
||||||
# make things noisy for a while and i'm not ready for that
|
# make things noisy for a while and i'm not ready for that
|
||||||
app = self.get_app()
|
app = self.get_app()
|
||||||
return app.get_title(**kwargs)
|
return app.get_title(**kwargs)
|
||||||
|
|
||||||
def node_title(self, **kwargs):
|
def node_title(self, **kwargs):
|
||||||
""" DEPRECATED """
|
"""
|
||||||
|
DEPRECATED; use
|
||||||
|
:meth:`rattail.app.AppHandler.get_node_title()` instead.
|
||||||
|
"""
|
||||||
# TODO: should put a deprecation warning here, but it could
|
# TODO: should put a deprecation warning here, but it could
|
||||||
# make things noisy for a while and i'm not ready for that
|
# make things noisy for a while and i'm not ready for that
|
||||||
app = self.get_app()
|
app = self.get_app()
|
||||||
|
@ -369,18 +362,28 @@ class RattailConfig(WuttaConfig):
|
||||||
"""
|
"""
|
||||||
Returns boolean indicating whether the app is running from
|
Returns boolean indicating whether the app is running from
|
||||||
source, as opposed to official release.
|
source, as opposed to official release.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The utility of this method is questionable and it ideally
|
||||||
|
will go away in the future.
|
||||||
"""
|
"""
|
||||||
return self.getbool('rattail', 'running_from_source', default=False)
|
return self.getbool('rattail', 'running_from_source', default=False)
|
||||||
|
|
||||||
def demo(self):
|
def demo(self):
|
||||||
"""
|
"""
|
||||||
Returns boolean indicating whether the app is running in demo mode
|
Returns boolean indicating whether the app is running in demo mode
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The utility of this method is questionable and it ideally
|
||||||
|
will go away in the future.
|
||||||
"""
|
"""
|
||||||
return self.getbool('rattail', 'demo', default=False)
|
return self.getbool('rattail', 'demo', default=False)
|
||||||
|
|
||||||
def appdir(self, require=True, **kwargs):
|
def appdir(self, require=True, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns path to the 'app' dir, if known.
|
Returns path to the local 'app' dir.
|
||||||
"""
|
"""
|
||||||
if require:
|
if require:
|
||||||
path = os.path.join(sys.prefix, 'app')
|
path = os.path.join(sys.prefix, 'app')
|
||||||
|
@ -405,7 +408,7 @@ class RattailConfig(WuttaConfig):
|
||||||
def batch_filedir(self, key=None):
|
def batch_filedir(self, key=None):
|
||||||
"""
|
"""
|
||||||
Returns path to root folder where batches (optionally of type
|
Returns path to root folder where batches (optionally of type
|
||||||
'key') are stored.
|
``key``) are stored.
|
||||||
"""
|
"""
|
||||||
path = os.path.abspath(self.require('rattail', 'batch.files'))
|
path = os.path.abspath(self.require('rattail', 'batch.files'))
|
||||||
if key:
|
if key:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2023 Lance Edgar
|
# Copyright © 2010-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -22,6 +22,9 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
"""
|
"""
|
||||||
Rattail data models
|
Rattail data models
|
||||||
|
|
||||||
|
This namespace will be populated with all data model classes defined
|
||||||
|
by Rattail.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .core import Base, ModelBase, uuid_column, getset_factory, GPCType, Setting, Change, Note
|
from .core import Base, ModelBase, uuid_column, getset_factory, GPCType, Setting, Change, Note
|
||||||
|
|
|
@ -3,120 +3,17 @@
|
||||||
import configparser
|
import configparser
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from wuttjamaican.testing import FileConfigTestCase
|
from wuttjamaican.testing import FileConfigTestCase
|
||||||
|
from wuttjamaican.exc import ConfigurationError
|
||||||
|
|
||||||
from rattail import config
|
from rattail import config
|
||||||
from rattail.app import AppHandler
|
from rattail.app import AppHandler
|
||||||
|
|
||||||
|
|
||||||
class TestRattailConfig(FileConfigTestCase):
|
|
||||||
|
|
||||||
def setup_files(self):
|
|
||||||
self.site_path = self.write_file('site.conf', """
|
|
||||||
[rattail]
|
|
||||||
""")
|
|
||||||
|
|
||||||
self.host_path = self.write_file('host.conf', """
|
|
||||||
[rattail.config]
|
|
||||||
include = "{}"
|
|
||||||
""".format(self.site_path))
|
|
||||||
|
|
||||||
self.app_path = self.write_file('app.conf', """
|
|
||||||
[rattail.config]
|
|
||||||
include = "{}"
|
|
||||||
""".format(self.host_path))
|
|
||||||
|
|
||||||
self.custom_path = self.write_file('custom.conf', """
|
|
||||||
[rattail.config]
|
|
||||||
include = "%(here)s/app.conf"
|
|
||||||
""")
|
|
||||||
|
|
||||||
def test_init_defaults(self):
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
self.assertEqual(cfg.files_requested, [])
|
|
||||||
self.assertEqual(cfg.files_read, [])
|
|
||||||
|
|
||||||
def test_init_params(self):
|
|
||||||
self.setup_files()
|
|
||||||
|
|
||||||
# files
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
self.assertEqual(cfg.files_requested, [])
|
|
||||||
self.assertEqual(cfg.files_read, [])
|
|
||||||
cfg = config.RattailConfig(files=[self.site_path])
|
|
||||||
self.assertEqual(cfg.files_requested, [self.site_path])
|
|
||||||
self.assertEqual(cfg.files_read, [self.site_path])
|
|
||||||
|
|
||||||
# usedb
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
self.assertFalse(cfg.usedb)
|
|
||||||
cfg = config.RattailConfig(usedb=True)
|
|
||||||
self.assertTrue(cfg.usedb)
|
|
||||||
|
|
||||||
# preferdb
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
self.assertFalse(cfg.preferdb)
|
|
||||||
cfg = config.RattailConfig(preferdb=True)
|
|
||||||
self.assertTrue(cfg.preferdb)
|
|
||||||
|
|
||||||
def test_read_file_with_recurse(self):
|
|
||||||
self.setup_files()
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
cfg.read_file(self.custom_path, recurse=True)
|
|
||||||
self.assertEqual(cfg.files_requested, [self.custom_path, self.app_path, self.host_path, self.site_path])
|
|
||||||
self.assertEqual(cfg.files_read, [self.site_path, self.host_path, self.app_path, self.custom_path])
|
|
||||||
|
|
||||||
def test_read_file_once_only(self):
|
|
||||||
self.setup_files()
|
|
||||||
|
|
||||||
another_path = self.write_file('another.conf', """
|
|
||||||
[rattail.config]
|
|
||||||
include = "{custom}" "{site}" "{app}" "{site}" "{custom}"
|
|
||||||
""".format(custom=self.custom_path, app=self.app_path, site=self.site_path))
|
|
||||||
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
cfg.read_file(another_path, recurse=True)
|
|
||||||
self.assertEqual(cfg.files_requested, [another_path, self.custom_path, self.app_path, self.host_path, self.site_path])
|
|
||||||
self.assertEqual(cfg.files_read, [self.site_path, self.host_path, self.app_path, self.custom_path, another_path])
|
|
||||||
|
|
||||||
def test_read_file_skip_missing(self):
|
|
||||||
self.setup_files()
|
|
||||||
bogus_path = '/tmp/does-not/exist'
|
|
||||||
self.assertFalse(os.path.exists(bogus_path))
|
|
||||||
|
|
||||||
another_path = self.write_file('another.conf', """
|
|
||||||
[rattail.config]
|
|
||||||
include = "{bogus}" "{app}" "{bogus}" "{site}"
|
|
||||||
""".format(bogus=bogus_path, app=self.app_path, site=self.site_path))
|
|
||||||
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
cfg.read_file(another_path, recurse=True)
|
|
||||||
self.assertEqual(cfg.files_requested, [another_path, bogus_path, self.app_path, self.host_path, self.site_path])
|
|
||||||
self.assertEqual(cfg.files_read, [self.site_path, self.host_path, self.app_path, another_path])
|
|
||||||
|
|
||||||
@patch('rattail.config.logging.config.fileConfig')
|
|
||||||
def test_configure_logging(self, fileConfig):
|
|
||||||
cfg = config.RattailConfig()
|
|
||||||
|
|
||||||
# logging not configured by default
|
|
||||||
cfg.configure_logging()
|
|
||||||
self.assertFalse(fileConfig.called)
|
|
||||||
|
|
||||||
# but config option can enable it
|
|
||||||
cfg.set('rattail.config', 'configure_logging', 'true')
|
|
||||||
cfg.configure_logging()
|
|
||||||
self.assertEqual(fileConfig.call_count, 1)
|
|
||||||
fileConfig.reset_mock()
|
|
||||||
|
|
||||||
# invalid logging config is ignored
|
|
||||||
fileConfig.side_effect = configparser.NoSectionError('loggers')
|
|
||||||
cfg.configure_logging()
|
|
||||||
self.assertEqual(fileConfig.call_count, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class TestRattailConfig(FileConfigTestCase):
|
class TestRattailConfig(FileConfigTestCase):
|
||||||
|
|
||||||
def test_prioritized_files(self):
|
def test_prioritized_files(self):
|
||||||
|
@ -135,7 +32,6 @@ require = %(here)s/first.conf
|
||||||
self.assertEqual(len(files), 2)
|
self.assertEqual(len(files), 2)
|
||||||
self.assertEqual(files[0], second)
|
self.assertEqual(files[0], second)
|
||||||
self.assertEqual(files[1], first)
|
self.assertEqual(files[1], first)
|
||||||
self.assertIs(files, myconfig.get_prioritized_files())
|
|
||||||
|
|
||||||
def test_setdefault(self):
|
def test_setdefault(self):
|
||||||
myconfig = config.RattailConfig()
|
myconfig = config.RattailConfig()
|
||||||
|
@ -164,12 +60,42 @@ require = %(here)s/first.conf
|
||||||
# try that for get() too
|
# try that for get() too
|
||||||
self.assertRaises(ValueError, myconfig.get, 'foo', 'bar', 'blarg', 'blast')
|
self.assertRaises(ValueError, myconfig.get, 'foo', 'bar', 'blarg', 'blast')
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
myconfig.setdefault('foo.bar', 'baz')
|
||||||
|
|
||||||
|
# can pass section + option
|
||||||
|
self.assertEqual(myconfig.get('foo', 'bar'), 'baz')
|
||||||
|
|
||||||
|
# or can pass just a key
|
||||||
|
self.assertEqual(myconfig.get('foo.bar'), 'baz')
|
||||||
|
|
||||||
|
# so 1 or 2 args required, otherwise error
|
||||||
|
self.assertRaises(ValueError, myconfig.get, 'foo', 'bar', 'baz')
|
||||||
|
self.assertRaises(ValueError, myconfig.get)
|
||||||
|
|
||||||
def test_getbool(self):
|
def test_getbool(self):
|
||||||
myconfig = config.RattailConfig()
|
myconfig = config.RattailConfig()
|
||||||
self.assertFalse(myconfig.getbool('foo.bar'))
|
self.assertFalse(myconfig.getbool('foo.bar'))
|
||||||
myconfig.setdefault('foo.bar', 'true')
|
myconfig.setdefault('foo.bar', 'true')
|
||||||
self.assertTrue(myconfig.getbool('foo.bar'))
|
self.assertTrue(myconfig.getbool('foo.bar'))
|
||||||
|
|
||||||
|
def test_get_date(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
self.assertIsNone(myconfig.get_date('foo.date'))
|
||||||
|
myconfig.setdefault('foo.date', '2023-11-20')
|
||||||
|
value = myconfig.get_date('foo.date')
|
||||||
|
self.assertIsInstance(value, datetime.date)
|
||||||
|
self.assertEqual(value, datetime.date(2023, 11, 20))
|
||||||
|
|
||||||
|
def test_getdate(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
self.assertIsNone(myconfig.getdate('foo.date'))
|
||||||
|
myconfig.setdefault('foo.date', '2023-11-20')
|
||||||
|
value = myconfig.getdate('foo.date')
|
||||||
|
self.assertIsInstance(value, datetime.date)
|
||||||
|
self.assertEqual(value, datetime.date(2023, 11, 20))
|
||||||
|
|
||||||
def test_getint(self):
|
def test_getint(self):
|
||||||
myconfig = config.RattailConfig()
|
myconfig = config.RattailConfig()
|
||||||
self.assertIsNone(myconfig.getint('foo.bar'))
|
self.assertIsNone(myconfig.getint('foo.bar'))
|
||||||
|
@ -182,20 +108,6 @@ require = %(here)s/first.conf
|
||||||
myconfig.setdefault('foo.bar', 'hello world')
|
myconfig.setdefault('foo.bar', 'hello world')
|
||||||
self.assertEqual(myconfig.getlist('foo.bar'), ['hello', 'world'])
|
self.assertEqual(myconfig.getlist('foo.bar'), ['hello', 'world'])
|
||||||
|
|
||||||
def test_getdate(self):
|
|
||||||
myconfig = config.RattailConfig()
|
|
||||||
self.assertIsNone(myconfig.getdate('foo.date'))
|
|
||||||
myconfig.setdefault('foo.date', '2023-11-20')
|
|
||||||
value = myconfig.getdate('foo.date')
|
|
||||||
self.assertIsInstance(value, datetime.date)
|
|
||||||
self.assertEqual(value, datetime.date(2023, 11, 20))
|
|
||||||
|
|
||||||
def test_get_app(self):
|
|
||||||
myconfig = config.RattailConfig()
|
|
||||||
app = myconfig.get_app()
|
|
||||||
self.assertIsInstance(app, AppHandler)
|
|
||||||
self.assertIs(type(app), AppHandler)
|
|
||||||
|
|
||||||
def test_parse_bool(self):
|
def test_parse_bool(self):
|
||||||
myconfig = config.RattailConfig()
|
myconfig = config.RattailConfig()
|
||||||
self.assertTrue(myconfig.parse_bool('true'))
|
self.assertTrue(myconfig.parse_bool('true'))
|
||||||
|
@ -218,11 +130,195 @@ require = %(here)s/first.conf
|
||||||
value = myconfig.make_list_string(["you don't", 'say'])
|
value = myconfig.make_list_string(["you don't", 'say'])
|
||||||
self.assertEqual(value, "\"you don't\", say")
|
self.assertEqual(value, "\"you don't\", say")
|
||||||
|
|
||||||
|
def test_get_app(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
app = myconfig.get_app()
|
||||||
|
self.assertIsInstance(app, AppHandler)
|
||||||
|
self.assertIs(type(app), AppHandler)
|
||||||
|
|
||||||
def test_beaker_invalidate_setting(self):
|
def test_beaker_invalidate_setting(self):
|
||||||
# TODO: this doesn't really test anything, just gives coverage
|
# TODO: this doesn't really test anything, just gives coverage
|
||||||
myconfig = config.RattailConfig()
|
myconfig = config.RattailConfig()
|
||||||
myconfig.beaker_invalidate_setting('foo')
|
myconfig.beaker_invalidate_setting('foo')
|
||||||
|
|
||||||
|
def test_production(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# false if not defined
|
||||||
|
self.assertFalse(myconfig.production())
|
||||||
|
|
||||||
|
# but config may specify
|
||||||
|
myconfig.setdefault('rattail.production', 'true')
|
||||||
|
self.assertTrue(myconfig.production())
|
||||||
|
|
||||||
|
def test_node_type(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if node type not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.node_type)
|
||||||
|
|
||||||
|
# unless default is provided
|
||||||
|
self.assertEqual(myconfig.node_type(default='foo'), 'foo')
|
||||||
|
|
||||||
|
# or config contains the definition
|
||||||
|
myconfig.setdefault('rattail.node_type', 'bar')
|
||||||
|
self.assertEqual(myconfig.node_type(), 'bar')
|
||||||
|
|
||||||
|
def test_get_model(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# default is rattail.db.model
|
||||||
|
model = myconfig.get_model()
|
||||||
|
self.assertIs(model, sys.modules['rattail.db.model'])
|
||||||
|
|
||||||
|
# or config may specify
|
||||||
|
myconfig.setdefault('rattail.model', 'rattail.trainwreck.db.model')
|
||||||
|
model = myconfig.get_model()
|
||||||
|
self.assertIs(model, sys.modules['rattail.trainwreck.db.model'])
|
||||||
|
|
||||||
|
def test_get_enum(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# default is rattail.enum
|
||||||
|
enum = myconfig.get_enum()
|
||||||
|
self.assertIs(enum, sys.modules['rattail.enum'])
|
||||||
|
|
||||||
|
# or config may specify
|
||||||
|
# (nb. using bogus example module here)
|
||||||
|
myconfig.setdefault('rattail.enum', 'rattail.auth')
|
||||||
|
enum = myconfig.get_enum()
|
||||||
|
self.assertIs(enum, sys.modules['rattail.auth'])
|
||||||
|
|
||||||
|
def test_get_trainwreck_model(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.get_trainwreck_model)
|
||||||
|
|
||||||
|
# but config may specify
|
||||||
|
myconfig.setdefault('rattail.trainwreck.model', 'rattail.trainwreck.db.model')
|
||||||
|
model = myconfig.get_trainwreck_model()
|
||||||
|
self.assertIs(model, sys.modules['rattail.trainwreck.db.model'])
|
||||||
|
|
||||||
|
def test_versioning_enabled(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# false by default
|
||||||
|
self.assertFalse(myconfig.versioning_enabled())
|
||||||
|
|
||||||
|
# but config may enable
|
||||||
|
myconfig.setdefault('rattail.db.versioning.enabled', 'true')
|
||||||
|
self.assertTrue(myconfig.versioning_enabled())
|
||||||
|
|
||||||
|
def test_app_package(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.app_package)
|
||||||
|
|
||||||
|
# unless default is provided
|
||||||
|
self.assertEqual(myconfig.app_package(default='foo'), 'foo')
|
||||||
|
|
||||||
|
# but config may specify
|
||||||
|
myconfig.setdefault('rattail.app_package', 'bar')
|
||||||
|
self.assertEqual(myconfig.app_package(), 'bar')
|
||||||
|
|
||||||
|
def test_app_title(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# default title
|
||||||
|
self.assertEqual(myconfig.app_title(), 'Rattail')
|
||||||
|
|
||||||
|
# but config may specify
|
||||||
|
myconfig.setdefault('rattail.app_title', 'Foo')
|
||||||
|
self.assertEqual(myconfig.app_title(), 'Foo')
|
||||||
|
|
||||||
|
def test_node_title(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# default title
|
||||||
|
self.assertEqual(myconfig.app_title(), 'Rattail')
|
||||||
|
|
||||||
|
# but config may specify
|
||||||
|
myconfig.setdefault('rattail.node_title', 'Foo (node)')
|
||||||
|
self.assertEqual(myconfig.node_title(), 'Foo (node)')
|
||||||
|
|
||||||
|
def test_running_from_source(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# false by default
|
||||||
|
self.assertFalse(myconfig.running_from_source())
|
||||||
|
|
||||||
|
# but config may enable
|
||||||
|
myconfig.setdefault('rattail.running_from_source', 'true')
|
||||||
|
self.assertTrue(myconfig.running_from_source())
|
||||||
|
|
||||||
|
def test_demo(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# false by default
|
||||||
|
self.assertFalse(myconfig.demo())
|
||||||
|
|
||||||
|
# but config may enable
|
||||||
|
myconfig.setdefault('rattail.demo', 'true')
|
||||||
|
self.assertTrue(myconfig.demo())
|
||||||
|
|
||||||
|
def test_appdir(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# can be none if required is false
|
||||||
|
self.assertIsNone(myconfig.appdir(require=False))
|
||||||
|
|
||||||
|
# otherwise sane fallback is used
|
||||||
|
with patch('rattail.config.sys') as sys:
|
||||||
|
sys.prefix = 'foo'
|
||||||
|
path = os.path.join('foo', 'app')
|
||||||
|
self.assertEqual(myconfig.appdir(), path)
|
||||||
|
|
||||||
|
# or config may specify
|
||||||
|
myconfig.setdefault('rattail.appdir', '/foo/bar/baz')
|
||||||
|
self.assertEqual(myconfig.appdir(), '/foo/bar/baz')
|
||||||
|
|
||||||
|
def test_datadir(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.datadir)
|
||||||
|
|
||||||
|
# but can avoid error if not required
|
||||||
|
self.assertIsNone(myconfig.datadir(require=False))
|
||||||
|
|
||||||
|
# or config may specify
|
||||||
|
myconfig.setdefault('rattail.datadir', '/foo/bar/baz')
|
||||||
|
self.assertEqual(myconfig.datadir(), '/foo/bar/baz')
|
||||||
|
|
||||||
|
def test_workdir(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.workdir)
|
||||||
|
|
||||||
|
# but can avoid error if not required
|
||||||
|
self.assertIsNone(myconfig.workdir(require=False))
|
||||||
|
|
||||||
|
# or config may specify
|
||||||
|
myconfig.setdefault('rattail.workdir', '/foo/bar/baz')
|
||||||
|
self.assertEqual(myconfig.workdir(), '/foo/bar/baz')
|
||||||
|
|
||||||
|
def test_batch_filedir(self):
|
||||||
|
myconfig = config.RattailConfig()
|
||||||
|
|
||||||
|
# error if not defined
|
||||||
|
self.assertRaises(ConfigurationError, myconfig.batch_filedir)
|
||||||
|
|
||||||
|
# config may specify
|
||||||
|
path = os.path.join(os.sep, 'foo', 'files')
|
||||||
|
myconfig.setdefault('rattail.batch.files', path)
|
||||||
|
self.assertEqual(myconfig.batch_filedir(), path)
|
||||||
|
|
||||||
|
# caller may specify a key
|
||||||
|
self.assertEqual(myconfig.batch_filedir(key='bar'), os.path.join(path, 'bar'))
|
||||||
|
|
||||||
|
|
||||||
class TestRattailDefaultFiles(FileConfigTestCase):
|
class TestRattailDefaultFiles(FileConfigTestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue