feat: add archive client foundation
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from archive_clients.config import ClientConfig, ConfigError, RootMapping
|
||||
|
||||
|
||||
class ConfigTests(unittest.TestCase):
|
||||
def test_strict_config_mode_override_secrets_and_mapping(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
for name in ("token", "qb-password", "syncthing-key"):
|
||||
path = root / name
|
||||
path.write_text(name, encoding="utf-8")
|
||||
os.chmod(path, 0o600)
|
||||
(root / "qb").mkdir()
|
||||
(root / "sync").mkdir()
|
||||
config_path = root / "client.toml"
|
||||
config_path.write_text(
|
||||
_config(root, role="cache"), encoding="utf-8"
|
||||
)
|
||||
config = ClientConfig.load(config_path, "archive")
|
||||
self.assertEqual(config.role, "archive")
|
||||
self.assertEqual(config.read_shared_token(), "token")
|
||||
self.assertEqual(
|
||||
config.qbittorrent.roots.api_to_local("/downloads/a/b"),
|
||||
root / "qb/a/b",
|
||||
)
|
||||
self.assertEqual(config.jobs.stall_after, 30 * 60)
|
||||
self.assertEqual(
|
||||
config.syncthing.advertised_addresses, ("dynamic",)
|
||||
)
|
||||
|
||||
def test_unknown_key_and_unsafe_secret_fail(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
for name in ("token", "qb-password", "syncthing-key"):
|
||||
path = root / name
|
||||
path.write_text(name, encoding="utf-8")
|
||||
os.chmod(path, 0o600)
|
||||
config_path = root / "client.toml"
|
||||
invalid = _config(root).replace(
|
||||
"[qbittorrent]", "typo = true\n[qbittorrent]"
|
||||
)
|
||||
config_path.write_text(invalid, encoding="utf-8")
|
||||
with self.assertRaisesRegex(ConfigError, "unknown root"):
|
||||
ClientConfig.load(config_path)
|
||||
os.chmod(root / "token", 0o640)
|
||||
config_path.write_text(_config(root), encoding="utf-8")
|
||||
with self.assertRaisesRegex(ConfigError, "permissions"):
|
||||
ClientConfig.load(config_path).read_shared_token()
|
||||
|
||||
def test_mapping_rejects_escape(self):
|
||||
mapping = RootMapping(Path("/api"), Path("/local"))
|
||||
with self.assertRaises(ConfigError):
|
||||
mapping.api_to_local("/elsewhere/file")
|
||||
|
||||
def test_endpoint_scheme_and_job_keys_are_strict(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
invalid_endpoint = _config(root).replace(
|
||||
'control_endpoint = "ws://control/archive_control"',
|
||||
'control_endpoint = "http://control/archive_control"',
|
||||
)
|
||||
path = root / "client.toml"
|
||||
path.write_text(invalid_endpoint, encoding="utf-8")
|
||||
with self.assertRaisesRegex(ConfigError, "ws/wss"):
|
||||
ClientConfig.load(path)
|
||||
path.write_text(
|
||||
_config(root) + "\n[jobs]\nunknown = true\n", encoding="utf-8"
|
||||
)
|
||||
with self.assertRaisesRegex(ConfigError, "unknown jobs"):
|
||||
ClientConfig.load(path)
|
||||
|
||||
|
||||
def _config(root: Path, role: str = "cache") -> str:
|
||||
return f'''client_id = "cache-1"
|
||||
display_name = "Cache 1"
|
||||
role = "{role}"
|
||||
control_endpoint = "ws://control/archive_control"
|
||||
shared_token_file = "{root / 'token'}"
|
||||
state_db = "{root / 'state.db'}"
|
||||
backup_dir = "{root / 'backups'}"
|
||||
[qbittorrent]
|
||||
endpoint = "http://qb"
|
||||
username = "admin"
|
||||
password_file = "{root / 'qb-password'}"
|
||||
api_root = "/downloads"
|
||||
local_root = "{root / 'qb'}"
|
||||
[syncthing]
|
||||
endpoint = "http://syncthing"
|
||||
api_key_file = "{root / 'syncthing-key'}"
|
||||
api_root = "/sync"
|
||||
local_root = "{root / 'sync'}"
|
||||
advertised_addresses = ["dynamic"]
|
||||
'''
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user