"""Test alt""" import os import string import py import pytest import utils TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR] @pytest.mark.usefixtures('ds1_copy') @pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree']) @pytest.mark.parametrize( 'tracked,encrypt,exclude', [ (False, False, False), (True, False, False), (False, True, False), (False, True, True), ], ids=['untracked', 'tracked', 'encrypted', 'excluded']) def test_alt_source( runner, paths, tracked, encrypt, exclude, yadm_alt): """Test yadm alt operates on all expected sources of alternates""" yadm_dir = setup_standard_yadm_dir(paths) utils.create_alt_files( paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude, yadm_alt=yadm_alt, yadm_dir=yadm_dir) run = runner([paths.pgm, '-Y', yadm_dir, 'alt']) assert run.success assert run.err == '' linked = utils.parse_alt_output(run.out) basepath = yadm_dir.join('alt') if yadm_alt else paths.work for link_path in TEST_PATHS: source_file_content = link_path + '##default' source_file = basepath.join(source_file_content) link_file = paths.work.join(link_path) if tracked or (encrypt and not exclude): assert link_file.islink() target = py.path.local(link_file.readlink()) if target.isfile(): assert link_file.read() == source_file_content assert str(source_file) in linked else: assert link_file.join( utils.CONTAINED).read() == source_file_content assert str(source_file) in linked else: assert not link_file.exists() assert str(source_file) not in linked @pytest.mark.usefixtures('ds1_copy') @pytest.mark.parametrize('suffix', [ '##default', '##o.$tst_sys', '##os.$tst_sys', '##d.$tst_distro', '##distro.$tst_distro', '##c.$tst_class', '##class.$tst_class', '##h.$tst_host', '##hostname.$tst_host', '##u.$tst_user', '##user.$tst_user', ]) def test_alt_conditions( runner, paths, tst_sys, tst_distro, tst_host, tst_user, suffix): """Test conditions supported by yadm alt""" yadm_dir = setup_standard_yadm_dir(paths) # set the class tst_class = 'testclass' utils.set_local(paths, 'class', tst_class) suffix = string.Template(suffix).substitute( tst_sys=tst_sys, tst_distro=tst_distro, tst_class=tst_class, tst_host=tst_host, tst_user=tst_user, ) utils.create_alt_files(paths, suffix) run = runner([paths.pgm, '-Y', yadm_dir, 'alt']) assert run.success assert run.err == '' linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: source_file = link_path + suffix assert paths.work.join(link_path).islink() target = py.path.local(paths.work.join(link_path).readlink()) if target.isfile(): assert paths.work.join(link_path).read() == source_file assert str(paths.work.join(source_file)) in linked else: assert paths.work.join(link_path).join( utils.CONTAINED).read() == source_file assert str(paths.work.join(source_file)) in linked @pytest.mark.usefixtures('ds1_copy') @pytest.mark.parametrize( 'kind', ['builtin', '', None, 'envtpl', 'j2cli', 'j2']) @pytest.mark.parametrize('label', ['t', 'template', 'yadm', ]) def test_alt_templates( runner, paths, kind, label): """Test templates supported by yadm alt""" yadm_dir = setup_standard_yadm_dir(paths) suffix = f'##{label}.{kind}' if kind is None: suffix = f'##{label}' utils.create_alt_files(paths, suffix) run = runner([paths.pgm, '-Y', yadm_dir, 'alt']) assert run.success assert run.err == '' created = utils.parse_alt_output(run.out, linked=False) for created_path in TEST_PATHS: if created_path != utils.ALT_DIR: source_file = created_path + suffix assert paths.work.join(created_path).isfile() assert paths.work.join(created_path).read().strip() == source_file assert str(paths.work.join(source_file)) in created @pytest.mark.usefixtures('ds1_copy') @pytest.mark.parametrize('autoalt', [None, 'true', 'false']) def test_auto_alt(runner, yadm_y, paths, autoalt): """Test auto alt""" # set the value of auto-alt if autoalt: os.system(' '.join(yadm_y('config', 'yadm.auto-alt', autoalt))) utils.create_alt_files(paths, '##default') run = runner(yadm_y('status')) assert run.success assert run.err == '' linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: source_file = link_path + '##default' if autoalt == 'false': assert not paths.work.join(link_path).exists() else: assert paths.work.join(link_path).islink() target = py.path.local(paths.work.join(link_path).readlink()) if target.isfile(): assert paths.work.join(link_path).read() == source_file # no linking output when run via auto-alt assert str(paths.work.join(source_file)) not in linked else: assert paths.work.join(link_path).join( utils.CONTAINED).read() == source_file # no linking output when run via auto-alt assert str(paths.work.join(source_file)) not in linked @pytest.mark.usefixtures('ds1_copy') def test_stale_link_removal(runner, yadm_y, paths): """Stale links to alternative files are removed This test ensures that when an already linked alternative becomes invalid due to a change in class, the alternate link is removed. """ # set the class tst_class = 'testclass' utils.set_local(paths, 'class', tst_class) # create files which match the test class utils.create_alt_files(paths, f'##class.{tst_class}') # run alt to trigger linking run = runner(yadm_y('alt')) assert run.success assert run.err == '' linked = utils.parse_alt_output(run.out) # assert the proper linking has occurred for stale_path in TEST_PATHS: source_file = stale_path + '##class.' + tst_class assert paths.work.join(stale_path).islink() target = py.path.local(paths.work.join(stale_path).readlink()) if target.isfile(): assert paths.work.join(stale_path).read() == source_file assert str(paths.work.join(source_file)) in linked else: assert paths.work.join(stale_path).join( utils.CONTAINED).read() == source_file assert str(paths.work.join(source_file)) in linked # change the class so there are no valid alternates utils.set_local(paths, 'class', 'changedclass') # run alt to trigger linking run = runner(yadm_y('alt')) assert run.success assert run.err == '' linked = utils.parse_alt_output(run.out) # assert the linking is removed for stale_path in TEST_PATHS: source_file = stale_path + '##class.' + tst_class assert not paths.work.join(stale_path).exists() assert str(paths.work.join(source_file)) not in linked @pytest.mark.usefixtures('ds1_repo_copy') def test_template_overwrite_symlink(runner, yadm_y, paths, tst_sys): """Remove symlinks before processing a template If a symlink is in the way of the output of a template, the target of the symlink will get the template content. To prevent this, the symlink should be removed just before processing a template. """ target = paths.work.join(f'test_link##os.{tst_sys}') target.write('target') link = paths.work.join('test_link') link.mksymlinkto(target, absolute=1) template = paths.work.join('test_link##template.builtin') template.write('test-data') run = runner(yadm_y('add', target, template)) assert run.success assert run.err == '' assert run.out == '' assert not link.islink() assert target.read().strip() == 'target' assert link.read().strip() == 'test-data' def setup_standard_yadm_dir(paths): """Configure a yadm home within the work tree""" std_yadm_dir = paths.work.mkdir('.config').mkdir('yadm') std_yadm_dir.join('repo.git').mksymlinkto(paths.repo, absolute=1) std_yadm_dir.join('encrypt').mksymlinkto(paths.encrypt, absolute=1) return std_yadm_dir