19 Commits

12 changed files with 341 additions and 98 deletions
Unified View
  1. +0
    -40
      CHANGELOG.md
  2. +3
    -0
      README.md
  3. +1
    -1
      pyproject.toml
  4. +26
    -0
      scripts/upgrade-2.2.py
  5. +0
    -4
      src/transpose/__init__.py
  6. +80
    -5
      src/transpose/console.py
  7. +0
    -6
      src/transpose/logger.py
  8. +78
    -19
      src/transpose/transpose.py
  9. +1
    -1
      src/transpose/utils.py
  10. +73
    -12
      tests/test_console.py
  11. +58
    -8
      tests/test_transpose.py
  12. +21
    -2
      tests/utils.py

+ 0
- 40
CHANGELOG.md View File

@ -1,40 +0,0 @@
# Change Log
## 1.1.0 (2022-07-26)
### Breaking Changes
* Moving store_path to only the store action
Note: I know I said this shouldn't happen again but I couldn't help myself. This time....for sure.
### Features
* Allow for short -s for --store-path
### Documentation
* Add Quick Reference section
### Tests
* Updating tests to support moving store_path to store action
## 1.0.2 (2022-07-17)
### Added
* The `name` argument is now optional when using the `transpose store`
### Changed
* Moved `name` argument to end of `transpose store`
Note: I'm abusing the versioning a bit as I'm the only user of this tool. Normally, this would be considered a breaking change due to the change in argument order. Shouldn't happen again.
## 1.0.1 (2022-07-16)
### Fixed
* Utilize `expanduser` and `~` in cache files to allow for more portable restorations
## 1.0.0 (2022-07-12)
Initial release

+ 3
- 0
README.md View File

@ -114,8 +114,11 @@ It's possible to modify the transpose configuration file, `STORE_PATH/transpose.
``` ```
transpose config add "NewEntry" "/path/to/location" transpose config add "NewEntry" "/path/to/location"
transpose config get "NewEntry" transpose config get "NewEntry"
transpose config disable "NewEntry"
transpose config enable "NewEntry"
transpose config list transpose config list
transpose config remove "NewEntry" transpose config remove "NewEntry"
transpose config update "NewEntry" "path" "/path/to/new/location"
``` ```


+ 1
- 1
pyproject.toml View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "transpose" name = "transpose"
version = "2.0.2"
version = "2.2.2"
description = "Move and symlink a path to a central location" description = "Move and symlink a path to a central location"
authors = ["Ryan Reed"] authors = ["Ryan Reed"]
license = "GPLv3" license = "GPLv3"


+ 26
- 0
scripts/upgrade-2.2.py View File

@ -0,0 +1,26 @@
"""
Loop through entries and ensure using the latest 2.2 entities
This means adding the following new fields to each entry:
* created (2.1)
* enabled (2.2)
"""
import json
from transpose import DEFAULT_STORE_PATH, TransposeConfig
def main() -> None:
config_file = f"{DEFAULT_STORE_PATH}/transpose.json"
with open(config_file, "r") as f:
d = json.load(f)
config = TransposeConfig()
for entry_name in d["entries"]:
config.add(entry_name, d["entries"][entry_name]["path"])
config.save(config_file)
if __name__ == "__main__":
main()

+ 0
- 4
src/transpose/__init__.py View File

@ -2,14 +2,10 @@ import os
from importlib.metadata import version from importlib.metadata import version
from .logger import create_logger
DEFAULT_XDG_PATH = os.environ.get("XDG_DATA_HOME", f"{os.environ['HOME']}/.local/share") DEFAULT_XDG_PATH = os.environ.get("XDG_DATA_HOME", f"{os.environ['HOME']}/.local/share")
STORE_PATH = f"{DEFAULT_XDG_PATH}/transpose" STORE_PATH = f"{DEFAULT_XDG_PATH}/transpose"
DEFAULT_STORE_PATH = os.environ.get("TRANSPOSE_STORE_PATH", STORE_PATH) DEFAULT_STORE_PATH = os.environ.get("TRANSPOSE_STORE_PATH", STORE_PATH)
version = version("transpose") version = version("transpose")
logger = create_logger(__package__)
from .transpose import Transpose, TransposeConfig, TransposeEntry # noqa: E402 from .transpose import Transpose, TransposeConfig, TransposeEntry # noqa: E402

