Files @ 8818c9dd1c7b
Branch filter:

Location: rattail-project/rattail/tests/test_config.py - annotation

lance
Overhaul the tox config

per lessons learned in wuttjamaican

also remove old reference to init.d scripts in manifest
6dea1e0e52ee
b3b544769d0b
4a3209f89701
fbd67bfea253
9917ed60efd7
ebd65a8ef921
ebd65a8ef921
4a3209f89701
4a3209f89701
b3b544769d0b
b3b544769d0b
fbd67bfea253
b3b544769d0b
b3b544769d0b
4a3209f89701
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
4a3209f89701
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
9917ed60efd7
b3b544769d0b
b3b544769d0b
b3b544769d0b
9917ed60efd7
b3b544769d0b
b3b544769d0b
b3b544769d0b
b3b544769d0b
9917ed60efd7
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
21172121aa62
9917ed60efd7
4a3209f89701
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
ebd65a8ef921
ebd65a8ef921
ebd65a8ef921
ebd65a8ef921
ebd65a8ef921
ebd65a8ef921
ebd65a8ef921
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
ebd65a8ef921
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
9917ed60efd7
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
2a4a63333713
c10b5fb50f4f
2a4a63333713
2a4a63333713
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
4a3209f89701
4a3209f89701
4a3209f89701
4a3209f89701
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
fbd67bfea253
# -*- coding: utf-8; -*-

import configparser
import datetime
import os
import shutil
import tempfile
from unittest import TestCase
from unittest.mock import patch

from rattail import config
from rattail.app import AppHandler


class TestParseBoolFunc(TestCase):

    def test_none(self):
        self.assertIsNone(config.parse_bool(None))

    def test_true(self):
        self.assertIs(config.parse_bool(True), True)

    def test_false(self):
        self.assertIs(config.parse_bool(False), False)

    def test_string(self):
        self.assertTrue(config.parse_bool('true'))
        self.assertTrue(config.parse_bool('yes'))
        self.assertTrue(config.parse_bool('on'))
        self.assertTrue(config.parse_bool('1'))

        self.assertFalse(config.parse_bool('false'))
        self.assertFalse(config.parse_bool('no'))
        self.assertFalse(config.parse_bool('off'))
        self.assertFalse(config.parse_bool('0'))


class TestParseListFunc(TestCase):

    def test_none(self):
        value = config.parse_list(None)
        self.assertEqual(len(value), 0)

    def test_single_value(self):
        value = config.parse_list(u'foo')
        self.assertEqual(len(value), 1)
        self.assertEqual(value[0], u'foo')

    def test_single_value_padded_by_spaces(self):
        value = config.parse_list(u'   foo   ')
        self.assertEqual(len(value), 1)
        self.assertEqual(value[0], u'foo')

    def test_slash_is_not_a_separator(self):
        value = config.parse_list(u'/dev/null')
        self.assertEqual(len(value), 1)
        self.assertEqual(value[0], u'/dev/null')

    def test_multiple_values_separated_by_whitespace(self):
        value = config.parse_list(u'foo bar baz')
        self.assertEqual(len(value), 3)
        self.assertEqual(value[0], u'foo')
        self.assertEqual(value[1], u'bar')
        self.assertEqual(value[2], u'baz')

    def test_multiple_values_separated_by_commas(self):
        value = config.parse_list(u'foo,bar,baz')
        self.assertEqual(len(value), 3)
        self.assertEqual(value[0], u'foo')
        self.assertEqual(value[1], u'bar')
        self.assertEqual(value[2], u'baz')

    def test_multiple_values_separated_by_whitespace_and_commas(self):
        value = config.parse_list(u'  foo,   bar   baz')
        self.assertEqual(len(value), 3)
        self.assertEqual(value[0], u'foo')
        self.assertEqual(value[1], u'bar')
        self.assertEqual(value[2], u'baz')

    def test_multiple_values_separated_by_whitespace_and_commas_with_some_quoting(self):
        value = config.parse_list("""
        foo
        "C:\\some path\\with spaces\\and, a comma",
        baz
        """)
        self.assertEqual(len(value), 3)
        self.assertEqual(value[0], u'foo')
        self.assertEqual(value[1], u'C:\\some path\\with spaces\\and, a comma')
        self.assertEqual(value[2], u'baz')

    def test_multiple_values_separated_by_whitespace_and_commas_with_single_quotes(self):
        value = config.parse_list("""
        foo
        'C:\\some path\\with spaces\\and, a comma',
        baz
        """)
        self.assertEqual(len(value), 3)
        self.assertEqual(value[0], 'foo')
        self.assertEqual(value[1], 'C:\\some path\\with spaces\\and, a comma')
        self.assertEqual(value[2], 'baz')


