test_migrate_cli.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. # Copyright 2023 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. import filecmp
  12. import os
  13. import shutil
  14. import sys
  15. from unittest.mock import patch
  16. import mongomock
  17. import pytest
  18. from taipy.core._entity._migrate_cli import _MigrateCLI
  19. @pytest.fixture(scope="function", autouse=True)
  20. def clean_data_folder():
  21. if os.path.exists("tests/core/_entity/.data"):
  22. shutil.rmtree("tests/core/_entity/.data")
  23. yield
  24. def test_migrate_fs_default(caplog):
  25. _MigrateCLI.create_parser()
  26. # Test migrate with default .data folder
  27. with pytest.raises(SystemExit):
  28. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", "--skip-backup"]):
  29. _MigrateCLI.parse_arguments()
  30. assert "Starting entity migration from '.data' folder" in caplog.text
  31. def test_migrate_fs_specified_folder(caplog):
  32. _MigrateCLI.create_parser()
  33. # Copy data_sample to .data folder for testing
  34. data_sample_path = "tests/core/_entity/data_sample"
  35. data_path = "tests/core/_entity/.data"
  36. shutil.copytree(data_sample_path, data_path)
  37. # Run with --skip-backup to only test the migration
  38. with pytest.raises(SystemExit):
  39. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path, "--skip-backup"]):
  40. _MigrateCLI.parse_arguments()
  41. assert f"Starting entity migration from '{data_path}' folder" in caplog.text
  42. # Compare migrated .data folder with data_sample_migrated
  43. dircmp_result = filecmp.dircmp(data_path, "tests/core/_entity/data_sample_migrated")
  44. assert not dircmp_result.diff_files and not dircmp_result.left_only and not dircmp_result.right_only
  45. for subdir in dircmp_result.subdirs.values():
  46. assert not subdir.diff_files and not subdir.left_only and not subdir.right_only
  47. def test_migrate_fs_backup_and_remove(caplog):
  48. _MigrateCLI.create_parser()
  49. # Copy data_sample to .data folder for testing
  50. data_sample_path = "tests/core/_entity/data_sample"
  51. data_path = "tests/core/_entity/.data"
  52. backup_path = "tests/core/_entity/.data_backup"
  53. shutil.copytree(data_sample_path, data_path)
  54. # Remove backup when it does not exist should raise an error
  55. with pytest.raises(SystemExit) as err:
  56. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path, "--remove-backup"]):
  57. _MigrateCLI.parse_arguments()
  58. assert err.value.code == 1
  59. assert f"The backup folder '{backup_path}' does not exist." in caplog.text
  60. assert not os.path.exists(backup_path)
  61. # Run without --skip-backup to create the backup folder
  62. with pytest.raises(SystemExit):
  63. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path]):
  64. _MigrateCLI.parse_arguments()
  65. assert f"Backed up entities from '{data_path}' to '{backup_path}' folder before migration." in caplog.text
  66. assert os.path.exists(backup_path)
  67. # Remove backup
  68. with pytest.raises(SystemExit):
  69. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path, "--remove-backup"]):
  70. _MigrateCLI.parse_arguments()
  71. assert f"Removed backup entities from the backup folder '{backup_path}'." in caplog.text
  72. assert not os.path.exists(backup_path)
  73. def test_migrate_fs_backup_and_restore(caplog):
  74. _MigrateCLI.create_parser()
  75. # Copy data_sample to .data folder for testing
  76. data_sample_path = "tests/core/_entity/data_sample"
  77. data_path = "tests/core/_entity/.data"
  78. backup_path = "tests/core/_entity/.data_backup"
  79. shutil.copytree(data_sample_path, data_path)
  80. # Restore backup when it does not exist should raise an error
  81. with pytest.raises(SystemExit) as err:
  82. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path, "--restore"]):
  83. _MigrateCLI.parse_arguments()
  84. assert err.value.code == 1
  85. assert f"The backup folder '{backup_path}' does not exist." in caplog.text
  86. assert not os.path.exists(backup_path)
  87. # Run without --skip-backup to create the backup folder
  88. with pytest.raises(SystemExit):
  89. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path]):
  90. _MigrateCLI.parse_arguments()
  91. assert os.path.exists(backup_path)
  92. # restore the backup
  93. with pytest.raises(SystemExit):
  94. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", data_path, "--restore"]):
  95. _MigrateCLI.parse_arguments()
  96. assert f"Restored entities from the backup folder '{backup_path}' to '{data_path}'." in caplog.text
  97. assert not os.path.exists(backup_path)
  98. # Compare migrated .data folder with data_sample to ensure restoring the backup worked
  99. dircmp_result = filecmp.dircmp(data_path, "tests/core/_entity/data_sample")
  100. assert not dircmp_result.diff_files and not dircmp_result.left_only and not dircmp_result.right_only
  101. for subdir in dircmp_result.subdirs.values():
  102. assert not subdir.diff_files and not subdir.left_only and not subdir.right_only
  103. def test_migrate_fs_non_existing_folder(caplog):
  104. _MigrateCLI.create_parser()
  105. # Test migrate with a non-existing folder
  106. with pytest.raises(SystemExit) as err:
  107. with patch("sys.argv", ["prog", "migrate", "--repository-type", "filesystem", "non-existing-folder"]):
  108. _MigrateCLI.parse_arguments()
  109. assert err.value.code == 1
  110. assert "Folder 'non-existing-folder' does not exist." in caplog.text
  111. @patch("taipy.core._entity._migrate_cli._migrate_sql_entities")
  112. def test_migrate_sql_specified_path(_migrate_sql_entities_mock, tmp_sqlite):
  113. _MigrateCLI.create_parser()
  114. # Test the _migrate_sql_entities is called once with the correct path
  115. with pytest.raises(SystemExit):
  116. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite, "--skip-backup"]):
  117. _MigrateCLI.parse_arguments()
  118. assert _migrate_sql_entities_mock.assert_called_once_with(path=tmp_sqlite)
  119. def test_migrate_sql_backup_and_remove(caplog, tmp_sqlite):
  120. _MigrateCLI.create_parser()
  121. # Create the .sqlite file to test
  122. with open(tmp_sqlite, "w") as f:
  123. f.write("")
  124. file_name, file_extension = tmp_sqlite.rsplit(".", 1)
  125. backup_sqlite = f"{file_name}_backup.{file_extension}"
  126. # Remove backup when it does not exist should raise an error
  127. with pytest.raises(SystemExit) as err:
  128. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite, "--remove-backup"]):
  129. _MigrateCLI.parse_arguments()
  130. assert err.value.code == 1
  131. assert f"The backup database '{backup_sqlite}' does not exist." in caplog.text
  132. assert not os.path.exists(backup_sqlite)
  133. # Run without --skip-backup to create the backup database
  134. with pytest.raises(Exception):
  135. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite]):
  136. _MigrateCLI.parse_arguments()
  137. assert os.path.exists(backup_sqlite)
  138. # Remove backup
  139. with pytest.raises(SystemExit):
  140. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite, "--remove-backup"]):
  141. _MigrateCLI.parse_arguments()
  142. assert f"Removed backup entities from the backup database '{backup_sqlite}'." in caplog.text
  143. assert not os.path.exists(backup_sqlite)
  144. @pytest.mark.skipif(sys.platform == "win32", reason="Does not run on windows due to PermissionError: [WinError 32]")
  145. def test_migrate_sql_backup_and_restore(caplog, tmp_sqlite):
  146. _MigrateCLI.create_parser()
  147. # Create the .sqlite file to test
  148. with open(tmp_sqlite, "w") as f:
  149. f.write("")
  150. file_name, file_extension = tmp_sqlite.rsplit(".", 1)
  151. backup_sqlite = f"{file_name}_backup.{file_extension}"
  152. # Restore backup when it does not exist should raise an error
  153. with pytest.raises(SystemExit) as err:
  154. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite, "--restore"]):
  155. _MigrateCLI.parse_arguments()
  156. assert err.value.code == 1
  157. assert f"The backup database '{backup_sqlite}' does not exist." in caplog.text
  158. assert not os.path.exists(backup_sqlite)
  159. # Run without --skip-backup to create the backup database
  160. with pytest.raises(Exception):
  161. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite]):
  162. _MigrateCLI.parse_arguments()
  163. assert os.path.exists(backup_sqlite)
  164. # Restore the backup
  165. with pytest.raises(SystemExit):
  166. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", tmp_sqlite, "--restore"]):
  167. _MigrateCLI.parse_arguments()
  168. assert f"Restored entities from the backup database '{backup_sqlite}' to '{tmp_sqlite}'." in caplog.text
  169. assert not os.path.exists(backup_sqlite)
  170. def test_migrate_sql_non_existing_path(caplog):
  171. _MigrateCLI.create_parser()
  172. # Test migrate without providing a path
  173. with pytest.raises(SystemExit) as err:
  174. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql"]):
  175. _MigrateCLI.parse_arguments()
  176. assert err.value.code == 1
  177. assert "Missing the required sqlite path." in caplog.text
  178. caplog.clear()
  179. # Test migrate with a non-existing-path.sqlite file
  180. with pytest.raises(SystemExit) as err:
  181. with patch("sys.argv", ["prog", "migrate", "--repository-type", "sql", "non-existing-path.sqlite"]):
  182. _MigrateCLI.parse_arguments()
  183. assert err.value.code == 1
  184. assert "File 'non-existing-path.sqlite' does not exist." in caplog.text
  185. @patch("taipy.core._entity._migrate_cli._migrate_mongo_entities")
  186. def test_call_to_migrate_mongo(_migrate_mongo_entities_mock):
  187. _MigrateCLI.create_parser()
  188. with pytest.raises(SystemExit):
  189. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo"]):
  190. _MigrateCLI.parse_arguments()
  191. assert _migrate_mongo_entities_mock.assert_called_once_with()
  192. with pytest.raises(SystemExit):
  193. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo", "host", "port", "user", "password"]):
  194. _MigrateCLI.parse_arguments()
  195. assert _migrate_mongo_entities_mock.assert_called_once_with("host", "port", "user", "password")
  196. @mongomock.patch(servers=(("localhost", 27017),))
  197. def test_migrate_mongo_backup_and_remove(caplog):
  198. _MigrateCLI.create_parser()
  199. mongo_backup_path = ".mongo_backup"
  200. # Remove backup when it does not exist should raise an error
  201. with pytest.raises(SystemExit) as err:
  202. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo", "--remove-backup"]):
  203. _MigrateCLI.parse_arguments()
  204. assert err.value.code == 1
  205. assert f"The backup folder '{mongo_backup_path}' does not exist." in caplog.text
  206. assert not os.path.exists(mongo_backup_path)
  207. # Run without --skip-backup to create the backup database
  208. with pytest.raises(Exception):
  209. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo"]):
  210. _MigrateCLI.parse_arguments()
  211. assert os.path.exists(mongo_backup_path)
  212. # Remove backup
  213. with pytest.raises(SystemExit):
  214. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo", "--remove-backup"]):
  215. _MigrateCLI.parse_arguments()
  216. assert f"Removed backup entities from the backup folder '{mongo_backup_path}'." in caplog.text
  217. assert not os.path.exists(mongo_backup_path)
  218. @mongomock.patch(servers=(("localhost", 27017),))
  219. def test_migrate_mongo_backup_and_restore(caplog):
  220. _MigrateCLI.create_parser()
  221. mongo_backup_path = ".mongo_backup"
  222. # Restore backup when it does not exist should raise an error
  223. with pytest.raises(SystemExit) as err:
  224. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo", "--restore"]):
  225. _MigrateCLI.parse_arguments()
  226. assert err.value.code == 1
  227. assert f"The backup folder '{mongo_backup_path}' does not exist." in caplog.text
  228. assert not os.path.exists(mongo_backup_path)
  229. # Run without --skip-backup to create the backup database
  230. with pytest.raises(Exception):
  231. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo"]):
  232. _MigrateCLI.parse_arguments()
  233. assert os.path.exists(mongo_backup_path)
  234. # Restore the backup
  235. with pytest.raises(SystemExit):
  236. with patch("sys.argv", ["prog", "migrate", "--repository-type", "mongo", "--restore"]):
  237. _MigrateCLI.parse_arguments()
  238. assert f"Restored entities from the backup folder '{mongo_backup_path}'." in caplog.text
  239. assert not os.path.exists(mongo_backup_path)
  240. def test_not_provide_valid_repository_type(caplog):
  241. _MigrateCLI.create_parser()
  242. with pytest.raises(SystemExit):
  243. with patch("sys.argv", ["prog", "migrate"]):
  244. _MigrateCLI.parse_arguments()
  245. assert "the following arguments are required: --repository-type" in caplog.text
  246. with pytest.raises(SystemExit):
  247. with patch("sys.argv", ["prog", "migrate", "--repository-type"]):
  248. _MigrateCLI.parse_arguments()
  249. assert "argument --repository-type: expected at least one argument" in caplog.text
  250. with pytest.raises(SystemExit):
  251. with patch("sys.argv", ["prog", "migrate", "--repository-type", "invalid-repository-type"]):
  252. _MigrateCLI.parse_arguments()
  253. assert "Unknown repository type invalid-repository-type" in caplog.text