+ 80
- 5
src/transpose/console.py View File

@ -21,6 +21,8 @@ def run(args, config_path) -> None:
if args.action == "apply": if args.action == "apply":
t.apply(args.name, force=args.force) t.apply(args.name, force=args.force)
elif args.action == "apply-all":
run_apply_all(t, force=args.force)
elif args.action == "restore": elif args.action == "restore":
t.restore(args.name, force=args.force) t.restore(args.name, force=args.force)
elif args.action == "store": elif args.action == "store":
@ -32,6 +34,12 @@ def run(args, config_path) -> None:
if args.config_action == "add": if args.config_action == "add":
t.config.add(args.name, args.path) t.config.add(args.name, args.path)
t.config.save(config_path) t.config.save(config_path)
elif args.config_action == "disable":
t.config.disable(args.name)
t.config.save(config_path)
elif args.config_action == "enable":
t.config.enable(args.name)
t.config.save(config_path)
elif args.config_action == "get": elif args.config_action == "get":
print(t.config.get(args.name)) print(t.config.get(args.name))
elif args.config_action == "list": elif args.config_action == "list":
@ -41,10 +49,31 @@ def run(args, config_path) -> None:
t.config.remove(args.name) t.config.remove(args.name)
t.config.save(config_path) t.config.save(config_path)
elif args.config_action == "update": elif args.config_action == "update":
t.config.update(args.name, args.path)
t.config.update(args.name, args.field_key, args.field_value)
t.config.save(config_path) t.config.save(config_path)
def run_apply_all(t: Transpose, force: bool = False) -> None:
"""
Loop over the entries and recreate the symlinks to the store location
Useful after restoring a machine
Args:
t: An instance of Transpose
force: If enabled and path already exists, move the path to '{path}.backup' first
Returns:
None
"""
for entry_name in sorted(t.config.entries):
try:
t.apply(entry_name, force)
print(f"\t{entry_name:<30}: success")
except TransposeError as e:
print(f"\t{entry_name:<30}: {e}")
def parse_arguments(args=None): def parse_arguments(args=None):
base_parser = argparse.ArgumentParser(add_help=False) base_parser = argparse.ArgumentParser(add_help=False)
@ -77,7 +106,24 @@ def parse_arguments(args=None):
"name", "name",
help="The name of the stored entity to apply", help="The name of the stored entity to apply",
) )
apply_parser.add_argument("--force", dest="force", action="store_true")
apply_parser.add_argument(
"--force",
dest="force",
help="If original path already exists, existing path to <path>.backup and continue",
action="store_true",
)
apply_all_parser = subparsers.add_parser(
"apply-all",
help="Recreate the symlink for all entities",
parents=[base_parser],
)
apply_all_parser.add_argument(
"--force",
dest="force",
help="Continue with apply even if original path already exists or entry is disabled in config",
action="store_true",
)
restore_parser = subparsers.add_parser( restore_parser = subparsers.add_parser(
"restore", "restore",
@ -88,7 +134,12 @@ def parse_arguments(args=None):
"name", "name",
help="The name of the stored entity to restore", help="The name of the stored entity to restore",
) )
restore_parser.add_argument("--force", dest="force", action="store_true")
restore_parser.add_argument(
"--force",
dest="force",
help="Continue with restore even if original path already exists or entry is disabled in config",
action="store_true",
)
store_parser = subparsers.add_parser( store_parser = subparsers.add_parser(
"store", "store",
@ -129,6 +180,26 @@ def parse_arguments(args=None):
help="The path of the directory that should be symlinked to the store", help="The path of the directory that should be symlinked to the store",
) )
config_disable_parser = config_subparsers.add_parser(
"disable",
help="Disable an entry within the config",
parents=[base_parser],
)
config_disable_parser.add_argument(
"name",
help="The name of the entry the config",
)
config_enable_parser = config_subparsers.add_parser(
"enable",
help="enable an entry within the config",
parents=[base_parser],
)
config_enable_parser.add_argument(
"name",
help="The name of the entry the config",
)
config_get_parser = config_subparsers.add_parser( config_get_parser = config_subparsers.add_parser(
"get", "get",
help="Retrieve the settings of a specific entity, such as the path", help="Retrieve the settings of a specific entity, such as the path",
@ -165,8 +236,12 @@ def parse_arguments(args=None):
help="The name of the entry in the store path", help="The name of the entry in the store path",
) )
config_update_parser.add_argument( config_update_parser.add_argument(
"path",
help="The path of the directory that should be symlinked to the store",
"field_key",
help="The config key to be updated",
)
config_update_parser.add_argument(
"field_value",
help="The value to updated in the config",
) )
return parser.parse_args(args) return parser.parse_args(args)


