class TestConfig(): @patch('couchapp.config.util.findcouchapp', return_value=None) @patch('couchapp.config.util.rcpath', return_value=['/mock/couchapp.conf']) def setup(self, rcpath, getcwd): self.config = Config() def teardown(self): del self.config @raises(AttributeError) def test_getattr(self): ''' Test case for Config.__getattr__() ''' assert self.config.conf == Config.DEFAULTS assert self.config.env == {} self.config.mock # raise AttributeError @raises(KeyError) def test_getitem(self): ''' Test case for Config.__getitem__() ''' assert self.config['conf'] == Config.DEFAULTS assert self.config['env'] == {} assert self.config['mock'] # raise KeyError def test_contains(self): ''' Test case for Config.__contains__() ''' assert 'env' in self.config assert 'hooks' in self.config assert 'extensions' in self.config @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_from_list(self, isfile, read_json): ''' Test case for Config.load(list, default) ''' default = {'foo': 'bar'} conf = self.config.load(['/mock/couchapp.conf'], default) assert conf == {'foo': 'bar', 'mock': True} isfile.assert_called_with('/mock/couchapp.conf') read_json.assert_called_with('/mock/couchapp.conf', use_environment=True, raise_on_error=True) @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_from_str(self, isfile, read_json): ''' Test case for Config.load(str) ''' conf = self.config.load('/mock/couchapp.conf') assert conf == {'mock': True} isfile.assert_called_with('/mock/couchapp.conf') read_json.assert_called_with('/mock/couchapp.conf', use_environment=True, raise_on_error=True) @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=False) def test_load_notfile(self, isfile, read_json): ''' Test case for Config.load(['/not_a_file'], default) ''' default = {'foo': 'bar'} conf = self.config.load(['/not_a_file'], default) assert conf == {'foo': 'bar'} isfile.assert_called_with('/not_a_file') assert not read_json.called @raises(AppError) @patch('couchapp.config.util.read_json', side_effect=ValueError) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_apperror(self, isfile, read_json): ''' Test case for Config.load() reading a invalid file ''' self.config.load('/mock/couchapp.conf') isfile.assert_called_with('/mock/couchapp.conf') @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_deepcopy_default(self, isfile, read_json): ''' Test case for checking Config.load() deepcopy param ``default`` ''' default = {'foo': {'bar': 'fake'}} conf = self.config.load('/mock.conf', default=default) assert conf == {'foo': {'bar': 'fake'}, 'mock': True} assert default == {'foo': {'bar': 'fake'}} @patch('couchapp.config.Config.load', return_value='mock') def test_load_local(self, load): ''' Test case for Config.load_local() ''' assert self.config.load_local('/mock') == 'mock' paths = (load.call_args_list[0][0][0], load.call_args_list[1][0][0]) assert paths == ('/mock/couchapp.json', '/mock/.couchapprc'), paths @raises(AppError) @patch('couchapp.config.Config.load') def test_load_local_apperror(self, load): ''' Test case for Config.load_local() with empty `app_dir` ''' self.config.load_local(None) assert not load.called @patch('couchapp.config.Config.load') def test_load_local_prevent_env(self, load): ''' Test case for Config.load_local() with ``env`` field in ``couchapp.json`` ''' def load_side_effect(path, default={}): print(path == '/mock/couchapp.json', path) if path == '/mock/couchapp.json': default.update({'env': 'fake_env', 'name': 'MockApp'}) elif path == '/mock/.couchapprc': default.update({'hook': 'mock_hook'}) else: raise AssertionError( 'Unknown local config file "{}"'.format(path)) return default load.side_effect = load_side_effect conf = self.config.load_local('/mock') assert conf['name'] == 'MockApp' assert conf['hook'] == 'mock_hook' assert conf.get('env', None) is None @patch('couchapp.config.Config.load_local', return_value={'mock': True}) def test_update(self, load_local): ''' Test case for Config.update() ''' self.config.update('/mock') assert self.config.conf.get('mock') == True load_local.assert_called_with('/mock') @patch('couchapp.config.util.load_py') def test_extensions_empty(self, load_py): ''' Test case for empty Config.extensions ''' assert self.config.extensions == [] assert not load_py.called @patch('couchapp.config.util.load_py', return_value='mock') def test_extensions_mock(self, load_py): ''' Test case for Config.extensions ''' self.config.conf['extensions'] = ['mock_path'] extensions = self.config.extensions assert extensions == ['mock'], extensions load_py.assert_called_with('mock_path', self.config) @patch('couchapp.config.util.hook_uri') def test_hook_empty(self, hook_uri): ''' Test case for empty Config.hooks ''' assert self.config.hooks == {} assert not hook_uri.called @patch('couchapp.config.util.hook_uri', return_value='mock_module') def test_hook_mock(self, hook_uri): ''' Test case for Config.hooks ''' self.config.conf['hooks'] = {'pre-push': ['mock_path']} hooks = self.config.hooks assert hooks == {'pre-push': ['mock_module']}, hooks hook_uri.assert_called_with('mock_path', self.config) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_full_uri(self, Database): ''' Test case for Config.get_dbs() with full uri ''' db_string = 'https://foo.bar' assert self.config.get_dbs(db_string) == ['mockdb'] Database.assert_called_with(db_string, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_short_uri(self, Database): ''' Test case for Config.get_dbs() with short uri ''' full_uri = 'http://127.0.0.1:5984/foo' assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with(full_uri, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_env(self, Database): ''' Test case for Config.get_dbs() with env set ''' db_string = 'http://foo.bar' self.config.conf['env'] = {'default': {'db': db_string}} assert self.config.get_dbs() == ['mockdb'] Database.assert_called_with(db_string, use_proxy=False) @raises(AppError) @patch('couchapp.config.Database') def test_get_dbs_empty_env(self, Database): ''' Test case for Config.get_dbs() without env set ''' self.config.get_dbs() assert not Database.called @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_short_env(self, Database): ''' Test case for Config.get_dbs() with a short name in env ''' self.config.conf['env'] = {'foo': {'db': 'http://foo.bar'}} assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with('http://foo.bar', use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_env_fake(self, Database): ''' Test case for Config.get_dbs() with an useless env Assume .couchapprc is (no `db` field in `foo`) ``` { 'foo':{ 'notdb': 'mock' } } ``` Expect behavior: fall back to DEFAULT_SERVER_URI/foo ''' self.config.conf['env'] = {'foo': {'notdb': 'mock'}} default_uri = 'http://127.0.0.1:5984/foo' assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with(default_uri, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_proxy(self, Database): ''' Test case for Config.get_dbs() with https_proxy env ''' with patch.dict('couchapp.config.os.environ', {'https_proxy': 'foo'}): assert self.config.get_dbs('https://bar') == ['mockdb'] Database.assert_called_with('https://bar', use_proxy=True) def test_get_app_name_default(self): ''' Test case for Config.get_app_name() without args and env ''' assert self.config.get_app_name() == None def test_get_app_name_default_env(self): ''' Test case for Config.get_app_name() without args but env env: { 'default': { 'name': 'MockApp' } } and env: { 'mockname': { 'name': 'MockApp2' } } ''' self.config.conf['env'] = {'default': {'name': 'MockApp'}} assert self.config.get_app_name() == 'MockApp' self.config.conf['env'] = {'mockname': {'name': 'MockApp2'}} assert self.config.get_app_name('mockname') == 'MockApp2' self.config.conf['env'] = {'default': {'name': 'MockApp3'}} assert self.config.get_app_name('strang') == 'MockApp3' def test_get_app_name_http_uri(self): ''' Test case for Config.get_app_name('http://foo.bar', default) If the dbstring is full uri, return ``default`` ''' ret = self.config.get_app_name('http://foo.bar', 'mockapp') assert ret == 'mockapp' def test_iter(self): ''' Test case for Config.__iter__() ''' self.config.conf['env'] = {'mock': True} ls = list(self.config) assert ('env', {'mock': True}) in ls, ls assert ('hooks', {}) in ls, ls def test_get(self): ''' Test case for Config.get('__init__') ''' assert callable(self.config.get('__init__')) def test_get_default(self): ''' Test case for Config.get('strang', 'default') ''' assert self.config.get('strang', 'default') == 'default' def test_get_conf(self): ''' Test case for Config.get('env') returning value from self.conf ''' self.config.conf['env'] = {'mock': True} assert self.config.get('env') == {'mock': True}
class Compress(object): def __init__(self, path): self.appdir = path self.attach_dir = os.path.join(path, '_attachments') self.conf = Config() self.conf.update(path) def is_hook(self): if not 'compress' in self.conf: return False return True def compress_css(self, css): re_url = re.compile('url\s*\(([^\s"].*)\)') src_fpath = '' fname_dir = '' def replace_url(mo): """ make sure urls are relative to css path """ css_url = mo.group(0)[4:].strip(")").replace("'", "").replace('"', '') css_path = os.path.join(os.path.dirname(src_fpath), css_url) rel_path = util.relpath(css_path, fname_dir) return "url(%s)" % rel_path for fname, src_files in css.iteritems(): output_css = '' dest_path = os.path.join(self.attach_dir, fname) fname_dir = os.path.dirname(dest_path) for src_fname in src_files: src_fpath = os.path.join(self.appdir, src_fname) if os.path.exists(src_fpath): content_css = \ str(compress_css.CSSParser(util.read(src_fpath))) content_css = re_url.sub(replace_url, content_css) output_css += content_css logger.debug("Merging %s in %s" % (src_fname, fname)) if not os.path.isdir(fname_dir): os.makedirs(fname_dir) util.write(dest_path, output_css) def compress_js(self, backend, js): logger.info("compress js with %s " % backend.__about__) for fname, src_files in js.iteritems(): output_js = '' dest_path = os.path.join(self.attach_dir, fname) fname_dir = os.path.dirname(dest_path) for src_fname in src_files: src_fpath = os.path.join(self.appdir, src_fname) if os.path.isfile(src_fpath): output_js += "/* %s */\n" % src_fpath output_js += util.read(src_fpath) logger.debug("merging %s in %s" % (src_fname, fname)) if not os.path.isdir(fname_dir): os.makedirs(fname_dir) output_js = backend.compress(output_js) util.write(dest_path, output_js) def run(self): conf = self.conf actions = conf.get('compress', {}) if 'css' in actions: self.compress_css(actions['css']) if 'js' in actions: if 'js_compressor' in conf['compress']: modname = conf['compress']['js_compressor'] if not isinstance(modname, basestring): logger.warning("Warning: js_compressor settings should " + "be a string") logger.warning("Selecting default backend (jsmin)") import couchapp.hooks.compress.default as backend else: try: backend = __import__(modname, {}, {}, ['']) except ImportError: import couchapp.hooks.compress.default as backend else: import couchapp.hooks.compress.default as backend self.compress_js(backend, actions['js'])
class Compress(object): def __init__(self, path): self.appdir = path self.attach_dir = os.path.join(path, '_attachments') self.conf = Config() self.conf.update(path) def is_hook(self): if not 'compress' in self.conf: return False return True def compress_css(self, css): re_url = re.compile('url\s*\(([^\s"].*)\)') src_fpath = '' fname_dir = '' def replace_url(mo): """ make sure urls are relative to css path """ css_url = mo.group(0)[4:].strip(")").replace("'", "").replace('"', '') css_path = os.path.join(os.path.dirname(src_fpath), css_url) rel_path = util.relpath(css_path, fname_dir) return "url(%s)" % rel_path for fname, src_files in css.iteritems(): output_css = '' dest_path = os.path.join(self.attach_dir, fname) fname_dir = os.path.dirname(dest_path) for src_fname in src_files: src_fpath = os.path.join(self.appdir, src_fname) if os.path.exists(src_fpath): content_css = str( compress_css.CSSParser(util.read(src_fpath))) content_css = re_url.sub(replace_url, content_css) output_css += content_css logger.debug("Merging %s in %s" % (src_fname, fname)) if not os.path.isdir(fname_dir): os.makedirs(fname_dir) util.write(dest_path, output_css) def compress_js(self, backend, js): logger.info("compress js with %s " % backend.__about__) for fname, src_files in js.iteritems(): output_js = '' dest_path = os.path.join(self.attach_dir, fname) fname_dir = os.path.dirname(dest_path) for src_fname in src_files: src_fpath = os.path.join(self.appdir, src_fname) if os.path.isfile(src_fpath): output_js += "/* %s */\n" % src_fpath output_js += util.read(src_fpath) logger.debug("merging %s in %s" % (src_fname, fname)) if not os.path.isdir(fname_dir): os.makedirs(fname_dir) output_js = backend.compress(output_js) util.write(dest_path, output_js) def run(self): conf = self.conf actions = conf.get('compress', {}) if 'css' in actions: self.compress_css(actions['css']) if 'js' in actions: if 'js_compressor' in conf['compress']: modname = conf['compress']['js_compressor'] if not isinstance(modname, basestring): logger.warning( "Warning: js_compressor settings should be a string") logger.warning("Selecting default backend (jsmin)") import couchapp.hooks.compress.jsmin as backend else: try: backend = __import__(modname, {}, {}, ['']) except ImportError: import couchapp.hooks.compress.jsmin as backend else: import couchapp.hooks.compress.jsmin as backend self.compress_js(backend, actions['js'])
class TestConfig(): @patch('couchapp.config.util.findcouchapp', return_value=None) @patch('couchapp.config.util.rcpath', return_value=['/mock/couchapp.conf']) def setup(self, rcpath, getcwd): self.config = Config() def teardown(self): del self.config @raises(AttributeError) def test_getattr(self): ''' Test case for Config.__getattr__() ''' assert self.config.conf == Config.DEFAULTS assert self.config.env == {} self.config.mock # raise AttributeError @raises(KeyError) def test_getitem(self): ''' Test case for Config.__getitem__() ''' assert self.config['conf'] == Config.DEFAULTS assert self.config['env'] == {} assert self.config['mock'] # raise KeyError def test_contains(self): ''' Test case for Config.__contains__() ''' assert 'env' in self.config assert 'hooks' in self.config assert 'extensions' in self.config @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_from_list(self, isfile, read_json): ''' Test case for Config.load(list, default) ''' default = {'foo': 'bar'} conf = self.config.load(['/mock/couchapp.conf'], default) assert conf == {'foo': 'bar', 'mock': True} isfile.assert_called_with('/mock/couchapp.conf') read_json.assert_called_with('/mock/couchapp.conf', use_environment=True, raise_on_error=True) @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_from_str(self, isfile, read_json): ''' Test case for Config.load(str) ''' conf = self.config.load('/mock/couchapp.conf') assert conf == {'mock': True} isfile.assert_called_with('/mock/couchapp.conf') read_json.assert_called_with('/mock/couchapp.conf', use_environment=True, raise_on_error=True) @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=False) def test_load_notfile(self, isfile, read_json): ''' Test case for Config.load(['/not_a_file'], default) ''' default = {'foo': 'bar'} conf = self.config.load(['/not_a_file'], default) assert conf == {'foo': 'bar'} isfile.assert_called_with('/not_a_file') assert not read_json.called @raises(AppError) @patch('couchapp.config.util.read_json', side_effect=ValueError) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_apperror(self, isfile, read_json): ''' Test case for Config.load() reading a invalid file ''' self.config.load('/mock/couchapp.conf') isfile.assert_called_with('/mock/couchapp.conf') @patch('couchapp.config.util.read_json', return_value={'mock': True}) @patch('couchapp.config.os.path.isfile', return_value=True) def test_load_deepcopy_default(self, isfile, read_json): ''' Test case for checking Config.load() deepcopy param ``default`` ''' default = {'foo': {'bar': 'fake'}} conf = self.config.load('/mock.conf', default=default) assert conf == { 'foo': {'bar': 'fake'}, 'mock': True } assert default == {'foo': {'bar': 'fake'}} @patch('couchapp.config.Config.load', return_value='mock') def test_load_local(self, load): ''' Test case for Config.load_local() ''' assert self.config.load_local('/mock') == 'mock' paths = (load.call_args_list[0][0][0], load.call_args_list[1][0][0]) assert paths == ('/mock/couchapp.json', '/mock/.couchapprc'), paths @raises(AppError) @patch('couchapp.config.Config.load') def test_load_local_apperror(self, load): ''' Test case for Config.load_local() with empty `app_dir` ''' self.config.load_local(None) assert not load.called @patch('couchapp.config.Config.load') def test_load_local_prevent_env(self, load): ''' Test case for Config.load_local() with ``env`` field in ``couchapp.json`` ''' def load_side_effect(path, default={}): print(path == '/mock/couchapp.json', path) if path == '/mock/couchapp.json': default.update({'env': 'fake_env', 'name': 'MockApp'}) elif path == '/mock/.couchapprc': default.update({'hook': 'mock_hook'}) else: raise AssertionError('Unknown local config file "{}"'.format( path)) return default load.side_effect = load_side_effect conf = self.config.load_local('/mock') assert conf['name'] == 'MockApp' assert conf['hook'] == 'mock_hook' assert conf.get('env', None) is None @patch('couchapp.config.Config.load_local', return_value={'mock': True}) def test_update(self, load_local): ''' Test case for Config.update() ''' self.config.update('/mock') assert self.config.conf.get('mock') == True load_local.assert_called_with('/mock') @patch('couchapp.config.util.load_py') def test_extensions_empty(self, load_py): ''' Test case for empty Config.extensions ''' assert self.config.extensions == [] assert not load_py.called @patch('couchapp.config.util.load_py', return_value='mock') def test_extensions_mock(self, load_py): ''' Test case for Config.extensions ''' self.config.conf['extensions'] = ['mock_path'] extensions = self.config.extensions assert extensions == ['mock'], extensions load_py.assert_called_with('mock_path', self.config) @patch('couchapp.config.util.hook_uri') def test_hook_empty(self, hook_uri): ''' Test case for empty Config.hooks ''' assert self.config.hooks == {} assert not hook_uri.called @patch('couchapp.config.util.hook_uri', return_value='mock_module') def test_hook_mock(self, hook_uri): ''' Test case for Config.hooks ''' self.config.conf['hooks'] = {'pre-push': ['mock_path']} hooks = self.config.hooks assert hooks == {'pre-push': ['mock_module']}, hooks hook_uri.assert_called_with('mock_path', self.config) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_full_uri(self, Database): ''' Test case for Config.get_dbs() with full uri ''' db_string = 'https://foo.bar' assert self.config.get_dbs(db_string) == ['mockdb'] Database.assert_called_with(db_string, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_short_uri(self, Database): ''' Test case for Config.get_dbs() with short uri ''' full_uri = 'http://127.0.0.1:5984/foo' assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with(full_uri, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_env(self, Database): ''' Test case for Config.get_dbs() with env set ''' db_string = 'http://foo.bar' self.config.conf['env'] = {'default': {'db': db_string}} assert self.config.get_dbs() == ['mockdb'] Database.assert_called_with(db_string, use_proxy=False) @raises(AppError) @patch('couchapp.config.Database') def test_get_dbs_empty_env(self, Database): ''' Test case for Config.get_dbs() without env set ''' self.config.get_dbs() assert not Database.called @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_short_env(self, Database): ''' Test case for Config.get_dbs() with a short name in env ''' self.config.conf['env'] = {'foo': {'db': 'http://foo.bar'}} assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with('http://foo.bar', use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_env_fake(self, Database): ''' Test case for Config.get_dbs() with an useless env Assume .couchapprc is (no `db` field in `foo`) ``` { 'foo':{ 'notdb': 'mock' } } ``` Expect behavior: fall back to DEFAULT_SERVER_URI/foo ''' self.config.conf['env'] = {'foo': {'notdb': 'mock'}} default_uri = 'http://127.0.0.1:5984/foo' assert self.config.get_dbs('foo') == ['mockdb'] Database.assert_called_with(default_uri, use_proxy=False) @patch('couchapp.config.Database', return_value='mockdb') def test_get_dbs_proxy(self, Database): ''' Test case for Config.get_dbs() with https_proxy env ''' with patch.dict('couchapp.config.os.environ', {'https_proxy': 'foo'}): assert self.config.get_dbs('https://bar') == ['mockdb'] Database.assert_called_with('https://bar', use_proxy=True) def test_get_app_name_default(self): ''' Test case for Config.get_app_name() without args and env ''' assert self.config.get_app_name() == None def test_get_app_name_default_env(self): ''' Test case for Config.get_app_name() without args but env env: { 'default': { 'name': 'MockApp' } } and env: { 'mockname': { 'name': 'MockApp2' } } ''' self.config.conf['env'] = {'default': {'name': 'MockApp'}} assert self.config.get_app_name() == 'MockApp' self.config.conf['env'] = {'mockname': {'name': 'MockApp2'}} assert self.config.get_app_name('mockname') == 'MockApp2' self.config.conf['env'] = {'default': {'name': 'MockApp3'}} assert self.config.get_app_name('strang') == 'MockApp3' def test_get_app_name_http_uri(self): ''' Test case for Config.get_app_name('http://foo.bar', default) If the dbstring is full uri, return ``default`` ''' ret = self.config.get_app_name('http://foo.bar', 'mockapp') assert ret == 'mockapp' def test_iter(self): ''' Test case for Config.__iter__() ''' self.config.conf['env'] = {'mock': True} ls = list(self.config) assert ('env', {'mock': True}) in ls, ls assert ('hooks', {}) in ls, ls def test_get(self): ''' Test case for Config.get('__init__') ''' assert callable(self.config.get('__init__')) def test_get_default(self): ''' Test case for Config.get('strang', 'default') ''' assert self.config.get('strang', 'default') == 'default' def test_get_conf(self): ''' Test case for Config.get('env') returning value from self.conf ''' self.config.conf['env'] = {'mock': True} assert self.config.get('env') == {'mock': True}