import importlib.util import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] DEPLOY_DOC = REPO_ROOT / "docs" / "nas-docker-deployment.md" COMPOSE_FILE = REPO_ROOT / "docker-compose.nas.yml" DEPLOY_ENTRY = REPO_ROOT / "deploy-music-server.ps1" DEPLOY_PS = REPO_ROOT / "scripts" / "deploy_to_nas.ps1" DEPLOY_PY = REPO_ROOT / "scripts" / "deploy_to_nas.py" DEPLOY_TEMPLATE = REPO_ROOT / "scripts" / "templates" / "deploy_and_restart.sh" ENV_EXAMPLE = REPO_ROOT / "config" / "music_server.env.example" def load_deploy_module(): spec = importlib.util.spec_from_file_location("music_server_deploy_to_nas", DEPLOY_PY) module = importlib.util.module_from_spec(spec) assert spec.loader is not None spec.loader.exec_module(module) return module class NasDeployLayoutTests(unittest.TestCase): def test_compose_uses_runtime_dirs_outside_repo_checkout(self): compose_text = COMPOSE_FILE.read_text(encoding="utf-8") self.assertIn("- ../config/music_server.env", compose_text) self.assertIn("- ../data:/app/data", compose_text) self.assertNotIn("- ./config/music_server.env", compose_text) self.assertNotIn("- ./data:/app/data", compose_text) def test_docs_and_scripts_reference_standard_music_cloud_layout(self): deploy_text = DEPLOY_DOC.read_text(encoding="utf-8") self.assertTrue(DEPLOY_ENTRY.exists(), f"missing deploy entry: {DEPLOY_ENTRY}") self.assertTrue(DEPLOY_PS.exists(), f"missing deploy powershell: {DEPLOY_PS}") self.assertTrue(DEPLOY_PY.exists(), f"missing deploy python: {DEPLOY_PY}") self.assertTrue(DEPLOY_TEMPLATE.exists(), f"missing deploy template: {DEPLOY_TEMPLATE}") self.assertIn("/volume4/Music_Cloud/Music_Server/app", deploy_text) self.assertIn("/volume4/Music_Cloud/Music_Server/config/music_server.env", deploy_text) self.assertIn("/volume4/Music_Cloud/Music_Server/data/catalog_read.db", deploy_text) self.assertIn("/volume4/Music_Cloud/Music_Server/bin/deploy_and_restart.sh", deploy_text) self.assertIn("deploy-music-server.ps1", deploy_text) def test_deploy_helper_skips_generated_archives_and_cache_dirs(self): module = load_deploy_module() with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) (root / "src").mkdir() (root / "__pycache__").mkdir() (root / ".git").mkdir() (root / "src" / "app.py").write_text("print('ok')\n", encoding="utf-8") (root / "__pycache__" / "ignored.pyc").write_bytes(b"123") (root / ".git" / "config").write_text("[core]\n", encoding="utf-8") (root / "music_server_deploy.tar").write_bytes(b"tar") (root / "music_server_deploy.zip").write_bytes(b"zip") actual = sorted(path.relative_to(root).as_posix() for path in module.iter_local_files(root)) self.assertEqual(actual, ["src/app.py"]) def test_deploy_template_removes_conflicting_fixed_name_music_server_container(self): template_text = DEPLOY_TEMPLATE.read_text(encoding="utf-8") self.assertIn( "docker rm -f music-server >/dev/null 2>&1 || true", template_text, ) def test_env_example_and_docs_do_not_reference_wireguard_settings(self): deploy_text = DEPLOY_DOC.read_text(encoding="utf-8") env_example_text = ENV_EXAMPLE.read_text(encoding="utf-8") self.assertNotIn("WIREGUARD_", deploy_text) self.assertNotIn("WIREGUARD_", env_example_text) if __name__ == "__main__": unittest.main()