+ 0
- 6
src/transpose/logger.py View File

@ -1,6 +0,0 @@
import logging
def create_logger(logger_name: str) -> logging:
logger = logging.getLogger(logger_name)
return logger

+ 78
- 19
src/transpose/transpose.py View File

@ -1,19 +1,23 @@
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any
# from typing import Self # from typing import Self
import datetime
import json import json
from . import version as transpose_version from . import version as transpose_version
from .exceptions import TransposeError from .exceptions import TransposeError
from .utils import move, remove, symlink
from .utils import move, symlink
@dataclass @dataclass
class TransposeEntry: class TransposeEntry:
name: str name: str
path: str path: str
created: str # Should be datetime.datetime but not really necessary here
enabled: bool = True
@dataclass @dataclass
@ -21,13 +25,14 @@ class TransposeConfig:
entries: dict = field(default_factory=dict) entries: dict = field(default_factory=dict)
version: str = field(default=transpose_version) version: str = field(default=transpose_version)
def add(self, name: str, path: str) -> None:
def add(self, name: str, path: str, created: str = None) -> None:
""" """
Add a new entry to the entries Add a new entry to the entries
Args: Args:
name: The name of the entry (must not exist) name: The name of the entry (must not exist)
path: The path where the entry originally exists path: The path where the entry originally exists
created: The date in datetime.now().__str__() format
Returns: Returns:
None None
@ -35,7 +40,44 @@ class TransposeConfig:
if self.entries.get(name): if self.entries.get(name):
raise TransposeError(f"'{name}' already exists") raise TransposeError(f"'{name}' already exists")
self.entries[name] = TransposeEntry(name=name, path=str(path))
if not created:
created = str(datetime.datetime.now())
self.entries[name] = TransposeEntry(
name=name,
path=str(path),
created=created,
)
def disable(self, name: str) -> None:
"""
Disable an entry by name. This ensures actions are not run against this entry, such as apply and restore
Args:
name: The name of the entry (must exist)
Returns:
None
"""
try:
self.entries[name].enabled = False
except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries")
def enable(self, name: str) -> None:
"""
Enable an entry by name
Args:
name: The name of the entry (must exist)
Returns:
None
"""
try:
self.entries[name].enabled = True
except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries")
def get(self, name: str) -> TransposeEntry: def get(self, name: str) -> TransposeEntry:
""" """
@ -67,33 +109,46 @@ class TransposeConfig:
except KeyError: except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries") raise TransposeError(f"'{name}' does not exist in Transpose config entries")
def update(self, name: str, path: str) -> None:
def update(self, name: str, field_key: str, field_value: Any) -> None:
""" """
Update an entry by name
Update an entry's field (attribute) value
Args: Args:
name: The name of the entry (must exist) name: The name of the entry (must exist)
path: The path where the entry originally exists
field_key: The key to update
field_value: The value to update
Returns: Returns:
None None
""" """
try: try:
self.entries[name].path = path
if not hasattr(self.entries[name], field_key):
raise TransposeError(f"Unknown TransposeEntry field: {field_key}")
except KeyError: except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries") raise TransposeError(f"'{name}' does not exist in Transpose config entries")
setattr(self.entries[name], field_key, field_value)
@staticmethod @staticmethod
def load(config_path: str): # -> Self: def load(config_path: str): # -> Self:
try: try:
in_config = json.load(open(config_path, "r")) in_config = json.load(open(config_path, "r"))
except json.decoder.JSONDecodeError as e: except json.decoder.JSONDecodeError as e:
raise TransposeError(f"Invalid JSON format for '{config_path}': {e}") raise TransposeError(f"Invalid JSON format for '{config_path}': {e}")
except FileNotFoundError:
in_config = {"entries": {}}
config = TransposeConfig() config = TransposeConfig()
try: try:
for name in in_config["entries"]: for name in in_config["entries"]:
config.add(name, in_config["entries"][name]["path"])
entry = in_config["entries"][name]
config.add(
name,
entry["path"],
created=entry["created"],
)
if not entry["enabled"]:
config.disable(name)
except (KeyError, TypeError) as e: except (KeyError, TypeError) as e:
raise TransposeError(f"Unrecognized Transpose config file format: {e}") raise TransposeError(f"Unrecognized Transpose config file format: {e}")
@ -113,7 +168,7 @@ class TransposeConfig:
config_path.parent.mkdir(parents=True, exist_ok=True) config_path.parent.mkdir(parents=True, exist_ok=True)
with open(str(config_path), "w") as f: with open(str(config_path), "w") as f:
json.dump(self.to_dict(), f)
json.dump(self.to_dict(), f, default=str)
def to_dict(self) -> dict: def to_dict(self) -> dict:
return asdict(self) return asdict(self)
@ -138,7 +193,7 @@ class Transpose:
Args: Args:
name: The name of the entry (must exist) name: The name of the entry (must exist)
force: If enabled and path already exists, move the path to '{path}-bak'
force: If enabled and path already exists, move the path to '{path}.backup' first
Returns: Returns:
None None
@ -146,11 +201,13 @@ class Transpose:
if not self.config.entries.get(name): if not self.config.entries.get(name):
raise TransposeError(f"Entry does not exist: '{name}'") raise TransposeError(f"Entry does not exist: '{name}'")
entry_path = Path(self.config.entries[name].path)
entry = self.config.entries[name]
if not entry.enabled and not force:
raise TransposeError(f"Entry '{name}' is not enabled in the config")
entry_path = Path(entry.path)
if entry_path.exists(): if entry_path.exists():
if entry_path.is_symlink():
remove(entry_path)
elif force: # Backup the existing path
if force: # Backup the existing path
move(entry_path, entry_path.with_suffix(".backup")) move(entry_path, entry_path.with_suffix(".backup"))
else: else:
raise TransposeError( raise TransposeError(
@ -168,7 +225,7 @@ class Transpose:
Args: Args:
name: The name of the entry (must exist) name: The name of the entry (must exist)
force: If enabled and path already exists, move the path to '{path}-bak'
force: If enabled and path already exists, move the path to '{path}.backup' first
Returns: Returns:
None None
@ -176,11 +233,13 @@ class Transpose:
if not self.config.entries.get(name): if not self.config.entries.get(name):
raise TransposeError(f"Could not locate entry by name: '{name}'") raise TransposeError(f"Could not locate entry by name: '{name}'")
entry_path = Path(self.config.entries[name].path)
entry = self.config.entries[name]
if not entry.enabled and not force:
raise TransposeError(f"Entry '{name}' is not enabled in the config")
entry_path = Path(entry.path)
if entry_path.exists(): if entry_path.exists():
if entry_path.is_symlink():
remove(entry_path)
elif force: # Backup the existing path
if force: # Backup the existing path
move(entry_path, entry_path.with_suffix(".backup")) move(entry_path, entry_path.with_suffix(".backup"))
else: else:
raise TransposeError( raise TransposeError(


+ 1
- 1
src/transpose/utils.py View File

@ -7,7 +7,7 @@ def move(source: Path, destination: Path) -> None:
""" """
Move a file using pathlib Move a file using pathlib
""" """
shutil.move(source, destination)
shutil.move(source.expanduser(), destination.expanduser())
def remove(path: Path) -> None: def remove(path: Path) -> None:


+ 73
- 12
tests/test_console.py View File

@ -7,18 +7,31 @@ from transpose.console import parse_arguments, run as run_console
from .utils import ( from .utils import (
setup_restore, setup_restore,
setup_store,
setup_apply, setup_apply,
ENTRY_NAME,
SECOND_ENTRY_NAME,
STORE_PATH, STORE_PATH,
TARGET_PATH, TARGET_PATH,
SECOND_TARGET_PATH,
TRANSPOSE_CONFIG_PATH, TRANSPOSE_CONFIG_PATH,
) )
class RunArgs:
name: str = "MyName"
class RunActionArgs:
name: str = ENTRY_NAME
path: str = str(TARGET_PATH)
action: str
force: bool
def __init__(self, action: str, force: bool = False) -> None:
self.action = action
self.force = force
class RunConfigArgs:
name: str = ENTRY_NAME
action: str = "config" action: str = "config"
forced: bool = False
force: bool = False
path: str = str(TARGET_PATH) path: str = str(TARGET_PATH)
config_action: str config_action: str
@ -121,8 +134,35 @@ def test_parse_arguments_restore():
assert args.force is True assert args.force is True
@setup_apply()
def test_run_apply(): def test_run_apply():
pass
args = RunActionArgs("apply", False)
run_console(args, TRANSPOSE_CONFIG_PATH)
assert TARGET_PATH.is_symlink()
@setup_apply()
def test_run_apply_all(capsys):
args = RunActionArgs("apply-all", False)
run_console(args, TRANSPOSE_CONFIG_PATH)
captured = capsys.readouterr()
assert f"\t{ENTRY_NAME:<30}: success" in captured.out
assert f"\t{SECOND_ENTRY_NAME:<30}: Entry path already exists" in captured.out
assert TARGET_PATH.is_symlink()
assert SECOND_TARGET_PATH.is_dir()
args.force = True
run_console(args, TRANSPOSE_CONFIG_PATH)
captured = capsys.readouterr()
assert f"\t{ENTRY_NAME:<30}: success" in captured.out
assert f"\t{SECOND_ENTRY_NAME:<30}: success" in captured.out
assert SECOND_TARGET_PATH.is_symlink()
assert SECOND_TARGET_PATH.with_suffix(".backup").is_dir()
def test_run_restore(): def test_run_restore():
@ -135,7 +175,7 @@ def test_run_store():
@setup_restore() @setup_restore()
def test_run_config_add(): def test_run_config_add():
args = RunArgs("add")
args = RunConfigArgs("add")
args.name = "MyName2" args.name = "MyName2"
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
@ -144,9 +184,29 @@ def test_run_config_add():
assert config.entries.get(args.name) assert config.entries.get(args.name)
@setup_restore()
def test_run_config_disable():
args = RunConfigArgs("disable")
run_console(args, TRANSPOSE_CONFIG_PATH)
config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH)
assert config.entries[args.name].enabled is False
@setup_restore()
def test_run_config_enable():
args = RunConfigArgs("enable")
run_console(args, TRANSPOSE_CONFIG_PATH)
config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH)
assert config.entries[args.name].enabled is True
@setup_restore() @setup_restore()
def test_run_config_get(capsys): def test_run_config_get(capsys):
args = RunArgs("get")
args = RunConfigArgs("get")
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
captured = capsys.readouterr() captured = capsys.readouterr()
@ -156,7 +216,7 @@ def test_run_config_get(capsys):
@setup_restore() @setup_restore()
def test_run_config_list(capsys): def test_run_config_list(capsys):
args = RunArgs("list")
args = RunConfigArgs("list")
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
captured = capsys.readouterr() captured = capsys.readouterr()
@ -166,7 +226,7 @@ def test_run_config_list(capsys):
@setup_restore() @setup_restore()
def test_run_config_remove(): def test_run_config_remove():
args = RunArgs("remove")
args = RunConfigArgs("remove")
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH)
@ -176,10 +236,11 @@ def test_run_config_remove():
@setup_restore() @setup_restore()
def test_run_config_update(): def test_run_config_update():
args = RunArgs("update")
args.path = "/var/tmp/something"
args = RunConfigArgs("update")
args.field_key = "path"
args.field_value = "/var/tmp/something"
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH)
assert config.entries[args.name].path == args.path
assert config.entries[args.name].path == args.field_value

