test_config_io.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. """Tests d'intégration pour l'export et l'import de configuration."""
  2. import io
  3. import json
  4. def _payload(**overrides):
  5. """Payload d'import minimal valide."""
  6. base = {
  7. "version": 1,
  8. "jobs": [],
  9. "destinations": [],
  10. "remote_instances": [],
  11. "settings": {},
  12. }
  13. base.update(overrides)
  14. return base
  15. def _job_data(**overrides):
  16. """Données d'un job valide pour l'import."""
  17. base = {
  18. "name": "Mon job",
  19. "type": "ynh_system",
  20. "config_json": "{}",
  21. "cron_expr": "",
  22. "retention_mode": "count",
  23. "retention_value": 7,
  24. "retention_gfs_config": None,
  25. "enabled": True,
  26. "core_only": False,
  27. "destination_name": None,
  28. "remote_instance_name": None,
  29. }
  30. base.update(overrides)
  31. return base
  32. def _dest_data(**overrides):
  33. base = {
  34. "name": "VPS-OVH",
  35. "host": "vps.example.com",
  36. "port": 22,
  37. "user": "backup",
  38. "remote_path": "/backups",
  39. "key_name": None,
  40. "enabled": True,
  41. }
  42. base.update(overrides)
  43. return base
  44. def _do_import(client, payload):
  45. raw = json.dumps(payload).encode()
  46. return client.post(
  47. "/settings/import-config",
  48. data={"config_file": (io.BytesIO(raw), "config.json")},
  49. content_type="multipart/form-data",
  50. )
  51. # ---------------------------------------------------------------------------
  52. # Export
  53. # ---------------------------------------------------------------------------
  54. class TestExportConfig:
  55. def test_db_vide_retourne_json_valide(self, client):
  56. resp = client.get("/settings/export-config")
  57. assert resp.status_code == 200
  58. assert "application/json" in resp.content_type
  59. data = resp.get_json()
  60. assert data["version"] == 1
  61. assert data["instance_name"] == "test"
  62. assert data["jobs"] == []
  63. assert data["destinations"] == []
  64. assert data["remote_instances"] == []
  65. def test_exporte_jobs(self, client, app):
  66. with app.app_context():
  67. from db import db, Job
  68. job = Job(
  69. name="Sys backup", type="ynh_system", config_json="{}",
  70. cron_expr="0 3 * * *", retention_mode="count",
  71. retention_value=5, enabled=True, core_only=False,
  72. )
  73. db.session.add(job)
  74. db.session.commit()
  75. resp = client.get("/settings/export-config")
  76. data = resp.get_json()
  77. assert len(data["jobs"]) == 1
  78. j = data["jobs"][0]
  79. assert j["name"] == "Sys backup"
  80. assert j["type"] == "ynh_system"
  81. assert j["retention_value"] == 5
  82. assert j["destination_name"] is None
  83. def test_exporte_job_avec_destination(self, client, app):
  84. with app.app_context():
  85. from db import db, Job, Destination
  86. dest = Destination(
  87. name="VPS-OVH", host="vps.example.com", port=22,
  88. user="backup", remote_path="/backups", enabled=True,
  89. )
  90. db.session.add(dest)
  91. db.session.flush()
  92. job = Job(
  93. name="Job avec dest", type="ynh_system", config_json="{}",
  94. cron_expr="", retention_mode="count", retention_value=7,
  95. enabled=True, core_only=False, destination_id=dest.id,
  96. )
  97. db.session.add(job)
  98. db.session.commit()
  99. resp = client.get("/settings/export-config")
  100. data = resp.get_json()
  101. j = data["jobs"][0]
  102. assert j["destination_name"] == "VPS-OVH"
  103. def test_nom_fichier_dans_header(self, client):
  104. resp = client.get("/settings/export-config")
  105. cd = resp.headers.get("Content-Disposition", "")
  106. assert "backupmanager_config_" in cd
  107. assert ".json" in cd
  108. # ---------------------------------------------------------------------------
  109. # Import
  110. # ---------------------------------------------------------------------------
  111. class TestImportConfig:
  112. def test_cree_job(self, client, app):
  113. resp = _do_import(client, _payload(jobs=[_job_data(name="Nouveau job")]))
  114. assert resp.status_code == 302
  115. with app.app_context():
  116. from db import Job
  117. jobs = Job.query.all()
  118. assert len(jobs) == 1
  119. assert jobs[0].name == "Nouveau job"
  120. def test_met_a_jour_job_existant(self, client, app):
  121. with app.app_context():
  122. from db import db, Job
  123. job = Job(
  124. name="Mon job", type="ynh_system", config_json="{}",
  125. cron_expr="", retention_mode="count", retention_value=3,
  126. enabled=True, core_only=False,
  127. )
  128. db.session.add(job)
  129. db.session.commit()
  130. resp = _do_import(client, _payload(jobs=[_job_data(retention_value=10)]))
  131. assert resp.status_code == 302
  132. with app.app_context():
  133. from db import Job
  134. jobs = Job.query.all()
  135. assert len(jobs) == 1
  136. assert jobs[0].retention_value == 10
  137. def test_cree_destination(self, client, app):
  138. resp = _do_import(client, _payload(destinations=[_dest_data()]))
  139. assert resp.status_code == 302
  140. with app.app_context():
  141. from db import Destination
  142. dests = Destination.query.all()
  143. assert len(dests) == 1
  144. assert dests[0].name == "VPS-OVH"
  145. assert dests[0].host == "vps.example.com"
  146. def test_lie_job_a_destination(self, client, app):
  147. payload = _payload(
  148. jobs=[_job_data(destination_name="VPS-OVH")],
  149. destinations=[_dest_data()],
  150. )
  151. _do_import(client, payload)
  152. with app.app_context():
  153. from db import Job, Destination
  154. dest = Destination.query.filter_by(name="VPS-OVH").first()
  155. job = Job.query.filter_by(name="Mon job").first()
  156. assert job.destination_id == dest.id
  157. def test_cree_instance_distante(self, client, app):
  158. payload = _payload(remote_instances=[{
  159. "name": "Tom", "url": "https://tom.example.com", "api_key": "secret123",
  160. }])
  161. _do_import(client, payload)
  162. with app.app_context():
  163. from db import RemoteInstance
  164. instances = RemoteInstance.query.all()
  165. assert len(instances) == 1
  166. assert instances[0].name == "Tom"
  167. assert instances[0].url == "https://tom.example.com"
  168. def test_importe_retention_gfs(self, client, app):
  169. gfs = {"daily": 7, "weekly": 4, "monthly": 12}
  170. payload = _payload(jobs=[_job_data(
  171. retention_mode="gfs",
  172. retention_value=0,
  173. retention_gfs_config=json.dumps(gfs),
  174. )])
  175. _do_import(client, payload)
  176. with app.app_context():
  177. from db import Job
  178. job = Job.query.first()
  179. assert job.retention_mode == "gfs"
  180. cfg = json.loads(job.retention_gfs_config)
  181. assert cfg == gfs
  182. def test_json_invalide_redirige_avec_erreur(self, client):
  183. resp = client.post(
  184. "/settings/import-config",
  185. data={"config_file": (io.BytesIO(b"pas du json"), "config.json")},
  186. content_type="multipart/form-data",
  187. )
  188. assert resp.status_code == 302
  189. def test_version_incorrecte_rejete(self, client, app):
  190. payload = _payload()
  191. payload["version"] = 99
  192. _do_import(client, payload)
  193. with app.app_context():
  194. from db import Job
  195. assert Job.query.count() == 0
  196. def test_import_idempotent(self, client, app):
  197. payload = _payload(jobs=[_job_data()])
  198. _do_import(client, payload)
  199. _do_import(client, payload)
  200. with app.app_context():
  201. from db import Job
  202. assert Job.query.count() == 1
  203. def test_sans_fichier_redirige(self, client):
  204. resp = client.post("/settings/import-config", data={})
  205. assert resp.status_code == 302