test_config_manager.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """Tests for config migration behavior."""
  2. from __future__ import annotations
  3. from pathlib import Path
  4. from unittest.mock import patch
  5. import yaml
  6. from cli.core.config.config_manager import (
  7. DEFAULT_LIBRARY_BRANCH,
  8. DEFAULT_LIBRARY_DIRECTORY,
  9. DEFAULT_LIBRARY_NAME,
  10. DEFAULT_LIBRARY_URL,
  11. LEGACY_DEFAULT_LIBRARY_DIRECTORY,
  12. LEGACY_DEFAULT_LIBRARY_URL,
  13. ConfigManager,
  14. )
  15. from cli.core.exceptions import ConfigError
  16. def _write_config(path: Path, data: dict) -> None:
  17. path.parent.mkdir(parents=True, exist_ok=True)
  18. path.write_text(yaml.safe_dump(data), encoding="utf-8")
  19. def test_default_config_uses_new_library_repo(tmp_path: Path) -> None:
  20. """A fresh config should point at the new default template library."""
  21. config_path = tmp_path / "config.yaml"
  22. manager = ConfigManager(config_path=config_path)
  23. libraries = manager.get_libraries()
  24. assert len(libraries) == 1
  25. assert libraries[0]["name"] == DEFAULT_LIBRARY_NAME
  26. assert libraries[0]["url"] == DEFAULT_LIBRARY_URL
  27. assert libraries[0]["branch"] == DEFAULT_LIBRARY_BRANCH
  28. assert libraries[0]["directory"] == DEFAULT_LIBRARY_DIRECTORY
  29. def test_migrate_legacy_default_library_and_queue_notice(tmp_path: Path) -> None:
  30. """Only the legacy built-in default library entry should be rewritten."""
  31. config_path = tmp_path / "config.yaml"
  32. ConfigManager.consume_migration_notices()
  33. _write_config(
  34. config_path,
  35. {
  36. "defaults": {},
  37. "preferences": {},
  38. "libraries": [
  39. {
  40. "name": DEFAULT_LIBRARY_NAME,
  41. "type": "git",
  42. "url": LEGACY_DEFAULT_LIBRARY_URL,
  43. "branch": "feature/test",
  44. "directory": LEGACY_DEFAULT_LIBRARY_DIRECTORY,
  45. "enabled": True,
  46. },
  47. {
  48. "name": "custom",
  49. "type": "git",
  50. "url": "https://github.com/example/custom-library.git",
  51. "branch": "dev",
  52. "directory": "templates",
  53. "enabled": True,
  54. },
  55. ],
  56. },
  57. )
  58. manager = ConfigManager(config_path=config_path)
  59. libraries = manager.get_libraries()
  60. notices = ConfigManager.consume_migration_notices()
  61. assert libraries[0]["name"] == DEFAULT_LIBRARY_NAME
  62. assert libraries[0]["url"] == DEFAULT_LIBRARY_URL
  63. assert libraries[0]["branch"] == "feature/test"
  64. assert libraries[0]["directory"] == DEFAULT_LIBRARY_DIRECTORY
  65. assert libraries[0]["enabled"] is True
  66. assert libraries[1]["name"] == "custom"
  67. assert libraries[1]["url"] == "https://github.com/example/custom-library.git"
  68. assert libraries[1]["branch"] == "dev"
  69. assert libraries[1]["directory"] == "templates"
  70. assert len(notices) == 1
  71. assert "boilerplates-library" in notices[0].message
  72. # Notices are consumed once.
  73. assert ConfigManager.consume_migration_notices() == []
  74. def test_does_not_migrate_custom_default_library(tmp_path: Path) -> None:
  75. """A user-customized default entry should be left untouched."""
  76. config_path = tmp_path / "config.yaml"
  77. ConfigManager.consume_migration_notices()
  78. _write_config(
  79. config_path,
  80. {
  81. "defaults": {},
  82. "preferences": {},
  83. "libraries": [
  84. {
  85. "name": DEFAULT_LIBRARY_NAME,
  86. "type": "git",
  87. "url": "/Users/test/local/boilerplates",
  88. "branch": "feature/test",
  89. "directory": ".",
  90. "enabled": True,
  91. }
  92. ],
  93. },
  94. )
  95. manager = ConfigManager(config_path=config_path)
  96. libraries = manager.get_libraries()
  97. assert libraries[0]["name"] == DEFAULT_LIBRARY_NAME
  98. assert libraries[0]["url"] == "/Users/test/local/boilerplates"
  99. assert libraries[0]["branch"] == "feature/test"
  100. assert libraries[0]["directory"] == "."
  101. assert ConfigManager.consume_migration_notices() == []
  102. def test_migration_write_failure_raises_config_error(tmp_path: Path) -> None:
  103. """Migration failures should surface as config errors instead of being swallowed."""
  104. config_path = tmp_path / "config.yaml"
  105. _write_config(
  106. config_path,
  107. {
  108. "defaults": {},
  109. "preferences": {},
  110. "libraries": [
  111. {
  112. "name": DEFAULT_LIBRARY_NAME,
  113. "type": "git",
  114. "url": LEGACY_DEFAULT_LIBRARY_URL,
  115. "branch": "main",
  116. "directory": LEGACY_DEFAULT_LIBRARY_DIRECTORY,
  117. "enabled": True,
  118. }
  119. ],
  120. },
  121. )
  122. with patch.object(ConfigManager, "_write_config", side_effect=ConfigError("disk full")):
  123. try:
  124. ConfigManager(config_path=config_path)
  125. except ConfigError as exc:
  126. assert "disk full" in str(exc)
  127. else:
  128. raise AssertionError("Expected ConfigError for failed config migration write")