+ 58
- 8
tests/test_transpose.py View File

@ -38,12 +38,6 @@ def test_apply():
with pytest.raises(TransposeError, match="Entry does not exist"): with pytest.raises(TransposeError, match="Entry does not exist"):
t.apply("BadName") t.apply("BadName")
# Will remove the symlink created above and reapply
# TODO: Check symlink path
t.apply(ENTRY_NAME)
assert TARGET_PATH.is_symlink()
assert ENTRY_STORE_PATH.is_dir()
# Target already exists, force not set # Target already exists, force not set
TARGET_PATH.unlink() TARGET_PATH.unlink()
TARGET_PATH.mkdir() TARGET_PATH.mkdir()
@ -58,11 +52,26 @@ def test_apply():
assert TARGET_PATH.is_symlink() assert TARGET_PATH.is_symlink()
assert ENTRY_STORE_PATH.is_dir() assert ENTRY_STORE_PATH.is_dir()
# Target is disabled in the config
t.config.entries[ENTRY_NAME].enabled = False
with pytest.raises(
TransposeError, match=f"Entry '{ENTRY_NAME}' is not enabled in the config"
):
t.apply(ENTRY_NAME)
@setup_restore() @setup_restore()
def test_restore(): def test_restore():
t = Transpose(config_path=TRANSPOSE_CONFIG_PATH) t = Transpose(config_path=TRANSPOSE_CONFIG_PATH)
# Target is disabled in the config
t.config.entries[ENTRY_NAME].enabled = False
with pytest.raises(
TransposeError, match=f"Entry '{ENTRY_NAME}' is not enabled in the config"
):
t.restore(ENTRY_NAME)
t.config.entries[ENTRY_NAME].enabled = True
# Success # Success
t.restore(ENTRY_NAME) t.restore(ENTRY_NAME)
assert TARGET_PATH.is_dir() assert TARGET_PATH.is_dir()
@ -131,6 +140,28 @@ def test_config_add():
assert config.entries["NewEntry"].path == str(TARGET_PATH) assert config.entries["NewEntry"].path == str(TARGET_PATH)
@setup_store()
def test_config_disable():
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
with pytest.raises(TransposeError, match="'UnknownEntry' does not exist"):
config.disable("UnknownEntry")
config.disable(ENTRY_NAME)
assert config.entries[ENTRY_NAME].enabled is False
@setup_store()
def test_config_enable():
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
with pytest.raises(TransposeError, match="'UnknownEntry' does not exist"):
config.enable("UnknownEntry")
config.enable(ENTRY_NAME)
assert config.entries[ENTRY_NAME].enabled is True
@setup_store() @setup_store()
def test_config_get(): def test_config_get():
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
@ -163,9 +194,12 @@ def test_config_update():
with pytest.raises( with pytest.raises(
TransposeError, match="does not exist in Transpose config entries" TransposeError, match="does not exist in Transpose config entries"
): ):
config.update("UnknownEntry", "/some/new/path")
config.update("UnknownEntry", "path", "/some/new/path")
config.update(ENTRY_NAME, "/some/new/path")
with pytest.raises(TransposeError, match="Unknown TransposeEntry field"):
config.update(ENTRY_NAME, "UnknownField", "Some Value")
config.update(ENTRY_NAME, "path", "/some/new/path")
assert config.entries[ENTRY_NAME].path == "/some/new/path" assert config.entries[ENTRY_NAME].path == "/some/new/path"
@ -182,6 +216,22 @@ def test_config_save():
) )
@setup_store()
def test_config_save_fresh():
"""
Verify creation of transpose config when doesn't initially exist
"""
TRANSPOSE_CONFIG_PATH.unlink()
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
assert len(config.entries) == 0
config.add("TestEntry", TARGET_PATH)
config.save(TRANSPOSE_CONFIG_PATH)
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
assert config.entries.get("TestEntry")
@setup_store() @setup_store()
def test_config_load(): def test_config_load():
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)