class TestRattailConfig(TestCase):

    def setUp(self):
        self.tempdir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.tempdir)

    def write_file(self, fname, content):
        path = os.path.join(self.tempdir, fname)
        with open(path, 'wt') as f:
            f.write(content)
        return path

    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 TestRattailConfigWrapper(TestCase):

    def setUp(self):
        self.tempdir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.tempdir)

    def write_file(self, filename, content):
        path = os.path.join(self.tempdir, filename)
        with open(path, 'wt') as f:
            f.write(content)
        return path

    def test_legacy(self):
        myconfig = config.RattailConfigWrapper()
        self.assertEqual(repr(myconfig), "RattailConfigWrapper(style=legacy)")

    def test_configuration(self):
        myfile = self.write_file('my.conf', """\
[rattail.config]
use_configuration = true
""")

        myconfig = config.RattailConfigWrapper(files=[myfile])
        self.assertEqual(repr(myconfig), "RattailConfigWrapper(style=configuration)")

    def test_wuttaconfig(self):
        myfile = self.write_file('my.conf', """\
[rattail.config]
use_wuttaconfig = true
""")

        myconfig = config.RattailConfigWrapper(files=[myfile])
        self.assertEqual(repr(myconfig), "RattailConfigWrapper(style=wuttaconfig)")

    def test_getattr(self):
        myconfig = config.RattailConfigWrapper()
        self.assertIs(myconfig.files_read, myconfig.config.files_read)

    def test_setattr(self):
        myconfig = config.RattailConfigWrapper()
        self.assertFalse(hasattr(myconfig.config, 'foo'))
        myconfig.foo = 'bar'
        self.assertEqual(myconfig.config.foo, 'bar')


class TestRattailWuttaConfig(TestCase):

    def setUp(self):
        self.tempdir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.tempdir)

    def write_file(self, filename, content):
        path = os.path.join(self.tempdir, filename)
        with open(path, 'wt') as f:
            f.write(content)
        return path

    def test_prioritized_files(self):
        first = self.write_file('first.conf', """\
[foo]
bar = 1
""")

        second = self.write_file('second.conf', """\
[rattail.config]
require = %(here)s/first.conf
""")

        myconfig = config.RattailWuttaConfig(files=[second])
        files = myconfig.prioritized_files
        self.assertEqual(len(files), 2)
        self.assertEqual(files[0], second)
        self.assertEqual(files[1], first)
        self.assertIs(files, myconfig.get_prioritized_files())

    def test_setdefault(self):
        myconfig = config.RattailWuttaConfig()

        # nb. the tests below are effectively testing the custom get()
        # method in addition to setdefault()

        # value is empty by default
        self.assertIsNone(myconfig.get('foo.bar'))
        self.assertIsNone(myconfig.get('foo', 'bar'))

        # but we can change that by setting default
        myconfig.setdefault('foo.bar', 'baz')
        self.assertEqual(myconfig.get('foo.bar'), 'baz')
        self.assertEqual(myconfig.get('foo', 'bar'), 'baz')

        # also can set a default via section, option (as well as key)
        self.assertIsNone(myconfig.get('foo.blarg'))
        myconfig.setdefault('foo' ,'blarg', 'blast')
        self.assertEqual(myconfig.get('foo.blarg'), 'blast')
        self.assertEqual(myconfig.get('foo', 'blarg'), 'blast')

        # error is raised if args are ambiguous
        self.assertRaises(ValueError, myconfig.setdefault, 'foo', 'bar', 'blarg', 'blast')

        # try that for get() too
        self.assertRaises(ValueError, myconfig.get, 'foo', 'bar', 'blarg', 'blast')

    def test_getbool(self):
        myconfig = config.RattailWuttaConfig()
        self.assertFalse(myconfig.getbool('foo.bar'))
        myconfig.setdefault('foo.bar', 'true')
        self.assertTrue(myconfig.getbool('foo.bar'))

    def test_getint(self):
        myconfig = config.RattailWuttaConfig()
        self.assertIsNone(myconfig.getint('foo.bar'))
        myconfig.setdefault('foo.bar', '42')
        self.assertEqual(myconfig.getint('foo.bar'), 42)

    def test_getlist(self):
        myconfig = config.RattailWuttaConfig()
        self.assertEqual(myconfig.getlist('foo.bar'), [])
        myconfig.setdefault('foo.bar', 'hello world')
        self.assertEqual(myconfig.getlist('foo.bar'), ['hello', 'world'])

    def test_getdate(self):
        myconfig = config.RattailWuttaConfig()
        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.RattailWuttaConfig()
        app = myconfig.get_app()
        self.assertIsInstance(app, AppHandler)

    def test_parse_list(self):
        myconfig = config.RattailWuttaConfig()
        self.assertEqual(myconfig.parse_list(None), [])
        self.assertEqual(myconfig.parse_list('hello world'), ['hello', 'world'])

    def test_beaker_invalidate_setting(self):
        # TODO: this doesn't really test anything, just gives coverage
        myconfig = config.RattailWuttaConfig()
        myconfig.beaker_invalidate_setting('foo')