+ 21
- 2
tests/utils.py View File

@ -9,9 +9,11 @@ from transpose import version
ENTRY_NAME = "MyName" ENTRY_NAME = "MyName"
SECOND_ENTRY_NAME = "SecondEntry"
TESTS_PATH = Path("tests-temp") TESTS_PATH = Path("tests-temp")
STORE_PATH = TESTS_PATH.joinpath("store") STORE_PATH = TESTS_PATH.joinpath("store")
TARGET_PATH = TESTS_PATH.joinpath("source") TARGET_PATH = TESTS_PATH.joinpath("source")
SECOND_TARGET_PATH = TESTS_PATH.joinpath("second_source")
SYMLINK_TEST_PATH = TESTS_PATH.joinpath("symlink_test") SYMLINK_TEST_PATH = TESTS_PATH.joinpath("symlink_test")
ENTRY_STORE_PATH = STORE_PATH.joinpath(ENTRY_NAME) ENTRY_STORE_PATH = STORE_PATH.joinpath(ENTRY_NAME)
@ -19,7 +21,20 @@ TRANSPOSE_CONFIG_PATH = STORE_PATH.joinpath("transpose.json")
TRANSPOSE_CONFIG = { TRANSPOSE_CONFIG = {
"version": version, "version": version,
"entries": {ENTRY_NAME: {"name": ENTRY_NAME, "path": str(TARGET_PATH)}},
"entries": {
ENTRY_NAME: {
"name": ENTRY_NAME,
"path": str(TARGET_PATH),
"created": "2023-01-21 01:02:03.1234567",
"enabled": True,
},
SECOND_ENTRY_NAME: {
"name": SECOND_ENTRY_NAME,
"path": str(SECOND_TARGET_PATH),
"created": "2023-02-23 01:02:03.1234567",
"enabled": True,
},
},
} }
@ -30,13 +45,17 @@ def setup_apply():
tests-temp/ tests-temp/
store/ store/
transpose.json transpose.json
MyName/
MyName/
SecondEntry/
second_source/
symlink_test/ -> source/ symlink_test/ -> source/
""" """
try: try:
with TemporaryDirectory(str(TESTS_PATH)): with TemporaryDirectory(str(TESTS_PATH)):
STORE_PATH.mkdir(parents=True, exist_ok=True) STORE_PATH.mkdir(parents=True, exist_ok=True)
ENTRY_STORE_PATH.mkdir(parents=True, exist_ok=True) ENTRY_STORE_PATH.mkdir(parents=True, exist_ok=True)
STORE_PATH.joinpath(SECOND_ENTRY_NAME).mkdir()
SECOND_TARGET_PATH.mkdir()
SYMLINK_TEST_PATH.symlink_to(TARGET_PATH.resolve()) SYMLINK_TEST_PATH.symlink_to(TARGET_PATH.resolve())
with open(str(TRANSPOSE_CONFIG_PATH), "w") as f: with open(str(TRANSPOSE_CONFIG_PATH), "w") as f:


Loading…
Cancel
Save